CA_DELETED = 202,
CA_BAD_REQ = 400,
CA_BAD_OPT = 402,
- CA_NOT_FOUND = 404
- /* Response status code - END HERE */
+ CA_NOT_FOUND = 404,
+ CA_RETRANSMIT_TIMEOUT = 500
+ /* Response status code - END HERE */
} CAResponseResult_t;
/**
*/
typedef struct
{
- unsigned char clientIdentity[DTLS_PSK_ID_LEN];
- unsigned char rsClientPsk[DTLS_PSK_PSK_LEN];
+ unsigned char clientIdentity[DTLS_PSK_ID_LEN];
+ unsigned char rsClientPsk[DTLS_PSK_PSK_LEN];
} CADtlsPskCreds_t;
/**
*/
typedef struct
{
- uint16_t blobVer; /**< version of the blob */
- uint16_t reserved; /**< reserved for future use */
- unsigned char rsIdentity[DTLS_PSK_ID_LEN]; /**< identity of self */
- uint32_t num; /**< number of credentials in this blob */
- CADtlsPskCreds_t *creds; /**< list of credentials. Size of this
+ uint16_t blobVer; /**< version of the blob */
+ uint16_t reserved; /**< reserved for future use */
+ unsigned char rsIdentity[DTLS_PSK_ID_LEN]; /**< identity of self */
+ uint32_t num; /**< number of credentials in this blob */
+ CADtlsPskCreds_t *creds; /**< list of credentials. Size of this
array is determined by 'num' variable. */
} CADtlsPskCredsBlob_t;
* @brief Create a Remote endpoint if the URI is available already.
* for FindResource-> unicast requests , this API is used.
* FindResource(URI)-> CACreateRemoteEndpoint , CASendRequest(GET)
- * @param uri [IN] Absolute URI of the resource to be used to generate the Remote endpoint
+ * @param uri [IN] Absolute URI of the resource to be used to generate the
+ * Remote endpoint
* for ex : coap://10.11.12.13:4545/resource_uri ( for IP)
* coap://10:11:12:13:45:45/resource_uri ( for BT)
+ * @param type [IN] connectivity type of the endpoint
* @param object [OUT ] Endpoint object which contains the above parsed data
* @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
*/
-CAResult_t CACreateRemoteEndpoint(const CAURI_t uri, CARemoteEndpoint_t **object);
+CAResult_t CACreateRemoteEndpoint(const CAURI_t uri,
+ const CAConnectivityType_t connectivityType, CARemoteEndpoint_t **object);
/**
* @brief API Destroy the remote endpoint created
#Assumes ndk directory is at ~/
#set ndk directory PATH in .bashrc and use the ndk-build directly
+#change NDK_PATH to ${your_ndk_path}
NDK_PATH = ~/ndk
NDK_BUILD = $(NDK_PATH)/ndk-build
$(NDK_BUILD) NDK_PROJECT_PATH=$(APPLICATION_BUILD)
clean :
- @$(RM) -rf $(LIBS_DIR) $(OBJ_DIR)
\ No newline at end of file
+ @$(RM) -rf $(LIBS_DIR) $(OBJ_DIR)
BUILD_FLAG = $(BUILD_FLAG.$(BUILD))
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-#Build glib
+#include glib
+
include $(CLEAR_VARS)
+LOCAL_PATH = $(PROJECT_LIB_PATH)/android
+LOCAL_MODULE = Glib
+LOCAL_SRC_FILES := libglib-2.0.so
+LOCAL_EXPORT_C_INCLUDES = $(PROJECT_LIB_PATH)/android/glib-master \
+ $(PROJECT_LIB_PATH)/android/glib-master/android
+include $(PREBUILT_SHARED_LIBRARY)
-include $(PROJECT_LIB_PATH)/android/glib-master/Android.mk
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+#include glibthread
+
+include $(CLEAR_VARS)
+LOCAL_PATH = $(PROJECT_LIB_PATH)/android
+LOCAL_MODULE = GLibThread
+LOCAL_SRC_FILES := libgthread-2.0.so
+LOCAL_EXPORT_C_INCLUDES = $(PROJECT_LIB_PATH)/android/glib-master \
+ $(PROJECT_LIB_PATH)/android/glib-master/android
+
+include $(PREBUILT_SHARED_LIBRARY)
+#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#Build CACommon
+
include $(CLEAR_VARS)
#Build Common Libraries
LOCAL_MODULE = CACommon
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
-LOCAL_SHARED_LIBRARIES = glib-2.0 gthread-2.0
-
+LOCAL_SHARED_LIBRARIES = Glib GLibThread
LOCAL_CFLAGS = -D__ANDROID__ $(DEBUG_FLAG)
LOCAL_C_INCLUDES = $(PROJECT_COMMON_INC_PATH)
LOCAL_C_INCLUDES += $(PROJECT_API_PATH)
-LOCAL_C_INCLUDES += $(PROJECT_LIB_PATH)/android/glib-master \
- $(PROJECT_LIB_PATH)/android/glib-master/android
LOCAL_SRC_FILES = oic_logger.c \
oic_console_logger.c logger.c oic_malloc.c \
LOCAL_C_INCLUDES += $(PROJECT_INC_PATH)
LOCAL_C_INCLUDES += $(PROJECT_LIB_PATH)/libcoap-4.1.1
-LOCAL_C_INCLUDES += $(PROJECT_LIB_PATH)/android/glib-master \
- $(PROJECT_LIB_PATH)/android/glib-master/android
LOCAL_CFLAGS += $(BUILD_FLAG)
caconnectivitymanager.c caremotehandler.c cainterfacecontroller.c camessagehandler.c \
canetworkconfigurator.c caprotocolmessage.c caretransmission.c \
caqueueingthread.c \
- $(ADAPTER_UTILS)/caadapterutils.c $(ADAPTER_UTILS)/camessagequeue.c \
+ $(ADAPTER_UTILS)/caadapterutils.c \
$(ADAPTER_UTILS)/camsgparser.c \
$(EDR_ADAPTER_PATH)/caedradapter.c \
- $(LE_ADAPTER_PATH)/caleadapter.c $(LE_ADAPTER_PATH)/caleclient.c $(LE_ADAPTER_PATH)/caleserver.c \
+ $(LE_ADAPTER_PATH)/caleadapter.c $(LE_ADAPTER_PATH)/caleclient.c $(LE_ADAPTER_PATH)/caleserver.c $(LE_ADAPTER_PATH)/caleutils.c $(LE_ADAPTER_PATH)/calenwmonitor.c \
wifi_adapter/cawifiadapter.c $(WIFI_ADAPTER_PATH)/cawifiserver.c \
$(WIFI_ADAPTER_PATH)/cawificlient.c $(WIFI_ADAPTER_PATH)/cawifinwmonitor.c
APP_PROJECT_PATH = ./
APP_STL = gnustl_shared
-
+APP_MODULES := CA
APP_PLATFORM = android-19
APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -frtti += -Wno-error=format-security
2) Arduino builds are dependent on latest Time and TimedAction libraries. Download it from here:
I. http://playground.arduino.cc/Code/Time
II.http://playground.arduino.cc/Code/TimedAction
-3) Go to "connectivity\build\arduino" directory, Open Makefile and change ARDUINO_PORT according to connected PORT
-4) BUILD : Go to "connectivity\build" directory. To build a specific TRANSPORT [ETHERNET,BLE,WIFI], use the following command :
+3) For TimedAction library. Apply the following patch.
+ (Also the updated library has been pushed for reference at : connectivity\lib\extlibs\TimedAction)
+ TimedAction.h Line 33 : Replace "WProgram.h" with "Arduino.h"
+4) Go to "connectivity\build\arduino" directory, Open Makefile and change ARDUINO_PORT according to connected PORT
+5) BUILD : Go to "connectivity\build" directory. To build a specific TRANSPORT [ETHERNET,BLE,WIFI], use the following command :
sudo make PLATFORM=arduinomega TRANSPORT=<ETHERNET/BLE/WIFI> ARDUINO_DIR = <PATH_TO_ARDUINO_INSTALL_DIR>/arduino-1.5.x
Note: Prior to building for BLE Transport, following additional steps are required:
-- Copy the directory "connectivity\src\bt_le_adapter\arduino\external\RBL_nRF8001" to "arduino-<x.x.x>/library" folder
-- Download BLE library from internet and place it in "arduino-<x.x.x>/library" folder
+1) Copy the directory "connectivity\src\bt_le_adapter\arduino\external\RBL_nRF8001" to "arduino-<x.x.x>/library" folder
+2) Download BLE library zip file from the link(https://github.com/NordicSemiconductor/ble-sdk-arduino)
+ Extract zip file and copy the "BLE" folder available under "libraries" folder and place it in "arduino-<x.x.x>/library" folder
+3) In caleadapter.cpp, modify BLE_ADDRESS macro to Target Arduino BLE Shield Address.
+ This file is present in "connectivity/src/bt_le_adapter/arduino" folder
Flashing Interface APIs for ARDUINOMEGA:
========================================
#define OIC_LOG_V(level,tag,fmt,args...) LOG_(LOG_ID_MAIN, level, tag, fmt,##args)
#else // These macros are defined for Linux, Android, and Arduino
#define OIC_LOG_INIT() OICLogInit()
-#define OIC_LOG_BUFFER(level, tag, buffer, bufferSize) OICLogBuffer((level), (tag), (buffer), (bufferSize))
+#define OIC_LOG_BUFFER(level, tag, buffer, bufferSize)\
+ OICLogBuffer((level), (tag), (buffer), (bufferSize))
#ifdef ARDUINO
#define OIC_LOG_CONFIG(ctx)
#define OIC_LOG_SHUTDOWN()
#define OIC_LOG(level, tag, logStr) OICLog((level), (tag), __LINE__, (logStr))
-#define OIC_LOG_V(level, tag, ...)
+#define OIC_LOG_V(level, tag, ...) OICLogv((level), (tag), __LINE__, __VA_ARGS__)
#else
#define OIC_LOG_CONFIG(ctx) OICLogConfig((ctx))
#define OIC_LOG_SHUTDOWN() OICLogShutdown()
* @return CA_STATUS_OK on success.
* @return Error on failure.
*/
-CAResult_t u_thread_pool_add_task(u_thread_pool_t thread_pool, void (*routine)(void *), void *data);
+CAResult_t u_thread_pool_add_task(u_thread_pool_t thread_pool, void (*routine)(void *),
+ void *data);
/**
* This function stops all the worker threads (stop & exit). And frees all the allocated memory.
#include <avr/pgmspace.h>
#endif
+#ifndef __TIZEN__
static oic_log_ctx_t *logCtx = 0;
static oic_log_level LEVEL_XTABLE[] =
{ OIC_LOG_DEBUG, OIC_LOG_INFO, OIC_LOG_WARNING, OIC_LOG_ERROR, OIC_LOG_FATAL };
+#endif
+
static const uint16_t LINE_BUFFER_SIZE = (16 * 2) + 16 +
1; // Show 16 bytes, 2 chars/byte, spaces between bytes, null termination
}
#endif //ARDUINO
-
return CA_STATUS_OK;
}
-CAResult_t u_thread_pool_add_task(u_thread_pool_t thread_pool, void (*routine)(void *), void *data)
+CAResult_t u_thread_pool_add_task(u_thread_pool_t thread_pool, void (*routine)(void *),
+ void *data)
{
OIC_LOG(DEBUG, TAG, "IN");
* @brief This will be used during the recive of network requests and response.
* @see SendUnitcastData(), SendMulticastData()
*/
-typedef void (*CABLEClientDataReceivedCallback)(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+typedef CAResult_t (*CABLEClientDataReceivedCallback)(const char *remoteAddress, const char *serviceUUID,
+ void *data, uint32_t dataLength, uint32_t *sentLength);
/**
* @brief This will be used during the recive of network requests and response.
* @see SendUnitcastData(), SendMulticastData()
*/
-typedef void (*CABLEServerDataReceivedCallback)(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+typedef CAResult_t (*CABLEServerDataReceivedCallback)(const char *remoteAddress, const char *serviceUUID,
+ void *data, uint32_t dataLength, uint32_t *sentLength);
#ifdef __cplusplus
} /* extern "C" */
#endif
{
CAPacketReceivedCallback recvCallback;
CAPacketSendCallback sendCallback;
-}stCAAdapterCallbacks_t;
+} stCAAdapterCallbacks_t;
/**
* @struct stCADtlsContext_t
*/
void CADTLSSetAdapterCallbacks(CAPacketReceivedCallback recvCallback,
- CAPacketSendCallback sendCallback, eDtlsAdapterType_t type);
+ CAPacketSendCallback sendCallback, eDtlsAdapterType_t type);
/**
* @fn CAAdapterNetDtlsInit
OIC_LOG_V(ERROR, log_tag, "Invalid input:%s", log_message); \
return CA_STATUS_INVALID_PARAM; \
} \
-
+
/**
* @def VERIFY_NON_NULL_RET
* @brief Macro to verify the validity of input argument
OIC_LOG_V(ERROR, log_tag, "Invalid input:%s", log_message); \
return ret; \
} \
-
+
/**
* @def VERIFY_NON_NULL_VOID
* @brief Macro to verify the validity of input argument
OIC_LOG_V(ERROR, log_tag, "Invalid input:%s", log_message); \
return; \
} \
-
+
/**
* @fn CAAdapterCreateLocalEndpoint
* @brief Create CALocalConnectivity_t instance.
* @pre Callback must be registered using CAEthernetSetPacketReceiveCallback()
*/
typedef void (*CAEthernetPacketReceivedCallback)(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength, const CABool_t isSecured);
+ const void *data, const uint32_t dataLength, const CABool_t isSecured);
/**
* @fn CAEthernetExceptionCallback
* @retval #CA_STATUS_FAILED Operation failed
*/
CAResult_t CAEthernetStartMulticastServer(const char *localAddress, const char *multicastAddress,
- const int16_t multicastPort, int32_t *serverFD);
+ const int16_t multicastPort, int32_t *serverFD);
/**
* @fn CAEthernetStartUnicastServer
* @retval #CA_STATUS_FAILED Operation failed
*/
CAResult_t CAEthernetGetUnicastServerInfo(const bool secure, char **ipAddress, int16_t *port,
- int32_t *serverFD);
+ int32_t *serverFD);
/**
* @fn CAEthernetSetPacketReceiveCallback
* @pre Callback must be registered using CAEthernetSetPacketReceiveCallback()
*/
typedef void (*CAEthernetPacketReceivedCallback)(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength);
+ const void *data, const uint32_t dataLength);
/**
* @fn CAEthernetExceptionCallback
* @retval #CA_STATUS_FAILED Operation failed
*/
CAResult_t CAEthernetStartMulticastServer(const char *localAddress, const char *multicastAddress,
- const int16_t multicastPort, int32_t *serverFD);
+ const int16_t multicastPort, int32_t *serverFD);
/**
* @fn CAEthernetStartUnicastServer
typedef void (*CAEthernetConnectionStateChangeCallback)(const char *ipAddress,
const CANetworkStatus_t status);
- /**
- * @fn CAEthernetInitializeServer
- * @brief API to initialize Ethernet server
- *
- * @return #CA_STATUS_OK on success otherwise proper error code.
- * @retval #CA_STATUS_OK Successful
- * @retval #CA_STATUS_FAILED Initialization failed
- */
+/**
+* @fn CAEthernetInitializeServer
+* @brief API to initialize Ethernet server
+*
+* @return #CA_STATUS_OK on success otherwise proper error code.
+* @retval #CA_STATUS_OK Successful
+* @retval #CA_STATUS_FAILED Initialization failed
+*/
CAResult_t CAEthernetInitializeNetworkMonitor(void);
/**
void CATerminateLE();
CAResult_t CABLEServerReceivedData(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+ void *data, uint32_t dataLength, uint32_t *sentLength);
CAResult_t CABLEClientReceivedData(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+ void *data, uint32_t dataLength, uint32_t *sentLength);
void CASetBLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback);
CAResult_t CABLEServerSendData(const CARemoteEndpoint_t *remoteEndpoint,
- void *data, uint32_t dataLen);
+ void *data, uint32_t dataLen);
CAResult_t CABLEClientSendData(const CARemoteEndpoint_t *remoteEndpoint,
- void *data, uint32_t dataLen);
+ void *data, uint32_t dataLen);
void CABLEClientSendDataThread(void *threadData);
*
*/
CABLEData *CACreateBLEData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLength);
+ uint32_t dataLength);
/**
* @fn CAFreeBLEData
void CATerminateLE();
CAResult_t CABLEServerReceivedData(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+ void *data, uint32_t dataLength, uint32_t *sentLength);
CAResult_t CABLEClientReceivedData(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+ void *data, uint32_t dataLength, uint32_t *sentLength);
void CASetBLEReqRespAdapterCallback(CANetworkPacketReceivedCallback callback);
CAResult_t CABLEServerSendData(const CARemoteEndpoint_t *remoteEndpoint,
- void *data, uint32_t dataLen);
+ void *data, uint32_t dataLen);
CAResult_t CABLEClientSendData(const CARemoteEndpoint_t *remoteEndpoint,
- void *data, uint32_t dataLen);
+ void *data, uint32_t dataLen);
void CABLEClientSendDataThread(void *threadData);
*
*/
CABLEData *CACreateBLEData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLength);
+ uint32_t dataLength);
/**
* @fn CAFreeBLEData
typedef void (*CAPacketReceiveCallback)(const char *address, const char *data);
+void CALEClientJNISetContext(JNIEnv *env, jobject context);
+
+void CALeCreateJniInterfaceObject();
+
void CALEInitialize(u_thread_pool_t handle);
void CALETerminate();
+void CANativeSendFinsih(JNIEnv *env, jobject gatt);
+
int32_t CALESendUnicastMessage(const char *address, const char *data, uint32_t dataLen);
int32_t CALESendMulticastMessage(const char *data, uint32_t dataLen);
void CANativeLEStopScanImpl(JNIEnv *env, jobject callback);
-void CANativeLEConnect(JNIEnv *env, jobject bluetoothDevice, jobject context, jboolean autoconnect,
- jobject callback);
+int32_t CANativeLEConnect(JNIEnv *env, jobject bluetoothDevice, jobject context,
+ jboolean autoconnect, jobject callback);
void CANativeLEDisconnect(JNIEnv *env, jobject bluetoothGatt);
void CANativeLEDiscoverServices(JNIEnv *env, jobject bluetoothGatt);
-void CANativeLESendData(JNIEnv *env, jobject bluetoothGatt, jobject gattCharacteristic);
+jboolean CANativeLESendData(JNIEnv *env, jobject bluetoothGatt, jobject gattCharacteristic);
-jboolean CANativeSetCharacteristicNoti(JNIEnv *env, jobject bluetoothGatt);
+void CANativeReadCharacteristic(JNIEnv *env, jobject bluetoothGatt);
-jobject CANativeCreateGattCharacteristic(JNIEnv *env, jobject bluetoothGatt, jstring data);
+jboolean CANativeSetCharacteristicNoti(JNIEnv *env, jobject bluetoothGatt, const char* uuid);
+
+jobject CANativeCreateGattCharacteristic(JNIEnv *env, jobject bluetoothGatt, jbyteArray data);
jobject CANativeGetGattService(JNIEnv *env, jobject bluetoothGatt, jstring characterUUID);
jbyteArray CANativeGetValueFromCharacteristic(JNIEnv *env, jobject characteristic);
+void CANativeCreateUUIDList();
/**
* BluetoothDevice List
*/
void CAReorderingGattList(uint32_t index);
+void CANativeupdateSendCnt(JNIEnv *env);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
* @retval CA_STATUS_INVALID_PARAM Invalid input argumets
* @retval CA_STATUS_FAILED Operation failed
*/
-CAResult_t CAUpdateCharacteristicsInGattServer(const char *charValue, const uint32_t charValueLen);
+CAResult_t CAUpdateCharacteristicsInGattServer(const char *charValue,
+ const uint32_t charValueLen);
/**
* @fn CAStartBLEGattClient
* @retval CA_STATUS_INVALID_PARAM Invalid input argumets
* @retval CA_STATUS_FAILED Operation failed
*/
-CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data, const int32_t dataLen);
+CAResult_t CAUpdateCharacteristicsToAllGattServers(const char *data,
+ const int32_t dataLen);
/**
* @fn CASetBLEReqRespClientCallback
--- /dev/null
+/******************************************************************
+*
+* Copyright 2014 Samsung Electronics All Rights Reserved.
+*
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/
+
+/**
+* @file calenwmonitor.h
+* @brief This file contains the APIs for BT LE communications.
+*/
+#ifndef __CA_LENWMONITOR_H_
+#define __CA_LENWMONITOR_H_
+
+#include "cacommon.h"
+#include "uthreadpool.h"
+#include "uarraylist.h"
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void CALENetworkMonitorJNISetContext(JNIEnv *env, jobject context);
+
+void CALeNetworkMonitorJniInit(JNIEnv *env, JavaVM *jvm);
+
+void CALEStartNetworkMonitor(JNIEnv *env, jobject obj);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
+
--- /dev/null
+/******************************************************************
+*
+* Copyright 2014 Samsung Electronics All Rights Reserved.
+*
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/
+
+/**
+* @file calecore.h
+* @brief This file contains the APIs for BT LE communications.
+*/
+#ifndef __CA_LECORE_H_
+#define __CA_LECORE_H_
+
+#include "cacommon.h"
+#include "uthreadpool.h"
+#include "uarraylist.h"
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef void (*CAPacketReceiveCallback)(const char *address,
+ const char *data);
+
+void CALEServerInitialize(u_thread_pool_t handle);
+
+void CALEServerTerminate();
+
+int32_t CALEServerSendUnicastMessage(const char *address, const char *data,
+ uint32_t dataLen);
+
+int32_t CALEServerSendMulticastMessage(const char *data, uint32_t dataLen);
+
+int32_t CALEServerStartUnicastServer(const char *address);
+
+int32_t CALEServerStartMulticastServer();
+
+int32_t CALEServerStopUnicastServer(int32_t serverID);
+
+int32_t CALEServerStopMulticastServer(int32_t serverID);
+
+void CALEServerSetCallback(CAPacketReceiveCallback callback);
+
+void CALEServerGetInterfaceInfo(CALocalConnectivity_t **info,
+ uint32_t *size);
+
+void CALEServerGetLocalAddress(char *address);
+
+int32_t CALEServerSendUnicastMessageImpl(JNIEnv *env, const char *address,
+ const char *data, uint32_t dataLen);
+
+int32_t CALEServerSendMulticastMessageImpl(JNIEnv *env, const char *data,
+ uint32_t dataLen);
+
+/* BLE Server Functions */
+
+void CALEServerJNISetContext(JNIEnv *env, jobject context);
+
+void CALeServerJniInit(JNIEnv *env, JavaVM* jvm);
+
+void CALeServerCreateJniInterfaceObject();
+
+void LEServerStartAdvertise(JNIEnv *env, jobject advertiseCallback);
+
+jobject CALEServerOpenGattServer(JNIEnv *env);
+
+jobject CALEServerCreateGattService(JNIEnv *env);
+
+jboolean CALEServerAddGattService(JNIEnv *env, jobject bluetoothGattServer,
+ jobject bluetoothGattService);
+
+jboolean CALEStartGattServer(JNIEnv *env, jobject gattServerCallback);
+
+jboolean CALEServerSend(JNIEnv *env, jobject bluetoothDevice, jstring data);
+
+jobject CALEServerSetResponseData(JNIEnv *env, jbyteArray responseData);
+
+jboolean CALEServerSendResponseData(JNIEnv *env, jobject device, jobject responseData);
+
+jboolean CALEServerSendResponse(JNIEnv *env, jobject device, jint requestId, jint status,
+ jint offset, jbyteArray value);
+
+jboolean CALEServerConnect(JNIEnv *env, jobject bluetoothDevice);
+
+void CALEServerDisconnect(JNIEnv *env, jobject bluetoothDevice);
+
+/* BLE Server Utils */
+void CALEServerCreateCachedDeviceList();
+
+jboolean CALEServerIsDeviceInList(JNIEnv *env, const char* remoteAddress);
+
+void CALEServerAddDeviceToList(JNIEnv *env, jobject device);
+
+void CALEServerRemoveAllDevices(JNIEnv *env);
+
+void CALEServerRemoveDevice(JNIEnv *env, jstring address);
+
+//void CALEServerReorderinglist(uint32_t index);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
--- /dev/null
+/******************************************************************
+*
+* Copyright 2014 Samsung Electronics All Rights Reserved.
+*
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+******************************************************************/
+
+/**
+* @file caleutils.h
+* @brief This file contains the APIs for BT LE communications.
+*/
+#ifndef __CA_LEUTILES_H_
+#define __CA_LEUTILES_H_
+
+#include "cacommon.h"
+#include "uthreadpool.h"
+#include "uarraylist.h"
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+jobject CALEGetUuidFromString(JNIEnv *env, const char* uuid);
+
+jobject CALEGetParcelUuid(JNIEnv *env, jobject uuid);
+
+jstring CALEGetAddressFromBTDevice(JNIEnv *env, jobject bluetoothDevice);
+
+jstring CALEGetLocalDeviceAddress(JNIEnv* env);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
* @param RespHandler [IN] callback for receiving the response
* @return void
*/
-void CASetRequestResponseCallbacks(CARequestCallback ReqHandler, CAResponseCallback RespHandler);
+void CASetRequestResponseCallbacks(CARequestCallback ReqHandler,
+ CAResponseCallback RespHandler);
/**
- * @brief Initialize the message handler by starting thread pool and initializing the send and reive queue
+ * @brief Initialize the message handler by starting thread pool and initializing the
+ * send and reive queue
* @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
*/
CAResult_t CAInitializeMessageHandler();
* data segment.
* @param[in] data Pointer to the Complete data which needs to be fragmented.
* @param[in] TotalLen Total length of the data sent from the CA layer. This will be
-* decremented everytime when we call this function for the subsequent
-* time withthe MTU size.
+* decremented everytime when we call this function for the
+* subsequent time withthe MTU size.
* @param[in] Offset Initially this value will be zero. Later this value will be represented
* as the amount of data which has already been fragmented.
*
* @return -1 On error condition.
* @retval Returns the length of the data which has been fragmented and sent to the upper layer.
*/
-uint32_t CAFragmentData(const char *data, char **dataSegment, uint32_t TotalLen, uint32_t offset);
+uint32_t CAFragmentData(const char *data, char **dataSegment, uint32_t TotalLen,
+ uint32_t offset);
#ifdef __cplusplus
} /* extern "C" */
* @param size [IN] size of pdu data
* @return message type
*/
-CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void* pdu, uint32_t size);
+CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void *pdu, uint32_t size);
/**
* @brief gets message ID PDU binary data
* @param size [IN] size of pdu data
* @return message ID
*/
-uint16_t CAGetMessageIdFromPduBinaryData(const void* pdu, uint32_t size);
+uint16_t CAGetMessageIdFromPduBinaryData(const void *pdu, uint32_t size);
#ifdef __cplusplus
} /* extern "C" */
* @param size [IN] size of pdu data
* @return message type
*/
-CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void* pdu, uint32_t size);
+CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void *pdu, uint32_t size);
/**
* @brief gets message ID PDU binary data
* @param size [IN] size of pdu data
* @return message ID
*/
-uint16_t CAGetMessageIdFromPduBinaryData(const void* pdu, uint32_t size);
+uint16_t CAGetMessageIdFromPduBinaryData(const void *pdu, uint32_t size);
#ifdef __cplusplus
} /* extern "C" */
/**
* @brief Creates a new remote endpoint from the input uri
* @param uri [IN] absolute uri information to create remote endpoint
+ * @param type [IN] connectivity type of the endpoint
* @return remote endpoint created
*/
-CARemoteEndpoint_t *CACreateRemoteEndpointUriInternal(const CAURI_t uri);
+CARemoteEndpoint_t *CACreateRemoteEndpointUriInternal(const CAURI_t uri,
+ const CAConnectivityType_t connectivityType);
/**
* @brief Creates a new remote endpoint from the input and other information
/** CA_ETHERNET, CA_WIFI **/
#define DEFAULT_RETRANSMISSION_TYPE ((1<<0)|(1<<1))
-/** default retransmission trying count is 4. **/
-#define DEFAULT_RETRANSMISSION_COUNT 4
+/** default ACK time is 2 sec.(CoAP) **/
+#define DEFAULT_ACK_TIMEOUT 2
+
+/** default max retransmission trying count is 4.(CoAP) **/
+#define DEFAULT_MAX_RETRANSMIT 4
/** check period is 1 sec. **/
#define RETRANSMISSION_CHECK_PERIOD 1000000
/** retransmission data send method type**/
-typedef CAResult_t (*CADataSendMethod_t)(const CARemoteEndpoint_t *endpoint, void *data,
- uint32_t length);
+typedef CAResult_t (*CADataSendMethod_t)(const CARemoteEndpoint_t *endpoint, void *pdu,
+ uint32_t size);
+
+/** retransmission timeout callback type**/
+typedef void (*CATimeoutCallback_t)(const CARemoteEndpoint_t *endpoint, void *pdu, uint32_t size);
typedef struct
{
u_cond threadCond;
/** send method for retransmission data **/
CADataSendMethod_t dataSendMethod;
+ /** callback function for retransmit timeout **/
+ CATimeoutCallback_t timeoutCallback;
/** retransmission configure data **/
CARetransmissionConfig_t config;
/** Variable to inform the thread to stop **/
CABool_t isStop;
/** array list on which the thread is operating. **/
- u_arraylist_t* dataList;
+ u_arraylist_t *dataList;
} CARetransmission_t;
#ifdef __cplusplus
{
#endif
-CAResult_t CARetransmissionInitialize(CARetransmission_t* context, u_thread_pool_t handle,
- CADataSendMethod_t retransmissionSendMethod, CARetransmissionConfig_t* config);
+/**
+ * @brief Initializes the retransmission context
+ * @param context [IN]context for retransmission
+ * @param handle [IN]thread pool handle
+ * @param retransmissionSendMethod [IN]function to be called for retransmission
+ * @param timeoutCallback [IN]callback for retransmit timeout
+ * @param config [IN]configuration for retransmission. if NULL is coming, it will set default values.
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
+ CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
+ CARetransmissionConfig_t* config);
-CAResult_t CARetransmissionStart(CARetransmission_t* context);
+/**
+ * @brief Starting the retransmission context
+ * @param context [IN]context for retransmission
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionStart(CARetransmission_t *context);
-CAResult_t CARetransmissionSentData(CARetransmission_t* context, const CARemoteEndpoint_t* endpoint,
- const void* pdu, uint32_t size);
+/**
+ * @brief Pass the sent pdu data. if retransmission process need, internal thread will wake up and
+ * process the retransmission data.
+ * @param context [IN]context for retransmission
+ * @param endpoint [IN]endpoint information
+ * @param pdu [IN]sent pdu binary data
+ * @param size [IN]sent pdu binary data size
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionSentData(CARetransmission_t* context,
+ const CARemoteEndpoint_t* endpoint,const void* pdu, uint32_t size);
-CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size);
+/**
+ * @brief Paas the received pdu data. if received pdu is ACK data for the retransmission CON data,
+ * the specified CON data will remove on retransmission list.
+ * @param context [IN]context for retransmission
+ * @param endpoint [IN]endpoint information
+ * @param pdu [IN]received pdu binary data
+ * @param size [IN]received pdu binary data size
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint, const void *pdu, uint32_t size);
-CAResult_t CARetransmissionStop(CARetransmission_t* context);
+/**
+ * @brief Stopping the retransmission context
+ * @param context [IN]context for retransmission
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionStop(CARetransmission_t *context);
-CAResult_t CARetransmissionDestroy(CARetransmission_t* context);
+/**
+ * @brief Terminating the retransmission context
+ * @param context [IN]context for retransmission
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionDestroy(CARetransmission_t *context);
#ifdef __cplusplus
} /* extern "C" */
#include "uarraylist.h"
#include "cacommon.h"
-/** CA_ETHERNET, CA_WIFI **/
-#define DEFAULT_RETRANSMISSION_TYPE ((1<<0)|(1<<1))
+/** CA_ETHERNET, CA_WIFI, CA_LE **/
+#define DEFAULT_RETRANSMISSION_TYPE ((1<<0)|(1<<1)|(1<<3))
/** default retransmission trying count is 4. **/
#define DEFAULT_RETRANSMISSION_COUNT 4
#define RETRANSMISSION_CHECK_PERIOD 1000000
/** retransmission data send method type**/
-typedef CAResult_t (*CADataSendMethod_t)(const CARemoteEndpoint_t *endpoint, void *data,
- uint32_t length);
+typedef CAResult_t (*CADataSendMethod_t)(const CARemoteEndpoint_t *endpoint, void *pdu,
+ uint32_t size);
+
+/** retransmission timeout callback type**/
+typedef void (*CATimeoutCallback_t)(const CARemoteEndpoint_t *endpoint, void *pdu,
+ uint32_t size);
typedef struct
{
{
/** send method for retransmission data **/
CADataSendMethod_t dataSendMethod;
+ /** callback function for retransmit timeout **/
+ CATimeoutCallback_t timeoutCallback;
/** retransmission configure data **/
CARetransmissionConfig_t config;
/** Variable to inform the thread to stop **/
CABool_t isStop;
/** array list on which the thread is operating. **/
- u_arraylist_t* dataList;
+ u_arraylist_t *dataList;
} CARetransmission_t;
#ifdef __cplusplus
{
#endif
+/**
+ * @brief Initializes the retransmission context
+ * @param context [IN]context for retransmission
+ * @param handle [IN]thread pool handle
+ * @param retransmissionSendMethod [IN]function to be called for retransmission
+ * @param timeoutCallback [IN]callback for retransmit timeout
+ * @param config [IN]configuration for retransmission. if NULL is coming, it will set default values.
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
CAResult_t CARetransmissionInitialize(CARetransmission_t* context,
CADataSendMethod_t retransmissionSendMethod,
+ CATimeoutCallback_t timeoutCallback,
CARetransmissionConfig_t* config);
-CAResult_t CARetransmissionSentData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint,
- const void* pdu, uint32_t size);
+/**
+ * @brief Pass the sent pdu data. if retransmission process need, internal thread will wake up and
+ * process the retransmission data.
+ * @param context [IN]context for retransmission
+ * @param endpoint [IN]endpoint information
+ * @param pdu [IN]sent pdu binary data
+ * @param size [IN]sent pdu binary data size
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionSentData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint,
+ const void *pdu, uint32_t size);
-CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size);
+/**
+ * @brief Paas the received pdu data. if received pdu is ACK data for the retransmission CON data,
+ * the specified CON data will remove on retransmission list.
+ * @param context [IN]context for retransmission
+ * @param endpoint [IN]endpoint information
+ * @param pdu [IN]received pdu binary data
+ * @param size [IN]received pdu binary data size
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint, const void *pdu, uint32_t size);
-CAResult_t CARetransmissionStop(CARetransmission_t* context);
+/**
+ * @brief Stopping the retransmission context
+ * @param context [IN]context for retransmission
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionStop(CARetransmission_t *context);
-CAResult_t CARetransmissionDestroy(CARetransmission_t* context);
+/**
+ * @brief Terminating the retransmission context
+ * @param context [IN]context for retransmission
+ * @return CA_STATUS_OK or ERROR CODES ( CAResult_t error codes in cacommon.h)
+ */
+CAResult_t CARetransmissionDestroy(CARetransmission_t *context);
void CACheckRetransmissionList();
CAResult_t CAWiFiGetUnicastServerInfo(const CABool_t isSecured, char **ipAddress, int16_t *port,
int32_t *serverFD);
-
/**
* @fn CAWiFiSetPacketReceiveCallback
* @brief API to set callback for receiving data packets from peer devices.
* Method: CARegisterLeScanCallback
* Signature: (Landroid/bluetooth/BluetoothAdapter/LeScanCallback;)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CARegisterLeScanCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CARegisterLeScanCallback
(JNIEnv *, jobject, jobject);
/*
* Method: CARegisterLeGattCallback
* Signature: (Landroid/bluetooth/BluetoothGattCallback;)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CARegisterLeGattCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CARegisterLeGattCallback
+(JNIEnv *, jobject, jobject);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CARegisterLeGattServerCallback
+ * Signature: (Landroid/bluetooth/BluetoothGattServerCallback;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CARegisterLeGattServerCallback
+(JNIEnv *, jobject, jobject);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CARegisterBluetoothLeAdvertiseCallback
+ * Signature: (Landroid/bluetooth/le/AdvertiseCallback;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CARegisterBluetoothLeAdvertiseCallback
(JNIEnv *, jobject, jobject);
/*
* Method: CALeScanCallback
* Signature: (Landroid/bluetooth/BluetoothDevice;I[B)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeScanCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeScanCallback
(JNIEnv *, jobject, jobject, jint, jbyteArray);
/*
* Method: CALeGattConnectionStateChangeCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;II)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattConnectionStateChangeCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattConnectionStateChangeCallback
(JNIEnv *, jobject, jobject, jint, jint);
/*
* Method: CALeGattServicesDiscoveredCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattServicesDiscoveredCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServicesDiscoveredCallback
(JNIEnv *, jobject, jobject, jint);
/*
* Method: CALeGattCharacteristicReadCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicReadCallback
-(JNIEnv *, jobject, jobject, jobject, jstring, jint);
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicReadCallback
+(JNIEnv *, jobject, jobject, jobject, jbyteArray, jint);
/*
* Class: com_iotivity_jar_CALeInterface
* Method: CALeGattCharacteristicWritjclasseCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicWriteCallback
-(JNIEnv *, jobject, jobject, jobject, jstring, jint);
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicWriteCallback
+(JNIEnv *, jobject, jobject, jobject, jbyteArray, jint);
/*
* Class: com_iotivity_jar_CALeInterface
* Method: CALeGattCharacteristicChangedCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicChangedCallback
-(JNIEnv *, jobject, jobject, jobject, jstring);
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicChangedCallback
+(JNIEnv *, jobject, jobject, jobject, jbyteArray);
/*
* Class: com_iotivity_jar_CALeInterface
* Method: CALeGattDescriptorReadCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattDescriptor;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattDescriptorReadCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattDescriptorReadCallback
(JNIEnv *, jobject, jobject, jobject, jint);
/*
* Method: CALeGattDescriptorWriteCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattDescriptor;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattDescriptorWriteCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattDescriptorWriteCallback
(JNIEnv *, jobject, jobject, jobject, jint);
/*
* Method: CALeGattReliableWriteCompletedCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;I)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattReliableWriteCompletedCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattReliableWriteCompletedCallback
(JNIEnv *, jobject, jobject, jint);
/*
* Method: CALeGattReadRemoteRssiCallback
* Signature: (Landroid/bluetooth/BluetoothGatt;II)V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattReadRemoteRssiCallback
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattReadRemoteRssiCallback
+(JNIEnv *, jobject, jobject, jint, jint);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerConnectionStateChangeCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;II)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerConnectionStateChangeCallback
(JNIEnv *, jobject, jobject, jint, jint);
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerServiceAddedCallback
+ * Signature: (ILandroid/bluetooth/BluetoothGattService;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerServiceAddedCallback
+(JNIEnv *, jobject, jint, jobject);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerCharacteristicReadRequestCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;IILandroid/
+ * bluetooth/BluetoothGattCharacteristic;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerCharacteristicReadRequestCallback
+(JNIEnv *, jobject, jobject, jint, jint, jobject, jbyteArray);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerCharacteristicWriteRequestCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;ILandroid/bluetooth/
+ * BluetoothGattCharacteristic;ZZI[B)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerCharacteristicWriteRequestCallback
+(JNIEnv *, jobject, jobject, jint, jobject, jbyteArray, jboolean, jboolean, jint, jbyteArray);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerDescriptorReadRequestCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;IILandroid/bluetooth/
+ * BluetoothGattDescriptor;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerDescriptorReadRequestCallback
+(JNIEnv *, jobject, jobject, jint, jint, jobject);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerDescriptorWriteRequestCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;ILandroid/bluetooth/
+ * BluetoothGattDescriptor;ZZI[B)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerDescriptorWriteRequestCallback
+(JNIEnv *, jobject, jobject, jint, jobject, jboolean, jboolean, jint, jbyteArray);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerExecuteWriteCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;IZ)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerExecuteWriteCallback
+(JNIEnv *, jobject, jobject, jint, jboolean);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeGattServerNotificationSentCallback
+ * Signature: (Landroid/bluetooth/BluetoothDevice;I)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerNotificationSentCallback
+(JNIEnv *, jobject, jobject, jint);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeAdvertiseStartSuccessCallback
+ * Signature: (Landroid/bluetooth/le/AdvertiseSettings;)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeAdvertiseStartSuccessCallback
+(JNIEnv *, jobject, jobject);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeAdvertiseStartFailureCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeAdvertiseStartFailureCallback
+(JNIEnv *, jobject, jint);
+
+/*
+ * Class: com_iotivity_jar_CALeInterface
+ * Method: CALeStateChangedCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeStateChangedCallback
+(JNIEnv *, jobject, jint);
+
#ifdef __cplusplus
}
#endif
* Method: CAWiFiStateEnabled
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CAWiFiInterface_CAWiFiStateEnabled
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CAWiFiInterface_CAWiFiStateEnabled
(JNIEnv *, jclass);
/*
* Method: CAWiFiStateDisabled
* Signature: ()V
*/
-JNIEXPORT void JNICALL Java_com_iotivity_jar_CAWiFiInterface_CAWiFiStateDisabled
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CAWiFiInterface_CAWiFiStateDisabled
(JNIEnv *, jclass);
#ifdef __cplusplus
+++ /dev/null
-# This file is the top android makefile for all sub-modules.
-
-LOCAL_PATH := $(call my-dir)
-
-GLIB_TOP := $(LOCAL_PATH)
-
-GLIB_BUILD_STATIC := $(BUILD_STATIC)
-
-GLIB_C_INCLUDES := \
- $(GLIB_TOP) \
- $(GLIB_TOP)/glib \
- $(GLIB_TOP)/android
-
-GTHREAD_C_INCLUDES := \
- $(GLIB_C_INCLUDES) \
- $(GLIB_TOP)/gthread
-
-#GOBJECT_C_INCLUDES := \
-# $(GLIB_C_INCLUDES) \
-# $(GLIB_TOP)/gobject \
-# $(GLIB_TOP)/gobject/android
-
-#GMODULE_C_INCLUDES := \
-# $(GLIB_C_INCLUDES) \
-# $(GLIB_TOP)/gmodule
-
-#GIO_C_INCLUDES := \
-# $(GLIB_C_INCLUDES) \
-# $(GLIB_TOP)/gio \
-# $(GLIB_TOP)/gio/android
-
-#GLIB_SHARED_LIBRARIES := \
-# libgmodule-2.0 \
-# libgobject-2.0 \
-# libgthread-2.0 \
-# libglib-2.0
-
-GLIB_SHARED_LIBRARIES := \
- libgthread-2.0 \
- libglib-2.0
-
-GLIB_STATIC_LIBRARIES := \
- $(GLIB_SHARED_LIBRARIES) \
- libpcre
-
-include $(CLEAR_VARS)
-
-
-include $(GLIB_TOP)/glib/Android.mk
-include $(GLIB_TOP)/gthread/Android.mk
-#include $(GLIB_TOP)/gobject/Android.mk
-#include $(GLIB_TOP)/gmodule/Android.mk
-#include $(GLIB_TOP)/gio/Android.mk
-#include $(GLIB_TOP)/tests/Android.mk
-
+++ /dev/null
-/* config.h. Generated from config.h.in by configure. */
-/* config.h.in. Generated from configure.in by autoheader. */
-
-/* Define if building universal (internal helper macro) */
-/* #undef AC_APPLE_UNIVERSAL_BUILD */
-
-/* define if asm blocks can use numeric local labels */
-/* #undef ASM_NUMERIC_LABELS */
-
-/* poll doesn't work on devices */
-/* #undef BROKEN_POLL */
-
-/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
- systems. This function is required for `alloca.c' support on those systems.
- */
-/* #undef CRAY_STACKSEG_END */
-
-/* Define to 1 if using `alloca.c'. */
-/* #undef C_ALLOCA */
-
-/* Whether to disable memory pools */
-#define DISABLE_MEM_POOLS
-
-/* Whether to enable GC friendliness by default */
-/* #undef ENABLE_GC_FRIENDLY_DEFAULT */
-
-/* always defined to indicate that i18n is enabled */
-/* #define ENABLE_NLS 1 */
-
-/* include GRegex */
-#define ENABLE_REGEX 1
-
-/* Define the gettext package to be used */
-#define GETTEXT_PACKAGE "glib20"
-
-/* Define to the GLIB binary age */
-#define GLIB_BINARY_AGE 2401
-
-/* Byte contents of gmutex */
-#define GLIB_BYTE_CONTENTS_GMUTEX no
-
-/* Define to the GLIB interface age */
-#define GLIB_INTERFACE_AGE 1
-
-/* Define the location where the catalogs will be installed */
-#define GLIB_LOCALE_DIR ""
-
-/* Define to the GLIB major version */
-#define GLIB_MAJOR_VERSION 2
-
-/* Define to the GLIB micro version */
-#define GLIB_MICRO_VERSION 1
-
-/* Define to the GLIB minor version */
-#define GLIB_MINOR_VERSION 24
-
-/* The size of gmutex, as computed by sizeof. */
-#define GLIB_SIZEOF_GMUTEX 24
-
-/* The size of system_thread, as computed by sizeof. */
-#define GLIB_SIZEOF_SYSTEM_THREAD 4
-
-/* alpha atomic implementation */
-/* #undef G_ATOMIC_ALPHA */
-
-/* arm atomic implementation */
-/* #undef G_ATOMIC_ARM */
-
-/* cris atomic implementation */
-/* #undef G_ATOMIC_CRIS */
-
-/* crisv32 atomic implementation */
-/* #undef G_ATOMIC_CRISV32 */
-
-/* i486 atomic implementation */
-/* #undef G_ATOMIC_I486 */
-
-/* ia64 atomic implementation */
-/* #undef G_ATOMIC_IA64 */
-
-/* powerpc atomic implementation */
-/* #undef G_ATOMIC_POWERPC */
-
-/* s390 atomic implementation */
-/* #undef G_ATOMIC_S390 */
-
-/* sparcv9 atomic implementation */
-/* #undef G_ATOMIC_SPARCV9 */
-
-/* x86_64 atomic implementation */
-/* #undef G_ATOMIC_X86_64 */
-
-/* Have inline keyword */
-#define G_HAVE_INLINE 1
-
-/* Have __inline keyword */
-#define G_HAVE___INLINE 1
-
-/* Have __inline__ keyword */
-#define G_HAVE___INLINE__ 1
-
-/* Source file containing theread implementation */
-#define G_THREAD_SOURCE "gthread-posix.c"
-
-/* A 'va_copy' style function */
-#define G_VA_COPY va_copy
-
-/* 'va_lists' cannot be copies as values */
-/* #undef G_VA_COPY_AS_ARRAY */
-
-/* Define to 1 if you have `alloca', as a function or macro. */
-#define HAVE_ALLOCA 1
-
-/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
- */
-#define HAVE_ALLOCA_H 1
-
-/* Define to 1 if you have the <arpa/nameser_compat.h> header file. */
-/* #define HAVE_ARPA_NAMESER_COMPAT_H 1 */
-
-/* Define to 1 if you have the `atexit' function. */
-#define HAVE_ATEXIT 1
-
-/* Define to 1 if you have the <attr/xattr.h> header file. */
-/* #undef HAVE_ATTR_XATTR_H */
-
-/* Define to 1 if you have the `bind_textdomain_codeset' function. */
-/* #define HAVE_BIND_TEXTDOMAIN_CODESET 1 */
-
-/* Define if you have a version of the snprintf function with semantics as
- specified by the ISO C99 standard. */
-/* #define HAVE_C99_SNPRINTF 1 */
-
-/* Define if you have a version of the vsnprintf function with semantics as
- specified by the ISO C99 standard. */
-/* #define HAVE_C99_VSNPRINTF 1 */
-
-/* define to 1 if Carbon is available */
-/* #undef HAVE_CARBON */
-
-/* Define to 1 if you have the `chown' function. */
-#define HAVE_CHOWN 1
-
-/* Define to 1 if you have the `clock_gettime' function. */
-#define HAVE_CLOCK_GETTIME 1
-
-/* Have nl_langinfo (CODESET) */
-/* #define HAVE_CODESET 1 */
-
-/* Define to 1 if you have the <crt_externs.h> header file. */
-/* #undef HAVE_CRT_EXTERNS_H */
-
-/* Define to 1 if you have the `dcgettext' function. */
-#define HAVE_DCGETTEXT 1
-
-/* Define to 1 if you have the <dirent.h> header file. */
-#define HAVE_DIRENT_H 1
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
-/* #undef HAVE_DOPRNT */
-
-/* define for working do while(0) macros */
-#define HAVE_DOWHILE_MACROS 1
-
-/* Define to 1 if you have the `endmntent' function. */
-/* #undef HAVE_ENDMNTENT */
-
-/* Define to 1 if you have the `endservent' function. */
-#define HAVE_ENDSERVENT 1
-
-/* Define if we have FAM */
-/* #undef HAVE_FAM */
-
-/* Define to 1 if you have the <fam.h> header file. */
-/* #undef HAVE_FAM_H */
-
-/* Define if we have FAMNoExists in fam */
-/* #undef HAVE_FAM_NO_EXISTS */
-
-/* Define to 1 if you have the `fchmod' function. */
-#define HAVE_FCHMOD 1
-
-/* Define to 1 if you have the `fchown' function. */
-#define HAVE_FCHOWN 1
-
-/* Define to 1 if you have the `fdwalk' function. */
-/* #undef HAVE_FDWALK */
-
-/* Define to 1 if you have the <float.h> header file. */
-#define HAVE_FLOAT_H 1
-
-/* Define to 1 if you have the <fstab.h> header file. */
-#define HAVE_FSTAB_H 1
-
-/* Define to 1 if you have the `fsync' function. */
-#define HAVE_FSYNC 1
-
-/* we have the futex(2) system call */
-/* #define HAVE_FUTEX test "$have_futex" = "yes" */
-
-/* Define to 1 if you have the `getcwd' function. */
-#define HAVE_GETCWD 1
-
-/* Define to 1 if you have the `getc_unlocked' function. */
-#define HAVE_GETC_UNLOCKED 1
-
-/* Define to 1 if you have the `getgrgid' function. */
-#define HAVE_GETGRGID 1
-
-/* Define to 1 if you have the `getmntent_r' function. */
-/* #undef HAVE_GETMNTENT_R */
-
-/* Define to 1 if you have the `getmntinfo' function. */
-/* #undef HAVE_GETMNTINFO */
-
-/* Define to 1 if you have the `getprotobyname_r' function. */
-#define HAVE_GETPROTOBYNAME_R 1
-
-/* Define to 1 if you have the `getpwuid' function. */
-#define HAVE_GETPWUID 1
-
-/* Define if the GNU gettext() function is already present or preinstalled. */
-#define HAVE_GETTEXT 1
-
-/* Define to 1 if you have the `gmtime_r' function. */
-#define HAVE_GMTIME_R 1
-
-/* define to use system printf */
-#define HAVE_GOOD_PRINTF 1
-
-/* Define to 1 if you have the <grp.h> header file. */
-#define HAVE_GRP_H 1
-
-/* Define to 1 if you have the `hasmntopt' function. */
-/* #undef HAVE_HASMNTOPT */
-
-/* Define to 1 if you have the `inotify_init1' function. */
-/* #undef HAVE_INOTIFY_INIT1 */
-
-/* define to support printing 64-bit integers with format I64 */
-/* #undef HAVE_INT64_AND_I64 */
-
-/* Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>. */
-#define HAVE_INTMAX_T 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
- declares uintmax_t. */
-#define HAVE_INTTYPES_H_WITH_UINTMAX 1
-
-/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
-/* #define HAVE_LANGINFO_CODESET 1 */
-
-/* Define to 1 if you have the `lchmod' function. */
-/* #undef HAVE_LCHMOD */
-
-/* Define to 1 if you have the `lchown' function. */
-#define HAVE_LCHOWN 1
-
-/* Define if your <locale.h> file defines LC_MESSAGES. */
-#define HAVE_LC_MESSAGES 1
-
-/* Define to 1 if you have the <limits.h> header file. */
-#define HAVE_LIMITS_H 1
-
-/* Define to 1 if you have the `link' function. */
-#define HAVE_LINK 1
-
-/* Define to 1 if you have the <locale.h> header file. */
-/* #define HAVE_LOCALE_H 1 */
-
-/* Define to 1 if you have the `localtime_r' function. */
-#define HAVE_LOCALTIME_R 1
-
-/* Define if you have the 'long double' type. */
-#define HAVE_LONG_DOUBLE 1
-
-/* Define if you have the 'long long' type. */
-#define HAVE_LONG_LONG 1
-
-/* define if system printf can print long long */
-/* #define HAVE_LONG_LONG_FORMAT 1 */
-
-/* Define to 1 if you have the `lstat' function. */
-#define HAVE_LSTAT 1
-
-/* Define to 1 if you have the <malloc.h> header file. */
-#define HAVE_MALLOC_H 1
-
-/* Define to 1 if you have the `memalign' function. */
-#define HAVE_MEMALIGN 1
-
-/* Define to 1 if you have the `memmove' function. */
-#define HAVE_MEMMOVE 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `mmap' function. */
-/* #define HAVE_MMAP 1 */
-
-/* Define to 1 if you have the <mntent.h> header file. */
-#define HAVE_MNTENT_H 1
-
-/* Have a monotonic clock */
-/* #define HAVE_MONOTONIC_CLOCK 1 */
-
-/* Define to 1 if you have the `nanosleep' function. */
-#define HAVE_NANOSLEEP 1
-
-/* Define to 1 if you have the <netdb.h> header file. */
-#define HAVE_NETDB_H 1
-
-/* Have non-POSIX function getgrgid_r */
-/* #undef HAVE_NONPOSIX_GETGRGID_R */
-
-/* Have non-POSIX function getpwuid_r */
-/* #undef HAVE_NONPOSIX_GETPWUID_R */
-
-/* Define to 1 if you have the `nsleep' function. */
-/* #undef HAVE_NSLEEP */
-
-/* Define to 1 if you have the `on_exit' function. */
-#define HAVE_ON_EXIT 1
-
-/* Define to 1 if you have the `pipe2' function. */
-/* #define HAVE_PIPE2 1 */
-
-/* Define to 1 if you have the `poll' function. */
-#define HAVE_POLL 1
-
-/* Have POSIX function getgrgid_r */
-/* #undef HAVE_POSIX_GETGRGID_R */
-
-/* Have POSIX function getpwuid_r */
-/* #define HAVE_POSIX_GETPWUID_R 1 */
-
-/* Define to 1 if you have the `posix_memalign' function. */
-/* #define HAVE_POSIX_MEMALIGN 1 */
-
-/* Have function pthread_attr_setstacksize */
-#define HAVE_PTHREAD_ATTR_SETSTACKSIZE 1
-
-/* Define to 1 if the system has the type `ptrdiff_t'. */
-#define HAVE_PTRDIFF_T 1
-
-/* Define to 1 if you have the <pwd.h> header file. */
-#define HAVE_PWD_H 1
-
-/* Define to 1 if `pw_gecos' is a member of `struct passwd'. */
-/* #undef HAVE_STRUCT_PASSWD_PW_GECOS */
-
-/* Define to 1 if you have the `readlink' function. */
-#define HAVE_READLINK 1
-
-/* Define to 1 if you have the <sched.h> header file. */
-#define HAVE_SCHED_H 1
-
-/* Define to 1 if libselinux is available */
-/* #undef HAVE_SELINUX */
-
-/* Define to 1 if you have the <selinux/selinux.h> header file. */
-/* #undef HAVE_SELINUX_SELINUX_H */
-
-/* Define to 1 if you have the `setenv' function. */
-#define HAVE_SETENV 1
-
-/* Define to 1 if you have the `setlocale' function. */
-#define HAVE_SETLOCALE 1
-
-/* Define to 1 if you have the `setmntent' function. */
-/* #undef HAVE_SETMNTENT */
-
-/* Define to 1 if you have the `setresuid' function. */
-#define HAVE_SETRESUID 1
-
-/* Define to 1 if you have the `setreuid' function. */
-#define HAVE_SETREUID 1
-
-/* Define to 1 if you have the `snprintf' function. */
-#define HAVE_SNPRINTF 1
-
-/* Define to 1 if you have the `splice' function. */
-/* #define HAVE_SPLICE 1 */
-
-/* Define to 1 if you have the `statfs' function. */
-#define HAVE_STATFS 1
-
-/* Define to 1 if you have the `statvfs' function. */
-/* #undef HAVE_STATVFS */
-
-/* Define to 1 if you have the <stddef.h> header file. */
-#define HAVE_STDDEF_H 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
- uintmax_t. */
-#define HAVE_STDINT_H_WITH_UINTMAX 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the `stpcpy' function. */
-/* #define HAVE_STPCPY 1 */
-
-/* Define to 1 if you have the `strcasecmp' function. */
-#define HAVE_STRCASECMP 1
-
-/* Define to 1 if you have the `strerror' function. */
-#define HAVE_STRERROR 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Have functions strlcpy and strlcat */
-/* #undef HAVE_STRLCPY */
-
-/* Define to 1 if you have the `strncasecmp' function. */
-#define HAVE_STRNCASECMP 1
-
-/* Define to 1 if you have the `strndup' function. */
-#define HAVE_STRNDUP 1
-
-/* Define to 1 if you have the `strsignal' function. */
-#define HAVE_STRSIGNAL 1
-
-/* Define to 1 if `f_bavail' is a member of `struct statfs'. */
-#define HAVE_STRUCT_STATFS_F_BAVAIL 1
-
-/* Define to 1 if `f_fstypename' is a member of `struct statfs'. */
-/* #undef HAVE_STRUCT_STATFS_F_FSTYPENAME */
-
-/* Define to 1 if `f_basetype' is a member of `struct statvfs'. */
-/* #undef HAVE_STRUCT_STATVFS_F_BASETYPE */
-
-/* Define to 1 if `st_atimensec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_ATIMENSEC */
-
-/* Define to 1 if `st_atim.tv_nsec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC */
-
-/* Define to 1 if `st_blksize' is a member of `struct stat'. */
-#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
-
-/* Define to 1 if `st_blocks' is a member of `struct stat'. */
-#define HAVE_STRUCT_STAT_ST_BLOCKS 1
-
-/* Define to 1 if `st_ctimensec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_CTIMENSEC */
-
-/* Define to 1 if `st_ctim.tv_nsec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC */
-
-/* Define to 1 if `st_mtimensec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_MTIMENSEC */
-
-/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
-/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */
-
-/* Define to 1 if you have the `symlink' function. */
-#define HAVE_SYMLINK 1
-
-/* Define to 1 if you have the <sys/inotify.h> header file. */
-#define HAVE_SYS_INOTIFY_H 1
-
-/* Define to 1 if you have the <sys/mntctl.h> header file. */
-/* #undef HAVE_SYS_MNTCTL_H */
-
-/* Define to 1 if you have the <sys/mnttab.h> header file. */
-/* #undef HAVE_SYS_MNTTAB_H */
-
-/* Define to 1 if you have the <sys/mount.h> header file. */
-#define HAVE_SYS_MOUNT_H 1
-
-/* Define to 1 if you have the <sys/param.h> header file. */
-#define HAVE_SYS_PARAM_H 1
-
-/* Define to 1 if you have the <sys/poll.h> header file. */
-#define HAVE_SYS_POLL_H 1
-
-/* Define to 1 if you have the <sys/prctl.h> header file. */
-#define HAVE_SYS_PRCTL_H 1
-
-/* Define to 1 if you have the <sys/resource.h> header file. */
-#define HAVE_SYS_RESOURCE_H 1
-
-/* found fd_set in sys/select.h */
-#define HAVE_SYS_SELECT_H 1
-
-/* Define to 1 if you have the <sys/statfs.h> header file. */
-#define HAVE_SYS_STATFS_H 1
-
-/* Define to 1 if you have the <sys/statvfs.h> header file. */
-/* #undef HAVE_SYS_STATVFS_H */
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/sysctl.h> header file. */
-#define HAVE_SYS_SYSCTL_H 1
-
-/* Define to 1 if you have the <sys/times.h> header file. */
-#define HAVE_SYS_TIMES_H 1
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#define HAVE_SYS_TIME_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <sys/uio.h> header file. */
-#define HAVE_SYS_UIO_H 1
-
-/* Define to 1 if you have the <sys/vfstab.h> header file. */
-/* #undef HAVE_SYS_VFSTAB_H */
-
-/* Define to 1 if you have the <sys/vfs.h> header file. */
-#define HAVE_SYS_VFS_H 1
-
-/* Define to 1 if you have the <sys/vmount.h> header file. */
-/* #undef HAVE_SYS_VMOUNT_H */
-
-/* Define to 1 if you have the <sys/wait.h> header file. */
-#define HAVE_SYS_WAIT_H 1
-
-/* Define to 1 if you have the <sys/xattr.h> header file. */
-/* #undef HAVE_SYS_XATTR_H */
-
-/* Define to 1 if you have the `timegm' function. */
-/* #define HAVE_TIMEGM 1 */
-
-/* Define to 1 if you have the ucred structure. */
-#define HAVE_STRUCT_UCRED 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define if your printf function family supports positional parameters as
- specified by Unix98. */
-/* #define HAVE_UNIX98_PRINTF 1 */
-
-/* Define to 1 if you have the `unsetenv' function. */
-#define HAVE_UNSETENV 1
-
-/* Define to 1 if you have the `utimes' function. */
-#define HAVE_UTIMES 1
-
-/* Define to 1 if you have the `valloc' function. */
-#define HAVE_VALLOC 1
-
-/* Define to 1 if you have the <values.h> header file. */
-#define HAVE_VALUES_H 1
-
-/* Define to 1 if you have the `vasprintf' function. */
-#define HAVE_VASPRINTF 1
-
-/* Define to 1 if you have the `vprintf' function. */
-#define HAVE_VPRINTF 1
-
-/* Define to 1 if you have the `vsnprintf' function. */
-#define HAVE_VSNPRINTF 1
-
-/* Define if you have the 'wchar_t' type. */
-/* #define HAVE_WCHAR_T 1 */
-
-/* Define to 1 if you have the `wcslen' function. */
-/* #define HAVE_WCSLEN 1 */
-
-/* Define if you have the 'wint_t' type. */
-/* #define HAVE_WINT_T 1 */
-
-/* Have a working bcopy */
-/* #undef HAVE_WORKING_BCOPY */
-
-/* Define to 1 if you have the <wspiapi.h> header file. */
-/* #undef HAVE_WSPIAPI_H */
-
-/* Define to 1 if xattr is available */
-/* #undef HAVE_XATTR */
-
-/* Define to 1 if xattr API uses XATTR_NOFOLLOW */
-/* #undef HAVE_XATTR_NOFOLLOW */
-
-/* Define to 1 if you have the `_NSGetEnviron' function. */
-/* #undef HAVE__NSGETENVIRON */
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
-#define LT_OBJDIR ".libs/"
-
-/* Do we cache iconv descriptors */
-#undef NEED_ICONV_CACHE
-
-/* didn't find fd_set */
-/* #undef NO_FD_SET */
-
-/* Define to 1 if your C compiler doesn't accept -c and -o together. */
-/* #undef NO_MINUS_C_MINUS_O */
-
-/* global 'sys_errlist' not found */
-/* #undef NO_SYS_ERRLIST */
-
-/* global 'sys_siglist' not found */
-/* #undef NO_SYS_SIGLIST */
-
-/* global 'sys_siglist' not declared */
-/* #undef NO_SYS_SIGLIST_DECL */
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "http://bugzilla.gnome.org/enter_bug.cgi?product=glib"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "glib"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "glib 2.24.1"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "glib"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.24.1"
-
-/* Maximum POSIX RT priority */
-#define POSIX_MAX_PRIORITY sched_get_priority_max(SCHED_OTHER)
-
-/* define if posix_memalign() can allocate any size */
-#define POSIX_MEMALIGN_WITH_COMPLIANT_ALLOCS 1
-
-/* Minimum POSIX RT priority */
-#define POSIX_MIN_PRIORITY sched_get_priority_min(SCHED_OTHER)
-
-/* The POSIX RT yield function */
-#define POSIX_YIELD_FUNC sched_yield()
-
-/* whether realloc (NULL,) works */
-#define REALLOC_0_WORKS 1
-
-/* Define if you have correct malloc prototypes */
-#define SANE_MALLOC_PROTOS 1
-
-/* The size of `char', as computed by sizeof. */
-#define SIZEOF_CHAR 1
-
-/* The size of `int', as computed by sizeof. */
-#define SIZEOF_INT 4
-
-/* The size of `long', as computed by sizeof. */
-#define SIZEOF_LONG 4
-
-/* The size of `long long', as computed by sizeof. */
-#define SIZEOF_LONG_LONG 8
-
-/* The size of `short', as computed by sizeof. */
-#define SIZEOF_SHORT 2
-
-/* The size of `size_t', as computed by sizeof. */
-#define SIZEOF_SIZE_T 4
-
-/* The size of `void *', as computed by sizeof. */
-#define SIZEOF_VOID_P 4
-
-/* The size of `__int64', as computed by sizeof. */
-#define SIZEOF___INT64 0
-
-/* If using the C implementation of alloca, define if you know the
- direction of stack growth for your system; otherwise it will be
- automatically deduced at runtime.
- STACK_DIRECTION > 0 => grows toward higher addresses
- STACK_DIRECTION < 0 => grows toward lower addresses
- STACK_DIRECTION = 0 => direction of growth unknown */
-/* #undef STACK_DIRECTION */
-
-/* Number of arguments to statfs() */
-#define STATFS_ARGS 2
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Using GNU libiconv */
-#undef USE_LIBICONV_GNU
-
-/* Using a native implementation of iconv in a separate library */
-#undef USE_LIBICONV_NATIVE
-
-/* using the system-supplied PCRE library */
-/* #undef USE_SYSTEM_PCRE */
-
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
- significant byte first (like Motorola and SPARC, unlike Intel). */
-#if defined AC_APPLE_UNIVERSAL_BUILD
-# if defined __BIG_ENDIAN__
-# define WORDS_BIGENDIAN 1
-# endif
-#else
-# ifndef WORDS_BIGENDIAN
-/* # undef WORDS_BIGENDIAN */
-# endif
-#endif
-
-/* Number of bits in a file offset, on hosts where this is settable. */
-#define _FILE_OFFSET_BITS 64
-
-/* Define for large files, on AIX-style hosts. */
-/* #undef _LARGE_FILES */
-
-/* Needed to get declarations for msg_control and msg_controllen on Solaris */
-/* #undef _XOPEN_SOURCE */
-
-/* Needed to get declarations for msg_control and msg_controllen on Solaris */
-/* #undef _XOPEN_SOURCE_EXTENDED */
-
-/* Needed to get declarations for msg_control and msg_controllen on Solaris */
-/* #undef __EXTENSIONS__ */
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to long or long long if <inttypes.h> and <stdint.h> don't define. */
-/* #undef intmax_t */
-
-/* Define to empty if the C compiler doesn't support this keyword. */
-/* #undef signed */
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
-
-/* Android specific build define */
-#define BUILD_WITH_ANDROID 1
+++ /dev/null
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- ./libcharset/localcharset.c \
- garray.c \
- gasyncqueue.c \
- gatomic.c \
- gbacktrace.c \
- gbase64.c \
- gbitlock.c \
- gbookmarkfile.c \
- gbuffer.c \
- gcache.c \
- gchecksum.c \
- gcompletion.c \
- gconvert.c \
- gdataset.c \
- gdate.c \
- gdatetime.c \
- gdir.c \
- gerror.c \
- gfileutils.c \
- ghash.c \
- ghook.c \
- ghostutils.c \
- giochannel.c \
- gkeyfile.c \
- glist.c \
- gmain.c \
- gmappedfile.c \
- gmarkup.c \
- gmem.c \
- gmessages.c \
- gnode.c \
- goption.c \
- gpattern.c \
- gpoll.c \
- gprimes.c \
- gqsort.c \
- gqueue.c \
- grel.c \
- grand.c \
- gregex.c \
- gscanner.c \
- gsequence.c \
- gshell.c \
- gslice.c \
- gslist.c \
- gstdio.c \
- gstrfuncs.c \
- gstring.c \
- gtestutils.c \
- gthread.c \
- gthreadpool.c \
- gtimer.c \
- gtimezone.c \
- gtree.c \
- guniprop.c \
- gutf8.c \
- gunibreak.c \
- gunicollate.c \
- gunidecomp.c \
- gurifuncs.c \
- gutils.c \
- gvariant.c \
- gvariant-core.c \
- gvariant-parser.c \
- gvariant-serialiser.c \
- gvarianttypeinfo.c \
- gvarianttype.c \
- gprintf.c \
- giounix.c \
- gspawn.c
-
-LOCAL_MODULE := libglib-2.0
-
-LOCAL_C_INCLUDES := \
- $(GLIB_TOP) \
- $(GLIB_TOP)/android \
- $(GLIB_TOP)/android-internal \
- $(LOCAL_PATH)/android-internal \
- $(LOCAL_PATH)/libcharset \
- $(LOCAL_PATH)/gnulib \
- $(LOCAL_PATH)/pcre
-
-# ./glib private macros, copy from Makefile.am
-LOCAL_CFLAGS := \
- -DLIBDIR=\"$(libdir)\" \
- -DHAVE_CONFIG_H \
- -DG_LOG_DOMAIN=\"GLib\" \
- -DPCRE_STATIC \
- -DG_DISABLE_DEPRECATED \
- -DGLIB_COMPILATION
-
-ifeq ($(GLIB_BUILD_STATIC),true)
-include $(BUILD_STATIC_LIBRARY)
-else
-LOCAL_STATIC_LIBRARIES := libpcre
-LOCAL_LDLIBS := \
- -llog
-
-include $(BUILD_SHARED_LIBRARY)
-endif
-
-include $(LOCAL_PATH)/pcre/Android.mk
+++ /dev/null
-#! /bin/sh
-
-egrep '^#([^i]|if).*[^\]$' "${builddir:-.}/glibconfig.h" > glibconfig.cpp
-
-INCLUDES="-include ${top_builddir:-..}/config.h"
-INCLUDES="$INCLUDES -include glibconfig.cpp $GLIB_DEBUG_FLAGS"
-
-cpp -P -DINCLUDE_INTERNAL_SYMBOLS -DINCLUDE_VARIABLES -DG_STDIO_NO_WRAP_ON_UNIX -DALL_FILES $INCLUDES "${srcdir:-.}/glib.symbols" | sed -e '/^$/d' -e 's/ G_GNUC.*$//' -e 's/ PRIVATE$//' | sort > expected-abi
-rm -f glibconfig.cpp
-
-nm -D -g --defined-only .libs/libglib-2.0.so | cut -d ' ' -f 3 | egrep -v '^(__bss_start|_edata|_end)' | sort > actual-abi
-
-diff -u expected-abi actual-abi && rm -f expected-abi actual-abi
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "garray.h"
-
-#include "gmem.h"
-#include "gthread.h"
-#include "gmessages.h"
-#include "gqsort.h"
-
-
-/**
- * SECTION: arrays
- * @title: Arrays
- * @short_description: arrays of arbitrary elements which grow
- * automatically as elements are added
- *
- * Arrays are similar to standard C arrays, except that they grow
- * automatically as elements are added.
- *
- * Array elements can be of any size (though all elements of one array
- * are the same size), and the array can be automatically cleared to
- * '0's and zero-terminated.
- *
- * To create a new array use g_array_new().
- *
- * To add elements to an array, use g_array_append_val(),
- * g_array_append_vals(), g_array_prepend_val(), and
- * g_array_prepend_vals().
- *
- * To access an element of an array, use g_array_index().
- *
- * To set the size of an array, use g_array_set_size().
- *
- * To free an array, use g_array_free().
- *
- * <example>
- * <title>Using a #GArray to store #gint values</title>
- * <programlisting>
- * GArray *garray;
- * gint i;
- * /<!-- -->* We create a new array to store gint values.
- * We don't want it zero-terminated or cleared to 0's. *<!-- -->/
- * garray = g_array_new (FALSE, FALSE, sizeof (gint));
- * for (i = 0; i < 10000; i++)
- * g_array_append_val (garray, i);
- * for (i = 0; i < 10000; i++)
- * if (g_array_index (garray, gint, i) != i)
- * g_print ("ERROR: got %d instead of %d\n",
- * g_array_index (garray, gint, i), i);
- * g_array_free (garray, TRUE);
- * </programlisting>
- * </example>
- **/
-
-#define MIN_ARRAY_SIZE 16
-
-typedef struct _GRealArray GRealArray;
-
-/**
- * GArray:
- * @data: a pointer to the element data. The data may be moved as
- * elements are added to the #GArray.
- * @len: the number of elements in the #GArray not including the
- * possible terminating zero element.
- *
- * Contains the public fields of an <link
- * linkend="glib-arrays">Array</link>.
- **/
-struct _GRealArray
-{
- guint8 *data;
- guint len;
- guint alloc;
- guint elt_size;
- guint zero_terminated : 1;
- guint clear : 1;
- volatile gint ref_count;
-};
-
-/**
- * g_array_index:
- * @a: a #GArray.
- * @t: the type of the elements.
- * @i: the index of the element to return.
- * @Returns: the element of the #GArray at the index given by @i.
- *
- * Returns the element of a #GArray at the given index. The return
- * value is cast to the given type.
- *
- * <example>
- * <title>Getting a pointer to an element in a #GArray</title>
- * <programlisting>
- * EDayViewEvent *event;
- * /<!-- -->* This gets a pointer to the 4th element
- * in the array of EDayViewEvent structs. *<!-- -->/
- * event = &g_array_index (events, EDayViewEvent, 3);
- * </programlisting>
- * </example>
- **/
-
-#define g_array_elt_len(array,i) ((array)->elt_size * (i))
-#define g_array_elt_pos(array,i) ((array)->data + g_array_elt_len((array),(i)))
-#define g_array_elt_zero(array, pos, len) \
- (memset (g_array_elt_pos ((array), pos), 0, g_array_elt_len ((array), len)))
-#define g_array_zero_terminate(array) G_STMT_START{ \
- if ((array)->zero_terminated) \
- g_array_elt_zero ((array), (array)->len, 1); \
-}G_STMT_END
-
-static guint g_nearest_pow (gint num) G_GNUC_CONST;
-static void g_array_maybe_expand (GRealArray *array,
- gint len);
-
-/**
- * g_array_new:
- * @zero_terminated: %TRUE if the array should have an extra element at
- * the end which is set to 0.
- * @clear_: %TRUE if #GArray elements should be automatically cleared
- * to 0 when they are allocated.
- * @element_size: the size of each element in bytes.
- * @Returns: the new #GArray.
- *
- * Creates a new #GArray with a reference count of 1.
- **/
-GArray*
-g_array_new (gboolean zero_terminated,
- gboolean clear,
- guint elt_size)
-{
- return (GArray*) g_array_sized_new (zero_terminated, clear, elt_size, 0);
-}
-
-/**
- * g_array_sized_new:
- * @zero_terminated: %TRUE if the array should have an extra element at
- * the end with all bits cleared.
- * @clear_: %TRUE if all bits in the array should be cleared to 0 on
- * allocation.
- * @element_size: size of each element in the array.
- * @reserved_size: number of elements preallocated.
- * @Returns: the new #GArray.
- *
- * Creates a new #GArray with @reserved_size elements preallocated and
- * a reference count of 1. This avoids frequent reallocation, if you
- * are going to add many elements to the array. Note however that the
- * size of the array is still 0.
- **/
-GArray* g_array_sized_new (gboolean zero_terminated,
- gboolean clear,
- guint elt_size,
- guint reserved_size)
-{
- GRealArray *array = g_slice_new (GRealArray);
-
- array->data = NULL;
- array->len = 0;
- array->alloc = 0;
- array->zero_terminated = (zero_terminated ? 1 : 0);
- array->clear = (clear ? 1 : 0);
- array->elt_size = elt_size;
- array->ref_count = 1;
-
- if (array->zero_terminated || reserved_size != 0)
- {
- g_array_maybe_expand (array, reserved_size);
- g_array_zero_terminate(array);
- }
-
- return (GArray*) array;
-}
-
-/**
- * g_array_ref:
- * @array: A #GArray.
- *
- * Atomically increments the reference count of @array by one. This
- * function is MT-safe and may be called from any thread.
- *
- * Returns: The passed in #GArray.
- *
- * Since: 2.22
- **/
-GArray *
-g_array_ref (GArray *array)
-{
- GRealArray *rarray = (GRealArray*) array;
- g_return_val_if_fail (array, NULL);
- g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array);
- g_atomic_int_inc (&rarray->ref_count);
- return array;
-}
-
-/**
- * g_array_unref:
- * @array: A #GArray.
- *
- * Atomically decrements the reference count of @array by one. If the
- * reference count drops to 0, all memory allocated by the array is
- * released. This function is MT-safe and may be called from any
- * thread.
- *
- * Since: 2.22
- **/
-void
-g_array_unref (GArray *array)
-{
- GRealArray *rarray = (GRealArray*) array;
- g_return_if_fail (array);
- g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0);
- if (g_atomic_int_dec_and_test (&rarray->ref_count))
- g_array_free (array, TRUE);
-}
-
-/**
- * g_array_get_element_size:
- * @array: A #GArray.
- *
- * Gets the size of the elements in @array.
- *
- * Returns: Size of each element, in bytes.
- *
- * Since: 2.22
- **/
-guint
-g_array_get_element_size (GArray *array)
-{
- GRealArray *rarray = (GRealArray*) array;
-
- g_return_val_if_fail (array, 0);
-
- return rarray->elt_size;
-}
-
-/**
- * g_array_free:
- * @array: a #GArray.
- * @free_segment: if %TRUE the actual element data is freed as well.
- * @Returns: the element data if @free_segment is %FALSE, otherwise
- * %NULL. The element data should be freed using g_free().
- *
- * Frees the memory allocated for the #GArray. If @free_segment is
- * %TRUE it frees the memory block holding the elements as well and
- * also each element if @array has a @element_free_func set. Pass
- * %FALSE if you want to free the #GArray wrapper but preserve the
- * underlying array for use elsewhere. If the reference count of @array
- * is greater than one, the #GArray wrapper is preserved but the size
- * of @array will be set to zero.
- *
- * <note><para>If array elements contain dynamically-allocated memory,
- * they should be freed separately.</para></note>
- **/
-gchar*
-g_array_free (GArray *farray,
- gboolean free_segment)
-{
- GRealArray *array = (GRealArray*) farray;
- gchar* segment;
- gboolean preserve_wrapper;
-
- g_return_val_if_fail (array, NULL);
-
- /* if others are holding a reference, preserve the wrapper but do free/return the data */
- preserve_wrapper = FALSE;
- if (g_atomic_int_get (&array->ref_count) > 1)
- preserve_wrapper = TRUE;
-
- if (free_segment)
- {
- g_free (array->data);
- segment = NULL;
- }
- else
- segment = (gchar*) array->data;
-
- if (preserve_wrapper)
- {
- array->data = NULL;
- array->len = 0;
- array->alloc = 0;
- }
- else
- {
- g_slice_free1 (sizeof (GRealArray), array);
- }
-
- return segment;
-}
-
-/**
- * g_array_append_vals:
- * @array: a #GArray.
- * @data: a pointer to the elements to append to the end of the array.
- * @len: the number of elements to append.
- * @Returns: the #GArray.
- *
- * Adds @len elements onto the end of the array.
- **/
-/**
- * g_array_append_val:
- * @a: a #GArray.
- * @v: the value to append to the #GArray.
- * @Returns: the #GArray.
- *
- * Adds the value on to the end of the array. The array will grow in
- * size automatically if necessary.
- *
- * <note><para>g_array_append_val() is a macro which uses a reference
- * to the value parameter @v. This means that you cannot use it with
- * literal values such as "27". You must use variables.</para></note>
- **/
-GArray*
-g_array_append_vals (GArray *farray,
- gconstpointer data,
- guint len)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- g_array_maybe_expand (array, len);
-
- memcpy (g_array_elt_pos (array, array->len), data,
- g_array_elt_len (array, len));
-
- array->len += len;
-
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_prepend_vals:
- * @array: a #GArray.
- * @data: a pointer to the elements to prepend to the start of the
- * array.
- * @len: the number of elements to prepend.
- * @Returns: the #GArray.
- *
- * Adds @len elements onto the start of the array.
- *
- * This operation is slower than g_array_append_vals() since the
- * existing elements in the array have to be moved to make space for
- * the new elements.
- **/
-/**
- * g_array_prepend_val:
- * @a: a #GArray.
- * @v: the value to prepend to the #GArray.
- * @Returns: the #GArray.
- *
- * Adds the value on to the start of the array. The array will grow in
- * size automatically if necessary.
- *
- * This operation is slower than g_array_append_val() since the
- * existing elements in the array have to be moved to make space for
- * the new element.
- *
- * <note><para>g_array_prepend_val() is a macro which uses a reference
- * to the value parameter @v. This means that you cannot use it with
- * literal values such as "27". You must use variables.</para></note>
- **/
-GArray*
-g_array_prepend_vals (GArray *farray,
- gconstpointer data,
- guint len)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- g_array_maybe_expand (array, len);
-
- g_memmove (g_array_elt_pos (array, len), g_array_elt_pos (array, 0),
- g_array_elt_len (array, array->len));
-
- memcpy (g_array_elt_pos (array, 0), data, g_array_elt_len (array, len));
-
- array->len += len;
-
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_insert_vals:
- * @array: a #GArray.
- * @index_: the index to place the elements at.
- * @data: a pointer to the elements to insert.
- * @len: the number of elements to insert.
- * @Returns: the #GArray.
- *
- * Inserts @len elements into a #GArray at the given index.
- **/
-/**
- * g_array_insert_val:
- * @a: a #GArray.
- * @i: the index to place the element at.
- * @v: the value to insert into the array.
- * @Returns: the #GArray.
- *
- * Inserts an element into an array at the given index.
- *
- * <note><para>g_array_insert_val() is a macro which uses a reference
- * to the value parameter @v. This means that you cannot use it with
- * literal values such as "27". You must use variables.</para></note>
- **/
-GArray*
-g_array_insert_vals (GArray *farray,
- guint index_,
- gconstpointer data,
- guint len)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- g_array_maybe_expand (array, len);
-
- g_memmove (g_array_elt_pos (array, len + index_),
- g_array_elt_pos (array, index_),
- g_array_elt_len (array, array->len - index_));
-
- memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array, len));
-
- array->len += len;
-
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_set_size:
- * @array: a #GArray.
- * @length: the new size of the #GArray.
- * @Returns: the #GArray.
- *
- * Sets the size of the array, expanding it if necessary. If the array
- * was created with @clear_ set to %TRUE, the new elements are set to 0.
- **/
-GArray*
-g_array_set_size (GArray *farray,
- guint length)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- if (length > array->len)
- {
- g_array_maybe_expand (array, length - array->len);
-
- if (array->clear)
- g_array_elt_zero (array, array->len, length - array->len);
- }
- else if (G_UNLIKELY (g_mem_gc_friendly) && length < array->len)
- g_array_elt_zero (array, length, array->len - length);
-
- array->len = length;
-
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_remove_index:
- * @array: a #GArray.
- * @index_: the index of the element to remove.
- * @Returns: the #GArray.
- *
- * Removes the element at the given index from a #GArray. The following
- * elements are moved down one place.
- **/
-GArray*
-g_array_remove_index (GArray *farray,
- guint index_)
-{
- GRealArray* array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- g_return_val_if_fail (index_ < array->len, NULL);
-
- if (index_ != array->len - 1)
- g_memmove (g_array_elt_pos (array, index_),
- g_array_elt_pos (array, index_ + 1),
- g_array_elt_len (array, array->len - index_ - 1));
-
- array->len -= 1;
-
- if (G_UNLIKELY (g_mem_gc_friendly))
- g_array_elt_zero (array, array->len, 1);
- else
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_remove_index_fast:
- * @array: a @GArray.
- * @index_: the index of the element to remove.
- * @Returns: the #GArray.
- *
- * Removes the element at the given index from a #GArray. The last
- * element in the array is used to fill in the space, so this function
- * does not preserve the order of the #GArray. But it is faster than
- * g_array_remove_index().
- **/
-GArray*
-g_array_remove_index_fast (GArray *farray,
- guint index_)
-{
- GRealArray* array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
-
- g_return_val_if_fail (index_ < array->len, NULL);
-
- if (index_ != array->len - 1)
- memcpy (g_array_elt_pos (array, index_),
- g_array_elt_pos (array, array->len - 1),
- g_array_elt_len (array, 1));
-
- array->len -= 1;
-
- if (G_UNLIKELY (g_mem_gc_friendly))
- g_array_elt_zero (array, array->len, 1);
- else
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_remove_range:
- * @array: a @GArray.
- * @index_: the index of the first element to remove.
- * @length: the number of elements to remove.
- * @Returns: the #GArray.
- *
- * Removes the given number of elements starting at the given index
- * from a #GArray. The following elements are moved to close the gap.
- *
- * Since: 2.4
- **/
-GArray*
-g_array_remove_range (GArray *farray,
- guint index_,
- guint length)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_val_if_fail (array, NULL);
- g_return_val_if_fail (index_ < array->len, NULL);
- g_return_val_if_fail (index_ + length <= array->len, NULL);
-
- if (index_ + length != array->len)
- g_memmove (g_array_elt_pos (array, index_),
- g_array_elt_pos (array, index_ + length),
- (array->len - (index_ + length)) * array->elt_size);
-
- array->len -= length;
- if (G_UNLIKELY (g_mem_gc_friendly))
- g_array_elt_zero (array, array->len, length);
- else
- g_array_zero_terminate (array);
-
- return farray;
-}
-
-/**
- * g_array_sort:
- * @array: a #GArray.
- * @compare_func: comparison function.
- *
- * Sorts a #GArray using @compare_func which should be a qsort()-style
- * comparison function (returns less than zero for first arg is less
- * than second arg, zero for equal, greater zero if first arg is
- * greater than second arg).
- *
- * If two array elements compare equal, their order in the sorted array
- * is undefined.
- **/
-void
-g_array_sort (GArray *farray,
- GCompareFunc compare_func)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_if_fail (array != NULL);
-
- qsort (array->data,
- array->len,
- array->elt_size,
- compare_func);
-}
-
-/**
- * g_array_sort_with_data:
- * @array: a #GArray.
- * @compare_func: comparison function.
- * @user_data: data to pass to @compare_func.
- *
- * Like g_array_sort(), but the comparison function receives an extra
- * user data argument.
- **/
-void
-g_array_sort_with_data (GArray *farray,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- GRealArray *array = (GRealArray*) farray;
-
- g_return_if_fail (array != NULL);
-
- g_qsort_with_data (array->data,
- array->len,
- array->elt_size,
- compare_func,
- user_data);
-}
-
-/* Returns the smallest power of 2 greater than n, or n if
- * such power does not fit in a guint
- */
-static guint
-g_nearest_pow (gint num)
-{
- guint n = 1;
-
- while (n < num && n > 0)
- n <<= 1;
-
- return n ? n : num;
-}
-
-static void
-g_array_maybe_expand (GRealArray *array,
- gint len)
-{
- guint want_alloc = g_array_elt_len (array, array->len + len +
- array->zero_terminated);
-
- if (want_alloc > array->alloc)
- {
- want_alloc = g_nearest_pow (want_alloc);
- want_alloc = MAX (want_alloc, MIN_ARRAY_SIZE);
-
- array->data = g_realloc (array->data, want_alloc);
-
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (array->data + array->alloc, 0, want_alloc - array->alloc);
-
- array->alloc = want_alloc;
- }
-}
-
-/**
- * SECTION: arrays_pointer
- * @title: Pointer Arrays
- * @short_description: arrays of pointers to any type of data, which
- * grow automatically as new elements are added
- *
- * Pointer Arrays are similar to Arrays but are used only for storing
- * pointers.
- *
- * <note><para>If you remove elements from the array, elements at the
- * end of the array are moved into the space previously occupied by the
- * removed element. This means that you should not rely on the index of
- * particular elements remaining the same. You should also be careful
- * when deleting elements while iterating over the array.</para></note>
- *
- * To create a pointer array, use g_ptr_array_new().
- *
- * To add elements to a pointer array, use g_ptr_array_add().
- *
- * To remove elements from a pointer array, use g_ptr_array_remove(),
- * g_ptr_array_remove_index() or g_ptr_array_remove_index_fast().
- *
- * To access an element of a pointer array, use g_ptr_array_index().
- *
- * To set the size of a pointer array, use g_ptr_array_set_size().
- *
- * To free a pointer array, use g_ptr_array_free().
- *
- * <example>
- * <title>Using a #GPtrArray</title>
- * <programlisting>
- * GPtrArray *gparray;
- * gchar *string1 = "one", *string2 = "two", *string3 = "three";
- *
- * gparray = g_ptr_array_new (<!-- -->);
- * g_ptr_array_add (gparray, (gpointer) string1);
- * g_ptr_array_add (gparray, (gpointer) string2);
- * g_ptr_array_add (gparray, (gpointer) string3);
- *
- * if (g_ptr_array_index (gparray, 0) != (gpointer) string1)
- * g_print ("ERROR: got %p instead of %p\n",
- * g_ptr_array_index (gparray, 0), string1);
- *
- * g_ptr_array_free (gparray, TRUE);
- * </programlisting>
- * </example>
- **/
-
-typedef struct _GRealPtrArray GRealPtrArray;
-
-/**
- * GPtrArray:
- * @pdata: points to the array of pointers, which may be moved when the
- * array grows.
- * @len: number of pointers in the array.
- *
- * Contains the public fields of a pointer array.
- **/
-struct _GRealPtrArray
-{
- gpointer *pdata;
- guint len;
- guint alloc;
- volatile gint ref_count;
- GDestroyNotify element_free_func;
-};
-
-/**
- * g_ptr_array_index:
- * @array: a #GPtrArray.
- * @index_: the index of the pointer to return.
- * @Returns: the pointer at the given index.
- *
- * Returns the pointer at the given index of the pointer array.
- **/
-
-static void g_ptr_array_maybe_expand (GRealPtrArray *array,
- gint len);
-
-/**
- * g_ptr_array_new:
- * @Returns: the new #GPtrArray.
- *
- * Creates a new #GPtrArray with a reference count of 1.
- **/
-GPtrArray*
-g_ptr_array_new (void)
-{
- return g_ptr_array_sized_new (0);
-}
-
-/**
- * g_ptr_array_sized_new:
- * @reserved_size: number of pointers preallocated.
- * @Returns: the new #GPtrArray.
- *
- * Creates a new #GPtrArray with @reserved_size pointers preallocated
- * and a reference count of 1. This avoids frequent reallocation, if
- * you are going to add many pointers to the array. Note however that
- * the size of the array is still 0.
- **/
-GPtrArray*
-g_ptr_array_sized_new (guint reserved_size)
-{
- GRealPtrArray *array = g_slice_new (GRealPtrArray);
-
- array->pdata = NULL;
- array->len = 0;
- array->alloc = 0;
- array->ref_count = 1;
- array->element_free_func = NULL;
-
- if (reserved_size != 0)
- g_ptr_array_maybe_expand (array, reserved_size);
-
- return (GPtrArray*) array;
-}
-
-/**
- * g_ptr_array_new_with_free_func:
- * @element_free_func: A function to free elements with destroy @array or %NULL.
- *
- * Creates a new #GPtrArray with a reference count of 1 and use @element_free_func
- * for freeing each element when the array is destroyed either via
- * g_ptr_array_unref(), when g_ptr_array_free() is called with @free_segment
- * set to %TRUE or when removing elements.
- *
- * Returns: A new #GPtrArray.
- *
- * Since: 2.22
- **/
-GPtrArray *
-g_ptr_array_new_with_free_func (GDestroyNotify element_free_func)
-{
- GPtrArray *array;
-
- array = g_ptr_array_new ();
- g_ptr_array_set_free_func (array, element_free_func);
- return array;
-}
-
-/**
- * g_ptr_array_set_free_func:
- * @array: A #GPtrArray.
- * @element_free_func: A function to free elements with destroy @array or %NULL.
- *
- * Sets a function for freeing each element when @array is destroyed
- * either via g_ptr_array_unref(), when g_ptr_array_free() is called
- * with @free_segment set to %TRUE or when removing elements.
- *
- * Since: 2.22
- **/
-void
-g_ptr_array_set_free_func (GPtrArray *array,
- GDestroyNotify element_free_func)
-{
- GRealPtrArray* rarray = (GRealPtrArray*) array;
-
- g_return_if_fail (array);
-
- rarray->element_free_func = element_free_func;
-}
-
-/**
- * g_ptr_array_ref:
- * @array: A #GArray.
- *
- * Atomically increments the reference count of @array by one. This
- * function is MT-safe and may be called from any thread.
- *
- * Returns: The passed in #GPtrArray.
- *
- * Since: 2.22
- **/
-GPtrArray *
-g_ptr_array_ref (GPtrArray *array)
-{
- GRealPtrArray *rarray = (GRealPtrArray*) array;
-
- g_return_val_if_fail (array, NULL);
- g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array);
- g_atomic_int_inc (&rarray->ref_count);
- return array;
-}
-
-/**
- * g_ptr_array_unref:
- * @array: A #GPtrArray.
- *
- * Atomically decrements the reference count of @array by one. If the
- * reference count drops to 0, the effect is the same as calling
- * g_ptr_array_free() with @free_segment set to %TRUE. This function
- * is MT-safe and may be called from any thread.
- *
- * Since: 2.22
- **/
-void
-g_ptr_array_unref (GPtrArray *array)
-{
- GRealPtrArray *rarray = (GRealPtrArray*) array;
-
- g_return_if_fail (array);
- g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0);
- if (g_atomic_int_dec_and_test (&rarray->ref_count))
- g_ptr_array_free (array, TRUE);
-}
-
-/**
- * g_ptr_array_free:
- * @array: a #GPtrArray.
- * @free_seg: if %TRUE the actual pointer array is freed as well.
- * @Returns: the pointer array if @free_seg is %FALSE, otherwise %NULL.
- * The pointer array should be freed using g_free().
- *
- * Frees the memory allocated for the #GPtrArray. If @free_seg is %TRUE
- * it frees the memory block holding the elements as well. Pass %FALSE
- * if you want to free the #GPtrArray wrapper but preserve the
- * underlying array for use elsewhere. If the reference count of @array
- * is greater than one, the #GPtrArray wrapper is preserved but the
- * size of @array will be set to zero.
- *
- * <note><para>If array contents point to dynamically-allocated
- * memory, they should be freed separately if @free_seg is %TRUE and no
- * #GDestroyNotify function has been set for @array.</para></note>
- **/
-gpointer*
-g_ptr_array_free (GPtrArray *farray,
- gboolean free_segment)
-{
- GRealPtrArray *array = (GRealPtrArray*) farray;
- gpointer* segment;
- gboolean preserve_wrapper;
-
- g_return_val_if_fail (array, NULL);
-
- /* if others are holding a reference, preserve the wrapper but do free/return the data */
- preserve_wrapper = FALSE;
- if (g_atomic_int_get (&array->ref_count) > 1)
- preserve_wrapper = TRUE;
-
- if (free_segment)
- {
- if (array->element_free_func != NULL)
- g_ptr_array_foreach (farray, (GFunc) array->element_free_func, NULL);
- g_free (array->pdata);
- segment = NULL;
- }
- else
- segment = array->pdata;
-
- if (preserve_wrapper)
- {
- array->pdata = NULL;
- array->len = 0;
- array->alloc = 0;
- }
- else
- {
- g_slice_free1 (sizeof (GRealPtrArray), array);
- }
-
- return segment;
-}
-
-static void
-g_ptr_array_maybe_expand (GRealPtrArray *array,
- gint len)
-{
- if ((array->len + len) > array->alloc)
- {
- guint old_alloc = array->alloc;
- array->alloc = g_nearest_pow (array->len + len);
- array->alloc = MAX (array->alloc, MIN_ARRAY_SIZE);
- array->pdata = g_realloc (array->pdata, sizeof (gpointer) * array->alloc);
- if (G_UNLIKELY (g_mem_gc_friendly))
- for ( ; old_alloc < array->alloc; old_alloc++)
- array->pdata [old_alloc] = NULL;
- }
-}
-
-/**
- * g_ptr_array_set_size:
- * @array: a #GPtrArray.
- * @length: the new length of the pointer array.
- *
- * Sets the size of the array. When making the array larger,
- * newly-added elements will be set to %NULL. When making it smaller,
- * if @array has a non-%NULL #GDestroyNotify function then it will be
- * called for the removed elements.
- **/
-void
-g_ptr_array_set_size (GPtrArray *farray,
- gint length)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
-
- g_return_if_fail (array);
-
- if (length > array->len)
- {
- int i;
- g_ptr_array_maybe_expand (array, (length - array->len));
- /* This is not
- * memset (array->pdata + array->len, 0,
- * sizeof (gpointer) * (length - array->len));
- * to make it really portable. Remember (void*)NULL needn't be
- * bitwise zero. It of course is silly not to use memset (..,0,..).
- */
- for (i = array->len; i < length; i++)
- array->pdata[i] = NULL;
- }
- else if (length < array->len)
- g_ptr_array_remove_range (farray, length, array->len - length);
-
- array->len = length;
-}
-
-/**
- * g_ptr_array_remove_index:
- * @array: a #GPtrArray.
- * @index_: the index of the pointer to remove.
- * @Returns: the pointer which was removed.
- *
- * Removes the pointer at the given index from the pointer array. The
- * following elements are moved down one place. If @array has a
- * non-%NULL #GDestroyNotify function it is called for the removed
- * element.
- **/
-gpointer
-g_ptr_array_remove_index (GPtrArray *farray,
- guint index_)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
- gpointer result;
-
- g_return_val_if_fail (array, NULL);
-
- g_return_val_if_fail (index_ < array->len, NULL);
-
- result = array->pdata[index_];
-
- if (array->element_free_func != NULL)
- array->element_free_func (array->pdata[index_]);
-
- if (index_ != array->len - 1)
- g_memmove (array->pdata + index_, array->pdata + index_ + 1,
- sizeof (gpointer) * (array->len - index_ - 1));
-
- array->len -= 1;
-
- if (G_UNLIKELY (g_mem_gc_friendly))
- array->pdata[array->len] = NULL;
-
- return result;
-}
-
-/**
- * g_ptr_array_remove_index_fast:
- * @array: a #GPtrArray.
- * @index_: the index of the pointer to remove.
- * @Returns: the pointer which was removed.
- *
- * Removes the pointer at the given index from the pointer array. The
- * last element in the array is used to fill in the space, so this
- * function does not preserve the order of the array. But it is faster
- * than g_ptr_array_remove_index(). If @array has a non-%NULL
- * #GDestroyNotify function it is called for the removed element.
- **/
-gpointer
-g_ptr_array_remove_index_fast (GPtrArray *farray,
- guint index_)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
- gpointer result;
-
- g_return_val_if_fail (array, NULL);
-
- g_return_val_if_fail (index_ < array->len, NULL);
-
- result = array->pdata[index_];
-
- if (array->element_free_func != NULL)
- array->element_free_func (array->pdata[index_]);
-
- if (index_ != array->len - 1)
- array->pdata[index_] = array->pdata[array->len - 1];
-
- array->len -= 1;
-
- if (G_UNLIKELY (g_mem_gc_friendly))
- array->pdata[array->len] = NULL;
-
- return result;
-}
-
-/**
- * g_ptr_array_remove_range:
- * @array: a @GPtrArray.
- * @index_: the index of the first pointer to remove.
- * @length: the number of pointers to remove.
- *
- * Removes the given number of pointers starting at the given index
- * from a #GPtrArray. The following elements are moved to close the
- * gap. If @array has a non-%NULL #GDestroyNotify function it is called
- * for the removed elements.
- *
- * Since: 2.4
- **/
-void
-g_ptr_array_remove_range (GPtrArray *farray,
- guint index_,
- guint length)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
- guint n;
-
- g_return_if_fail (array);
- g_return_if_fail (index_ < array->len);
- g_return_if_fail (index_ + length <= array->len);
-
- if (array->element_free_func != NULL)
- {
- for (n = index_; n < index_ + length; n++)
- array->element_free_func (array->pdata[n]);
- }
-
- if (index_ + length != array->len)
- {
- g_memmove (&array->pdata[index_],
- &array->pdata[index_ + length],
- (array->len - (index_ + length)) * sizeof (gpointer));
- }
-
- array->len -= length;
- if (G_UNLIKELY (g_mem_gc_friendly))
- {
- guint i;
- for (i = 0; i < length; i++)
- array->pdata[array->len + i] = NULL;
- }
-}
-
-/**
- * g_ptr_array_remove:
- * @array: a #GPtrArray.
- * @data: the pointer to remove.
- * @Returns: %TRUE if the pointer is removed. %FALSE if the pointer is
- * not found in the array.
- *
- * Removes the first occurrence of the given pointer from the pointer
- * array. The following elements are moved down one place. If @array
- * has a non-%NULL #GDestroyNotify function it is called for the
- * removed element.
- *
- * It returns %TRUE if the pointer was removed, or %FALSE if the
- * pointer was not found.
- **/
-gboolean
-g_ptr_array_remove (GPtrArray *farray,
- gpointer data)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
- guint i;
-
- g_return_val_if_fail (array, FALSE);
-
- for (i = 0; i < array->len; i += 1)
- {
- if (array->pdata[i] == data)
- {
- g_ptr_array_remove_index (farray, i);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- * g_ptr_array_remove_fast:
- * @array: a #GPtrArray.
- * @data: the pointer to remove.
- * @Returns: %TRUE if the pointer was found in the array.
- *
- * Removes the first occurrence of the given pointer from the pointer
- * array. The last element in the array is used to fill in the space,
- * so this function does not preserve the order of the array. But it is
- * faster than g_ptr_array_remove(). If @array has a non-%NULL
- * #GDestroyNotify function it is called for the removed element.
- *
- * It returns %TRUE if the pointer was removed, or %FALSE if the
- * pointer was not found.
- **/
-gboolean
-g_ptr_array_remove_fast (GPtrArray *farray,
- gpointer data)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
- guint i;
-
- g_return_val_if_fail (array, FALSE);
-
- for (i = 0; i < array->len; i += 1)
- {
- if (array->pdata[i] == data)
- {
- g_ptr_array_remove_index_fast (farray, i);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- * g_ptr_array_add:
- * @array: a #GPtrArray.
- * @data: the pointer to add.
- *
- * Adds a pointer to the end of the pointer array. The array will grow
- * in size automatically if necessary.
- **/
-void
-g_ptr_array_add (GPtrArray *farray,
- gpointer data)
-{
- GRealPtrArray* array = (GRealPtrArray*) farray;
-
- g_return_if_fail (array);
-
- g_ptr_array_maybe_expand (array, 1);
-
- array->pdata[array->len++] = data;
-}
-
-/**
- * g_ptr_array_sort:
- * @array: a #GPtrArray.
- * @compare_func: comparison function.
- *
- * Sorts the array, using @compare_func which should be a qsort()-style
- * comparison function (returns less than zero for first arg is less
- * than second arg, zero for equal, greater than zero if irst arg is
- * greater than second arg).
- *
- * If two array elements compare equal, their order in the sorted array
- * is undefined.
- *
- * <note><para>The comparison function for g_ptr_array_sort() doesn't
- * take the pointers from the array as arguments, it takes pointers to
- * the pointers in the array.</para></note>
- **/
-void
-g_ptr_array_sort (GPtrArray *array,
- GCompareFunc compare_func)
-{
- g_return_if_fail (array != NULL);
-
- qsort (array->pdata,
- array->len,
- sizeof (gpointer),
- compare_func);
-}
-
-/**
- * g_ptr_array_sort_with_data:
- * @array: a #GPtrArray.
- * @compare_func: comparison function.
- * @user_data: data to pass to @compare_func.
- *
- * Like g_ptr_array_sort(), but the comparison function has an extra
- * user data argument.
- *
- * <note><para>The comparison function for g_ptr_array_sort_with_data()
- * doesn't take the pointers from the array as arguments, it takes
- * pointers to the pointers in the array.</para></note>
- **/
-void
-g_ptr_array_sort_with_data (GPtrArray *array,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- g_return_if_fail (array != NULL);
-
- g_qsort_with_data (array->pdata,
- array->len,
- sizeof (gpointer),
- compare_func,
- user_data);
-}
-
-/**
- * g_ptr_array_foreach:
- * @array: a #GPtrArray
- * @func: the function to call for each array element
- * @user_data: user data to pass to the function
- *
- * Calls a function for each element of a #GPtrArray.
- *
- * Since: 2.4
- **/
-void
-g_ptr_array_foreach (GPtrArray *array,
- GFunc func,
- gpointer user_data)
-{
- guint i;
-
- g_return_if_fail (array);
-
- for (i = 0; i < array->len; i++)
- (*func) (array->pdata[i], user_data);
-}
-
-/**
- * SECTION: arrays_byte
- * @title: Byte Arrays
- * @short_description: arrays of bytes, which grow automatically as
- * elements are added
- *
- * #GByteArray is based on #GArray, to provide arrays of bytes which
- * grow automatically as elements are added.
- *
- * To create a new #GByteArray use g_byte_array_new().
- *
- * To add elements to a #GByteArray, use g_byte_array_append(), and
- * g_byte_array_prepend().
- *
- * To set the size of a #GByteArray, use g_byte_array_set_size().
- *
- * To free a #GByteArray, use g_byte_array_free().
- *
- * <example>
- * <title>Using a #GByteArray</title>
- * <programlisting>
- * GByteArray *gbarray;
- * gint i;
- *
- * gbarray = g_byte_array_new (<!-- -->);
- * for (i = 0; i < 10000; i++)
- * g_byte_array_append (gbarray, (guint8*) "abcd", 4);
- *
- * for (i = 0; i < 10000; i++)
- * {
- * g_assert (gbarray->data[4*i] == 'a');
- * g_assert (gbarray->data[4*i+1] == 'b');
- * g_assert (gbarray->data[4*i+2] == 'c');
- * g_assert (gbarray->data[4*i+3] == 'd');
- * }
- *
- * g_byte_array_free (gbarray, TRUE);
- * </programlisting>
- * </example>
- **/
-
-/**
- * GByteArray:
- * @data: a pointer to the element data. The data may be moved as
- * elements are added to the #GByteArray.
- * @len: the number of elements in the #GByteArray.
- *
- * The <structname>GByteArray</structname> struct allows access to the
- * public fields of a <structname>GByteArray</structname>.
- **/
-
-/**
- * g_byte_array_new:
- * @Returns: the new #GByteArray.
- *
- * Creates a new #GByteArray with a reference count of 1.
- **/
-GByteArray* g_byte_array_new (void)
-{
- return (GByteArray*) g_array_sized_new (FALSE, FALSE, 1, 0);
-}
-
-/**
- * g_byte_array_sized_new:
- * @reserved_size: number of bytes preallocated.
- * @Returns: the new #GByteArray.
- *
- * Creates a new #GByteArray with @reserved_size bytes preallocated.
- * This avoids frequent reallocation, if you are going to add many
- * bytes to the array. Note however that the size of the array is still
- * 0.
- **/
-GByteArray* g_byte_array_sized_new (guint reserved_size)
-{
- return (GByteArray*) g_array_sized_new (FALSE, FALSE, 1, reserved_size);
-}
-
-/**
- * g_byte_array_free:
- * @array: a #GByteArray.
- * @free_segment: if %TRUE the actual byte data is freed as well.
- * @Returns: the element data if @free_segment is %FALSE, otherwise
- * %NULL. The element data should be freed using g_free().
- *
- * Frees the memory allocated by the #GByteArray. If @free_segment is
- * %TRUE it frees the actual byte data. If the reference count of
- * @array is greater than one, the #GByteArray wrapper is preserved but
- * the size of @array will be set to zero.
- **/
-guint8* g_byte_array_free (GByteArray *array,
- gboolean free_segment)
-{
- return (guint8*) g_array_free ((GArray*) array, free_segment);
-}
-
-/**
- * g_byte_array_ref:
- * @array: A #GByteArray.
- *
- * Atomically increments the reference count of @array by one. This
- * function is MT-safe and may be called from any thread.
- *
- * Returns: The passed in #GByteArray.
- *
- * Since: 2.22
- **/
-GByteArray *
-g_byte_array_ref (GByteArray *array)
-{
- return (GByteArray *) g_array_ref ((GArray *) array);
-}
-
-/**
- * g_byte_array_unref:
- * @array: A #GByteArray.
- *
- * Atomically decrements the reference count of @array by one. If the
- * reference count drops to 0, all memory allocated by the array is
- * released. This function is MT-safe and may be called from any
- * thread.
- *
- * Since: 2.22
- **/
-void
-g_byte_array_unref (GByteArray *array)
-{
- g_array_unref ((GArray *) array);
-}
-
-/**
- * g_byte_array_append:
- * @array: a #GByteArray.
- * @data: the byte data to be added.
- * @len: the number of bytes to add.
- * @Returns: the #GByteArray.
- *
- * Adds the given bytes to the end of the #GByteArray. The array will
- * grow in size automatically if necessary.
- **/
-GByteArray* g_byte_array_append (GByteArray *array,
- const guint8 *data,
- guint len)
-{
- g_array_append_vals ((GArray*) array, (guint8*)data, len);
-
- return array;
-}
-
-/**
- * g_byte_array_prepend:
- * @array: a #GByteArray.
- * @data: the byte data to be added.
- * @len: the number of bytes to add.
- * @Returns: the #GByteArray.
- *
- * Adds the given data to the start of the #GByteArray. The array will
- * grow in size automatically if necessary.
- **/
-GByteArray* g_byte_array_prepend (GByteArray *array,
- const guint8 *data,
- guint len)
-{
- g_array_prepend_vals ((GArray*) array, (guint8*)data, len);
-
- return array;
-}
-
-/**
- * g_byte_array_set_size:
- * @array: a #GByteArray.
- * @length: the new size of the #GByteArray.
- * @Returns: the #GByteArray.
- *
- * Sets the size of the #GByteArray, expanding it if necessary.
- **/
-GByteArray* g_byte_array_set_size (GByteArray *array,
- guint length)
-{
- g_array_set_size ((GArray*) array, length);
-
- return array;
-}
-
-/**
- * g_byte_array_remove_index:
- * @array: a #GByteArray.
- * @index_: the index of the byte to remove.
- * @Returns: the #GByteArray.
- *
- * Removes the byte at the given index from a #GByteArray. The
- * following bytes are moved down one place.
- **/
-GByteArray* g_byte_array_remove_index (GByteArray *array,
- guint index_)
-{
- g_array_remove_index ((GArray*) array, index_);
-
- return array;
-}
-
-/**
- * g_byte_array_remove_index_fast:
- * @array: a #GByteArray.
- * @index_: the index of the byte to remove.
- * @Returns: the #GByteArray.
- *
- * Removes the byte at the given index from a #GByteArray. The last
- * element in the array is used to fill in the space, so this function
- * does not preserve the order of the #GByteArray. But it is faster
- * than g_byte_array_remove_index().
- **/
-GByteArray* g_byte_array_remove_index_fast (GByteArray *array,
- guint index_)
-{
- g_array_remove_index_fast ((GArray*) array, index_);
-
- return array;
-}
-
-/**
- * g_byte_array_remove_range:
- * @array: a @GByteArray.
- * @index_: the index of the first byte to remove.
- * @length: the number of bytes to remove.
- * @Returns: the #GByteArray.
- *
- * Removes the given number of bytes starting at the given index from a
- * #GByteArray. The following elements are moved to close the gap.
- *
- * Since: 2.4
- **/
-GByteArray*
-g_byte_array_remove_range (GByteArray *array,
- guint index_,
- guint length)
-{
- g_return_val_if_fail (array, NULL);
- g_return_val_if_fail (index_ < array->len, NULL);
- g_return_val_if_fail (index_ + length <= array->len, NULL);
-
- return (GByteArray *)g_array_remove_range ((GArray*) array, index_, length);
-}
-
-/**
- * g_byte_array_sort:
- * @array: a #GByteArray.
- * @compare_func: comparison function.
- *
- * Sorts a byte array, using @compare_func which should be a
- * qsort()-style comparison function (returns less than zero for first
- * arg is less than second arg, zero for equal, greater than zero if
- * first arg is greater than second arg).
- *
- * If two array elements compare equal, their order in the sorted array
- * is undefined.
- **/
-void
-g_byte_array_sort (GByteArray *array,
- GCompareFunc compare_func)
-{
- g_array_sort ((GArray *) array, compare_func);
-}
-
-/**
- * g_byte_array_sort_with_data:
- * @array: a #GByteArray.
- * @compare_func: comparison function.
- * @user_data: data to pass to @compare_func.
- *
- * Like g_byte_array_sort(), but the comparison function takes an extra
- * user data argument.
- **/
-void
-g_byte_array_sort_with_data (GByteArray *array,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- g_array_sort_with_data ((GArray *) array, compare_func, user_data);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GAsyncQueue: asynchronous queue implementation, based on Gqueue.
- * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe
- *
- * 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 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.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gasyncqueue.h"
-
-#include "gmem.h"
-#include "gqueue.h"
-#include "gtestutils.h"
-#include "gthread.h"
-
-
-/**
- * SECTION: async_queues
- * @title: Asynchronous Queues
- * @short_description: asynchronous communication between threads
- *
- * Often you need to communicate between different threads. In general
- * it's safer not to do this by shared memory, but by explicit message
- * passing. These messages only make sense asynchronously for
- * multi-threaded applications though, as a synchronous operation could
- * as well be done in the same thread.
- *
- * Asynchronous queues are an exception from most other GLib data
- * structures, as they can be used simultaneously from multiple threads
- * without explicit locking and they bring their own builtin reference
- * counting. This is because the nature of an asynchronous queue is that
- * it will always be used by at least 2 concurrent threads.
- *
- * For using an asynchronous queue you first have to create one with
- * g_async_queue_new(). A newly-created queue will get the reference
- * count 1. Whenever another thread is creating a new reference of (that
- * is, pointer to) the queue, it has to increase the reference count
- * (using g_async_queue_ref()). Also, before removing this reference,
- * the reference count has to be decreased (using g_async_queue_unref()).
- * After that the queue might no longer exist so you must not access
- * it after that point.
- *
- * A thread, which wants to send a message to that queue simply calls
- * g_async_queue_push() to push the message to the queue.
- *
- * A thread, which is expecting messages from an asynchronous queue
- * simply calls g_async_queue_pop() for that queue. If no message is
- * available in the queue at that point, the thread is now put to sleep
- * until a message arrives. The message will be removed from the queue
- * and returned. The functions g_async_queue_try_pop() and
- * g_async_queue_timed_pop() can be used to only check for the presence
- * of messages or to only wait a certain time for messages respectively.
- *
- * For almost every function there exist two variants, one that locks
- * the queue and one that doesn't. That way you can hold the queue lock
- * (acquire it with g_async_queue_lock() and release it with
- * g_async_queue_unlock()) over multiple queue accessing instructions.
- * This can be necessary to ensure the integrity of the queue, but should
- * only be used when really necessary, as it can make your life harder
- * if used unwisely. Normally you should only use the locking function
- * variants (those without the suffix _unlocked)
- */
-
-/**
- * GAsyncQueue:
- *
- * The GAsyncQueue struct is an opaque data structure, which represents
- * an asynchronous queue. It should only be accessed through the
- * <function>g_async_queue_*</function> functions.
- */
-struct _GAsyncQueue
-{
- GMutex *mutex;
- GCond *cond;
- GQueue queue;
- GDestroyNotify item_free_func;
- guint waiting_threads;
- gint32 ref_count;
-};
-
-typedef struct {
- GCompareDataFunc func;
- gpointer user_data;
-} SortData;
-
-/**
- * g_async_queue_new:
- *
- * Creates a new asynchronous queue with the initial reference count of 1.
- *
- * Return value: the new #GAsyncQueue.
- **/
-GAsyncQueue*
-g_async_queue_new (void)
-{
- GAsyncQueue* retval = g_new (GAsyncQueue, 1);
- retval->mutex = g_mutex_new ();
- retval->cond = NULL;
- g_queue_init (&retval->queue);
- retval->waiting_threads = 0;
- retval->ref_count = 1;
- retval->item_free_func = NULL;
- return retval;
-}
-
-/**
- * g_async_queue_new_full:
- * @item_free_func: function to free queue elements
- *
- * Creates a new asynchronous queue with an initial reference count of 1 and
- * sets up a destroy notify function that is used to free any remaining
- * queue items when the queue is destroyed after the final unref.
- *
- * Return value: the new #GAsyncQueue.
- *
- * Since: 2.16
- **/
-GAsyncQueue*
-g_async_queue_new_full (GDestroyNotify item_free_func)
-{
- GAsyncQueue *async_queue = g_async_queue_new ();
- async_queue->item_free_func = item_free_func;
- return async_queue;
-}
-
-/**
- * g_async_queue_ref:
- * @queue: a #GAsyncQueue.
- *
- * Increases the reference count of the asynchronous @queue by 1. You
- * do not need to hold the lock to call this function.
- *
- * Returns: the @queue that was passed in (since 2.6)
- **/
-GAsyncQueue *
-g_async_queue_ref (GAsyncQueue *queue)
-{
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- g_atomic_int_inc (&queue->ref_count);
-
- return queue;
-}
-
-/**
- * g_async_queue_ref_unlocked:
- * @queue: a #GAsyncQueue.
- *
- * Increases the reference count of the asynchronous @queue by 1.
- *
- * @Deprecated: Since 2.8, reference counting is done atomically
- * so g_async_queue_ref() can be used regardless of the @queue's
- * lock.
- **/
-void
-g_async_queue_ref_unlocked (GAsyncQueue *queue)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
-
- g_atomic_int_inc (&queue->ref_count);
-}
-
-/**
- * g_async_queue_unref_and_unlock:
- * @queue: a #GAsyncQueue.
- *
- * Decreases the reference count of the asynchronous @queue by 1 and
- * releases the lock. This function must be called while holding the
- * @queue's lock. If the reference count went to 0, the @queue will be
- * destroyed and the memory allocated will be freed.
- *
- * @Deprecated: Since 2.8, reference counting is done atomically
- * so g_async_queue_unref() can be used regardless of the @queue's
- * lock.
- **/
-void
-g_async_queue_unref_and_unlock (GAsyncQueue *queue)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
-
- g_mutex_unlock (queue->mutex);
- g_async_queue_unref (queue);
-}
-
-/**
- * g_async_queue_unref:
- * @queue: a #GAsyncQueue.
- *
- * Decreases the reference count of the asynchronous @queue by 1. If
- * the reference count went to 0, the @queue will be destroyed and the
- * memory allocated will be freed. So you are not allowed to use the
- * @queue afterwards, as it might have disappeared. You do not need to
- * hold the lock to call this function.
- **/
-void
-g_async_queue_unref (GAsyncQueue *queue)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
-
- if (g_atomic_int_dec_and_test (&queue->ref_count))
- {
- g_return_if_fail (queue->waiting_threads == 0);
- g_mutex_free (queue->mutex);
- if (queue->cond)
- g_cond_free (queue->cond);
- if (queue->item_free_func)
- g_queue_foreach (&queue->queue, (GFunc) queue->item_free_func, NULL);
- g_queue_clear (&queue->queue);
- g_free (queue);
- }
-}
-
-/**
- * g_async_queue_lock:
- * @queue: a #GAsyncQueue.
- *
- * Acquires the @queue's lock. After that you can only call the
- * <function>g_async_queue_*_unlocked()</function> function variants on that
- * @queue. Otherwise it will deadlock.
- **/
-void
-g_async_queue_lock (GAsyncQueue *queue)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
-
- g_mutex_lock (queue->mutex);
-}
-
-/**
- * g_async_queue_unlock:
- * @queue: a #GAsyncQueue.
- *
- * Releases the queue's lock.
- **/
-void
-g_async_queue_unlock (GAsyncQueue *queue)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
-
- g_mutex_unlock (queue->mutex);
-}
-
-/**
- * g_async_queue_push:
- * @queue: a #GAsyncQueue.
- * @data: @data to push into the @queue.
- *
- * Pushes the @data into the @queue. @data must not be %NULL.
- **/
-void
-g_async_queue_push (GAsyncQueue* queue, gpointer data)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
- g_return_if_fail (data);
-
- g_mutex_lock (queue->mutex);
- g_async_queue_push_unlocked (queue, data);
- g_mutex_unlock (queue->mutex);
-}
-
-/**
- * g_async_queue_push_unlocked:
- * @queue: a #GAsyncQueue.
- * @data: @data to push into the @queue.
- *
- * Pushes the @data into the @queue. @data must not be %NULL. This
- * function must be called while holding the @queue's lock.
- **/
-void
-g_async_queue_push_unlocked (GAsyncQueue* queue, gpointer data)
-{
- g_return_if_fail (queue);
- g_return_if_fail (g_atomic_int_get (&queue->ref_count) > 0);
- g_return_if_fail (data);
-
- g_queue_push_head (&queue->queue, data);
- if (queue->waiting_threads > 0)
- g_cond_signal (queue->cond);
-}
-
-/**
- * g_async_queue_push_sorted:
- * @queue: a #GAsyncQueue
- * @data: the @data to push into the @queue
- * @func: the #GCompareDataFunc is used to sort @queue. This function
- * is passed two elements of the @queue. The function should return
- * 0 if they are equal, a negative value if the first element
- * should be higher in the @queue or a positive value if the first
- * element should be lower in the @queue than the second element.
- * @user_data: user data passed to @func.
- *
- * Inserts @data into @queue using @func to determine the new
- * position.
- *
- * This function requires that the @queue is sorted before pushing on
- * new elements.
- *
- * This function will lock @queue before it sorts the queue and unlock
- * it when it is finished.
- *
- * For an example of @func see g_async_queue_sort().
- *
- * Since: 2.10
- **/
-void
-g_async_queue_push_sorted (GAsyncQueue *queue,
- gpointer data,
- GCompareDataFunc func,
- gpointer user_data)
-{
- g_return_if_fail (queue != NULL);
-
- g_mutex_lock (queue->mutex);
- g_async_queue_push_sorted_unlocked (queue, data, func, user_data);
- g_mutex_unlock (queue->mutex);
-}
-
-static gint
-g_async_queue_invert_compare (gpointer v1,
- gpointer v2,
- SortData *sd)
-{
- return -sd->func (v1, v2, sd->user_data);
-}
-
-/**
- * g_async_queue_push_sorted_unlocked:
- * @queue: a #GAsyncQueue
- * @data: the @data to push into the @queue
- * @func: the #GCompareDataFunc is used to sort @queue. This function
- * is passed two elements of the @queue. The function should return
- * 0 if they are equal, a negative value if the first element
- * should be higher in the @queue or a positive value if the first
- * element should be lower in the @queue than the second element.
- * @user_data: user data passed to @func.
- *
- * Inserts @data into @queue using @func to determine the new
- * position.
- *
- * This function requires that the @queue is sorted before pushing on
- * new elements.
- *
- * This function is called while holding the @queue's lock.
- *
- * For an example of @func see g_async_queue_sort().
- *
- * Since: 2.10
- **/
-void
-g_async_queue_push_sorted_unlocked (GAsyncQueue *queue,
- gpointer data,
- GCompareDataFunc func,
- gpointer user_data)
-{
- SortData sd;
-
- g_return_if_fail (queue != NULL);
-
- sd.func = func;
- sd.user_data = user_data;
-
- g_queue_insert_sorted (&queue->queue,
- data,
- (GCompareDataFunc)g_async_queue_invert_compare,
- &sd);
- if (queue->waiting_threads > 0)
- g_cond_signal (queue->cond);
-}
-
-static gpointer
-g_async_queue_pop_intern_unlocked (GAsyncQueue *queue,
- gboolean try,
- GTimeVal *end_time)
-{
- gpointer retval;
-
- if (!g_queue_peek_tail_link (&queue->queue))
- {
- if (try)
- return NULL;
-
- if (!queue->cond)
- queue->cond = g_cond_new ();
-
- if (!end_time)
- {
- queue->waiting_threads++;
- while (!g_queue_peek_tail_link (&queue->queue))
- g_cond_wait (queue->cond, queue->mutex);
- queue->waiting_threads--;
- }
- else
- {
- queue->waiting_threads++;
- while (!g_queue_peek_tail_link (&queue->queue))
- if (!g_cond_timed_wait (queue->cond, queue->mutex, end_time))
- break;
- queue->waiting_threads--;
- if (!g_queue_peek_tail_link (&queue->queue))
- return NULL;
- }
- }
-
- retval = g_queue_pop_tail (&queue->queue);
-
- g_assert (retval);
-
- return retval;
-}
-
-/**
- * g_async_queue_pop:
- * @queue: a #GAsyncQueue.
- *
- * Pops data from the @queue. This function blocks until data become
- * available.
- *
- * Return value: data from the queue.
- **/
-gpointer
-g_async_queue_pop (GAsyncQueue* queue)
-{
- gpointer retval;
-
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- g_mutex_lock (queue->mutex);
- retval = g_async_queue_pop_intern_unlocked (queue, FALSE, NULL);
- g_mutex_unlock (queue->mutex);
-
- return retval;
-}
-
-/**
- * g_async_queue_pop_unlocked:
- * @queue: a #GAsyncQueue.
- *
- * Pops data from the @queue. This function blocks until data become
- * available. This function must be called while holding the @queue's
- * lock.
- *
- * Return value: data from the queue.
- **/
-gpointer
-g_async_queue_pop_unlocked (GAsyncQueue* queue)
-{
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- return g_async_queue_pop_intern_unlocked (queue, FALSE, NULL);
-}
-
-/**
- * g_async_queue_try_pop:
- * @queue: a #GAsyncQueue.
- *
- * Tries to pop data from the @queue. If no data is available, %NULL is
- * returned.
- *
- * Return value: data from the queue or %NULL, when no data is
- * available immediately.
- **/
-gpointer
-g_async_queue_try_pop (GAsyncQueue* queue)
-{
- gpointer retval;
-
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- g_mutex_lock (queue->mutex);
- retval = g_async_queue_pop_intern_unlocked (queue, TRUE, NULL);
- g_mutex_unlock (queue->mutex);
-
- return retval;
-}
-
-/**
- * g_async_queue_try_pop_unlocked:
- * @queue: a #GAsyncQueue.
- *
- * Tries to pop data from the @queue. If no data is available, %NULL is
- * returned. This function must be called while holding the @queue's
- * lock.
- *
- * Return value: data from the queue or %NULL, when no data is
- * available immediately.
- **/
-gpointer
-g_async_queue_try_pop_unlocked (GAsyncQueue* queue)
-{
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- return g_async_queue_pop_intern_unlocked (queue, TRUE, NULL);
-}
-
-/**
- * g_async_queue_timed_pop:
- * @queue: a #GAsyncQueue.
- * @end_time: a #GTimeVal, determining the final time.
- *
- * Pops data from the @queue. If no data is received before @end_time,
- * %NULL is returned.
- *
- * To easily calculate @end_time a combination of g_get_current_time()
- * and g_time_val_add() can be used.
- *
- * Return value: data from the queue or %NULL, when no data is
- * received before @end_time.
- **/
-gpointer
-g_async_queue_timed_pop (GAsyncQueue* queue, GTimeVal *end_time)
-{
- gpointer retval;
-
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- g_mutex_lock (queue->mutex);
- retval = g_async_queue_pop_intern_unlocked (queue, FALSE, end_time);
- g_mutex_unlock (queue->mutex);
-
- return retval;
-}
-
-/**
- * g_async_queue_timed_pop_unlocked:
- * @queue: a #GAsyncQueue.
- * @end_time: a #GTimeVal, determining the final time.
- *
- * Pops data from the @queue. If no data is received before @end_time,
- * %NULL is returned. This function must be called while holding the
- * @queue's lock.
- *
- * To easily calculate @end_time a combination of g_get_current_time()
- * and g_time_val_add() can be used.
- *
- * Return value: data from the queue or %NULL, when no data is
- * received before @end_time.
- **/
-gpointer
-g_async_queue_timed_pop_unlocked (GAsyncQueue* queue, GTimeVal *end_time)
-{
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- return g_async_queue_pop_intern_unlocked (queue, FALSE, end_time);
-}
-
-/**
- * g_async_queue_length:
- * @queue: a #GAsyncQueue.
- *
- * Returns the length of the queue, negative values mean waiting
- * threads, positive values mean available entries in the
- * @queue. Actually this function returns the number of data items in
- * the queue minus the number of waiting threads. Thus a return value
- * of 0 could mean 'n' entries in the queue and 'n' thread waiting.
- * That can happen due to locking of the queue or due to
- * scheduling.
- *
- * Return value: the length of the @queue.
- **/
-gint
-g_async_queue_length (GAsyncQueue* queue)
-{
- gint retval;
-
- g_return_val_if_fail (queue, 0);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, 0);
-
- g_mutex_lock (queue->mutex);
- retval = queue->queue.length - queue->waiting_threads;
- g_mutex_unlock (queue->mutex);
-
- return retval;
-}
-
-/**
- * g_async_queue_length_unlocked:
- * @queue: a #GAsyncQueue.
- *
- * Returns the length of the queue, negative values mean waiting
- * threads, positive values mean available entries in the
- * @queue. Actually this function returns the number of data items in
- * the queue minus the number of waiting threads. Thus a return value
- * of 0 could mean 'n' entries in the queue and 'n' thread waiting.
- * That can happen due to locking of the queue or due to
- * scheduling. This function must be called while holding the @queue's
- * lock.
- *
- * Return value: the length of the @queue.
- **/
-gint
-g_async_queue_length_unlocked (GAsyncQueue* queue)
-{
- g_return_val_if_fail (queue, 0);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, 0);
-
- return queue->queue.length - queue->waiting_threads;
-}
-
-/**
- * g_async_queue_sort:
- * @queue: a #GAsyncQueue
- * @func: the #GCompareDataFunc is used to sort @queue. This
- * function is passed two elements of the @queue. The function
- * should return 0 if they are equal, a negative value if the
- * first element should be higher in the @queue or a positive
- * value if the first element should be lower in the @queue than
- * the second element.
- * @user_data: user data passed to @func
- *
- * Sorts @queue using @func.
- *
- * This function will lock @queue before it sorts the queue and unlock
- * it when it is finished.
- *
- * If you were sorting a list of priority numbers to make sure the
- * lowest priority would be at the top of the queue, you could use:
- * |[
- * gint32 id1;
- * gint32 id2;
- *
- * id1 = GPOINTER_TO_INT (element1);
- * id2 = GPOINTER_TO_INT (element2);
- *
- * return (id1 > id2 ? +1 : id1 == id2 ? 0 : -1);
- * ]|
- *
- * Since: 2.10
- **/
-void
-g_async_queue_sort (GAsyncQueue *queue,
- GCompareDataFunc func,
- gpointer user_data)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (func != NULL);
-
- g_mutex_lock (queue->mutex);
- g_async_queue_sort_unlocked (queue, func, user_data);
- g_mutex_unlock (queue->mutex);
-}
-
-/**
- * g_async_queue_sort_unlocked:
- * @queue: a #GAsyncQueue
- * @func: the #GCompareDataFunc is used to sort @queue. This
- * function is passed two elements of the @queue. The function
- * should return 0 if they are equal, a negative value if the
- * first element should be higher in the @queue or a positive
- * value if the first element should be lower in the @queue than
- * the second element.
- * @user_data: user data passed to @func
- *
- * Sorts @queue using @func.
- *
- * This function is called while holding the @queue's lock.
- *
- * Since: 2.10
- **/
-void
-g_async_queue_sort_unlocked (GAsyncQueue *queue,
- GCompareDataFunc func,
- gpointer user_data)
-{
- SortData sd;
-
- g_return_if_fail (queue != NULL);
- g_return_if_fail (func != NULL);
-
- sd.func = func;
- sd.user_data = user_data;
-
- g_queue_sort (&queue->queue,
- (GCompareDataFunc)g_async_queue_invert_compare,
- &sd);
-}
-
-/*
- * Private API
- */
-
-GMutex*
-_g_async_queue_get_mutex (GAsyncQueue* queue)
-{
- g_return_val_if_fail (queue, NULL);
- g_return_val_if_fail (g_atomic_int_get (&queue->ref_count) > 0, NULL);
-
- return queue->mutex;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * gatomic-gcc.c: atomic operations using GCC builtins.
- * Copyright (C) 2009 Hiroyuki Ikezoe
- *
- * 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 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 "config.h"
-
-#include "gatomic.h"
-
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- return __sync_fetch_and_add (atomic, val);
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- __sync_fetch_and_add (atomic, val);
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- return __sync_bool_compare_and_swap (atomic, oldval, newval);
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- return __sync_bool_compare_and_swap (atomic, oldval, newval);
-}
-
-void
-_g_atomic_thread_init (void)
-{
-}
-
-gint
-(g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
-{
- __sync_synchronize ();
- return *atomic;
-}
-
-void
-(g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint newval)
-{
- *atomic = newval;
- __sync_synchronize ();
-}
-
-gpointer
-(g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
-{
- __sync_synchronize ();
- return *atomic;
-}
-
-void
-(g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer newval)
-{
- *atomic = newval;
- __sync_synchronize ();
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * g_atomic_*: atomic operations.
- * Copyright (C) 2003 Sebastian Wilhelmi
- * Copyright (C) 2007 Nokia Corporation
- *
- * 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 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 "config.h"
-
-#if defined (G_ATOMIC_ARM)
-#include <sched.h>
-#endif
-
-#include "gatomic.h"
-#include "gthreadprivate.h"
-
-/**
- * SECTION:atomic_operations
- * @title: Atomic Operations
- * @short_description: basic atomic integer and pointer operations
- * @see_also: #GMutex
- *
- * The following functions can be used to atomically access integers and
- * pointers. They are implemented as inline assembler function on most
- * platforms and use slower fall-backs otherwise. Using them can sometimes
- * save you from using a performance-expensive #GMutex to protect the
- * integer or pointer.
- *
- * The most important usage is reference counting. Using
- * g_atomic_int_inc() and g_atomic_int_dec_and_test() makes reference
- * counting a very fast operation.
- *
- * <note><para>You must not directly read integers or pointers concurrently
- * accessed by multiple threads, but use the atomic accessor functions
- * instead. That is, always use g_atomic_int_get() and g_atomic_pointer_get()
- * for read outs. They provide the neccessary synchonization mechanisms
- * like memory barriers to access memory locations concurrently.
- * </para></note>
- *
- * <note><para>If you are using those functions for anything apart from
- * simple reference counting, you should really be aware of the implications
- * of doing that. There are literally thousands of ways to shoot yourself
- * in the foot. So if in doubt, use a #GMutex. If you don't know, what
- * memory barriers are, do not use anything but g_atomic_int_inc() and
- * g_atomic_int_dec_and_test().
- * </para></note>
- *
- * <note><para>It is not safe to set an integer or pointer just by assigning
- * to it, when it is concurrently accessed by other threads with the following
- * functions. Use g_atomic_int_compare_and_exchange() or
- * g_atomic_pointer_compare_and_exchange() respectively.
- * </para></note>
- */
-
-#if defined (__GNUC__)
-# if defined (G_ATOMIC_I486)
-/* Adapted from CVS version 1.10 of glibc's sysdeps/i386/i486/bits/atomic.h
- */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- __asm__ __volatile__ ("lock; xaddl %0,%1"
- : "=r" (result), "=m" (*atomic)
- : "0" (val), "m" (*atomic));
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- __asm__ __volatile__ ("lock; addl %1,%0"
- : "=m" (*atomic)
- : "ir" (val), "m" (*atomic));
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gint result;
-
- __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
- : "=a" (result), "=m" (*atomic)
- : "r" (newval), "m" (*atomic), "0" (oldval));
-
- return result == oldval;
-}
-
-/* The same code as above, as on i386 gpointer is 32 bit as well.
- * Duplicating the code here seems more natural than casting the
- * arguments and calling the former function */
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
-
- __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
- : "=a" (result), "=m" (*atomic)
- : "r" (newval), "m" (*atomic), "0" (oldval));
-
- return result == oldval;
-}
-
-# elif defined (G_ATOMIC_SPARCV9)
-/* Adapted from CVS version 1.3 of glibc's sysdeps/sparc/sparc64/bits/atomic.h
- */
-# define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \
- ({ \
- gint __result; \
- __asm__ __volatile__ ("cas [%4], %2, %0" \
- : "=r" (__result), "=m" (*(atomic)) \
- : "r" (oldval), "m" (*(atomic)), "r" (atomic),\
- "0" (newval)); \
- __result == oldval; \
- })
-
-# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
- __asm__ __volatile__ ("cas [%4], %2, %0"
- : "=r" (result), "=m" (*atomic)
- : "r" (oldval), "m" (*atomic), "r" (atomic),
- "0" (newval));
- return result == oldval;
-}
-# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
- gpointer *a = atomic;
- __asm__ __volatile__ ("casx [%4], %2, %0"
- : "=r" (result), "=m" (*a)
- : "r" (oldval), "m" (*a), "r" (a),
- "0" (newval));
- return result == oldval;
-}
-# else /* What's that */
-# error "Your system has an unsupported pointer size"
-# endif /* GLIB_SIZEOF_VOID_P */
-# define G_ATOMIC_MEMORY_BARRIER \
- __asm__ __volatile__ ("membar #LoadLoad | #LoadStore" \
- " | #StoreLoad | #StoreStore" : : : "memory")
-
-# elif defined (G_ATOMIC_ALPHA)
-/* Adapted from CVS version 1.3 of glibc's sysdeps/alpha/bits/atomic.h
- */
-# define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \
- ({ \
- gint __result; \
- gint __prev; \
- __asm__ __volatile__ ( \
- " mb\n" \
- "1: ldl_l %0,%2\n" \
- " cmpeq %0,%3,%1\n" \
- " beq %1,2f\n" \
- " mov %4,%1\n" \
- " stl_c %1,%2\n" \
- " beq %1,1b\n" \
- " mb\n" \
- "2:" \
- : "=&r" (__prev), \
- "=&r" (__result) \
- : "m" (*(atomic)), \
- "Ir" (oldval), \
- "Ir" (newval) \
- : "memory"); \
- __result != 0; \
- })
-# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gint result;
- gpointer prev;
- __asm__ __volatile__ (
- " mb\n"
- "1: ldl_l %0,%2\n"
- " cmpeq %0,%3,%1\n"
- " beq %1,2f\n"
- " mov %4,%1\n"
- " stl_c %1,%2\n"
- " beq %1,1b\n"
- " mb\n"
- "2:"
- : "=&r" (prev),
- "=&r" (result)
- : "m" (*atomic),
- "Ir" (oldval),
- "Ir" (newval)
- : "memory");
- return result != 0;
-}
-# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gint result;
- gpointer prev;
- __asm__ __volatile__ (
- " mb\n"
- "1: ldq_l %0,%2\n"
- " cmpeq %0,%3,%1\n"
- " beq %1,2f\n"
- " mov %4,%1\n"
- " stq_c %1,%2\n"
- " beq %1,1b\n"
- " mb\n"
- "2:"
- : "=&r" (prev),
- "=&r" (result)
- : "m" (*atomic),
- "Ir" (oldval),
- "Ir" (newval)
- : "memory");
- return result != 0;
-}
-# else /* What's that */
-# error "Your system has an unsupported pointer size"
-# endif /* GLIB_SIZEOF_VOID_P */
-# define G_ATOMIC_MEMORY_BARRIER __asm__ ("mb" : : : "memory")
-# elif defined (G_ATOMIC_X86_64)
-/* Adapted from CVS version 1.9 of glibc's sysdeps/x86_64/bits/atomic.h
- */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- __asm__ __volatile__ ("lock; xaddl %0,%1"
- : "=r" (result), "=m" (*atomic)
- : "0" (val), "m" (*atomic));
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- __asm__ __volatile__ ("lock; addl %1,%0"
- : "=m" (*atomic)
- : "ir" (val), "m" (*atomic));
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gint result;
-
- __asm__ __volatile__ ("lock; cmpxchgl %2, %1"
- : "=a" (result), "=m" (*atomic)
- : "r" (newval), "m" (*atomic), "0" (oldval));
-
- return result == oldval;
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
-
- __asm__ __volatile__ ("lock; cmpxchgq %q2, %1"
- : "=a" (result), "=m" (*atomic)
- : "r" (newval), "m" (*atomic), "0" (oldval));
-
- return result == oldval;
-}
-
-# elif defined (G_ATOMIC_POWERPC)
-/* Adapted from CVS version 1.16 of glibc's sysdeps/powerpc/bits/atomic.h
- * and CVS version 1.4 of glibc's sysdeps/powerpc/powerpc32/bits/atomic.h
- * and CVS version 1.7 of glibc's sysdeps/powerpc/powerpc64/bits/atomic.h
- */
-# ifdef __OPTIMIZE__
-/* Non-optimizing compile bails on the following two asm statements
- * for reasons unknown to the author */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result, temp;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("1: lwarx %0,0,%3\n"
- " add %1,%0,%4\n"
- " stwcx. %1,0,%3\n"
- " bne- 1b"
- : "=&b" (result), "=&r" (temp), "=m" (*atomic)
- : "b" (atomic), "r" (val), "m" (*atomic)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ (".Lieaa%=: lwarx %0,0,%3\n"
- " add %1,%0,%4\n"
- " stwcx. %1,0,%3\n"
- " bne- .Lieaa%="
- : "=&b" (result), "=&r" (temp), "=m" (*atomic)
- : "b" (atomic), "r" (val), "m" (*atomic)
- : "cr0", "memory");
-#endif
- return result;
-}
-
-/* The same as above, to save a function call repeated here */
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result, temp;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("1: lwarx %0,0,%3\n"
- " add %1,%0,%4\n"
- " stwcx. %1,0,%3\n"
- " bne- 1b"
- : "=&b" (result), "=&r" (temp), "=m" (*atomic)
- : "b" (atomic), "r" (val), "m" (*atomic)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ (".Lia%=: lwarx %0,0,%3\n"
- " add %1,%0,%4\n"
- " stwcx. %1,0,%3\n"
- " bne- .Lia%="
- : "=&b" (result), "=&r" (temp), "=m" (*atomic)
- : "b" (atomic), "r" (val), "m" (*atomic)
- : "cr0", "memory");
-#endif
-}
-# else /* !__OPTIMIZE__ */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
- do
- result = *atomic;
- while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
-
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
- do
- result = *atomic;
- while (!g_atomic_int_compare_and_exchange (atomic, result, result + val));
-}
-# endif /* !__OPTIMIZE__ */
-
-# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gint result;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("sync\n"
- "1: lwarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne 2f\n"
- " stwcx. %3,0,%1\n"
- " bne- 1b\n"
- "2: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ ("sync\n"
- ".L1icae%=: lwarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne .L2icae%=\n"
- " stwcx. %3,0,%1\n"
- " bne- .L1icae%=\n"
- ".L2icae%=: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#endif
- return result == 0;
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("sync\n"
- "1: lwarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne 2f\n"
- " stwcx. %3,0,%1\n"
- " bne- 1b\n"
- "2: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ ("sync\n"
- ".L1pcae%=: lwarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne .L2pcae%=\n"
- " stwcx. %3,0,%1\n"
- " bne- .L1pcae%=\n"
- ".L2pcae%=: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#endif
- return result == 0;
-}
-# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gpointer result;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("sync\n"
- "1: lwarx %0,0,%1\n"
- " extsw %0,%0\n"
- " subf. %0,%2,%0\n"
- " bne 2f\n"
- " stwcx. %3,0,%1\n"
- " bne- 1b\n"
- "2: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ ("sync\n"
- ".L1icae%=: lwarx %0,0,%1\n"
- " extsw %0,%0\n"
- " subf. %0,%2,%0\n"
- " bne .L2icae%=\n"
- " stwcx. %3,0,%1\n"
- " bne- .L1icae%=\n"
- ".L2icae%=: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#endif
- return result == 0;
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result;
-#if ASM_NUMERIC_LABELS
- __asm__ __volatile__ ("sync\n"
- "1: ldarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne 2f\n"
- " stdcx. %3,0,%1\n"
- " bne- 1b\n"
- "2: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#else
- __asm__ __volatile__ ("sync\n"
- ".L1pcae%=: ldarx %0,0,%1\n"
- " subf. %0,%2,%0\n"
- " bne .L2pcae%=\n"
- " stdcx. %3,0,%1\n"
- " bne- .L1pcae%=\n"
- ".L2pcae%=: isync"
- : "=&r" (result)
- : "b" (atomic), "r" (oldval), "r" (newval)
- : "cr0", "memory");
-#endif
- return result == 0;
-}
-# else /* What's that */
-# error "Your system has an unsupported pointer size"
-# endif /* GLIB_SIZEOF_VOID_P */
-
-# define G_ATOMIC_MEMORY_BARRIER __asm__ ("sync" : : : "memory")
-
-# elif defined (G_ATOMIC_IA64)
-/* Adapted from CVS version 1.8 of glibc's sysdeps/ia64/bits/atomic.h
- */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- return __sync_fetch_and_add (atomic, val);
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- __sync_fetch_and_add (atomic, val);
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- return __sync_bool_compare_and_swap (atomic, oldval, newval);
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- return __sync_bool_compare_and_swap ((long *)atomic,
- (long)oldval, (long)newval);
-}
-
-# define G_ATOMIC_MEMORY_BARRIER __sync_synchronize ()
-# elif defined (G_ATOMIC_S390)
-/* Adapted from glibc's sysdeps/s390/bits/atomic.h
- */
-# define ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \
- ({ \
- gint __result = oldval; \
- __asm__ __volatile__ ("cs %0, %2, %1" \
- : "+d" (__result), "=Q" (*(atomic)) \
- : "d" (newval), "m" (*(atomic)) : "cc" ); \
- __result == oldval; \
- })
-
-# if GLIB_SIZEOF_VOID_P == 4 /* 32-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result = oldval;
- __asm__ __volatile__ ("cs %0, %2, %1"
- : "+d" (result), "=Q" (*(atomic))
- : "d" (newval), "m" (*(atomic)) : "cc" );
- return result == oldval;
-}
-# elif GLIB_SIZEOF_VOID_P == 8 /* 64-bit system */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gpointer result = oldval;
- gpointer *a = atomic;
- __asm__ __volatile__ ("csg %0, %2, %1"
- : "+d" (result), "=Q" (*a)
- : "d" ((long)(newval)), "m" (*a) : "cc" );
- return result == oldval;
-}
-# else /* What's that */
-# error "Your system has an unsupported pointer size"
-# endif /* GLIB_SIZEOF_VOID_P */
-# elif defined (G_ATOMIC_ARM)
-static volatile int atomic_spin = 0;
-
-static int atomic_spin_trylock (void)
-{
- int result;
-
- asm volatile (
- "swp %0, %1, [%2]\n"
- : "=&r,&r" (result)
- : "r,0" (1), "r,r" (&atomic_spin)
- : "memory");
- if (result == 0)
- return 0;
- else
- return -1;
-}
-
-static void atomic_spin_lock (void)
-{
- while (atomic_spin_trylock())
- sched_yield();
-}
-
-static void atomic_spin_unlock (void)
-{
- atomic_spin = 0;
-}
-
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- atomic_spin_lock();
- result = *atomic;
- *atomic += val;
- atomic_spin_unlock();
-
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- atomic_spin_lock();
- *atomic += val;
- atomic_spin_unlock();
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gboolean result;
-
- atomic_spin_lock();
- if (*atomic == oldval)
- {
- result = TRUE;
- *atomic = newval;
- }
- else
- result = FALSE;
- atomic_spin_unlock();
-
- return result;
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gboolean result;
-
- atomic_spin_lock();
- if (*atomic == oldval)
- {
- result = TRUE;
- *atomic = newval;
- }
- else
- result = FALSE;
- atomic_spin_unlock();
-
- return result;
-}
-# elif defined (G_ATOMIC_CRIS) || defined (G_ATOMIC_CRISV32)
-# ifdef G_ATOMIC_CRIS
-# define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \
- ({ \
- gboolean __result; \
- __asm__ __volatile__ ("\n" \
- "0:\tclearf\n\t" \
- "cmp.d [%[Atomic]], %[OldVal]\n\t" \
- "bne 1f\n\t" \
- "ax\n\t" \
- "move.d %[NewVal], [%[Atomic]]\n\t" \
- "bwf 0b\n" \
- "1:\tseq %[Result]" \
- : [Result] "=&r" (__result), \
- "=m" (*(atomic)) \
- : [Atomic] "r" (atomic), \
- [OldVal] "r" (oldval), \
- [NewVal] "r" (newval), \
- "g" (*(gpointer*) (atomic)) \
- : "memory"); \
- __result; \
- })
-# else
-# define CRIS_ATOMIC_INT_CMP_XCHG(atomic, oldval, newval) \
- ({ \
- gboolean __result; \
- __asm__ __volatile__ ("\n" \
- "0:\tclearf p\n\t" \
- "cmp.d [%[Atomic]], %[OldVal]\n\t" \
- "bne 1f\n\t" \
- "ax\n\t" \
- "move.d %[NewVal], [%[Atomic]]\n\t" \
- "bcs 0b\n" \
- "1:\tseq %[Result]" \
- : [Result] "=&r" (__result), \
- "=m" (*(atomic)) \
- : [Atomic] "r" (atomic), \
- [OldVal] "r" (oldval), \
- [NewVal] "r" (newval), \
- "g" (*(gpointer*) (atomic)) \
- : "memory"); \
- __result; \
- })
-# endif
-
-#define CRIS_CACHELINE_SIZE 32
-#define CRIS_ATOMIC_BREAKS_CACHELINE(atomic) \
- (((gulong)(atomic) & (CRIS_CACHELINE_SIZE - 1)) > (CRIS_CACHELINE_SIZE - sizeof (atomic)))
-
-gint __g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val);
-void __g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val);
-gboolean __g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval);
-gboolean __g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval);
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
- return __g_atomic_pointer_compare_and_exchange (atomic, oldval, newval);
-
- return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
- return __g_atomic_int_compare_and_exchange (atomic, oldval, newval);
-
- return CRIS_ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
-}
-
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
- return __g_atomic_int_exchange_and_add (atomic, val);
-
- do
- result = *atomic;
- while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
-
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- if (G_UNLIKELY (CRIS_ATOMIC_BREAKS_CACHELINE (atomic)))
- return __g_atomic_int_add (atomic, val);
-
- do
- result = *atomic;
- while (!CRIS_ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
-}
-
-/* We need the atomic mutex for atomic operations where the atomic variable
- * breaks the 32 byte cache line since the CRIS architecture does not support
- * atomic operations on such variables. Fortunately this should be rare.
- */
-# define DEFINE_WITH_MUTEXES
-# define g_atomic_int_exchange_and_add __g_atomic_int_exchange_and_add
-# define g_atomic_int_add __g_atomic_int_add
-# define g_atomic_int_compare_and_exchange __g_atomic_int_compare_and_exchange
-# define g_atomic_pointer_compare_and_exchange __g_atomic_pointer_compare_and_exchange
-
-# else /* !G_ATOMIC_* */
-# define DEFINE_WITH_MUTEXES
-# endif /* G_ATOMIC_* */
-#else /* !__GNUC__ */
-# ifdef G_PLATFORM_WIN32
-# define DEFINE_WITH_WIN32_INTERLOCKED
-# else
-# define DEFINE_WITH_MUTEXES
-# endif
-#endif /* __GNUC__ */
-
-#ifdef DEFINE_WITH_WIN32_INTERLOCKED
-# include <windows.h>
-/* Following indicates that InterlockedCompareExchangePointer is
- * declared in winbase.h (included by windows.h) and needs to be
- * commented out if not true. It is defined iff WINVER > 0x0400,
- * which is usually correct but can be wrong if WINVER is set before
- * windows.h is included.
- */
-# if WINVER > 0x0400
-# define HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
-# endif
-
-gint32
-g_atomic_int_exchange_and_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
- gint32 val)
-{
- return InterlockedExchangeAdd (atomic, val);
-}
-
-void
-g_atomic_int_add (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
- gint32 val)
-{
- InterlockedExchangeAdd (atomic, val);
-}
-
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint32 G_GNUC_MAY_ALIAS *atomic,
- gint32 oldval,
- gint32 newval)
-{
-#ifndef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
- return (guint32) InterlockedCompareExchange ((PVOID*)atomic,
- (PVOID)newval,
- (PVOID)oldval) == oldval;
-#else
- return InterlockedCompareExchange (atomic,
- newval,
- oldval) == oldval;
-#endif
-}
-
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
-# ifdef HAVE_INTERLOCKED_COMPARE_EXCHANGE_POINTER
- return InterlockedCompareExchangePointer (atomic, newval, oldval) == oldval;
-# else
-# if GLIB_SIZEOF_VOID_P != 4 /* no 32-bit system */
-# error "InterlockedCompareExchangePointer needed"
-# else
- return InterlockedCompareExchange (atomic, newval, oldval) == oldval;
-# endif
-# endif
-}
-#endif /* DEFINE_WITH_WIN32_INTERLOCKED */
-
-#ifdef DEFINE_WITH_MUTEXES
-#include "gthread.h"
-/* We have to use the slow, but safe locking method */
-static GMutex *g_atomic_mutex;
-
-/**
- * g_atomic_int_exchange_and_add:
- * @atomic: a pointer to an integer
- * @val: the value to add to *@atomic
- *
- * Atomically adds @val to the integer pointed to by @atomic.
- * It returns the value of *@atomic just before the addition
- * took place. Also acts as a memory barrier.
- *
- * Returns: the value of *@atomic before the addition.
- *
- * Since: 2.4
- */
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
-
- g_mutex_lock (g_atomic_mutex);
- result = *atomic;
- *atomic += val;
- g_mutex_unlock (g_atomic_mutex);
-
- return result;
-}
-
-/**
- * g_atomic_int_add:
- * @atomic: a pointer to an integer
- * @val: the value to add to *@atomic
- *
- * Atomically adds @val to the integer pointed to by @atomic.
- * Also acts as a memory barrier.
- *
- * Since: 2.4
- */
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- g_mutex_lock (g_atomic_mutex);
- *atomic += val;
- g_mutex_unlock (g_atomic_mutex);
-}
-
-/**
- * g_atomic_int_compare_and_exchange:
- * @atomic: a pointer to an integer
- * @oldval: the assumed old value of *@atomic
- * @newval: the new value of *@atomic
- *
- * Compares @oldval with the integer pointed to by @atomic and
- * if they are equal, atomically exchanges *@atomic with @newval.
- * Also acts as a memory barrier.
- *
- * Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise.
- *
- * Since: 2.4
- */
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- gboolean result;
-
- g_mutex_lock (g_atomic_mutex);
- if (*atomic == oldval)
- {
- result = TRUE;
- *atomic = newval;
- }
- else
- result = FALSE;
- g_mutex_unlock (g_atomic_mutex);
-
- return result;
-}
-
-/**
- * g_atomic_pointer_compare_and_exchange:
- * @atomic: a pointer to a #gpointer
- * @oldval: the assumed old value of *@atomic
- * @newval: the new value of *@atomic
- *
- * Compares @oldval with the pointer pointed to by @atomic and
- * if they are equal, atomically exchanges *@atomic with @newval.
- * Also acts as a memory barrier.
- *
- * Returns: %TRUE, if *@atomic was equal @oldval. %FALSE otherwise.
- *
- * Since: 2.4
- */
-gboolean
-g_atomic_pointer_compare_and_exchange (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer oldval,
- gpointer newval)
-{
- gboolean result;
-
- g_mutex_lock (g_atomic_mutex);
- if (*atomic == oldval)
- {
- result = TRUE;
- *atomic = newval;
- }
- else
- result = FALSE;
- g_mutex_unlock (g_atomic_mutex);
-
- return result;
-}
-
-#ifdef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED
-
-/**
- * g_atomic_int_get:
- * @atomic: a pointer to an integer
- *
- * Reads the value of the integer pointed to by @atomic.
- * Also acts as a memory barrier.
- *
- * Returns: the value of *@atomic
- *
- * Since: 2.4
- */
-gint
-(g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
-{
- gint result;
-
- g_mutex_lock (g_atomic_mutex);
- result = *atomic;
- g_mutex_unlock (g_atomic_mutex);
-
- return result;
-}
-
-/**
- * g_atomic_int_set:
- * @atomic: a pointer to an integer
- * @newval: the new value
- *
- * Sets the value of the integer pointed to by @atomic.
- * Also acts as a memory barrier.
- *
- * Since: 2.10
- */
-void
-(g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint newval)
-{
- g_mutex_lock (g_atomic_mutex);
- *atomic = newval;
- g_mutex_unlock (g_atomic_mutex);
-}
-
-/**
- * g_atomic_pointer_get:
- * @atomic: a pointer to a #gpointer.
- *
- * Reads the value of the pointer pointed to by @atomic.
- * Also acts as a memory barrier.
- *
- * Returns: the value to add to *@atomic.
- *
- * Since: 2.4
- */
-gpointer
-(g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
-{
- gpointer result;
-
- g_mutex_lock (g_atomic_mutex);
- result = *atomic;
- g_mutex_unlock (g_atomic_mutex);
-
- return result;
-}
-
-/**
- * g_atomic_pointer_set:
- * @atomic: a pointer to a #gpointer
- * @newval: the new value
- *
- * Sets the value of the pointer pointed to by @atomic.
- * Also acts as a memory barrier.
- *
- * Since: 2.10
- */
-void
-(g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer newval)
-{
- g_mutex_lock (g_atomic_mutex);
- *atomic = newval;
- g_mutex_unlock (g_atomic_mutex);
-}
-#endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
-#elif defined (G_ATOMIC_OP_MEMORY_BARRIER_NEEDED)
-gint
-(g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
-{
- G_ATOMIC_MEMORY_BARRIER;
- return *atomic;
-}
-
-void
-(g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint newval)
-{
- *atomic = newval;
- G_ATOMIC_MEMORY_BARRIER;
-}
-
-gpointer
-(g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
-{
- G_ATOMIC_MEMORY_BARRIER;
- return *atomic;
-}
-
-void
-(g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer newval)
-{
- *atomic = newval;
- G_ATOMIC_MEMORY_BARRIER;
-}
-#endif /* DEFINE_WITH_MUTEXES || G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
-
-#ifdef ATOMIC_INT_CMP_XCHG
-gboolean
-g_atomic_int_compare_and_exchange (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint oldval,
- gint newval)
-{
- return ATOMIC_INT_CMP_XCHG (atomic, oldval, newval);
-}
-
-gint
-g_atomic_int_exchange_and_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
- do
- result = *atomic;
- while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
-
- return result;
-}
-
-void
-g_atomic_int_add (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint val)
-{
- gint result;
- do
- result = *atomic;
- while (!ATOMIC_INT_CMP_XCHG (atomic, result, result + val));
-}
-#endif /* ATOMIC_INT_CMP_XCHG */
-
-void
-_g_atomic_thread_init (void)
-{
-#ifdef DEFINE_WITH_MUTEXES
- g_atomic_mutex = g_mutex_new ();
-#endif /* DEFINE_WITH_MUTEXES */
-}
-
-#ifndef G_ATOMIC_OP_MEMORY_BARRIER_NEEDED
-gint
-(g_atomic_int_get) (volatile gint G_GNUC_MAY_ALIAS *atomic)
-{
- return g_atomic_int_get (atomic);
-}
-
-void
-(g_atomic_int_set) (volatile gint G_GNUC_MAY_ALIAS *atomic,
- gint newval)
-{
- g_atomic_int_set (atomic, newval);
-}
-
-gpointer
-(g_atomic_pointer_get) (volatile gpointer G_GNUC_MAY_ALIAS *atomic)
-{
- return g_atomic_pointer_get (atomic);
-}
-
-void
-(g_atomic_pointer_set) (volatile gpointer G_GNUC_MAY_ALIAS *atomic,
- gpointer newval)
-{
- g_atomic_pointer_set (atomic, newval);
-}
-#endif /* G_ATOMIC_OP_MEMORY_BARRIER_NEEDED */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe ; except for g_on_error_stack_trace, but who wants thread safety
- * then
- */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_SYS_TIMES_H
-#include <sys/times.h>
-#endif
-#include <sys/types.h>
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include <time.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-
-#include <string.h> /* for bzero on BSD systems */
-
-#ifdef G_OS_WIN32
-# define STRICT /* Strict typing, please */
-# define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
-# include <windows.h>
-# undef STRICT
-#endif
-
-#include "gbacktrace.h"
-
-#include "gtypes.h"
-#include "gmain.h"
-#include "gprintfint.h"
-
-
-#ifndef NO_FD_SET
-# define SELECT_MASK fd_set
-#else
-# if defined(_IBMR2)
-# define SELECT_MASK void
-# else
-# define SELECT_MASK int
-# endif
-#endif
-
-
-#ifndef G_OS_WIN32
-static void stack_trace (char **args);
-#endif
-
-extern volatile gboolean glib_on_error_halt;
-volatile gboolean glib_on_error_halt = TRUE;
-
-void
-g_on_error_query (const gchar *prg_name)
-{
-#ifndef G_OS_WIN32
- static const gchar * const query1 = "[E]xit, [H]alt";
- static const gchar * const query2 = ", show [S]tack trace";
- static const gchar * const query3 = " or [P]roceed";
- gchar buf[16];
-
- if (!prg_name)
- prg_name = g_get_prgname ();
-
- retry:
-
- if (prg_name)
- _g_fprintf (stdout,
- "%s (pid:%u): %s%s%s: ",
- prg_name,
- (guint) getpid (),
- query1,
- query2,
- query3);
- else
- _g_fprintf (stdout,
- "(process:%u): %s%s: ",
- (guint) getpid (),
- query1,
- query3);
- fflush (stdout);
-
- if (isatty(0) && isatty(1))
- fgets (buf, 8, stdin);
- else
- strcpy (buf, "E\n");
-
- if ((buf[0] == 'E' || buf[0] == 'e')
- && buf[1] == '\n')
- _exit (0);
- else if ((buf[0] == 'P' || buf[0] == 'p')
- && buf[1] == '\n')
- return;
- else if (prg_name
- && (buf[0] == 'S' || buf[0] == 's')
- && buf[1] == '\n')
- {
- g_on_error_stack_trace (prg_name);
- goto retry;
- }
- else if ((buf[0] == 'H' || buf[0] == 'h')
- && buf[1] == '\n')
- {
- while (glib_on_error_halt)
- ;
- glib_on_error_halt = TRUE;
- return;
- }
- else
- goto retry;
-#else
- if (!prg_name)
- prg_name = g_get_prgname ();
-
- MessageBox (NULL, "g_on_error_query called, program terminating",
- (prg_name && *prg_name) ? prg_name : NULL,
- MB_OK|MB_ICONERROR);
- _exit(0);
-#endif
-}
-
-void
-g_on_error_stack_trace (const gchar *prg_name)
-{
-#if defined(G_OS_UNIX) || defined(G_OS_BEOS)
- pid_t pid;
- gchar buf[16];
- gchar *args[4] = { "gdb", NULL, NULL, NULL };
- int status;
-
- if (!prg_name)
- return;
-
- _g_sprintf (buf, "%u", (guint) getpid ());
-
- args[1] = (gchar*) prg_name;
- args[2] = buf;
-
- pid = fork ();
- if (pid == 0)
- {
- stack_trace (args);
- _exit (0);
- }
- else if (pid == (pid_t) -1)
- {
- perror ("unable to fork gdb");
- return;
- }
-
- waitpid (pid, &status, 0);
-#else
- if (IsDebuggerPresent ())
- G_BREAKPOINT ();
- else
- abort ();
-#endif
-}
-
-#ifndef G_OS_WIN32
-
-static gboolean stack_trace_done = FALSE;
-
-static void
-stack_trace_sigchld (int signum)
-{
- stack_trace_done = TRUE;
-}
-
-static void
-stack_trace (char **args)
-{
- pid_t pid;
- int in_fd[2];
- int out_fd[2];
- SELECT_MASK fdset;
- SELECT_MASK readset;
- struct timeval tv;
- int sel, idx, state;
- char buffer[256];
- char c;
-
- stack_trace_done = FALSE;
- signal (SIGCHLD, stack_trace_sigchld);
-
- if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
- {
- perror ("unable to open pipe");
- _exit (0);
- }
-
- pid = fork ();
- if (pid == 0)
- {
- close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
- close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
- close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
-
- execvp (args[0], args); /* exec gdb */
- perror ("exec failed");
- _exit (0);
- }
- else if (pid == (pid_t) -1)
- {
- perror ("unable to fork");
- _exit (0);
- }
-
- FD_ZERO (&fdset);
- FD_SET (out_fd[0], &fdset);
-
- write (in_fd[1], "backtrace\n", 10);
- write (in_fd[1], "p x = 0\n", 8);
- write (in_fd[1], "quit\n", 5);
-
- idx = 0;
- state = 0;
-
- while (1)
- {
- readset = fdset;
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
- if (sel == -1)
- break;
-
- if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
- {
- if (read (out_fd[0], &c, 1))
- {
- switch (state)
- {
- case 0:
- if (c == '#')
- {
- state = 1;
- idx = 0;
- buffer[idx++] = c;
- }
- break;
- case 1:
- buffer[idx++] = c;
- if ((c == '\n') || (c == '\r'))
- {
- buffer[idx] = 0;
- _g_fprintf (stdout, "%s", buffer);
- state = 0;
- idx = 0;
- }
- break;
- default:
- break;
- }
- }
- }
- else if (stack_trace_done)
- break;
- }
-
- close (in_fd[0]);
- close (in_fd[1]);
- close (out_fd[0]);
- close (out_fd[1]);
- _exit (0);
-}
-
-#endif /* !G_OS_WIN32 */
+++ /dev/null
-/* gbase64.c - Base64 encoding/decoding
- *
- * Copyright (C) 2006 Alexander Larsson <alexl@redhat.com>
- * Copyright (C) 2000-2003 Ximian Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library 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 is based on code in camel, written by:
- * Michael Zucchi <notzed@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include "gbase64.h"
-#include "gtestutils.h"
-#include "glibintl.h"
-
-
-/**
- * SECTION:base64
- * @title: Base64 Encoding
- * @short_description: encodes and decodes data in Base64 format
- *
- * Base64 is an encoding that allows a sequence of arbitrary bytes to be
- * encoded as a sequence of printable ASCII characters. For the definition
- * of Base64, see <ulink url="http://www.ietf.org/rfc/rfc1421.txt">RFC
- * 1421</ulink> or <ulink url="http://www.ietf.org/rfc/rfc2045.txt">RFC
- * 2045</ulink>. Base64 is most commonly used as a MIME transfer encoding
- * for email.
- *
- * GLib supports incremental encoding using g_base64_encode_step() and
- * g_base64_encode_close(). Incremental decoding can be done with
- * g_base64_decode_step(). To encode or decode data in one go, use
- * g_base64_encode() or g_base64_decode(). To avoid memory allocation when
- * decoding, you can use g_base64_decode_inplace().
- *
- * Support for Base64 encoding has been added in GLib 2.12.
- */
-
-static const char base64_alphabet[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
-/**
- * g_base64_encode_step:
- * @in: the binary data to encode
- * @len: the length of @in
- * @break_lines: whether to break long lines
- * @out: pointer to destination buffer
- * @state: Saved state between steps, initialize to 0
- * @save: Saved state between steps, initialize to 0
- *
- * Incrementally encode a sequence of binary data into its Base-64 stringified
- * representation. By calling this function multiple times you can convert
- * data in chunks to avoid having to have the full encoded data in memory.
- *
- * When all of the data has been converted you must call
- * g_base64_encode_close() to flush the saved state.
- *
- * The output buffer must be large enough to fit all the data that will
- * be written to it. Due to the way base64 encodes you will need
- * at least: (@len / 3 + 1) * 4 + 4 bytes (+ 4 may be needed in case of
- * non-zero state). If you enable line-breaking you will need at least:
- * ((@len / 3 + 1) * 4 + 4) / 72 + 1 bytes of extra space.
- *
- * @break_lines is typically used when putting base64-encoded data in emails.
- * It breaks the lines at 72 columns instead of putting all of the text on
- * the same line. This avoids problems with long lines in the email system.
- *
- * Return value: The number of bytes of output that was written
- *
- * Since: 2.12
- */
-gsize
-g_base64_encode_step (const guchar *in,
- gsize len,
- gboolean break_lines,
- gchar *out,
- gint *state,
- gint *save)
-{
- char *outptr;
- const guchar *inptr;
-
- g_return_val_if_fail (in != NULL, 0);
- g_return_val_if_fail (out != NULL, 0);
- g_return_val_if_fail (state != NULL, 0);
- g_return_val_if_fail (save != NULL, 0);
-
- if (len <= 0)
- return 0;
-
- inptr = in;
- outptr = out;
-
- if (len + ((char *) save) [0] > 2)
- {
- const guchar *inend = in+len-2;
- int c1, c2, c3;
- int already;
-
- already = *state;
-
- switch (((char *) save) [0])
- {
- case 1:
- c1 = ((unsigned char *) save) [1];
- goto skip1;
- case 2:
- c1 = ((unsigned char *) save) [1];
- c2 = ((unsigned char *) save) [2];
- goto skip2;
- }
-
- /*
- * yes, we jump into the loop, no i'm not going to change it,
- * it's beautiful!
- */
- while (inptr < inend)
- {
- c1 = *inptr++;
- skip1:
- c2 = *inptr++;
- skip2:
- c3 = *inptr++;
- *outptr++ = base64_alphabet [ c1 >> 2 ];
- *outptr++ = base64_alphabet [ c2 >> 4 |
- ((c1&0x3) << 4) ];
- *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) |
- (c3 >> 6) ];
- *outptr++ = base64_alphabet [ c3 & 0x3f ];
- /* this is a bit ugly ... */
- if (break_lines && (++already) >= 19)
- {
- *outptr++ = '\n';
- already = 0;
- }
- }
-
- ((char *)save)[0] = 0;
- len = 2 - (inptr - inend);
- *state = already;
- }
-
- if (len>0)
- {
- char *saveout;
-
- /* points to the slot for the next char to save */
- saveout = & (((char *)save)[1]) + ((char *)save)[0];
-
- /* len can only be 0 1 or 2 */
- switch(len)
- {
- case 2: *saveout++ = *inptr++;
- case 1: *saveout++ = *inptr++;
- }
- ((char *)save)[0] += len;
- }
-
- return outptr - out;
-}
-
-/**
- * g_base64_encode_close:
- * @break_lines: whether to break long lines
- * @out: pointer to destination buffer
- * @state: Saved state from g_base64_encode_step()
- * @save: Saved state from g_base64_encode_step()
- *
- * Flush the status from a sequence of calls to g_base64_encode_step().
- *
- * The output buffer must be large enough to fit all the data that will
- * be written to it. It will need up to 4 bytes, or up to 5 bytes if
- * line-breaking is enabled.
- *
- * Return value: The number of bytes of output that was written
- *
- * Since: 2.12
- */
-gsize
-g_base64_encode_close (gboolean break_lines,
- gchar *out,
- gint *state,
- gint *save)
-{
- int c1, c2;
- char *outptr = out;
-
- g_return_val_if_fail (out != NULL, 0);
- g_return_val_if_fail (state != NULL, 0);
- g_return_val_if_fail (save != NULL, 0);
-
- c1 = ((unsigned char *) save) [1];
- c2 = ((unsigned char *) save) [2];
-
- switch (((char *) save) [0])
- {
- case 2:
- outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
- g_assert (outptr [2] != 0);
- goto skip;
- case 1:
- outptr[2] = '=';
- skip:
- outptr [0] = base64_alphabet [ c1 >> 2 ];
- outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )];
- outptr [3] = '=';
- outptr += 4;
- break;
- }
- if (break_lines)
- *outptr++ = '\n';
-
- *save = 0;
- *state = 0;
-
- return outptr - out;
-}
-
-/**
- * g_base64_encode:
- * @data: the binary data to encode
- * @len: the length of @data
- *
- * Encode a sequence of binary data into its Base-64 stringified
- * representation.
- *
- * Return value: a newly allocated, zero-terminated Base-64 encoded
- * string representing @data. The returned string must
- * be freed with g_free().
- *
- * Since: 2.12
- */
-gchar *
-g_base64_encode (const guchar *data,
- gsize len)
-{
- gchar *out;
- gint state = 0, outlen;
- gint save = 0;
-
- g_return_val_if_fail (data != NULL || len == 0, NULL);
-
- /* We can use a smaller limit here, since we know the saved state is 0,
- +1 is needed for trailing \0, also check for unlikely integer overflow */
- if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3)
- g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)",
- G_STRLOC, len);
-
- out = g_malloc ((len / 3 + 1) * 4 + 1);
-
- outlen = g_base64_encode_step (data, len, FALSE, out, &state, &save);
- outlen += g_base64_encode_close (FALSE, out + outlen, &state, &save);
- out[outlen] = '\0';
-
- return (gchar *) out;
-}
-
-static const unsigned char mime_base64_rank[256] = {
- 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, 62,255,255,255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
- 255, 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,255,255,255,255,255,
- 255, 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,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,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,255,255,255,255,255,
-};
-
-/**
- * g_base64_decode_step:
- * @in: binary input data
- * @len: max length of @in data to decode
- * @out: output buffer
- * @state: Saved state between steps, initialize to 0
- * @save: Saved state between steps, initialize to 0
- *
- * Incrementally decode a sequence of binary data from its Base-64 stringified
- * representation. By calling this function multiple times you can convert
- * data in chunks to avoid having to have the full encoded data in memory.
- *
- * The output buffer must be large enough to fit all the data that will
- * be written to it. Since base64 encodes 3 bytes in 4 chars you need
- * at least: (@len / 4) * 3 + 3 bytes (+ 3 may be needed in case of non-zero
- * state).
- *
- * Return value: The number of bytes of output that was written
- *
- * Since: 2.12
- **/
-gsize
-g_base64_decode_step (const gchar *in,
- gsize len,
- guchar *out,
- gint *state,
- guint *save)
-{
- const guchar *inptr;
- guchar *outptr;
- const guchar *inend;
- guchar c, rank;
- guchar last[2];
- unsigned int v;
- int i;
-
- g_return_val_if_fail (in != NULL, 0);
- g_return_val_if_fail (out != NULL, 0);
- g_return_val_if_fail (state != NULL, 0);
- g_return_val_if_fail (save != NULL, 0);
-
- if (len <= 0)
- return 0;
-
- inend = (const guchar *)in+len;
- outptr = out;
-
- /* convert 4 base64 bytes to 3 normal bytes */
- v=*save;
- i=*state;
- inptr = (const guchar *)in;
- last[0] = last[1] = 0;
- while (inptr < inend)
- {
- c = *inptr++;
- rank = mime_base64_rank [c];
- if (rank != 0xff)
- {
- last[1] = last[0];
- last[0] = c;
- v = (v<<6) | rank;
- i++;
- if (i==4)
- {
- *outptr++ = v>>16;
- if (last[1] != '=')
- *outptr++ = v>>8;
- if (last[0] != '=')
- *outptr++ = v;
- i=0;
- }
- }
- }
-
- *save = v;
- *state = i;
-
- return outptr - out;
-}
-
-/**
- * g_base64_decode:
- * @text: zero-terminated string with base64 text to decode
- * @out_len: The length of the decoded data is written here
- *
- * Decode a sequence of Base-64 encoded text into binary data
- *
- * Return value: a newly allocated buffer containing the binary data
- * that @text represents. The returned buffer must
- * be freed with g_free().
- *
- * Since: 2.12
- */
-guchar *
-g_base64_decode (const gchar *text,
- gsize *out_len)
-{
- guchar *ret;
- gsize input_length;
- gint state = 0;
- guint save = 0;
-
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (out_len != NULL, NULL);
-
- input_length = strlen (text);
-
- /* We can use a smaller limit here, since we know the saved state is 0,
- +1 used to avoid calling g_malloc0(0), and hence retruning NULL */
- ret = g_malloc0 ((input_length / 4) * 3 + 1);
-
- *out_len = g_base64_decode_step (text, input_length, ret, &state, &save);
-
- return ret;
-}
-
-/**
- * g_base64_decode_inplace:
- * @text: zero-terminated string with base64 text to decode
- * @out_len: The length of the decoded data is written here
- *
- * Decode a sequence of Base-64 encoded text into binary data
- * by overwriting the input data.
- *
- * Return value: The binary data that @text responds. This pointer
- * is the same as the input @text.
- *
- * Since: 2.20
- */
-guchar *
-g_base64_decode_inplace (gchar *text,
- gsize *out_len)
-{
- gint input_length, state = 0;
- guint save = 0;
-
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (out_len != NULL, NULL);
-
- input_length = strlen (text);
-
- g_return_val_if_fail (input_length > 1, NULL);
-
- *out_len = g_base64_decode_step (text, input_length, (guchar *) text, &state, &save);
-
- return (guchar *) text;
-}
+++ /dev/null
-/*
- * Copyright © 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "gbitlock.h"
-
-#include <glib/gatomic.h>
-#include <glib/gslist.h>
-#include <glib/gthread.h>
-
-#include "gthreadprivate.h"
-#include "config.h"
-
-
-#ifdef G_BIT_LOCK_FORCE_FUTEX_EMULATION
-#undef HAVE_FUTEX
-#endif
-
-#ifndef HAVE_FUTEX
-static GSList *g_futex_address_list = NULL;
-static GMutex *g_futex_mutex = NULL;
-#endif
-
-void
-_g_futex_thread_init (void) {
-#ifndef HAVE_FUTEX
- g_futex_mutex = g_mutex_new ();
-#endif
-}
-
-#ifdef HAVE_FUTEX
-/*
- * We have headers for futex(2) on the build machine. This does not
- * imply that every system that ever runs the resulting glib will have
- * kernel support for futex, but you'd have to have a pretty old
- * kernel in order for that not to be the case.
- *
- * If anyone actually gets bit by this, please file a bug. :)
- */
-#include <linux/futex.h>
-#include <syscall.h>
-#include <unistd.h>
-
-/* < private >
- * g_futex_wait:
- * @address: a pointer to an integer
- * @value: the value that should be at @address
- *
- * Atomically checks that the value stored at @address is equal to
- * @value and then blocks. If the value stored at @address is not
- * equal to @value then this function returns immediately.
- *
- * To unblock, call g_futex_wake() on @address.
- *
- * This call may spuriously unblock (for example, in response to the
- * process receiving a signal) but this is not guaranteed. Unlike the
- * Linux system call of a similar name, there is no guarantee that a
- * waiting process will unblock due to a g_futex_wake() call in a
- * separate process.
- */
-static void
-g_futex_wait (const volatile gint *address,
- gint value)
-{
- syscall (SYS_futex, address, (gsize) FUTEX_WAIT, (gsize) value, NULL);
-}
-
-/* < private >
- * g_futex_wake:
- * @address: a pointer to an integer
- *
- * Nominally, wakes one thread that is blocked in g_futex_wait() on
- * @address (if any thread is currently waiting).
- *
- * As mentioned in the documention for g_futex_wait(), spurious
- * wakeups may occur. As such, this call may result in more than one
- * thread being woken up.
- */
-static void
-g_futex_wake (const volatile gint *address)
-{
- syscall (SYS_futex, address, (gsize) FUTEX_WAKE, (gsize) 1, NULL);
-}
-
-#else
-
-/* emulate futex(2) */
-typedef struct
-{
- const volatile gint *address;
- gint ref_count;
- GCond *wait_queue;
-} WaitAddress;
-
-static WaitAddress *
-g_futex_find_address (const volatile gint *address)
-{
- GSList *node;
-
- for (node = g_futex_address_list; node; node = node->next)
- {
- WaitAddress *waiter = node->data;
-
- if (waiter->address == address)
- return waiter;
- }
-
- return NULL;
-}
-
-static void
-g_futex_wait (const volatile gint *address,
- gint value)
-{
- g_mutex_lock (g_futex_mutex);
- if G_LIKELY (g_atomic_int_get (address) == value)
- {
- WaitAddress *waiter;
-
- if ((waiter = g_futex_find_address (address)) == NULL)
- {
- waiter = g_slice_new (WaitAddress);
- waiter->address = address;
- waiter->wait_queue = g_cond_new ();
- waiter->ref_count = 0;
- g_futex_address_list =
- g_slist_prepend (g_futex_address_list, waiter);
- }
-
- waiter->ref_count++;
- g_cond_wait (waiter->wait_queue, g_futex_mutex);
-
- if (!--waiter->ref_count)
- {
- g_futex_address_list =
- g_slist_remove (g_futex_address_list, waiter);
- g_cond_free (waiter->wait_queue);
- g_slice_free (WaitAddress, waiter);
- }
- }
- g_mutex_unlock (g_futex_mutex);
-}
-
-static void
-g_futex_wake (const volatile gint *address)
-{
- WaitAddress *waiter;
-
- /* need to lock here for two reasons:
- * 1) need to acquire/release lock to ensure waiter is not in
- * the process of registering a wait
- * 2) need to -stay- locked until the end to ensure a wake()
- * in another thread doesn't cause 'waiter' to stop existing
- */
- g_mutex_lock (g_futex_mutex);
- if ((waiter = g_futex_find_address (address)))
- g_cond_signal (waiter->wait_queue);
- g_mutex_unlock (g_futex_mutex);
-}
-#endif
-
-#define CONTENTION_CLASSES 11
-static volatile gint g_bit_lock_contended[CONTENTION_CLASSES];
-
-/**
- * g_bit_lock:
- * @address: a pointer to an integer
- * @lock_bit: a bit value between 0 and 31
- *
- * Sets the indicated @lock_bit in @address. If the bit is already
- * set, this call will block until g_bit_unlock() unsets the
- * corresponding bit.
- *
- * Attempting to lock on two different bits within the same integer is
- * not supported and will very probably cause deadlocks.
- *
- * The value of the bit that is set is (1u << @bit). If @bit is not
- * between 0 and 31 then the result is undefined.
- *
- * This function accesses @address atomically. All other accesses to
- * @address must be atomic in order for this function to work
- * reliably.
- *
- * Since: 2.24
- **/
-void
-g_bit_lock (volatile gint *address,
- gint lock_bit)
-{
- guint v;
-
- retry:
- v = g_atomic_int_get (address);
- if (v & (1u << lock_bit))
- /* already locked */
- {
- guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
-
- g_atomic_int_add (&g_bit_lock_contended[class], +1);
- g_futex_wait (address, v);
- g_atomic_int_add (&g_bit_lock_contended[class], -1);
-
- goto retry;
- }
-
- if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
- goto retry;
-}
-
-/**
- * g_bit_trylock:
- * @address: a pointer to an integer
- * @lock_bit: a bit value between 0 and 31
- * @returns: %TRUE if the lock was acquired
- *
- * Sets the indicated @lock_bit in @address, returning %TRUE if
- * successful. If the bit is already set, returns %FALSE immediately.
- *
- * Attempting to lock on two different bits within the same integer is
- * not supported.
- *
- * The value of the bit that is set is (1u << @bit). If @bit is not
- * between 0 and 31 then the result is undefined.
- *
- * This function accesses @address atomically. All other accesses to
- * @address must be atomic in order for this function to work
- * reliably.
- *
- * Since: 2.24
- **/
-gboolean
-g_bit_trylock (volatile gint *address,
- gint lock_bit)
-{
- guint v;
-
- retry:
- v = g_atomic_int_get (address);
- if (v & (1u << lock_bit))
- /* already locked */
- return FALSE;
-
- if (!g_atomic_int_compare_and_exchange (address, v, v | (1u << lock_bit)))
- goto retry;
-
- return TRUE;
-}
-
-/**
- * g_bit_unlock:
- * @address: a pointer to an integer
- * @lock_bit: a bit value between 0 and 31
- *
- * Clears the indicated @lock_bit in @address. If another thread is
- * currently blocked in g_bit_lock() on this same bit then it will be
- * woken up.
- *
- * This function accesses @address atomically. All other accesses to
- * @address must be atomic in order for this function to work
- * reliably.
- *
- * Since: 2.24
- **/
-void
-g_bit_unlock (volatile gint *address,
- gint lock_bit)
-{
- guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
- guint v;
-
- retry:
- v = g_atomic_int_get (address);
- if (!g_atomic_int_compare_and_exchange (address, v, v & ~(1u << lock_bit)))
- goto retry;
-
- if (g_atomic_int_get (&g_bit_lock_contended[class]))
- g_futex_wake (address);
-}
+++ /dev/null
-/* gbookmarkfile.c: parsing and building desktop bookmarks
- *
- * Copyright (C) 2005-2006 Emmanuele Bassi
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- */
-
-#include "config.h"
-
-#include "gbookmarkfile.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <locale.h>
-#include <time.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "gconvert.h"
-#include "gdataset.h"
-#include "gerror.h"
-#include "gfileutils.h"
-#include "ghash.h"
-#include "glibintl.h"
-#include "glist.h"
-#include "gslist.h"
-#include "gmain.h"
-#include "gmarkup.h"
-#include "gmem.h"
-#include "gmessages.h"
-#include "gshell.h"
-#include "gslice.h"
-#include "gstdio.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "gtimer.h"
-#include "gutils.h"
-
-
-/**
- * SECTION:bookmarkfile
- * @title: Bookmark file parser
- * @short_description: parses files containing bookmarks
- *
- * GBookmarkFile lets you parse, edit or create files containing bookmarks
- * to URI, along with some meta-data about the resource pointed by the URI
- * like its MIME type, the application that is registering the bookmark and
- * the icon that should be used to represent the bookmark. The data is stored
- * using the
- * <ulink url="http://www.gnome.org/~ebassi/bookmark-spec">Desktop Bookmark
- * Specification</ulink>.
- *
- * The syntax of the bookmark files is described in detail inside the Desktop
- * Bookmark Specification, here is a quick summary: bookmark files use a
- * sub-class of the <ulink url="">XML Bookmark Exchange Language</ulink>
- * specification, consisting of valid UTF-8 encoded XML, under the
- * <literal>xbel</literal> root element; each bookmark is stored inside a
- * <literal>bookmark</literal> element, using its URI: no relative paths can
- * be used inside a bookmark file. The bookmark may have a user defined title
- * and description, to be used instead of the URI. Under the
- * <literal>metadata</literal> element, with its <literal>owner</literal>
- * attribute set to <literal>http://freedesktop.org</literal>, is stored the
- * meta-data about a resource pointed by its URI. The meta-data consists of
- * the resource's MIME type; the applications that have registered a bookmark;
- * the groups to which a bookmark belongs to; a visibility flag, used to set
- * the bookmark as "private" to the applications and groups that has it
- * registered; the URI and MIME type of an icon, to be used when displaying
- * the bookmark inside a GUI.
- * |[<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../glib/tests/bookmarks.xbel"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include>]|
- *
- * A bookmark file might contain more than one bookmark; each bookmark
- * is accessed through its URI.
- *
- * The important caveat of bookmark files is that when you add a new
- * bookmark you must also add the application that is registering it, using
- * g_bookmark_file_add_application() or g_bookmark_file_set_app_info().
- * If a bookmark has no applications then it won't be dumped when creating
- * the on disk representation, using g_bookmark_file_to_data() or
- * g_bookmark_file_to_file().
- *
- * The #GBookmarkFile parser was added in GLib 2.12.
- */
-
-/* XBEL 1.0 standard entities */
-#define XBEL_VERSION "1.0"
-#define XBEL_DTD_NICK "xbel"
-#define XBEL_DTD_SYSTEM "+//IDN python.org//DTD XML Bookmark " \
- "Exchange Language 1.0//EN//XML"
-
-#define XBEL_DTD_URI "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd"
-
-#define XBEL_ROOT_ELEMENT "xbel"
-#define XBEL_FOLDER_ELEMENT "folder" /* unused */
-#define XBEL_BOOKMARK_ELEMENT "bookmark"
-#define XBEL_ALIAS_ELEMENT "alias" /* unused */
-#define XBEL_SEPARATOR_ELEMENT "separator" /* unused */
-#define XBEL_TITLE_ELEMENT "title"
-#define XBEL_DESC_ELEMENT "desc"
-#define XBEL_INFO_ELEMENT "info"
-#define XBEL_METADATA_ELEMENT "metadata"
-
-#define XBEL_VERSION_ATTRIBUTE "version"
-#define XBEL_FOLDED_ATTRIBUTE "folded" /* unused */
-#define XBEL_OWNER_ATTRIBUTE "owner"
-#define XBEL_ADDED_ATTRIBUTE "added"
-#define XBEL_VISITED_ATTRIBUTE "visited"
-#define XBEL_MODIFIED_ATTRIBUTE "modified"
-#define XBEL_ID_ATTRIBUTE "id"
-#define XBEL_HREF_ATTRIBUTE "href"
-#define XBEL_REF_ATTRIBUTE "ref" /* unused */
-
-#define XBEL_YES_VALUE "yes"
-#define XBEL_NO_VALUE "no"
-
-/* Desktop bookmark spec entities */
-#define BOOKMARK_METADATA_OWNER "http://freedesktop.org"
-
-#define BOOKMARK_NAMESPACE_NAME "bookmark"
-#define BOOKMARK_NAMESPACE_URI "http://www.freedesktop.org/standards/desktop-bookmarks"
-
-#define BOOKMARK_GROUPS_ELEMENT "groups"
-#define BOOKMARK_GROUP_ELEMENT "group"
-#define BOOKMARK_APPLICATIONS_ELEMENT "applications"
-#define BOOKMARK_APPLICATION_ELEMENT "application"
-#define BOOKMARK_ICON_ELEMENT "icon"
-#define BOOKMARK_PRIVATE_ELEMENT "private"
-
-#define BOOKMARK_NAME_ATTRIBUTE "name"
-#define BOOKMARK_EXEC_ATTRIBUTE "exec"
-#define BOOKMARK_COUNT_ATTRIBUTE "count"
-#define BOOKMARK_TIMESTAMP_ATTRIBUTE "timestamp" /* deprecated by "modified" */
-#define BOOKMARK_MODIFIED_ATTRIBUTE "modified"
-#define BOOKMARK_HREF_ATTRIBUTE "href"
-#define BOOKMARK_TYPE_ATTRIBUTE "type"
-
-/* Shared MIME Info entities */
-#define MIME_NAMESPACE_NAME "mime"
-#define MIME_NAMESPACE_URI "http://www.freedesktop.org/standards/shared-mime-info"
-#define MIME_TYPE_ELEMENT "mime-type"
-#define MIME_TYPE_ATTRIBUTE "type"
-
-
-typedef struct _BookmarkAppInfo BookmarkAppInfo;
-typedef struct _BookmarkMetadata BookmarkMetadata;
-typedef struct _BookmarkItem BookmarkItem;
-typedef struct _ParseData ParseData;
-
-struct _BookmarkAppInfo
-{
- gchar *name;
- gchar *exec;
-
- guint count;
-
- time_t stamp;
-};
-
-struct _BookmarkMetadata
-{
- gchar *mime_type;
-
- GList *groups;
-
- GList *applications;
- GHashTable *apps_by_name;
-
- gchar *icon_href;
- gchar *icon_mime;
-
- guint is_private : 1;
-};
-
-struct _BookmarkItem
-{
- gchar *uri;
-
- gchar *title;
- gchar *description;
-
- time_t added;
- time_t modified;
- time_t visited;
-
- BookmarkMetadata *metadata;
-};
-
-struct _GBookmarkFile
-{
- gchar *title;
- gchar *description;
-
- /* we store our items in a list and keep a copy inside
- * an hash table for faster lookup performances
- */
- GList *items;
- GHashTable *items_by_uri;
-};
-
-/* parser state machine */
-enum
-{
- STATE_STARTED = 0,
-
- STATE_ROOT,
- STATE_BOOKMARK,
- STATE_TITLE,
- STATE_DESC,
- STATE_INFO,
- STATE_METADATA,
- STATE_APPLICATIONS,
- STATE_APPLICATION,
- STATE_GROUPS,
- STATE_GROUP,
- STATE_MIME,
- STATE_ICON,
-
- STATE_FINISHED
-};
-
-static void g_bookmark_file_init (GBookmarkFile *bookmark);
-static void g_bookmark_file_clear (GBookmarkFile *bookmark);
-static gboolean g_bookmark_file_parse (GBookmarkFile *bookmark,
- const gchar *buffer,
- gsize length,
- GError **error);
-static gchar * g_bookmark_file_dump (GBookmarkFile *bookmark,
- gsize *length,
- GError **error);
-static BookmarkItem *g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
- const gchar *uri);
-static void g_bookmark_file_add_item (GBookmarkFile *bookmark,
- BookmarkItem *item,
- GError **error);
-
-static time_t timestamp_from_iso8601 (const gchar *iso_date);
-static gchar * timestamp_to_iso8601 (time_t timestamp);
-
-/********************************
- * BookmarkAppInfo *
- * *
- * Application metadata storage *
- ********************************/
-static BookmarkAppInfo *
-bookmark_app_info_new (const gchar *name)
-{
- BookmarkAppInfo *retval;
-
- g_warn_if_fail (name != NULL);
-
- retval = g_slice_new (BookmarkAppInfo);
-
- retval->name = g_strdup (name);
- retval->exec = NULL;
- retval->count = 0;
- retval->stamp = 0;
-
- return retval;
-}
-
-static void
-bookmark_app_info_free (BookmarkAppInfo *app_info)
-{
- if (!app_info)
- return;
-
- g_free (app_info->name);
- g_free (app_info->exec);
-
- g_slice_free (BookmarkAppInfo, app_info);
-}
-
-static gchar *
-bookmark_app_info_dump (BookmarkAppInfo *app_info)
-{
- gchar *retval;
- gchar *name, *exec, *modified, *count;
-
- g_warn_if_fail (app_info != NULL);
-
- if (app_info->count == 0)
- return NULL;
-
- name = g_markup_escape_text (app_info->name, -1);
- exec = g_markup_escape_text (app_info->exec, -1);
- modified = timestamp_to_iso8601 (app_info->stamp);
- count = g_strdup_printf ("%u", app_info->count);
-
- retval = g_strconcat (" "
- "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT
- " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\""
- " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\""
- " " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", modified, "\""
- " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n",
- NULL);
-
- g_free (name);
- g_free (exec);
- g_free (modified);
- g_free (count);
-
- return retval;
-}
-
-
-/***********************
- * BookmarkMetadata *
- * *
- * Metadata storage *
- ***********************/
-static BookmarkMetadata *
-bookmark_metadata_new (void)
-{
- BookmarkMetadata *retval;
-
- retval = g_slice_new (BookmarkMetadata);
-
- retval->mime_type = NULL;
-
- retval->groups = NULL;
-
- retval->applications = NULL;
- retval->apps_by_name = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- NULL,
- NULL);
-
- retval->is_private = FALSE;
-
- retval->icon_href = NULL;
- retval->icon_mime = NULL;
-
- return retval;
-}
-
-static void
-bookmark_metadata_free (BookmarkMetadata *metadata)
-{
- if (!metadata)
- return;
-
- g_free (metadata->mime_type);
-
- if (metadata->groups)
- {
- g_list_foreach (metadata->groups,
- (GFunc) g_free,
- NULL);
- g_list_free (metadata->groups);
- }
-
- if (metadata->applications)
- {
- g_list_foreach (metadata->applications,
- (GFunc) bookmark_app_info_free,
- NULL);
- g_list_free (metadata->applications);
- }
-
- g_hash_table_destroy (metadata->apps_by_name);
-
- g_free (metadata->icon_href);
- g_free (metadata->icon_mime);
-
- g_slice_free (BookmarkMetadata, metadata);
-}
-
-static gchar *
-bookmark_metadata_dump (BookmarkMetadata *metadata)
-{
- GString *retval;
- gchar *buffer;
-
- if (!metadata->applications)
- return NULL;
-
- retval = g_string_sized_new (1024);
-
- /* metadata container */
- g_string_append (retval,
- " "
- "<" XBEL_METADATA_ELEMENT
- " " XBEL_OWNER_ATTRIBUTE "=\"" BOOKMARK_METADATA_OWNER
- "\">\n");
-
- /* mime type */
- if (metadata->mime_type) {
- buffer = g_strconcat (" "
- "<" MIME_NAMESPACE_NAME ":" MIME_TYPE_ELEMENT " "
- MIME_TYPE_ATTRIBUTE "=\"", metadata->mime_type, "\"/>\n",
- NULL);
- g_string_append (retval, buffer);
- g_free (buffer);
- }
-
- if (metadata->groups)
- {
- GList *l;
-
- /* open groups container */
- g_string_append (retval,
- " "
- "<" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_GROUPS_ELEMENT ">\n");
-
- for (l = g_list_last (metadata->groups); l != NULL; l = l->prev)
- {
- gchar *group_name;
-
- group_name = g_markup_escape_text ((gchar *) l->data, -1);
- buffer = g_strconcat (" "
- "<" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_GROUP_ELEMENT ">",
- group_name,
- "</" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_GROUP_ELEMENT ">\n", NULL);
- g_string_append (retval, buffer);
-
- g_free (buffer);
- g_free (group_name);
- }
-
- /* close groups container */
- g_string_append (retval,
- " "
- "</" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_GROUPS_ELEMENT ">\n");
- }
-
- if (metadata->applications)
- {
- GList *l;
-
- /* open applications container */
- g_string_append (retval,
- " "
- "<" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
-
- for (l = g_list_last (metadata->applications); l != NULL; l = l->prev)
- {
- BookmarkAppInfo *app_info = (BookmarkAppInfo *) l->data;
- gchar *app_data;
-
- g_warn_if_fail (app_info != NULL);
-
- app_data = bookmark_app_info_dump (app_info);
-
- if (app_data)
- {
- retval = g_string_append (retval, app_data);
-
- g_free (app_data);
- }
- }
-
- /* close applications container */
- g_string_append (retval,
- " "
- "</" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_APPLICATIONS_ELEMENT ">\n");
- }
-
- /* icon */
- if (metadata->icon_href)
- {
- if (!metadata->icon_mime)
- metadata->icon_mime = g_strdup ("application/octet-stream");
-
- buffer = g_strconcat (" "
- "<" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_ICON_ELEMENT
- " " BOOKMARK_HREF_ATTRIBUTE "=\"", metadata->icon_href,
- "\" " BOOKMARK_TYPE_ATTRIBUTE "=\"", metadata->icon_mime, "\"/>\n", NULL);
- g_string_append (retval, buffer);
-
- g_free (buffer);
- }
-
- /* private hint */
- if (metadata->is_private)
- g_string_append (retval,
- " "
- "<" BOOKMARK_NAMESPACE_NAME
- ":" BOOKMARK_PRIVATE_ELEMENT "/>\n");
-
- /* close metadata container */
- g_string_append (retval,
- " "
- "</" XBEL_METADATA_ELEMENT ">\n");
-
- return g_string_free (retval, FALSE);
-}
-
-/******************************************************
- * BookmarkItem *
- * *
- * Storage for a single bookmark item inside the list *
- ******************************************************/
-static BookmarkItem *
-bookmark_item_new (const gchar *uri)
-{
- BookmarkItem *item;
-
- g_warn_if_fail (uri != NULL);
-
- item = g_slice_new (BookmarkItem);
- item->uri = g_strdup (uri);
-
- item->title = NULL;
- item->description = NULL;
-
- item->added = (time_t) -1;
- item->modified = (time_t) -1;
- item->visited = (time_t) -1;
-
- item->metadata = NULL;
-
- return item;
-}
-
-static void
-bookmark_item_free (BookmarkItem *item)
-{
- if (!item)
- return;
-
- g_free (item->uri);
- g_free (item->title);
- g_free (item->description);
-
- if (item->metadata)
- bookmark_metadata_free (item->metadata);
-
- g_slice_free (BookmarkItem, item);
-}
-
-static gchar *
-bookmark_item_dump (BookmarkItem *item)
-{
- GString *retval;
- gchar *added, *visited, *modified;
- gchar *escaped_uri;
- gchar *buffer;
-
- /* at this point, we must have at least a registered application; if we don't
- * we don't screw up the bookmark file, and just skip this item
- */
- if (!item->metadata || !item->metadata->applications)
- {
- g_warning ("Item for URI '%s' has no registered applications: skipping.\n", item->uri);
- return NULL;
- }
-
- retval = g_string_sized_new (4096);
-
- added = timestamp_to_iso8601 (item->added);
- modified = timestamp_to_iso8601 (item->modified);
- visited = timestamp_to_iso8601 (item->visited);
-
- escaped_uri = g_markup_escape_text (item->uri, -1);
-
- buffer = g_strconcat (" <"
- XBEL_BOOKMARK_ELEMENT
- " "
- XBEL_HREF_ATTRIBUTE "=\"", escaped_uri, "\" "
- XBEL_ADDED_ATTRIBUTE "=\"", added, "\" "
- XBEL_MODIFIED_ATTRIBUTE "=\"", modified, "\" "
- XBEL_VISITED_ATTRIBUTE "=\"", visited, "\">\n",
- NULL);
-
- g_string_append (retval, buffer);
-
- g_free (escaped_uri);
- g_free (visited);
- g_free (modified);
- g_free (added);
- g_free (buffer);
-
- if (item->title)
- {
- gchar *escaped_title;
-
- escaped_title = g_markup_escape_text (item->title, -1);
- buffer = g_strconcat (" "
- "<" XBEL_TITLE_ELEMENT ">",
- escaped_title,
- "</" XBEL_TITLE_ELEMENT ">\n",
- NULL);
- g_string_append (retval, buffer);
-
- g_free (escaped_title);
- g_free (buffer);
- }
-
- if (item->description)
- {
- gchar *escaped_desc;
-
- escaped_desc = g_markup_escape_text (item->description, -1);
- buffer = g_strconcat (" "
- "<" XBEL_DESC_ELEMENT ">",
- escaped_desc,
- "</" XBEL_DESC_ELEMENT ">\n",
- NULL);
- g_string_append (retval, buffer);
-
- g_free (escaped_desc);
- g_free (buffer);
- }
-
- if (item->metadata)
- {
- gchar *metadata;
-
- metadata = bookmark_metadata_dump (item->metadata);
- if (metadata)
- {
- buffer = g_strconcat (" "
- "<" XBEL_INFO_ELEMENT ">\n",
- metadata,
- " "
- "</" XBEL_INFO_ELEMENT ">\n",
- NULL);
- retval = g_string_append (retval, buffer);
-
- g_free (buffer);
- g_free (metadata);
- }
- }
-
- g_string_append (retval, " </" XBEL_BOOKMARK_ELEMENT ">\n");
-
- return g_string_free (retval, FALSE);
-}
-
-static BookmarkAppInfo *
-bookmark_item_lookup_app_info (BookmarkItem *item,
- const gchar *app_name)
-{
- g_warn_if_fail (item != NULL && app_name != NULL);
-
- if (!item->metadata)
- return NULL;
-
- return g_hash_table_lookup (item->metadata->apps_by_name, app_name);
-}
-
-/*************************
- * GBookmarkFile *
- *************************/
-
-static void
-g_bookmark_file_init (GBookmarkFile *bookmark)
-{
- bookmark->title = NULL;
- bookmark->description = NULL;
-
- bookmark->items = NULL;
- bookmark->items_by_uri = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- NULL,
- NULL);
-}
-
-static void
-g_bookmark_file_clear (GBookmarkFile *bookmark)
-{
- g_free (bookmark->title);
- g_free (bookmark->description);
-
- if (bookmark->items)
- {
- g_list_foreach (bookmark->items,
- (GFunc) bookmark_item_free,
- NULL);
- g_list_free (bookmark->items);
-
- bookmark->items = NULL;
- }
-
- if (bookmark->items_by_uri)
- {
- g_hash_table_destroy (bookmark->items_by_uri);
-
- bookmark->items_by_uri = NULL;
- }
-}
-
-struct _ParseData
-{
- gint state;
-
- GHashTable *namespaces;
-
- GBookmarkFile *bookmark_file;
- BookmarkItem *current_item;
-};
-
-static ParseData *
-parse_data_new (void)
-{
- ParseData *retval;
-
- retval = g_new (ParseData, 1);
-
- retval->state = STATE_STARTED;
- retval->namespaces = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) g_free);
- retval->bookmark_file = NULL;
- retval->current_item = NULL;
-
- return retval;
-}
-
-static void
-parse_data_free (ParseData *parse_data)
-{
- g_hash_table_destroy (parse_data->namespaces);
-
- g_free (parse_data);
-}
-
-#define IS_ATTRIBUTE(s,a) ((0 == strcmp ((s), (a))))
-
-static void
-parse_bookmark_element (GMarkupParseContext *context,
- ParseData *parse_data,
- const gchar **attribute_names,
- const gchar **attribute_values,
- GError **error)
-{
- const gchar *uri, *added, *modified, *visited;
- const gchar *attr;
- gint i;
- BookmarkItem *item;
- GError *add_error;
-
- g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_BOOKMARK));
-
- i = 0;
- uri = added = modified = visited = NULL;
- for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
- {
- if (IS_ATTRIBUTE (attr, XBEL_HREF_ATTRIBUTE))
- uri = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, XBEL_ADDED_ATTRIBUTE))
- added = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, XBEL_MODIFIED_ATTRIBUTE))
- modified = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, XBEL_VISITED_ATTRIBUTE))
- visited = attribute_values[i];
- else
- {
- /* bookmark is defined by the XBEL spec, so we need
- * to error out if the element has different or
- * missing attributes
- */
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
- _("Unexpected attribute '%s' for element '%s'"),
- attr,
- XBEL_BOOKMARK_ELEMENT);
- return;
- }
- }
-
- if (!uri)
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Attribute '%s' of element '%s' not found"),
- XBEL_HREF_ATTRIBUTE,
- XBEL_BOOKMARK_ELEMENT);
- return;
- }
-
- g_warn_if_fail (parse_data->current_item == NULL);
-
- item = bookmark_item_new (uri);
-
- if (added)
- item->added = timestamp_from_iso8601 (added);
-
- if (modified)
- item->modified = timestamp_from_iso8601 (modified);
-
- if (visited)
- item->visited = timestamp_from_iso8601 (visited);
-
- add_error = NULL;
- g_bookmark_file_add_item (parse_data->bookmark_file,
- item,
- &add_error);
- if (add_error)
- {
- bookmark_item_free (item);
-
- g_propagate_error (error, add_error);
-
- return;
- }
-
- parse_data->current_item = item;
-}
-
-static void
-parse_application_element (GMarkupParseContext *context,
- ParseData *parse_data,
- const gchar **attribute_names,
- const gchar **attribute_values,
- GError **error)
-{
- const gchar *name, *exec, *count, *stamp, *modified;
- const gchar *attr;
- gint i;
- BookmarkItem *item;
- BookmarkAppInfo *ai;
-
- g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_APPLICATION));
-
- i = 0;
- name = exec = count = stamp = modified = NULL;
- for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
- {
- if (IS_ATTRIBUTE (attr, BOOKMARK_NAME_ATTRIBUTE))
- name = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, BOOKMARK_EXEC_ATTRIBUTE))
- exec = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, BOOKMARK_COUNT_ATTRIBUTE))
- count = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, BOOKMARK_TIMESTAMP_ATTRIBUTE))
- stamp = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, BOOKMARK_MODIFIED_ATTRIBUTE))
- modified = attribute_values[i];
- }
-
- /* the "name" and "exec" attributes are mandatory */
- if (!name)
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Attribute '%s' of element '%s' not found"),
- BOOKMARK_NAME_ATTRIBUTE,
- BOOKMARK_APPLICATION_ELEMENT);
- return;
- }
-
- if (!exec)
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Attribute '%s' of element '%s' not found"),
- BOOKMARK_EXEC_ATTRIBUTE,
- BOOKMARK_APPLICATION_ELEMENT);
- return;
- }
-
- g_warn_if_fail (parse_data->current_item != NULL);
- item = parse_data->current_item;
-
- ai = bookmark_item_lookup_app_info (item, name);
- if (!ai)
- {
- ai = bookmark_app_info_new (name);
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
- g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
- }
-
- ai->exec = g_strdup (exec);
-
- if (count)
- ai->count = atoi (count);
- else
- ai->count = 1;
-
- if (modified)
- ai->stamp = timestamp_from_iso8601 (modified);
- else
- {
- /* the timestamp attribute has been deprecated but we still parse
- * it for backward compatibility
- */
- if (stamp)
- ai->stamp = (time_t) atol (stamp);
- else
- ai->stamp = time (NULL);
- }
-}
-
-static void
-parse_mime_type_element (GMarkupParseContext *context,
- ParseData *parse_data,
- const gchar **attribute_names,
- const gchar **attribute_values,
- GError **error)
-{
- const gchar *type;
- const gchar *attr;
- gint i;
- BookmarkItem *item;
-
- g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_MIME));
-
- i = 0;
- type = NULL;
- for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
- {
- if (IS_ATTRIBUTE (attr, MIME_TYPE_ATTRIBUTE))
- type = attribute_values[i];
- }
-
- if (!type)
- type = "application/octet-stream";
-
- g_warn_if_fail (parse_data->current_item != NULL);
- item = parse_data->current_item;
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- item->metadata->mime_type = g_strdup (type);
-}
-
-static void
-parse_icon_element (GMarkupParseContext *context,
- ParseData *parse_data,
- const gchar **attribute_names,
- const gchar **attribute_values,
- GError **error)
-{
- const gchar *href;
- const gchar *type;
- const gchar *attr;
- gint i;
- BookmarkItem *item;
-
- g_warn_if_fail ((parse_data != NULL) && (parse_data->state == STATE_ICON));
-
- i = 0;
- href = NULL;
- type = NULL;
- for (attr = attribute_names[i]; attr != NULL; attr = attribute_names[++i])
- {
- if (IS_ATTRIBUTE (attr, BOOKMARK_HREF_ATTRIBUTE))
- href = attribute_values[i];
- else if (IS_ATTRIBUTE (attr, BOOKMARK_TYPE_ATTRIBUTE))
- type = attribute_values[i];
- }
-
- /* the "href" attribute is mandatory */
- if (!href)
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Attribute '%s' of element '%s' not found"),
- BOOKMARK_HREF_ATTRIBUTE,
- BOOKMARK_ICON_ELEMENT);
- return;
- }
-
- if (!type)
- type = "application/octet-stream";
-
- g_warn_if_fail (parse_data->current_item != NULL);
- item = parse_data->current_item;
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- item->metadata->icon_href = g_strdup (href);
- item->metadata->icon_mime = g_strdup (type);
-}
-
-/* scans through the attributes of an element for the "xmlns" pragma, and
- * adds any resulting namespace declaration to a per-parser hashtable, using
- * the namespace name as a key for the namespace URI; if no key was found,
- * the namespace is considered as default, and stored under the "default" key.
- *
- * FIXME: this works on the assumption that the generator of the XBEL file
- * is either this code or is smart enough to place the namespace declarations
- * inside the main root node or inside the metadata node and does not redefine
- * a namespace inside an inner node; this does *not* conform to the
- * XML-NS standard, although is a close approximation. In order to make this
- * conformant to the XML-NS specification we should use a per-element
- * namespace table inside GMarkup and ask it to resolve the namespaces for us.
- */
-static void
-map_namespace_to_name (ParseData *parse_data,
- const gchar **attribute_names,
- const gchar **attribute_values)
-{
- const gchar *attr;
- gint i;
-
- g_warn_if_fail (parse_data != NULL);
-
- if (!attribute_names || !attribute_names[0])
- return;
-
- i = 0;
- for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
- {
- if (g_str_has_prefix (attr, "xmlns"))
- {
- gchar *namespace_name, *namespace_uri;
- gchar *p;
-
- p = g_utf8_strchr (attr, -1, ':');
- if (p)
- p = g_utf8_next_char (p);
- else
- p = "default";
-
- namespace_name = g_strdup (p);
- namespace_uri = g_strdup (attribute_values[i]);
-
- g_hash_table_replace (parse_data->namespaces,
- namespace_name,
- namespace_uri);
- }
- }
-}
-
-/* checks whether @element_full is equal to @element.
- *
- * if @namespace is set, it tries to resolve the namespace to a known URI,
- * and if found is prepended to the element name, from which is separated
- * using the character specified in the @sep parameter.
- */
-static gboolean
-is_element_full (ParseData *parse_data,
- const gchar *element_full,
- const gchar *namespace,
- const gchar *element,
- const gchar sep)
-{
- gchar *ns_uri, *ns_name;
- const gchar *p, *element_name;
- gboolean retval;
-
- g_warn_if_fail (parse_data != NULL);
- g_warn_if_fail (element_full != NULL);
-
- if (!element)
- return FALSE;
-
- /* no namespace requested: dumb element compare */
- if (!namespace)
- return (0 == strcmp (element_full, element));
-
- /* search for namespace separator; if none found, assume we are under the
- * default namespace, and set ns_name to our "default" marker; if no default
- * namespace has been set, just do a plain comparison between @full_element
- * and @element.
- */
- p = g_utf8_strchr (element_full, -1, ':');
- if (p)
- {
- ns_name = g_strndup (element_full, p - element_full);
- element_name = g_utf8_next_char (p);
- }
- else
- {
- ns_name = g_strdup ("default");
- element_name = element_full;
- }
-
- ns_uri = g_hash_table_lookup (parse_data->namespaces, ns_name);
- if (!ns_uri)
- {
- /* no default namespace found */
- g_free (ns_name);
-
- return (0 == strcmp (element_full, element));
- }
-
- retval = (0 == strcmp (ns_uri, namespace) &&
- 0 == strcmp (element_name, element));
-
- g_free (ns_name);
-
- return retval;
-}
-
-#define IS_ELEMENT(p,s,e) (is_element_full ((p), (s), NULL, (e), '\0'))
-#define IS_ELEMENT_NS(p,s,n,e) (is_element_full ((p), (s), (n), (e), '|'))
-
-static void
-start_element_raw_cb (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error)
-{
- ParseData *parse_data = (ParseData *) user_data;
-
- /* we must check for namespace declarations first
- *
- * XXX - we could speed up things by checking for namespace declarations
- * only on the root node, where they usually are; this would probably break
- * on streams not produced by us or by "smart" generators
- */
- map_namespace_to_name (parse_data, attribute_names, attribute_values);
-
- switch (parse_data->state)
- {
- case STATE_STARTED:
- if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
- {
- const gchar *attr;
- gint i;
-
- i = 0;
- for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
- {
- if ((IS_ATTRIBUTE (attr, XBEL_VERSION_ATTRIBUTE)) &&
- (0 == strcmp (attribute_values[i], XBEL_VERSION)))
- parse_data->state = STATE_ROOT;
- }
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s', tag '%s' expected"),
- element_name, XBEL_ROOT_ELEMENT);
- break;
- case STATE_ROOT:
- if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
- parse_data->state = STATE_TITLE;
- else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
- parse_data->state = STATE_DESC;
- else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
- {
- GError *inner_error = NULL;
-
- parse_data->state = STATE_BOOKMARK;
-
- parse_bookmark_element (context,
- parse_data,
- attribute_names,
- attribute_values,
- &inner_error);
- if (inner_error)
- g_propagate_error (error, inner_error);
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s' inside '%s'"),
- element_name,
- XBEL_ROOT_ELEMENT);
- break;
- case STATE_BOOKMARK:
- if (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT))
- parse_data->state = STATE_TITLE;
- else if (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT))
- parse_data->state = STATE_DESC;
- else if (IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT))
- parse_data->state = STATE_INFO;
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s' inside '%s'"),
- element_name,
- XBEL_BOOKMARK_ELEMENT);
- break;
- case STATE_INFO:
- if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
- {
- const gchar *attr;
- gint i;
-
- i = 0;
- for (attr = attribute_names[i]; attr; attr = attribute_names[++i])
- {
- if ((IS_ATTRIBUTE (attr, XBEL_OWNER_ATTRIBUTE)) &&
- (0 == strcmp (attribute_values[i], BOOKMARK_METADATA_OWNER)))
- {
- parse_data->state = STATE_METADATA;
-
- if (!parse_data->current_item->metadata)
- parse_data->current_item->metadata = bookmark_metadata_new ();
- }
- }
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s', tag '%s' expected"),
- element_name,
- XBEL_METADATA_ELEMENT);
- break;
- case STATE_METADATA:
- if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT))
- parse_data->state = STATE_APPLICATIONS;
- else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT))
- parse_data->state = STATE_GROUPS;
- else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT))
- parse_data->current_item->metadata->is_private = TRUE;
- else if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
- {
- GError *inner_error = NULL;
-
- parse_data->state = STATE_ICON;
-
- parse_icon_element (context,
- parse_data,
- attribute_names,
- attribute_values,
- &inner_error);
- if (inner_error)
- g_propagate_error (error, inner_error);
- }
- else if (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT))
- {
- GError *inner_error = NULL;
-
- parse_data->state = STATE_MIME;
-
- parse_mime_type_element (context,
- parse_data,
- attribute_names,
- attribute_values,
- &inner_error);
- if (inner_error)
- g_propagate_error (error, inner_error);
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_UNKNOWN_ELEMENT,
- _("Unexpected tag '%s' inside '%s'"),
- element_name,
- XBEL_METADATA_ELEMENT);
- break;
- case STATE_APPLICATIONS:
- if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATION_ELEMENT))
- {
- GError *inner_error = NULL;
-
- parse_data->state = STATE_APPLICATION;
-
- parse_application_element (context,
- parse_data,
- attribute_names,
- attribute_values,
- &inner_error);
- if (inner_error)
- g_propagate_error (error, inner_error);
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s', tag '%s' expected"),
- element_name,
- BOOKMARK_APPLICATION_ELEMENT);
- break;
- case STATE_GROUPS:
- if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUP_ELEMENT))
- parse_data->state = STATE_GROUP;
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- _("Unexpected tag '%s', tag '%s' expected"),
- element_name,
- BOOKMARK_GROUP_ELEMENT);
- break;
- case STATE_ICON:
- if (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT))
- {
- GError *inner_error = NULL;
-
- parse_icon_element (context,
- parse_data,
- attribute_names,
- attribute_values,
- &inner_error);
- if (inner_error)
- g_propagate_error (error, inner_error);
- }
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_UNKNOWN_ELEMENT,
- _("Unexpected tag '%s' inside '%s'"),
- element_name,
- XBEL_METADATA_ELEMENT);
- break;
- default:
- g_warn_if_reached ();
- break;
- }
-}
-
-static void
-end_element_raw_cb (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- ParseData *parse_data = (ParseData *) user_data;
-
- if (IS_ELEMENT (parse_data, element_name, XBEL_ROOT_ELEMENT))
- parse_data->state = STATE_FINISHED;
- else if (IS_ELEMENT (parse_data, element_name, XBEL_BOOKMARK_ELEMENT))
- {
- parse_data->current_item = NULL;
-
- parse_data->state = STATE_ROOT;
- }
- else if ((IS_ELEMENT (parse_data, element_name, XBEL_INFO_ELEMENT)) ||
- (IS_ELEMENT (parse_data, element_name, XBEL_TITLE_ELEMENT)) ||
- (IS_ELEMENT (parse_data, element_name, XBEL_DESC_ELEMENT)))
- {
- if (parse_data->current_item)
- parse_data->state = STATE_BOOKMARK;
- else
- parse_data->state = STATE_ROOT;
- }
- else if (IS_ELEMENT (parse_data, element_name, XBEL_METADATA_ELEMENT))
- parse_data->state = STATE_INFO;
- else if (IS_ELEMENT_NS (parse_data, element_name,
- BOOKMARK_NAMESPACE_URI,
- BOOKMARK_APPLICATION_ELEMENT))
- parse_data->state = STATE_APPLICATIONS;
- else if (IS_ELEMENT_NS (parse_data, element_name,
- BOOKMARK_NAMESPACE_URI,
- BOOKMARK_GROUP_ELEMENT))
- parse_data->state = STATE_GROUPS;
- else if ((IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_APPLICATIONS_ELEMENT)) ||
- (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_GROUPS_ELEMENT)) ||
- (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_PRIVATE_ELEMENT)) ||
- (IS_ELEMENT_NS (parse_data, element_name, BOOKMARK_NAMESPACE_URI, BOOKMARK_ICON_ELEMENT)) ||
- (IS_ELEMENT_NS (parse_data, element_name, MIME_NAMESPACE_URI, MIME_TYPE_ELEMENT)))
- parse_data->state = STATE_METADATA;
-}
-
-static void
-text_raw_cb (GMarkupParseContext *context,
- const gchar *text,
- gsize length,
- gpointer user_data,
- GError **error)
-{
- ParseData *parse_data = (ParseData *) user_data;
- gchar *payload;
-
- payload = g_strndup (text, length);
-
- switch (parse_data->state)
- {
- case STATE_TITLE:
- if (parse_data->current_item)
- {
- g_free (parse_data->current_item->title);
- parse_data->current_item->title = g_strdup (payload);
- }
- else
- {
- g_free (parse_data->bookmark_file->title);
- parse_data->bookmark_file->title = g_strdup (payload);
- }
- break;
- case STATE_DESC:
- if (parse_data->current_item)
- {
- g_free (parse_data->current_item->description);
- parse_data->current_item->description = g_strdup (payload);
- }
- else
- {
- g_free (parse_data->bookmark_file->description);
- parse_data->bookmark_file->description = g_strdup (payload);
- }
- break;
- case STATE_GROUP:
- {
- GList *groups;
-
- g_warn_if_fail (parse_data->current_item != NULL);
-
- if (!parse_data->current_item->metadata)
- parse_data->current_item->metadata = bookmark_metadata_new ();
-
- groups = parse_data->current_item->metadata->groups;
- parse_data->current_item->metadata->groups = g_list_prepend (groups, g_strdup (payload));
- }
- break;
- case STATE_ROOT:
- case STATE_BOOKMARK:
- case STATE_INFO:
- case STATE_METADATA:
- case STATE_APPLICATIONS:
- case STATE_APPLICATION:
- case STATE_GROUPS:
- case STATE_MIME:
- case STATE_ICON:
- break;
- default:
- g_warn_if_reached ();
- break;
- }
-
- g_free (payload);
-}
-
-static const GMarkupParser markup_parser =
-{
- start_element_raw_cb, /* start_element */
- end_element_raw_cb, /* end_element */
- text_raw_cb, /* text */
- NULL, /* passthrough */
- NULL
-};
-
-static gboolean
-g_bookmark_file_parse (GBookmarkFile *bookmark,
- const gchar *buffer,
- gsize length,
- GError **error)
-{
- GMarkupParseContext *context;
- ParseData *parse_data;
- GError *parse_error, *end_error;
- gboolean retval;
-
- g_warn_if_fail (bookmark != NULL);
-
- if (!buffer)
- return FALSE;
-
- if (length == (gsize) -1)
- length = strlen (buffer);
-
- parse_data = parse_data_new ();
- parse_data->bookmark_file = bookmark;
-
- context = g_markup_parse_context_new (&markup_parser,
- 0,
- parse_data,
- (GDestroyNotify) parse_data_free);
-
- parse_error = NULL;
- retval = g_markup_parse_context_parse (context,
- buffer,
- length,
- &parse_error);
- if (!retval)
- {
- g_propagate_error (error, parse_error);
-
- return FALSE;
- }
-
- end_error = NULL;
- retval = g_markup_parse_context_end_parse (context, &end_error);
- if (!retval)
- {
- g_propagate_error (error, end_error);
-
- return FALSE;
- }
-
- g_markup_parse_context_free (context);
-
- return TRUE;
-}
-
-static gchar *
-g_bookmark_file_dump (GBookmarkFile *bookmark,
- gsize *length,
- GError **error)
-{
- GString *retval;
- gchar *buffer;
- GList *l;
-
- retval = g_string_sized_new (4096);
-
- g_string_append (retval,
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-#if 0
- /* XXX - do we really need the doctype? */
- "<!DOCTYPE " XBEL_DTD_NICK "\n"
- " PUBLIC \"" XBEL_DTD_SYSTEM "\"\n"
- " \"" XBEL_DTD_URI "\">\n"
-#endif
- "<" XBEL_ROOT_ELEMENT " " XBEL_VERSION_ATTRIBUTE "=\"" XBEL_VERSION "\"\n"
- " xmlns:" BOOKMARK_NAMESPACE_NAME "=\"" BOOKMARK_NAMESPACE_URI "\"\n"
- " xmlns:" MIME_NAMESPACE_NAME "=\"" MIME_NAMESPACE_URI "\"\n>");
-
- if (bookmark->title)
- {
- gchar *escaped_title;
-
- escaped_title = g_markup_escape_text (bookmark->title, -1);
-
- buffer = g_strconcat (" "
- "<" XBEL_TITLE_ELEMENT ">",
- escaped_title,
- "</" XBEL_TITLE_ELEMENT ">\n", NULL);
-
- g_string_append (retval, buffer);
-
- g_free (buffer);
- g_free (escaped_title);
- }
-
- if (bookmark->description)
- {
- gchar *escaped_desc;
-
- escaped_desc = g_markup_escape_text (bookmark->description, -1);
-
- buffer = g_strconcat (" "
- "<" XBEL_DESC_ELEMENT ">",
- escaped_desc,
- "</" XBEL_DESC_ELEMENT ">\n", NULL);
- g_string_append (retval, buffer);
-
- g_free (buffer);
- g_free (escaped_desc);
- }
-
- if (!bookmark->items)
- goto out;
- else
- retval = g_string_append (retval, "\n");
-
- /* the items are stored in reverse order */
- for (l = g_list_last (bookmark->items);
- l != NULL;
- l = l->prev)
- {
- BookmarkItem *item = (BookmarkItem *) l->data;
- gchar *item_dump;
-
- item_dump = bookmark_item_dump (item);
- if (!item_dump)
- continue;
-
- retval = g_string_append (retval, item_dump);
-
- g_free (item_dump);
- }
-
-out:
- g_string_append (retval, "</" XBEL_ROOT_ELEMENT ">");
-
- if (length)
- *length = retval->len;
-
- return g_string_free (retval, FALSE);
-}
-
-/**************
- * Misc *
- **************/
-
-/* converts a Unix timestamp in a ISO 8601 compliant string; you
- * should free the returned string.
- */
-static gchar *
-timestamp_to_iso8601 (time_t timestamp)
-{
- GTimeVal stamp;
-
- if (timestamp == (time_t) -1)
- g_get_current_time (&stamp);
- else
- {
- stamp.tv_sec = timestamp;
- stamp.tv_usec = 0;
- }
-
- return g_time_val_to_iso8601 (&stamp);
-}
-
-static time_t
-timestamp_from_iso8601 (const gchar *iso_date)
-{
- GTimeVal stamp;
-
- if (!g_time_val_from_iso8601 (iso_date, &stamp))
- return (time_t) -1;
-
- return (time_t) stamp.tv_sec;
-}
-
-
-
-GQuark
-g_bookmark_file_error_quark (void)
-{
- return g_quark_from_static_string ("g-bookmark-file-error-quark");
-}
-
-
-
-/********************
- * Public API *
- ********************/
-
-/**
- * g_bookmark_file_new:
- *
- * Creates a new empty #GBookmarkFile object.
- *
- * Use g_bookmark_file_load_from_file(), g_bookmark_file_load_from_data()
- * or g_bookmark_file_load_from_data_dirs() to read an existing bookmark
- * file.
- *
- * Return value: an empty #GBookmarkFile
- *
- * Since: 2.12
- */
-GBookmarkFile *
-g_bookmark_file_new (void)
-{
- GBookmarkFile *bookmark;
-
- bookmark = g_new (GBookmarkFile, 1);
-
- g_bookmark_file_init (bookmark);
-
- return bookmark;
-}
-
-/**
- * g_bookmark_file_free:
- * @bookmark: a #GBookmarkFile
- *
- * Frees a #GBookmarkFile.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_free (GBookmarkFile *bookmark)
-{
- if (!bookmark)
- return;
-
- g_bookmark_file_clear (bookmark);
-
- g_free (bookmark);
-}
-
-/**
- * g_bookmark_file_load_from_data:
- * @bookmark: an empty #GBookmarkFile struct
- * @data: desktop bookmarks loaded in memory
- * @length: the length of @data in bytes
- * @error: return location for a #GError, or %NULL
- *
- * Loads a bookmark file from memory into an empty #GBookmarkFile
- * structure. If the object cannot be created then @error is set to a
- * #GBookmarkFileError.
- *
- * Return value: %TRUE if a desktop bookmark could be loaded.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_load_from_data (GBookmarkFile *bookmark,
- const gchar *data,
- gsize length,
- GError **error)
-{
- GError *parse_error;
- gboolean retval;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
-
- if (length == (gsize) -1)
- length = strlen (data);
-
- if (bookmark->items)
- {
- g_bookmark_file_clear (bookmark);
- g_bookmark_file_init (bookmark);
- }
-
- parse_error = NULL;
- retval = g_bookmark_file_parse (bookmark, data, length, &parse_error);
- if (!retval)
- {
- g_propagate_error (error, parse_error);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * g_bookmark_file_load_from_file:
- * @bookmark: an empty #GBookmarkFile struct
- * @filename: the path of a filename to load, in the GLib file name encoding
- * @error: return location for a #GError, or %NULL
- *
- * Loads a desktop bookmark file into an empty #GBookmarkFile structure.
- * If the file could not be loaded then @error is set to either a #GFileError
- * or #GBookmarkFileError.
- *
- * Return value: %TRUE if a desktop bookmark file could be loaded
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_load_from_file (GBookmarkFile *bookmark,
- const gchar *filename,
- GError **error)
-{
- gchar *buffer;
- gsize len;
- GError *read_error;
- gboolean retval;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (filename != NULL, FALSE);
-
- read_error = NULL;
- g_file_get_contents (filename, &buffer, &len, &read_error);
- if (read_error)
- {
- g_propagate_error (error, read_error);
-
- return FALSE;
- }
-
- read_error = NULL;
- retval = g_bookmark_file_load_from_data (bookmark,
- buffer,
- len,
- &read_error);
- if (read_error)
- {
- g_propagate_error (error, read_error);
-
- g_free (buffer);
-
- return FALSE;
- }
-
- g_free (buffer);
-
- return retval;
-}
-
-
-/* Iterates through all the directories in *dirs trying to
- * find file. When it successfully locates file, returns a
- * string its absolute path. It also leaves the unchecked
- * directories in *dirs. You should free the returned string
- *
- * Adapted from gkeyfile.c
- */
-static gchar *
-find_file_in_data_dirs (const gchar *file,
- gchar ***dirs,
- GError **error)
-{
- gchar **data_dirs, *data_dir, *path;
-
- path = NULL;
-
- if (dirs == NULL)
- return NULL;
-
- data_dirs = *dirs;
- path = NULL;
- while (data_dirs && (data_dir = *data_dirs) && !path)
- {
- gchar *candidate_file, *sub_dir;
-
- candidate_file = (gchar *) file;
- sub_dir = g_strdup ("");
- while (candidate_file != NULL && !path)
- {
- gchar *p;
-
- path = g_build_filename (data_dir, sub_dir,
- candidate_file, NULL);
-
- candidate_file = strchr (candidate_file, '-');
-
- if (candidate_file == NULL)
- break;
-
- candidate_file++;
-
- g_free (sub_dir);
- sub_dir = g_strndup (file, candidate_file - file - 1);
-
- for (p = sub_dir; *p != '\0'; p++)
- {
- if (*p == '-')
- *p = G_DIR_SEPARATOR;
- }
- }
- g_free (sub_dir);
- data_dirs++;
- }
-
- *dirs = data_dirs;
-
- if (!path)
- {
- g_set_error_literal (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_FILE_NOT_FOUND,
- _("No valid bookmark file found in data dirs"));
-
- return NULL;
- }
-
- return path;
-}
-
-
-/**
- * g_bookmark_file_load_from_data_dirs:
- * @bookmark: a #GBookmarkFile
- * @file: a relative path to a filename to open and parse
- * @full_path: return location for a string containing the full path
- * of the file, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * This function looks for a desktop bookmark file named @file in the
- * paths returned from g_get_user_data_dir() and g_get_system_data_dirs(),
- * loads the file into @bookmark and returns the file's full path in
- * @full_path. If the file could not be loaded then an %error is
- * set to either a #GFileError or #GBookmarkFileError.
- *
- * Return value: %TRUE if a key file could be loaded, %FALSE othewise
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_load_from_data_dirs (GBookmarkFile *bookmark,
- const gchar *file,
- gchar **full_path,
- GError **error)
-{
- GError *file_error = NULL;
- gchar **all_data_dirs, **data_dirs;
- const gchar *user_data_dir;
- const gchar * const * system_data_dirs;
- gsize i, j;
- gchar *output_path;
- gboolean found_file;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
-
- user_data_dir = g_get_user_data_dir ();
- system_data_dirs = g_get_system_data_dirs ();
- all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
-
- i = 0;
- all_data_dirs[i++] = g_strdup (user_data_dir);
-
- j = 0;
- while (system_data_dirs[j] != NULL)
- all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
-
- found_file = FALSE;
- data_dirs = all_data_dirs;
- output_path = NULL;
- while (*data_dirs != NULL && !found_file)
- {
- g_free (output_path);
-
- output_path = find_file_in_data_dirs (file, &data_dirs, &file_error);
-
- if (file_error)
- {
- g_propagate_error (error, file_error);
- break;
- }
-
- found_file = g_bookmark_file_load_from_file (bookmark,
- output_path,
- &file_error);
- if (file_error)
- {
- g_propagate_error (error, file_error);
- break;
- }
- }
-
- if (found_file && full_path)
- *full_path = output_path;
- else
- g_free (output_path);
-
- g_strfreev (all_data_dirs);
-
- return found_file;
-}
-
-
-/**
- * g_bookmark_file_to_data:
- * @bookmark: a #GBookmarkFile
- * @length: return location for the length of the returned string, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * This function outputs @bookmark as a string.
- *
- * Return value: a newly allocated string holding
- * the contents of the #GBookmarkFile
- *
- * Since: 2.12
- */
-gchar *
-g_bookmark_file_to_data (GBookmarkFile *bookmark,
- gsize *length,
- GError **error)
-{
- GError *write_error = NULL;
- gchar *retval;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
-
- retval = g_bookmark_file_dump (bookmark, length, &write_error);
- if (write_error)
- {
- g_propagate_error (error, write_error);
-
- return NULL;
- }
-
- return retval;
-}
-
-/**
- * g_bookmark_file_to_file:
- * @bookmark: a #GBookmarkFile
- * @filename: path of the output file
- * @error: return location for a #GError, or %NULL
- *
- * This function outputs @bookmark into a file. The write process is
- * guaranteed to be atomic by using g_file_set_contents() internally.
- *
- * Return value: %TRUE if the file was successfully written.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_to_file (GBookmarkFile *bookmark,
- const gchar *filename,
- GError **error)
-{
- gchar *data;
- GError *data_error, *write_error;
- gsize len;
- gboolean retval;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (filename != NULL, FALSE);
-
- data_error = NULL;
- data = g_bookmark_file_to_data (bookmark, &len, &data_error);
- if (data_error)
- {
- g_propagate_error (error, data_error);
-
- return FALSE;
- }
-
- write_error = NULL;
- g_file_set_contents (filename, data, len, &write_error);
- if (write_error)
- {
- g_propagate_error (error, write_error);
-
- retval = FALSE;
- }
- else
- retval = TRUE;
-
- g_free (data);
-
- return retval;
-}
-
-static BookmarkItem *
-g_bookmark_file_lookup_item (GBookmarkFile *bookmark,
- const gchar *uri)
-{
- g_warn_if_fail (bookmark != NULL && uri != NULL);
-
- return g_hash_table_lookup (bookmark->items_by_uri, uri);
-}
-
-/* this function adds a new item to the list */
-static void
-g_bookmark_file_add_item (GBookmarkFile *bookmark,
- BookmarkItem *item,
- GError **error)
-{
- g_warn_if_fail (bookmark != NULL);
- g_warn_if_fail (item != NULL);
-
- /* this should never happen; and if it does, then we are
- * screwing up something big time.
- */
- if (G_UNLIKELY (g_bookmark_file_has_item (bookmark, item->uri)))
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_INVALID_URI,
- _("A bookmark for URI '%s' already exists"),
- item->uri);
- return;
- }
-
- bookmark->items = g_list_prepend (bookmark->items, item);
-
- g_hash_table_replace (bookmark->items_by_uri,
- item->uri,
- item);
-
- if (item->added == (time_t) -1)
- item->added = time (NULL);
-
- if (item->modified == (time_t) -1)
- item->modified = time (NULL);
-}
-
-/**
- * g_bookmark_file_remove_item:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Removes the bookmark for @uri from the bookmark file @bookmark.
- *
- * Return value: %TRUE if the bookmark was removed successfully.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_remove_item (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
-
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- bookmark->items = g_list_remove (bookmark->items, item);
- g_hash_table_remove (bookmark->items_by_uri, item->uri);
-
- bookmark_item_free (item);
-
- return TRUE;
-}
-
-/**
- * g_bookmark_file_has_item:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- *
- * Looks whether the desktop bookmark has an item with its URI set to @uri.
- *
- * Return value: %TRUE if @uri is inside @bookmark, %FALSE otherwise
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_has_item (GBookmarkFile *bookmark,
- const gchar *uri)
-{
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- return (NULL != g_hash_table_lookup (bookmark->items_by_uri, uri));
-}
-
-/**
- * g_bookmark_file_get_uris:
- * @bookmark: a #GBookmarkFile
- * @length: return location for the number of returned URIs, or %NULL
- *
- * Returns all URIs of the bookmarks in the bookmark file @bookmark.
- * The array of returned URIs will be %NULL-terminated, so @length may
- * optionally be %NULL.
- *
- * Return value: a newly allocated %NULL-terminated array of strings.
- * Use g_strfreev() to free it.
- *
- * Since: 2.12
- */
-gchar **
-g_bookmark_file_get_uris (GBookmarkFile *bookmark,
- gsize *length)
-{
- GList *l;
- gchar **uris;
- gsize i, n_items;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
-
- n_items = g_list_length (bookmark->items);
- uris = g_new0 (gchar *, n_items + 1);
-
- /* the items are stored in reverse order, so we walk the list backward */
- for (l = g_list_last (bookmark->items), i = 0; l != NULL; l = l->prev)
- {
- BookmarkItem *item = (BookmarkItem *) l->data;
-
- g_warn_if_fail (item != NULL);
-
- uris[i++] = g_strdup (item->uri);
- }
- uris[i] = NULL;
-
- if (length)
- *length = i;
-
- return uris;
-}
-
-/**
- * g_bookmark_file_set_title:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI or %NULL
- * @title: a UTF-8 encoded string
- *
- * Sets @title as the title of the bookmark for @uri inside the
- * bookmark file @bookmark.
- *
- * If @uri is %NULL, the title of @bookmark is set.
- *
- * If a bookmark for @uri cannot be found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_title (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *title)
-{
- g_return_if_fail (bookmark != NULL);
-
- if (!uri)
- {
- g_free (bookmark->title);
- bookmark->title = g_strdup (title);
- }
- else
- {
- BookmarkItem *item;
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- g_free (item->title);
- item->title = g_strdup (title);
-
- item->modified = time (NULL);
- }
-}
-
-/**
- * g_bookmark_file_get_title:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Returns the title of the bookmark for @uri.
- *
- * If @uri is %NULL, the title of @bookmark is returned.
- *
- * In the event the URI cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a newly allocated string or %NULL if the specified
- * URI cannot be found.
- *
- * Since: 2.12
- */
-gchar *
-g_bookmark_file_get_title (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
-
- if (!uri)
- return g_strdup (bookmark->title);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return NULL;
- }
-
- return g_strdup (item->title);
-}
-
-/**
- * g_bookmark_file_set_description:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI or %NULL
- * @description: a string
- *
- * Sets @description as the description of the bookmark for @uri.
- *
- * If @uri is %NULL, the description of @bookmark is set.
- *
- * If a bookmark for @uri cannot be found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_description (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *description)
-{
- g_return_if_fail (bookmark != NULL);
-
- if (!uri)
- {
- g_free (bookmark->description);
- bookmark->description = g_strdup (description);
- }
- else
- {
- BookmarkItem *item;
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- g_free (item->description);
- item->description = g_strdup (description);
-
- item->modified = time (NULL);
- }
-}
-
-/**
- * g_bookmark_file_get_description:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Retrieves the description of the bookmark for @uri.
- *
- * In the event the URI cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a newly allocated string or %NULL if the specified
- * URI cannot be found.
- *
- * Since: 2.12
- */
-gchar *
-g_bookmark_file_get_description (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
-
- if (!uri)
- return g_strdup (bookmark->description);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return NULL;
- }
-
- return g_strdup (item->description);
-}
-
-/**
- * g_bookmark_file_set_mime_type:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @mime_type: a MIME type
- *
- * Sets @mime_type as the MIME type of the bookmark for @uri.
- *
- * If a bookmark for @uri cannot be found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_mime_type (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *mime_type)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
- g_return_if_fail (mime_type != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- g_free (item->metadata->mime_type);
-
- item->metadata->mime_type = g_strdup (mime_type);
- item->modified = time (NULL);
-}
-
-/**
- * g_bookmark_file_get_mime_type:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Retrieves the MIME type of the resource pointed by @uri.
- *
- * In the event the URI cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
- * event that the MIME type cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: a newly allocated string or %NULL if the specified
- * URI cannot be found.
- *
- * Since: 2.12
- */
-gchar *
-g_bookmark_file_get_mime_type (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
- g_return_val_if_fail (uri != NULL, NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return NULL;
- }
-
- if (!item->metadata)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
- _("No MIME type defined in the bookmark for URI '%s'"),
- uri);
- return NULL;
- }
-
- return g_strdup (item->metadata->mime_type);
-}
-
-/**
- * g_bookmark_file_set_is_private:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @is_private: %TRUE if the bookmark should be marked as private
- *
- * Sets the private flag of the bookmark for @uri.
- *
- * If a bookmark for @uri cannot be found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_is_private (GBookmarkFile *bookmark,
- const gchar *uri,
- gboolean is_private)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- item->metadata->is_private = (is_private == TRUE);
- item->modified = time (NULL);
-}
-
-/**
- * g_bookmark_file_get_is_private:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Gets whether the private flag of the bookmark for @uri is set.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
- * event that the private flag cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: %TRUE if the private flag is set, %FALSE otherwise.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_get_is_private (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- if (!item->metadata)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
- _("No private flag has been defined in bookmark for URI '%s'"),
- uri);
- return FALSE;
- }
-
- return item->metadata->is_private;
-}
-
-/**
- * g_bookmark_file_set_added:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @added: a timestamp or -1 to use the current time
- *
- * Sets the time the bookmark for @uri was added into @bookmark.
- *
- * If no bookmark for @uri is found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_added (GBookmarkFile *bookmark,
- const gchar *uri,
- time_t added)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (added == (time_t) -1)
- time (&added);
-
- item->added = added;
- item->modified = added;
-}
-
-/**
- * g_bookmark_file_get_added:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Gets the time the bookmark for @uri was added to @bookmark
- *
- * In the event the URI cannot be found, -1 is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a timestamp
- *
- * Since: 2.12
- */
-time_t
-g_bookmark_file_get_added (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, (time_t) -1);
- g_return_val_if_fail (uri != NULL, (time_t) -1);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return (time_t) -1;
- }
-
- return item->added;
-}
-
-/**
- * g_bookmark_file_set_modified:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @modified: a timestamp or -1 to use the current time
- *
- * Sets the last time the bookmark for @uri was last modified.
- *
- * If no bookmark for @uri is found then it is created.
- *
- * The "modified" time should only be set when the bookmark's meta-data
- * was actually changed. Every function of #GBookmarkFile that
- * modifies a bookmark also changes the modification time, except for
- * g_bookmark_file_set_visited().
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_modified (GBookmarkFile *bookmark,
- const gchar *uri,
- time_t modified)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (modified == (time_t) -1)
- time (&modified);
-
- item->modified = modified;
-}
-
-/**
- * g_bookmark_file_get_modified:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Gets the time when the bookmark for @uri was last modified.
- *
- * In the event the URI cannot be found, -1 is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a timestamp
- *
- * Since: 2.12
- */
-time_t
-g_bookmark_file_get_modified (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, (time_t) -1);
- g_return_val_if_fail (uri != NULL, (time_t) -1);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return (time_t) -1;
- }
-
- return item->modified;
-}
-
-/**
- * g_bookmark_file_set_visited:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @visited: a timestamp or -1 to use the current time
- *
- * Sets the time the bookmark for @uri was last visited.
- *
- * If no bookmark for @uri is found then it is created.
- *
- * The "visited" time should only be set if the bookmark was launched,
- * either using the command line retrieved by g_bookmark_file_get_app_info()
- * or by the default application for the bookmark's MIME type, retrieved
- * using g_bookmark_file_get_mime_type(). Changing the "visited" time
- * does not affect the "modified" time.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_visited (GBookmarkFile *bookmark,
- const gchar *uri,
- time_t visited)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (visited == (time_t) -1)
- time (&visited);
-
- item->visited = visited;
-}
-
-/**
- * g_bookmark_file_get_visited:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @error: return location for a #GError, or %NULL
- *
- * Gets the time the bookmark for @uri was last visited.
- *
- * In the event the URI cannot be found, -1 is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a timestamp.
- *
- * Since: 2.12
- */
-time_t
-g_bookmark_file_get_visited (GBookmarkFile *bookmark,
- const gchar *uri,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, (time_t) -1);
- g_return_val_if_fail (uri != NULL, (time_t) -1);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return (time_t) -1;
- }
-
- return item->visited;
-}
-
-/**
- * g_bookmark_file_has_group:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @group: the group name to be searched
- * @error: return location for a #GError, or %NULL
- *
- * Checks whether @group appears in the list of groups to which
- * the bookmark for @uri belongs to.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: %TRUE if @group was found.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_has_group (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *group,
- GError **error)
-{
- BookmarkItem *item;
- GList *l;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- if (!item->metadata)
- return FALSE;
-
- for (l = item->metadata->groups; l != NULL; l = l->next)
- {
- if (strcmp (l->data, group) == 0)
- return TRUE;
- }
-
- return FALSE;
-
-}
-
-/**
- * g_bookmark_file_add_group:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @group: the group name to be added
- *
- * Adds @group to the list of groups to which the bookmark for @uri
- * belongs to.
- *
- * If no bookmark for @uri is found then it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_add_group (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *group)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
- g_return_if_fail (group != NULL && group[0] != '\0');
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- if (!g_bookmark_file_has_group (bookmark, uri, group, NULL))
- {
- item->metadata->groups = g_list_prepend (item->metadata->groups,
- g_strdup (group));
-
- item->modified = time (NULL);
- }
-}
-
-/**
- * g_bookmark_file_remove_group:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @group: the group name to be removed
- * @error: return location for a #GError, or %NULL
- *
- * Removes @group from the list of groups to which the bookmark
- * for @uri belongs to.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- * In the event no group was defined, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: %TRUE if @group was successfully removed.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_remove_group (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *group,
- GError **error)
-{
- BookmarkItem *item;
- GList *l;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- if (!item->metadata)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_INVALID_VALUE,
- _("No groups set in bookmark for URI '%s'"),
- uri);
- return FALSE;
- }
-
- for (l = item->metadata->groups; l != NULL; l = l->next)
- {
- if (strcmp (l->data, group) == 0)
- {
- item->metadata->groups = g_list_remove_link (item->metadata->groups, l);
- g_free (l->data);
- g_list_free_1 (l);
-
- item->modified = time (NULL);
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- * g_bookmark_file_set_groups:
- * @bookmark: a #GBookmarkFile
- * @uri: an item's URI
- * @groups: an array of group names, or %NULL to remove all groups
- * @length: number of group name values in @groups
- *
- * Sets a list of group names for the item with URI @uri. Each previously
- * set group name list is removed.
- *
- * If @uri cannot be found then an item for it is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_groups (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar **groups,
- gsize length)
-{
- BookmarkItem *item;
- gsize i;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
- g_return_if_fail (groups != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- if (item->metadata->groups != NULL)
- {
- g_list_foreach (item->metadata->groups,
- (GFunc) g_free,
- NULL);
- g_list_free (item->metadata->groups);
- item->metadata->groups = NULL;
- }
-
- if (groups)
- {
- for (i = 0; groups[i] != NULL && i < length; i++)
- item->metadata->groups = g_list_append (item->metadata->groups,
- g_strdup (groups[i]));
- }
-
- item->modified = time (NULL);
-}
-
-/**
- * g_bookmark_file_get_groups:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @length: return location for the length of the returned string, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Retrieves the list of group names of the bookmark for @uri.
- *
- * In the event the URI cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * The returned array is %NULL terminated, so @length may optionally
- * be %NULL.
- *
- * Return value: a newly allocated %NULL-terminated array of group names.
- * Use g_strfreev() to free it.
- *
- * Since: 2.12
- */
-gchar **
-g_bookmark_file_get_groups (GBookmarkFile *bookmark,
- const gchar *uri,
- gsize *length,
- GError **error)
-{
- BookmarkItem *item;
- GList *l;
- gsize len, i;
- gchar **retval;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
- g_return_val_if_fail (uri != NULL, NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return NULL;
- }
-
- if (!item->metadata)
- {
- if (length)
- *length = 0;
-
- return NULL;
- }
-
- len = g_list_length (item->metadata->groups);
- retval = g_new0 (gchar *, len + 1);
- for (l = g_list_last (item->metadata->groups), i = 0;
- l != NULL;
- l = l->prev)
- {
- gchar *group_name = (gchar *) l->data;
-
- g_warn_if_fail (group_name != NULL);
-
- retval[i++] = g_strdup (group_name);
- }
- retval[i] = NULL;
-
- if (length)
- *length = len;
-
- return retval;
-}
-
-/**
- * g_bookmark_file_add_application:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @name: the name of the application registering the bookmark
- * or %NULL
- * @exec: command line to be used to launch the bookmark or %NULL
- *
- * Adds the application with @name and @exec to the list of
- * applications that have registered a bookmark for @uri into
- * @bookmark.
- *
- * Every bookmark inside a #GBookmarkFile must have at least an
- * application registered. Each application must provide a name, a
- * command line useful for launching the bookmark, the number of times
- * the bookmark has been registered by the application and the last
- * time the application registered this bookmark.
- *
- * If @name is %NULL, the name of the application will be the
- * same returned by g_get_application_name(); if @exec is %NULL, the
- * command line will be a composition of the program name as
- * returned by g_get_prgname() and the "%u" modifier, which will be
- * expanded to the bookmark's URI.
- *
- * This function will automatically take care of updating the
- * registrations count and timestamping in case an application
- * with the same @name had already registered a bookmark for
- * @uri inside @bookmark.
- *
- * If no bookmark for @uri is found, one is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_add_application (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *name,
- const gchar *exec)
-{
- BookmarkItem *item;
- gchar *app_name, *app_exec;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (name && name[0] != '\0')
- app_name = g_strdup (name);
- else
- app_name = g_strdup (g_get_application_name ());
-
- if (exec && exec[0] != '\0')
- app_exec = g_strdup (exec);
- else
- app_exec = g_strjoin (" ", g_get_prgname(), "%u", NULL);
-
- g_bookmark_file_set_app_info (bookmark, uri,
- app_name,
- app_exec,
- -1,
- (time_t) -1,
- NULL);
-
- g_free (app_exec);
- g_free (app_name);
-}
-
-/**
- * g_bookmark_file_remove_application:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @name: the name of the application
- * @error: return location for a #GError or %NULL
- *
- * Removes application registered with @name from the list of applications
- * that have registered a bookmark for @uri inside @bookmark.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- * In the event that no application with name @app_name has registered
- * a bookmark for @uri, %FALSE is returned and error is set to
- * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED.
- *
- * Return value: %TRUE if the application was successfully removed.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_remove_application (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *name,
- GError **error)
-{
- GError *set_error;
- gboolean retval;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- set_error = NULL;
- retval = g_bookmark_file_set_app_info (bookmark, uri,
- name,
- "",
- 0,
- (time_t) -1,
- &set_error);
- if (set_error)
- {
- g_propagate_error (error, set_error);
-
- return FALSE;
- }
-
- return retval;
-}
-
-/**
- * g_bookmark_file_has_application:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @name: the name of the application
- * @error: return location for a #GError or %NULL
- *
- * Checks whether the bookmark for @uri inside @bookmark has been
- * registered by application @name.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: %TRUE if the application @name was found
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_has_application (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *name,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- return (NULL != bookmark_item_lookup_app_info (item, name));
-}
-
-/**
- * g_bookmark_file_set_app_info:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @name: an application's name
- * @exec: an application's command line
- * @count: the number of registrations done for this application
- * @stamp: the time of the last registration for this application
- * @error: return location for a #GError or %NULL
- *
- * Sets the meta-data of application @name inside the list of
- * applications that have registered a bookmark for @uri inside
- * @bookmark.
- *
- * You should rarely use this function; use g_bookmark_file_add_application()
- * and g_bookmark_file_remove_application() instead.
- *
- * @name can be any UTF-8 encoded string used to identify an
- * application.
- * @exec can have one of these two modifiers: "%f", which will
- * be expanded as the local file name retrieved from the bookmark's
- * URI; "%u", which will be expanded as the bookmark's URI.
- * The expansion is done automatically when retrieving the stored
- * command line using the g_bookmark_file_get_app_info() function.
- * @count is the number of times the application has registered the
- * bookmark; if is < 0, the current registration count will be increased
- * by one, if is 0, the application with @name will be removed from
- * the list of registered applications.
- * @stamp is the Unix time of the last registration; if it is -1, the
- * current time will be used.
- *
- * If you try to remove an application by setting its registration count to
- * zero, and no bookmark for @uri is found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND; similarly,
- * in the event that no application @name has registered a bookmark
- * for @uri, %FALSE is returned and error is set to
- * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. Otherwise, if no bookmark
- * for @uri is found, one is created.
- *
- * Return value: %TRUE if the application's meta-data was successfully
- * changed.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_set_app_info (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *name,
- const gchar *exec,
- gint count,
- time_t stamp,
- GError **error)
-{
- BookmarkItem *item;
- BookmarkAppInfo *ai;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
- g_return_val_if_fail (exec != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- if (count == 0)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
- else
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
- }
-
- ai = bookmark_item_lookup_app_info (item, name);
- if (!ai)
- {
- if (count == 0)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
- _("No application with name '%s' registered a bookmark for '%s'"),
- name,
- uri);
- return FALSE;
- }
- else
- {
- ai = bookmark_app_info_new (name);
-
- item->metadata->applications = g_list_prepend (item->metadata->applications, ai);
- g_hash_table_replace (item->metadata->apps_by_name, ai->name, ai);
- }
- }
-
- if (count == 0)
- {
- item->metadata->applications = g_list_remove (item->metadata->applications, ai);
- g_hash_table_remove (item->metadata->apps_by_name, ai->name);
- bookmark_app_info_free (ai);
-
- item->modified = time (NULL);
-
- return TRUE;
- }
- else if (count > 0)
- ai->count = count;
- else
- ai->count += 1;
-
- if (stamp != (time_t) -1)
- ai->stamp = stamp;
- else
- ai->stamp = time (NULL);
-
- if (exec && exec[0] != '\0')
- {
- g_free (ai->exec);
- ai->exec = g_shell_quote (exec);
- }
-
- item->modified = time (NULL);
-
- return TRUE;
-}
-
-/* expands the application's command line */
-static gchar *
-expand_exec_line (const gchar *exec_fmt,
- const gchar *uri)
-{
- GString *exec;
- gchar ch;
-
- exec = g_string_sized_new (512);
- while ((ch = *exec_fmt++) != '\0')
- {
- if (ch != '%')
- {
- exec = g_string_append_c (exec, ch);
- continue;
- }
-
- ch = *exec_fmt++;
- switch (ch)
- {
- case '\0':
- goto out;
- case 'U':
- case 'u':
- g_string_append (exec, uri);
- break;
- case 'F':
- case 'f':
- {
- gchar *file = g_filename_from_uri (uri, NULL, NULL);
- if (file)
- {
- g_string_append (exec, file);
- g_free (file);
- }
- else
- {
- g_string_free (exec, TRUE);
- return NULL;
- }
- }
- break;
- case '%':
- default:
- exec = g_string_append_c (exec, ch);
- break;
- }
- }
-
- out:
- return g_string_free (exec, FALSE);
-}
-
-/**
- * g_bookmark_file_get_app_info:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @name: an application's name
- * @exec: location for the command line of the application, or %NULL
- * @count: return location for the registration count, or %NULL
- * @stamp: return location for the last registration time, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Gets the registration informations of @app_name for the bookmark for
- * @uri. See g_bookmark_file_set_app_info() for more informations about
- * the returned data.
- *
- * The string returned in @app_exec must be freed.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND. In the
- * event that no application with name @app_name has registered a bookmark
- * for @uri, %FALSE is returned and error is set to
- * #G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED. In the event that unquoting
- * the command line fails, an error of the #G_SHELL_ERROR domain is
- * set and %FALSE is returned.
- *
- * Return value: %TRUE on success.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_get_app_info (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *name,
- gchar **exec,
- guint *count,
- time_t *stamp,
- GError **error)
-{
- BookmarkItem *item;
- BookmarkAppInfo *ai;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- ai = bookmark_item_lookup_app_info (item, name);
- if (!ai)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_APP_NOT_REGISTERED,
- _("No application with name '%s' registered a bookmark for '%s'"),
- name,
- uri);
- return FALSE;
- }
-
- if (exec)
- {
- GError *unquote_error = NULL;
- gchar *command_line;
-
- command_line = g_shell_unquote (ai->exec, &unquote_error);
- if (unquote_error)
- {
- g_propagate_error (error, unquote_error);
- return FALSE;
- }
-
- *exec = expand_exec_line (command_line, uri);
- if (!*exec)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_INVALID_URI,
- _("Failed to expand exec line '%s' with URI '%s'"),
- ai->exec, uri);
- g_free (command_line);
-
- return FALSE;
- }
- else
- g_free (command_line);
- }
-
- if (count)
- *count = ai->count;
-
- if (stamp)
- *stamp = ai->stamp;
-
- return TRUE;
-}
-
-/**
- * g_bookmark_file_get_applications:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @length: return location of the length of the returned list, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Retrieves the names of the applications that have registered the
- * bookmark for @uri.
- *
- * In the event the URI cannot be found, %NULL is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: a newly allocated %NULL-terminated array of strings.
- * Use g_strfreev() to free it.
- *
- * Since: 2.12
- */
-gchar **
-g_bookmark_file_get_applications (GBookmarkFile *bookmark,
- const gchar *uri,
- gsize *length,
- GError **error)
-{
- BookmarkItem *item;
- GList *l;
- gchar **apps;
- gsize i, n_apps;
-
- g_return_val_if_fail (bookmark != NULL, NULL);
- g_return_val_if_fail (uri != NULL, NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return NULL;
- }
-
- if (!item->metadata)
- {
- if (length)
- *length = 0;
-
- return NULL;
- }
-
- n_apps = g_list_length (item->metadata->applications);
- apps = g_new0 (gchar *, n_apps + 1);
-
- for (l = g_list_last (item->metadata->applications), i = 0;
- l != NULL;
- l = l->prev)
- {
- BookmarkAppInfo *ai;
-
- ai = (BookmarkAppInfo *) l->data;
-
- g_warn_if_fail (ai != NULL);
- g_warn_if_fail (ai->name != NULL);
-
- apps[i++] = g_strdup (ai->name);
- }
- apps[i] = NULL;
-
- if (length)
- *length = i;
-
- return apps;
-}
-
-/**
- * g_bookmark_file_get_size:
- * @bookmark: a #GBookmarkFile
- *
- * Gets the number of bookmarks inside @bookmark.
- *
- * Return value: the number of bookmarks
- *
- * Since: 2.12
- */
-gint
-g_bookmark_file_get_size (GBookmarkFile *bookmark)
-{
- g_return_val_if_fail (bookmark != NULL, 0);
-
- return g_list_length (bookmark->items);
-}
-
-/**
- * g_bookmark_file_move_item:
- * @bookmark: a #GBookmarkFile
- * @old_uri: a valid URI
- * @new_uri: a valid URI, or %NULL
- * @error: return location for a #GError or %NULL
- *
- * Changes the URI of a bookmark item from @old_uri to @new_uri. Any
- * existing bookmark for @new_uri will be overwritten. If @new_uri is
- * %NULL, then the bookmark is removed.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: %TRUE if the URI was successfully changed
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_move_item (GBookmarkFile *bookmark,
- const gchar *old_uri,
- const gchar *new_uri,
- GError **error)
-{
- BookmarkItem *item;
- GError *remove_error;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (old_uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, old_uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- old_uri);
- return FALSE;
- }
-
- if (new_uri && new_uri[0] != '\0')
- {
- if (g_bookmark_file_has_item (bookmark, new_uri))
- {
- remove_error = NULL;
- g_bookmark_file_remove_item (bookmark, new_uri, &remove_error);
- if (remove_error)
- {
- g_propagate_error (error, remove_error);
-
- return FALSE;
- }
- }
-
- g_hash_table_steal (bookmark->items_by_uri, item->uri);
-
- g_free (item->uri);
- item->uri = g_strdup (new_uri);
- item->modified = time (NULL);
-
- g_hash_table_replace (bookmark->items_by_uri, item->uri, item);
-
- return TRUE;
- }
- else
- {
- remove_error = NULL;
- g_bookmark_file_remove_item (bookmark, old_uri, &remove_error);
- if (remove_error)
- {
- g_propagate_error (error, remove_error);
-
- return FALSE;
- }
-
- return TRUE;
- }
-}
-
-/**
- * g_bookmark_file_set_icon:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @href: the URI of the icon for the bookmark, or %NULL
- * @mime_type: the MIME type of the icon for the bookmark
- *
- * Sets the icon for the bookmark for @uri. If @href is %NULL, unsets
- * the currently set icon. @href can either be a full URL for the icon
- * file or the icon name following the Icon Naming specification.
- *
- * If no bookmark for @uri is found one is created.
- *
- * Since: 2.12
- */
-void
-g_bookmark_file_set_icon (GBookmarkFile *bookmark,
- const gchar *uri,
- const gchar *href,
- const gchar *mime_type)
-{
- BookmarkItem *item;
-
- g_return_if_fail (bookmark != NULL);
- g_return_if_fail (uri != NULL);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- item = bookmark_item_new (uri);
- g_bookmark_file_add_item (bookmark, item, NULL);
- }
-
- if (!item->metadata)
- item->metadata = bookmark_metadata_new ();
-
- g_free (item->metadata->icon_href);
- g_free (item->metadata->icon_mime);
-
- item->metadata->icon_href = g_strdup (href);
-
- if (mime_type && mime_type[0] != '\0')
- item->metadata->icon_mime = g_strdup (mime_type);
- else
- item->metadata->icon_mime = g_strdup ("application/octet-stream");
-
- item->modified = time (NULL);
-}
-
-/**
- * g_bookmark_file_get_icon:
- * @bookmark: a #GBookmarkFile
- * @uri: a valid URI
- * @href: return location for the icon's location or %NULL
- * @mime_type: return location for the icon's MIME type or %NULL
- * @error: return location for a #GError or %NULL
- *
- * Gets the icon of the bookmark for @uri.
- *
- * In the event the URI cannot be found, %FALSE is returned and
- * @error is set to #G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND.
- *
- * Return value: %TRUE if the icon for the bookmark for the URI was found.
- * You should free the returned strings.
- *
- * Since: 2.12
- */
-gboolean
-g_bookmark_file_get_icon (GBookmarkFile *bookmark,
- const gchar *uri,
- gchar **href,
- gchar **mime_type,
- GError **error)
-{
- BookmarkItem *item;
-
- g_return_val_if_fail (bookmark != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- item = g_bookmark_file_lookup_item (bookmark, uri);
- if (!item)
- {
- g_set_error (error, G_BOOKMARK_FILE_ERROR,
- G_BOOKMARK_FILE_ERROR_URI_NOT_FOUND,
- _("No bookmark found for URI '%s'"),
- uri);
- return FALSE;
- }
-
- if ((!item->metadata) || (!item->metadata->icon_href))
- return FALSE;
-
- if (href)
- *href = g_strdup (item->metadata->icon_href);
-
- if (mime_type)
- *mime_type = g_strdup (item->metadata->icon_mime);
-
- return TRUE;
-}
+++ /dev/null
-/*
- * Copyright © 2009, 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "config.h"
-
-#include "gbuffer.h"
-
-#include <glib/gstrfuncs.h>
-#include <glib/gatomic.h>
-#include <glib/gmem.h>
-
-
-typedef struct
-{
- GBuffer buffer;
-
- GDestroyNotify user_destroy;
- gpointer user_data;
-} GUserNotifyBuffer;
-
-static void
-g_buffer_free_gfree (GBuffer *buffer)
-{
- g_free ((gpointer) buffer->data);
- g_slice_free (GBuffer, buffer);
-}
-
-/* < private >
- * g_buffer_new_from_data:
- * @data: the data to be used for the buffer
- * @size: the size of @data
- * @returns: a reference to a new #GBuffer
- *
- * Creates a new #GBuffer from @data.
- *
- * @data is copied.
- */
-
-GBuffer *
-g_buffer_new_from_data (gconstpointer data,
- gsize size)
-{
- GBuffer *buffer;
-
- buffer = g_slice_new (GBuffer);
- buffer->data = g_memdup (data, size);
- buffer->size = size;
- buffer->free_func = g_buffer_free_gfree;
- buffer->ref_count = 1;
-
- return buffer;
-}
-
-/* < private >
- * g_buffer_new_take_data:
- * @data: the data to be used for the buffer
- * @size: the size of @data
- * returns: a reference to a new #GBuffer
- *
- * Creates a new #GBuffer from @data.
- *
- * @data must have been created by a call to g_malloc(), g_malloc0() or
- * g_realloc() or by one of the many functions that wrap these calls
- * (such as g_new(), g_strdup(), etc).
- *
- * After this call, @data belongs to the buffer and may no longer be
- * modified by the caller. g_free() will be called on @data when the
- * buffer is no longer in use.
- */
-GBuffer *
-g_buffer_new_take_data (gpointer data,
- gsize size)
-{
- GBuffer *buffer;
-
- buffer = g_slice_new (GBuffer);
- buffer->data = data;
- buffer->size = size;
- buffer->free_func = g_buffer_free_gfree;
- buffer->ref_count = 1;
-
- return buffer;
-}
-
-static void
-g_buffer_free (GBuffer *buffer)
-{
- g_slice_free (GBuffer, buffer);
-}
-
-/* < private >
- * g_buffer_new_from_static_data:
- * @data: the data to be used for the buffer
- * @size: the size of @data
- * @returns: a reference to a new #GBuffer
- *
- * Creates a new #GBuffer from static data.
- *
- * @data must be static (ie: never modified or freed).
- */
-GBuffer *
-g_buffer_new_from_static_data (gconstpointer data,
- gsize size)
-{
- GBuffer *buffer;
-
- buffer = g_slice_new (GBuffer);
- buffer->data = data;
- buffer->size = size;
- buffer->free_func = g_buffer_free;
- buffer->ref_count = 1;
-
- return buffer;
-}
-
-static void
-g_buffer_free_usernotify (GBuffer *buffer)
-{
- GUserNotifyBuffer *ubuffer = (GUserNotifyBuffer *) buffer;
-
- ubuffer->user_destroy (ubuffer->user_data);
- g_slice_free (GUserNotifyBuffer, ubuffer);
-}
-
-/* < private >
- * g_buffer_new_from_pointer:
- * @data: the data to be used for the buffer
- * @size: the size of @data
- * @notify: the function to call to release the data
- * @user_data: the data to pass to @notify
- * @returns: a reference to a new #GBuffer
- *
- * Creates a #GBuffer from @data.
- *
- * When the last reference is dropped, @notify will be called on
- * @user_data.
- *
- * @data must not be modified after this call is made, until @notify has
- * been called to indicate that the buffer is no longer in use.
- */
-GBuffer *
-g_buffer_new_from_pointer (gconstpointer data,
- gsize size,
- GDestroyNotify notify,
- gpointer user_data)
-{
- GUserNotifyBuffer *ubuffer;
-
- ubuffer = g_slice_new (GUserNotifyBuffer);
- ubuffer->buffer.data = data;
- ubuffer->buffer.size = size;
- ubuffer->buffer.free_func = g_buffer_free_usernotify;
- ubuffer->buffer.ref_count = 1;
- ubuffer->user_destroy = notify;
- ubuffer->user_data = user_data;
-
- return (GBuffer *) ubuffer;
-}
-
-/* < private >
- * g_buffer_ref:
- * @buffer: a #GBuffer
- * @returns: @buffer
- *
- * Increase the reference count on @buffer.
- */
-GBuffer *
-g_buffer_ref (GBuffer *buffer)
-{
- g_atomic_int_inc (&buffer->ref_count);
-
- return buffer;
-}
-
-/* < private >
- * g_buffer_unref:
- * @buffer: a #GBuffer
- *
- * Releases a reference on @buffer. This may result in the buffer being
- * freed.
- */
-void
-g_buffer_unref (GBuffer *buffer)
-{
- if (g_atomic_int_dec_and_test (&buffer->ref_count))
- if (buffer->free_func != NULL)
- buffer->free_func (buffer);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gcache.h"
-
-#include "ghash.h"
-#include "gtestutils.h"
-
-/**
- * SECTION: caches
- * @title: Caches
- * @short_description: caches allow sharing of complex data structures
- * to save resources
- *
- * A #GCache allows sharing of complex data structures, in order to
- * save system resources.
- *
- * GTK+ uses caches for #GtkStyles and #GdkGCs. These consume a lot of
- * resources, so a #GCache is used to see if a #GtkStyle or #GdkGC with
- * the required properties already exists. If it does, then the
- * existing object is used instead of creating a new one.
- *
- * #GCache uses keys and values. A #GCache key describes the properties
- * of a particular resource. A #GCache value is the actual resource.
- **/
-
-typedef struct _GCacheNode GCacheNode;
-
-struct _GCacheNode
-{
- /* A reference counted node */
- gpointer value;
- gint ref_count;
-};
-
-/**
- * GCache:
- *
- * The #GCache struct is an opaque data structure containing
- * information about a #GCache. It should only be accessed via the
- * following functions.
- **/
-struct _GCache
-{
- /* Called to create a value from a key */
- GCacheNewFunc value_new_func;
-
- /* Called to destroy a value */
- GCacheDestroyFunc value_destroy_func;
-
- /* Called to duplicate a key */
- GCacheDupFunc key_dup_func;
-
- /* Called to destroy a key */
- GCacheDestroyFunc key_destroy_func;
-
- /* Associates keys with nodes */
- GHashTable *key_table;
-
- /* Associates nodes with keys */
- GHashTable *value_table;
-};
-
-static inline GCacheNode*
-g_cache_node_new (gpointer value)
-{
- GCacheNode *node = g_slice_new (GCacheNode);
- node->value = value;
- node->ref_count = 1;
- return node;
-}
-
-static inline void
-g_cache_node_destroy (GCacheNode *node)
-{
- g_slice_free (GCacheNode, node);
-}
-
-/**
- * g_cache_new:
- * @value_new_func: a function to create a new object given a key.
- * This is called by g_cache_insert() if an object
- * with the given key does not already exist.
- * @value_destroy_func: a function to destroy an object. It is called
- * by g_cache_remove() when the object is no
- * longer needed (i.e. its reference count drops
- * to 0).
- * @key_dup_func: a function to copy a key. It is called by
- * g_cache_insert() if the key does not already exist in
- * the #GCache.
- * @key_destroy_func: a function to destroy a key. It is called by
- * g_cache_remove() when the object is no longer
- * needed (i.e. its reference count drops to 0).
- * @hash_key_func: a function to create a hash value from a key.
- * @hash_value_func: a function to create a hash value from a value.
- * @key_equal_func: a function to compare two keys. It should return
- * %TRUE if the two keys are equivalent.
- * @Returns: a new #GCache.
- *
- * Creates a new #GCache.
- **/
-/**
- * GCacheNewFunc:
- * @key: a #GCache key.
- * @Returns: a new #GCache value corresponding to the key.
- *
- * Specifies the type of the @value_new_func function passed to
- * g_cache_new(). It is passed a #GCache key and should create the
- * value corresponding to the key.
- **/
-/**
- * GCacheDestroyFunc:
- * @value: the #GCache value to destroy.
- *
- * Specifies the type of the @value_destroy_func and @key_destroy_func
- * functions passed to g_cache_new(). The functions are passed a
- * pointer to the #GCache key or #GCache value and should free any
- * memory and other resources associated with it.
- **/
-/**
- * GCacheDupFunc:
- * @value: the #GCache key to destroy (<emphasis>not</emphasis> a
- * #GCache value as it seems).
- * @Returns: a copy of the #GCache key.
- *
- * Specifies the type of the @key_dup_func function passed to
- * g_cache_new(). The function is passed a key
- * (<emphasis>not</emphasis> a value as the prototype implies) and
- * should return a duplicate of the key.
- **/
-GCache*
-g_cache_new (GCacheNewFunc value_new_func,
- GCacheDestroyFunc value_destroy_func,
- GCacheDupFunc key_dup_func,
- GCacheDestroyFunc key_destroy_func,
- GHashFunc hash_key_func,
- GHashFunc hash_value_func,
- GEqualFunc key_equal_func)
-{
- GCache *cache;
-
- g_return_val_if_fail (value_new_func != NULL, NULL);
- g_return_val_if_fail (value_destroy_func != NULL, NULL);
- g_return_val_if_fail (key_dup_func != NULL, NULL);
- g_return_val_if_fail (key_destroy_func != NULL, NULL);
- g_return_val_if_fail (hash_key_func != NULL, NULL);
- g_return_val_if_fail (hash_value_func != NULL, NULL);
- g_return_val_if_fail (key_equal_func != NULL, NULL);
-
- cache = g_slice_new (GCache);
- cache->value_new_func = value_new_func;
- cache->value_destroy_func = value_destroy_func;
- cache->key_dup_func = key_dup_func;
- cache->key_destroy_func = key_destroy_func;
- cache->key_table = g_hash_table_new (hash_key_func, key_equal_func);
- cache->value_table = g_hash_table_new (hash_value_func, NULL);
-
- return cache;
-}
-
-/**
- * g_cache_destroy:
- * @cache: a #GCache.
- *
- * Frees the memory allocated for the #GCache.
- *
- * Note that it does not destroy the keys and values which were
- * contained in the #GCache.
- **/
-void
-g_cache_destroy (GCache *cache)
-{
- g_return_if_fail (cache != NULL);
-
- g_hash_table_destroy (cache->key_table);
- g_hash_table_destroy (cache->value_table);
- g_slice_free (GCache, cache);
-}
-
-/**
- * g_cache_insert:
- * @cache: a #GCache.
- * @key: a key describing a #GCache object.
- * @Returns: a pointer to a #GCache value.
- *
- * Gets the value corresponding to the given key, creating it if
- * necessary. It first checks if the value already exists in the
- * #GCache, by using the @key_equal_func function passed to
- * g_cache_new(). If it does already exist it is returned, and its
- * reference count is increased by one. If the value does not currently
- * exist, if is created by calling the @value_new_func. The key is
- * duplicated by calling @key_dup_func and the duplicated key and value
- * are inserted into the #GCache.
- **/
-gpointer
-g_cache_insert (GCache *cache,
- gpointer key)
-{
- GCacheNode *node;
- gpointer value;
-
- g_return_val_if_fail (cache != NULL, NULL);
-
- node = g_hash_table_lookup (cache->key_table, key);
- if (node)
- {
- node->ref_count += 1;
- return node->value;
- }
-
- key = (* cache->key_dup_func) (key);
- value = (* cache->value_new_func) (key);
- node = g_cache_node_new (value);
-
- g_hash_table_insert (cache->key_table, key, node);
- g_hash_table_insert (cache->value_table, value, key);
-
- return node->value;
-}
-
-/**
- * g_cache_remove:
- * @cache: a #GCache.
- * @value: the value to remove.
- *
- * Decreases the reference count of the given value. If it drops to 0
- * then the value and its corresponding key are destroyed, using the
- * @value_destroy_func and @key_destroy_func passed to g_cache_new().
- **/
-void
-g_cache_remove (GCache *cache,
- gconstpointer value)
-{
- GCacheNode *node;
- gpointer key;
-
- g_return_if_fail (cache != NULL);
-
- key = g_hash_table_lookup (cache->value_table, value);
- node = g_hash_table_lookup (cache->key_table, key);
-
- g_return_if_fail (node != NULL);
-
- node->ref_count -= 1;
- if (node->ref_count == 0)
- {
- g_hash_table_remove (cache->value_table, value);
- g_hash_table_remove (cache->key_table, key);
-
- (* cache->key_destroy_func) (key);
- (* cache->value_destroy_func) (node->value);
- g_cache_node_destroy (node);
- }
-}
-
-/**
- * g_cache_key_foreach:
- * @cache: a #GCache.
- * @func: the function to call with each #GCache key.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each of the keys in the #GCache.
- *
- * NOTE @func is passed three parameters, the value and key of a cache
- * entry and the @user_data. The order of value and key is different
- * from the order in which g_hash_table_foreach() passes key-value
- * pairs to its callback function !
- **/
-void
-g_cache_key_foreach (GCache *cache,
- GHFunc func,
- gpointer user_data)
-{
- g_return_if_fail (cache != NULL);
- g_return_if_fail (func != NULL);
-
- g_hash_table_foreach (cache->value_table, func, user_data);
-}
-
-/**
- * g_cache_value_foreach:
- * @cache: a #GCache.
- * @func: the function to call with each #GCache value.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each of the values in the #GCache.
- *
- * Deprecated:2.10: The reason is that it passes pointers to internal
- * data structures to @func; use g_cache_key_foreach()
- * instead
- **/
-void
-g_cache_value_foreach (GCache *cache,
- GHFunc func,
- gpointer user_data)
-{
- g_return_if_fail (cache != NULL);
- g_return_if_fail (func != NULL);
-
- g_hash_table_foreach (cache->key_table, func, user_data);
-}
+++ /dev/null
-/* gchecksum.h - data hashing functions
- *
- * Copyright (C) 2007 Emmanuele Bassi <ebassi@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library 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 "config.h"
-
-#include <string.h>
-
-#include "gchecksum.h"
-
-#include "gmem.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gtypes.h"
-#include "glibintl.h"
-
-
-/**
- * SECTION: checksum
- * @title: Data Checksums
- * @short_description: Computes the checksum for data
- *
- * GLib provides a generic API for computing checksums (or "digests")
- * for a sequence of arbitrary bytes, using various hashing algorithms
- * like MD5, SHA-1 and SHA-256. Checksums are commonly used in various
- * environments and specifications.
- *
- * GLib supports incremental checksums using the GChecksum data
- * structure, by calling g_checksum_update() as long as there's data
- * available and then using g_checksum_get_string() or
- * g_checksum_get_digest() to compute the checksum and return it either
- * as a string in hexadecimal form, or as a raw sequence of bytes. To
- * compute the checksum for binary blobs and NUL-terminated strings in
- * one go, use the convenience functions g_compute_checksum_for_data()
- * and g_compute_checksum_for_string(), respectively.
- *
- * Support for checksums has been added in GLib 2.16
- **/
-
-#define IS_VALID_TYPE(type) ((type) >= G_CHECKSUM_MD5 && (type) <= G_CHECKSUM_SHA256)
-
-/* The fact that these are lower case characters is part of the ABI */
-static const gchar hex_digits[] = "0123456789abcdef";
-
-#define MD5_DATASIZE 64
-#define MD5_DIGEST_LEN 16
-
-typedef struct
-{
- guint32 buf[4];
- guint32 bits[2];
-
- guchar data[MD5_DATASIZE];
-
- guchar digest[MD5_DIGEST_LEN];
-} Md5sum;
-
-#define SHA1_DATASIZE 64
-#define SHA1_DIGEST_LEN 20
-
-typedef struct
-{
- guint32 buf[5];
- guint32 bits[2];
-
- /* we pack 64 unsigned chars into 16 32-bit unsigned integers */
- guint32 data[16];
-
- guchar digest[SHA1_DIGEST_LEN];
-} Sha1sum;
-
-#define SHA256_DATASIZE 64
-#define SHA256_DIGEST_LEN 32
-
-typedef struct
-{
- guint32 buf[8];
- guint32 bits[2];
-
- guint8 data[SHA256_DATASIZE];
-
- guchar digest[SHA256_DIGEST_LEN];
-} Sha256sum;
-
-struct _GChecksum
-{
- GChecksumType type;
-
- gchar *digest_str;
-
- union {
- Md5sum md5;
- Sha1sum sha1;
- Sha256sum sha256;
- } sum;
-};
-
-/* we need different byte swapping functions because MD5 expects buffers
- * to be little-endian, while SHA1 and SHA256 expect them in big-endian
- * form.
- */
-
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-#define md5_byte_reverse(buffer,length)
-#else
-/* assume that the passed buffer is integer aligned */
-static inline void
-md5_byte_reverse (guchar *buffer,
- gulong length)
-{
- guint32 bit;
-
- do
- {
- bit = (guint32) ((unsigned) buffer[3] << 8 | buffer[2]) << 16 |
- ((unsigned) buffer[1] << 8 | buffer[0]);
- * (guint32 *) buffer = bit;
- buffer += 4;
- }
- while (--length);
-}
-#endif /* G_BYTE_ORDER == G_LITTLE_ENDIAN */
-
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-#define sha_byte_reverse(buffer,length)
-#else
-static inline void
-sha_byte_reverse (guint32 *buffer,
- gint length)
-{
- length /= sizeof (guint32);
- while (length--)
- {
- *buffer = GUINT32_SWAP_LE_BE (*buffer);
- ++buffer;
- }
-}
-#endif /* G_BYTE_ORDER == G_BIG_ENDIAN */
-
-static gchar *
-digest_to_string (guint8 *digest,
- gsize digest_len)
-{
- gint len = digest_len * 2;
- gint i;
- gchar *retval;
-
- retval = g_new (gchar, len + 1);
-
- for (i = 0; i < digest_len; i++)
- {
- guint8 byte = digest[i];
-
- retval[2 * i] = hex_digits[byte >> 4];
- retval[2 * i + 1] = hex_digits[byte & 0xf];
- }
-
- retval[len] = 0;
-
- return retval;
-}
-
-/*
- * MD5 Checksum
- */
-
-/* This MD5 digest computation is based on the equivalent code
- * written by Colin Plumb. It came with this notice:
- *
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest. This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- */
-
-static void
-md5_sum_init (Md5sum *md5)
-{
- /* arbitrary constants */
- md5->buf[0] = 0x67452301;
- md5->buf[1] = 0xefcdab89;
- md5->buf[2] = 0x98badcfe;
- md5->buf[3] = 0x10325476;
-
- md5->bits[0] = md5->bits[1] = 0;
-}
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data. md5_sum_update()
- * blocks the data and converts bytes into longwords for this routine.
- */
-static void
-md5_transform (guint32 buf[4],
- guint32 const in[16])
-{
- register guint32 a, b, c, d;
-
-/* The four core functions - F1 is optimized somewhat */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1 (z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#define md5_step(f, w, x, y, z, data, s) \
- ( w += f (x, y, z) + data, w = w << s | w >> (32 - s), w += x )
-
- a = buf[0];
- b = buf[1];
- c = buf[2];
- d = buf[3];
-
- md5_step (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
- md5_step (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
- md5_step (F1, c, d, a, b, in[2] + 0x242070db, 17);
- md5_step (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
- md5_step (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
- md5_step (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
- md5_step (F1, c, d, a, b, in[6] + 0xa8304613, 17);
- md5_step (F1, b, c, d, a, in[7] + 0xfd469501, 22);
- md5_step (F1, a, b, c, d, in[8] + 0x698098d8, 7);
- md5_step (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
- md5_step (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
- md5_step (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
- md5_step (F1, a, b, c, d, in[12] + 0x6b901122, 7);
- md5_step (F1, d, a, b, c, in[13] + 0xfd987193, 12);
- md5_step (F1, c, d, a, b, in[14] + 0xa679438e, 17);
- md5_step (F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
- md5_step (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
- md5_step (F2, d, a, b, c, in[6] + 0xc040b340, 9);
- md5_step (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
- md5_step (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
- md5_step (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
- md5_step (F2, d, a, b, c, in[10] + 0x02441453, 9);
- md5_step (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
- md5_step (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
- md5_step (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
- md5_step (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
- md5_step (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
- md5_step (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
- md5_step (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
- md5_step (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
- md5_step (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
- md5_step (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
- md5_step (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
- md5_step (F3, d, a, b, c, in[8] + 0x8771f681, 11);
- md5_step (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
- md5_step (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
- md5_step (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
- md5_step (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
- md5_step (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
- md5_step (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
- md5_step (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
- md5_step (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
- md5_step (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
- md5_step (F3, b, c, d, a, in[6] + 0x04881d05, 23);
- md5_step (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
- md5_step (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
- md5_step (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
- md5_step (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
- md5_step (F4, a, b, c, d, in[0] + 0xf4292244, 6);
- md5_step (F4, d, a, b, c, in[7] + 0x432aff97, 10);
- md5_step (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
- md5_step (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
- md5_step (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
- md5_step (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
- md5_step (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
- md5_step (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
- md5_step (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
- md5_step (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
- md5_step (F4, c, d, a, b, in[6] + 0xa3014314, 15);
- md5_step (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
- md5_step (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
- md5_step (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
- md5_step (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
- md5_step (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-
-#undef F1
-#undef F2
-#undef F3
-#undef F4
-#undef md5_step
-}
-
-static void
-md5_sum_update (Md5sum *md5,
- const guchar *data,
- gsize length)
-{
- guint32 bit;
-
- bit = md5->bits[0];
- md5->bits[0] = bit + ((guint32) length << 3);
-
- /* carry from low to high */
- if (md5->bits[0] < bit)
- md5->bits[1] += 1;
-
- md5->bits[1] += length >> 29;
-
- /* bytes already in Md5sum->data */
- bit = (bit >> 3) & 0x3f;
-
- /* handle any leading odd-sized chunks */
- if (bit)
- {
- guchar *p = (guchar *) md5->data + bit;
-
- bit = MD5_DATASIZE - bit;
- if (length < bit)
- {
- memcpy (p, data, length);
- return;
- }
-
- memcpy (p, data, bit);
-
- md5_byte_reverse (md5->data, 16);
- md5_transform (md5->buf, (guint32 *) md5->data);
-
- data += bit;
- length -= bit;
- }
-
- /* process data in 64-byte chunks */
- while (length >= MD5_DATASIZE)
- {
- memcpy (md5->data, data, MD5_DATASIZE);
-
- md5_byte_reverse (md5->data, 16);
- md5_transform (md5->buf, (guint32 *) md5->data);
-
- data += MD5_DATASIZE;
- length -= MD5_DATASIZE;
- }
-
- /* handle any remaining bytes of data */
- memcpy (md5->data, data, length);
-}
-
-/* closes a checksum */
-static void
-md5_sum_close (Md5sum *md5)
-{
- guint count;
- guchar *p;
-
- /* Compute number of bytes mod 64 */
- count = (md5->bits[0] >> 3) & 0x3F;
-
- /* Set the first char of padding to 0x80.
- * This is safe since there is always at least one byte free
- */
- p = md5->data + count;
- *p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = MD5_DATASIZE - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8)
- {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset (p, 0, count);
-
- md5_byte_reverse (md5->data, 16);
- md5_transform (md5->buf, (guint32 *) md5->data);
-
- /* Now fill the next block with 56 bytes */
- memset (md5->data, 0, MD5_DATASIZE - 8);
- }
- else
- {
- /* Pad block to 56 bytes */
- memset (p, 0, count - 8);
- }
-
- md5_byte_reverse (md5->data, 14);
-
- /* Append length in bits and transform */
- ((guint32 *) md5->data)[14] = md5->bits[0];
- ((guint32 *) md5->data)[15] = md5->bits[1];
-
- md5_transform (md5->buf, (guint32 *) md5->data);
- md5_byte_reverse ((guchar *) md5->buf, 4);
-
- memcpy (md5->digest, md5->buf, 16);
-
- /* Reset buffers in case they contain sensitive data */
- memset (md5->buf, 0, sizeof (md5->buf));
- memset (md5->data, 0, sizeof (md5->data));
-}
-
-static gchar *
-md5_sum_to_string (Md5sum *md5)
-{
- return digest_to_string (md5->digest, MD5_DIGEST_LEN);
-}
-
-static void
-md5_sum_digest (Md5sum *md5,
- guint8 *digest)
-{
- gint i;
-
- for (i = 0; i < MD5_DIGEST_LEN; i++)
- digest[i] = md5->digest[i];
-}
-
-/*
- * SHA-1 Checksum
- */
-
-/* The following implementation comes from D-Bus dbus-sha.c. I've changed
- * it to use GLib types and to work more like the MD5 implementation above.
- * I left the comments to have an history of this code.
- * -- Emmanuele Bassi, ebassi@gnome.org
- */
-
-/* The following comments have the history of where this code
- * comes from. I actually copied it from GNet in GNOME CVS.
- * - hp@redhat.com
- */
-
-/*
- * sha.h : Implementation of the Secure Hash Algorithm
- *
- * Part of the Python Cryptography Toolkit, version 1.0.0
- *
- * Copyright (C) 1995, A.M. Kuchling
- *
- * Distribute and use freely; there are no restrictions on further
- * dissemination and usage except those imposed by the laws of your
- * country of residence.
- *
- */
-
-/* SHA: NIST's Secure Hash Algorithm */
-
-/* Based on SHA code originally posted to sci.crypt by Peter Gutmann
- in message <30ajo5$oe8@ccu2.auckland.ac.nz>.
- Modified to test for endianness on creation of SHA objects by AMK.
- Also, the original specification of SHA was found to have a weakness
- by NSA/NIST. This code implements the fixed version of SHA.
-*/
-
-/* Here's the first paragraph of Peter Gutmann's posting:
-
-The following is my SHA (FIPS 180) code updated to allow use of the "fixed"
-SHA, thanks to Jim Gillogly and an anonymous contributor for the information on
-what's changed in the new version. The fix is a simple change which involves
-adding a single rotate in the initial expansion function. It is unknown
-whether this is an optimal solution to the problem which was discovered in the
-SHA or whether it's simply a bandaid which fixes the problem with a minimum of
-effort (for example the reengineering of a great many Capstone chips).
-*/
-
-static void
-sha1_sum_init (Sha1sum *sha1)
-{
- /* initialize constants */
- sha1->buf[0] = 0x67452301L;
- sha1->buf[1] = 0xEFCDAB89L;
- sha1->buf[2] = 0x98BADCFEL;
- sha1->buf[3] = 0x10325476L;
- sha1->buf[4] = 0xC3D2E1F0L;
-
- /* initialize bits */
- sha1->bits[0] = sha1->bits[1] = 0;
-}
-
-/* The SHA f()-functions. */
-
-#define f1(x,y,z) (z ^ (x & (y ^ z))) /* Rounds 0-19 */
-#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39 */
-#define f3(x,y,z) (( x & y) | (z & (x | y))) /* Rounds 40-59 */
-#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79 */
-
-/* The SHA Mysterious Constants */
-#define K1 0x5A827999L /* Rounds 0-19 */
-#define K2 0x6ED9EBA1L /* Rounds 20-39 */
-#define K3 0x8F1BBCDCL /* Rounds 40-59 */
-#define K4 0xCA62C1D6L /* Rounds 60-79 */
-
-/* 32-bit rotate left - kludged with shifts */
-#define ROTL(n,X) (((X) << n ) | ((X) >> (32 - n)))
-
-/* The initial expanding function. The hash function is defined over an
- 80-word expanded input array W, where the first 16 are copies of the input
- data, and the remaining 64 are defined by
-
- W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
-
- This implementation generates these values on the fly in a circular
- buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
- optimization.
-
- The updated SHA changes the expanding function by adding a rotate of 1
- bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
- for this information */
-
-#define expand(W,i) (W[ i & 15 ] = ROTL (1, (W[ i & 15] ^ \
- W[(i - 14) & 15] ^ \
- W[(i - 8) & 15] ^ \
- W[(i - 3) & 15])))
-
-
-/* The prototype SHA sub-round. The fundamental sub-round is:
-
- a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
- b' = a;
- c' = ROTL( 30, b );
- d' = c;
- e' = d;
-
- but this is implemented by unrolling the loop 5 times and renaming the
- variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
- This code is then replicated 20 times for each of the 4 functions, using
- the next 20 values from the W[] array each time */
-
-#define subRound(a, b, c, d, e, f, k, data) \
- (e += ROTL (5, a) + f(b, c, d) + k + data, b = ROTL (30, b))
-
-static void
-sha1_transform (guint32 buf[5],
- guint32 in[16])
-{
- guint32 A, B, C, D, E;
-
- A = buf[0];
- B = buf[1];
- C = buf[2];
- D = buf[3];
- E = buf[4];
-
- /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
- subRound (A, B, C, D, E, f1, K1, in[0]);
- subRound (E, A, B, C, D, f1, K1, in[1]);
- subRound (D, E, A, B, C, f1, K1, in[2]);
- subRound (C, D, E, A, B, f1, K1, in[3]);
- subRound (B, C, D, E, A, f1, K1, in[4]);
- subRound (A, B, C, D, E, f1, K1, in[5]);
- subRound (E, A, B, C, D, f1, K1, in[6]);
- subRound (D, E, A, B, C, f1, K1, in[7]);
- subRound (C, D, E, A, B, f1, K1, in[8]);
- subRound (B, C, D, E, A, f1, K1, in[9]);
- subRound (A, B, C, D, E, f1, K1, in[10]);
- subRound (E, A, B, C, D, f1, K1, in[11]);
- subRound (D, E, A, B, C, f1, K1, in[12]);
- subRound (C, D, E, A, B, f1, K1, in[13]);
- subRound (B, C, D, E, A, f1, K1, in[14]);
- subRound (A, B, C, D, E, f1, K1, in[15]);
- subRound (E, A, B, C, D, f1, K1, expand (in, 16));
- subRound (D, E, A, B, C, f1, K1, expand (in, 17));
- subRound (C, D, E, A, B, f1, K1, expand (in, 18));
- subRound (B, C, D, E, A, f1, K1, expand (in, 19));
-
- subRound (A, B, C, D, E, f2, K2, expand (in, 20));
- subRound (E, A, B, C, D, f2, K2, expand (in, 21));
- subRound (D, E, A, B, C, f2, K2, expand (in, 22));
- subRound (C, D, E, A, B, f2, K2, expand (in, 23));
- subRound (B, C, D, E, A, f2, K2, expand (in, 24));
- subRound (A, B, C, D, E, f2, K2, expand (in, 25));
- subRound (E, A, B, C, D, f2, K2, expand (in, 26));
- subRound (D, E, A, B, C, f2, K2, expand (in, 27));
- subRound (C, D, E, A, B, f2, K2, expand (in, 28));
- subRound (B, C, D, E, A, f2, K2, expand (in, 29));
- subRound (A, B, C, D, E, f2, K2, expand (in, 30));
- subRound (E, A, B, C, D, f2, K2, expand (in, 31));
- subRound (D, E, A, B, C, f2, K2, expand (in, 32));
- subRound (C, D, E, A, B, f2, K2, expand (in, 33));
- subRound (B, C, D, E, A, f2, K2, expand (in, 34));
- subRound (A, B, C, D, E, f2, K2, expand (in, 35));
- subRound (E, A, B, C, D, f2, K2, expand (in, 36));
- subRound (D, E, A, B, C, f2, K2, expand (in, 37));
- subRound (C, D, E, A, B, f2, K2, expand (in, 38));
- subRound (B, C, D, E, A, f2, K2, expand (in, 39));
-
- subRound (A, B, C, D, E, f3, K3, expand (in, 40));
- subRound (E, A, B, C, D, f3, K3, expand (in, 41));
- subRound (D, E, A, B, C, f3, K3, expand (in, 42));
- subRound (C, D, E, A, B, f3, K3, expand (in, 43));
- subRound (B, C, D, E, A, f3, K3, expand (in, 44));
- subRound (A, B, C, D, E, f3, K3, expand (in, 45));
- subRound (E, A, B, C, D, f3, K3, expand (in, 46));
- subRound (D, E, A, B, C, f3, K3, expand (in, 47));
- subRound (C, D, E, A, B, f3, K3, expand (in, 48));
- subRound (B, C, D, E, A, f3, K3, expand (in, 49));
- subRound (A, B, C, D, E, f3, K3, expand (in, 50));
- subRound (E, A, B, C, D, f3, K3, expand (in, 51));
- subRound (D, E, A, B, C, f3, K3, expand (in, 52));
- subRound (C, D, E, A, B, f3, K3, expand (in, 53));
- subRound (B, C, D, E, A, f3, K3, expand (in, 54));
- subRound (A, B, C, D, E, f3, K3, expand (in, 55));
- subRound (E, A, B, C, D, f3, K3, expand (in, 56));
- subRound (D, E, A, B, C, f3, K3, expand (in, 57));
- subRound (C, D, E, A, B, f3, K3, expand (in, 58));
- subRound (B, C, D, E, A, f3, K3, expand (in, 59));
-
- subRound (A, B, C, D, E, f4, K4, expand (in, 60));
- subRound (E, A, B, C, D, f4, K4, expand (in, 61));
- subRound (D, E, A, B, C, f4, K4, expand (in, 62));
- subRound (C, D, E, A, B, f4, K4, expand (in, 63));
- subRound (B, C, D, E, A, f4, K4, expand (in, 64));
- subRound (A, B, C, D, E, f4, K4, expand (in, 65));
- subRound (E, A, B, C, D, f4, K4, expand (in, 66));
- subRound (D, E, A, B, C, f4, K4, expand (in, 67));
- subRound (C, D, E, A, B, f4, K4, expand (in, 68));
- subRound (B, C, D, E, A, f4, K4, expand (in, 69));
- subRound (A, B, C, D, E, f4, K4, expand (in, 70));
- subRound (E, A, B, C, D, f4, K4, expand (in, 71));
- subRound (D, E, A, B, C, f4, K4, expand (in, 72));
- subRound (C, D, E, A, B, f4, K4, expand (in, 73));
- subRound (B, C, D, E, A, f4, K4, expand (in, 74));
- subRound (A, B, C, D, E, f4, K4, expand (in, 75));
- subRound (E, A, B, C, D, f4, K4, expand (in, 76));
- subRound (D, E, A, B, C, f4, K4, expand (in, 77));
- subRound (C, D, E, A, B, f4, K4, expand (in, 78));
- subRound (B, C, D, E, A, f4, K4, expand (in, 79));
-
- /* Build message digest */
- buf[0] += A;
- buf[1] += B;
- buf[2] += C;
- buf[3] += D;
- buf[4] += E;
-}
-
-#undef K1
-#undef K2
-#undef K3
-#undef K4
-#undef f1
-#undef f2
-#undef f3
-#undef f4
-#undef ROTL
-#undef expand
-#undef subRound
-
-static void
-sha1_sum_update (Sha1sum *sha1,
- const guchar *buffer,
- gsize count)
-{
- guint32 tmp;
- guint dataCount;
-
- /* Update bitcount */
- tmp = sha1->bits[0];
- if ((sha1->bits[0] = tmp + ((guint32) count << 3) ) < tmp)
- sha1->bits[1] += 1; /* Carry from low to high */
- sha1->bits[1] += count >> 29;
-
- /* Get count of bytes already in data */
- dataCount = (guint) (tmp >> 3) & 0x3F;
-
- /* Handle any leading odd-sized chunks */
- if (dataCount)
- {
- guchar *p = (guchar *) sha1->data + dataCount;
-
- dataCount = SHA1_DATASIZE - dataCount;
- if (count < dataCount)
- {
- memcpy (p, buffer, count);
- return;
- }
-
- memcpy (p, buffer, dataCount);
-
- sha_byte_reverse (sha1->data, SHA1_DATASIZE);
- sha1_transform (sha1->buf, sha1->data);
-
- buffer += dataCount;
- count -= dataCount;
- }
-
- /* Process data in SHA1_DATASIZE chunks */
- while (count >= SHA1_DATASIZE)
- {
- memcpy (sha1->data, buffer, SHA1_DATASIZE);
-
- sha_byte_reverse (sha1->data, SHA1_DATASIZE);
- sha1_transform (sha1->buf, sha1->data);
-
- buffer += SHA1_DATASIZE;
- count -= SHA1_DATASIZE;
- }
-
- /* Handle any remaining bytes of data. */
- memcpy (sha1->data, buffer, count);
-}
-
-/* Final wrapup - pad to SHA_DATASIZE-byte boundary with the bit pattern
- 1 0* (64-bit count of bits processed, MSB-first) */
-static void
-sha1_sum_close (Sha1sum *sha1)
-{
- gint count;
- guchar *data_p;
-
- /* Compute number of bytes mod 64 */
- count = (gint) ((sha1->bits[0] >> 3) & 0x3f);
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
- data_p = (guchar *) sha1->data + count;
- *data_p++ = 0x80;
-
- /* Bytes of padding needed to make 64 bytes */
- count = SHA1_DATASIZE - 1 - count;
-
- /* Pad out to 56 mod 64 */
- if (count < 8)
- {
- /* Two lots of padding: Pad the first block to 64 bytes */
- memset (data_p, 0, count);
-
- sha_byte_reverse (sha1->data, SHA1_DATASIZE);
- sha1_transform (sha1->buf, sha1->data);
-
- /* Now fill the next block with 56 bytes */
- memset (sha1->data, 0, SHA1_DATASIZE - 8);
- }
- else
- {
- /* Pad block to 56 bytes */
- memset (data_p, 0, count - 8);
- }
-
- /* Append length in bits and transform */
- sha1->data[14] = sha1->bits[1];
- sha1->data[15] = sha1->bits[0];
-
- sha_byte_reverse (sha1->data, SHA1_DATASIZE - 8);
- sha1_transform (sha1->buf, sha1->data);
- sha_byte_reverse (sha1->buf, SHA1_DIGEST_LEN);
-
- memcpy (sha1->digest, sha1->buf, SHA1_DIGEST_LEN);
-
- /* Reset buffers in case they contain sensitive data */
- memset (sha1->buf, 0, sizeof (sha1->buf));
- memset (sha1->data, 0, sizeof (sha1->data));
-}
-
-static gchar *
-sha1_sum_to_string (Sha1sum *sha1)
-{
- return digest_to_string (sha1->digest, SHA1_DIGEST_LEN);
-}
-
-static void
-sha1_sum_digest (Sha1sum *sha1,
- guint8 *digest)
-{
- gint i;
-
- for (i = 0; i < SHA1_DIGEST_LEN; i++)
- digest[i] = sha1->digest[i];
-}
-
-/*
- * SHA-256 Checksum
- */
-
-/* adapted from the SHA256 implementation in gsk/src/hash/gskhash.c.
- *
- * Copyright (C) 2006 Dave Benson
- * Released under the terms of the GNU Lesser General Public License
- */
-
-static void
-sha256_sum_init (Sha256sum *sha256)
-{
- sha256->buf[0] = 0x6a09e667;
- sha256->buf[1] = 0xbb67ae85;
- sha256->buf[2] = 0x3c6ef372;
- sha256->buf[3] = 0xa54ff53a;
- sha256->buf[4] = 0x510e527f;
- sha256->buf[5] = 0x9b05688c;
- sha256->buf[6] = 0x1f83d9ab;
- sha256->buf[7] = 0x5be0cd19;
-
- sha256->bits[0] = sha256->bits[1] = 0;
-}
-
-#define GET_UINT32(n,b,i) G_STMT_START{ \
- (n) = ((guint32) (b)[(i) ] << 24) \
- | ((guint32) (b)[(i) + 1] << 16) \
- | ((guint32) (b)[(i) + 2] << 8) \
- | ((guint32) (b)[(i) + 3] ); } G_STMT_END
-
-#define PUT_UINT32(n,b,i) G_STMT_START{ \
- (b)[(i) ] = (guint8) ((n) >> 24); \
- (b)[(i) + 1] = (guint8) ((n) >> 16); \
- (b)[(i) + 2] = (guint8) ((n) >> 8); \
- (b)[(i) + 3] = (guint8) ((n) ); } G_STMT_END
-
-static void
-sha256_transform (guint32 buf[8],
- guint8 const data[64])
-{
- guint32 temp1, temp2, W[64];
- guint32 A, B, C, D, E, F, G, H;
-
- GET_UINT32 (W[0], data, 0);
- GET_UINT32 (W[1], data, 4);
- GET_UINT32 (W[2], data, 8);
- GET_UINT32 (W[3], data, 12);
- GET_UINT32 (W[4], data, 16);
- GET_UINT32 (W[5], data, 20);
- GET_UINT32 (W[6], data, 24);
- GET_UINT32 (W[7], data, 28);
- GET_UINT32 (W[8], data, 32);
- GET_UINT32 (W[9], data, 36);
- GET_UINT32 (W[10], data, 40);
- GET_UINT32 (W[11], data, 44);
- GET_UINT32 (W[12], data, 48);
- GET_UINT32 (W[13], data, 52);
- GET_UINT32 (W[14], data, 56);
- GET_UINT32 (W[15], data, 60);
-
-#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
-#define ROTR(x,n) (SHR (x,n) | (x << (32 - n)))
-
-#define S0(x) (ROTR (x, 7) ^ ROTR (x,18) ^ SHR (x, 3))
-#define S1(x) (ROTR (x,17) ^ ROTR (x,19) ^ SHR (x,10))
-#define S2(x) (ROTR (x, 2) ^ ROTR (x,13) ^ ROTR (x,22))
-#define S3(x) (ROTR (x, 6) ^ ROTR (x,11) ^ ROTR (x,25))
-
-#define F0(x,y,z) ((x & y) | (z & (x | y)))
-#define F1(x,y,z) (z ^ (x & (y ^ z)))
-
-#define R(t) (W[t] = S1(W[t - 2]) + W[t - 7] + \
- S0(W[t - 15]) + W[t - 16])
-
-#define P(a,b,c,d,e,f,g,h,x,K) G_STMT_START { \
- temp1 = h + S3(e) + F1(e,f,g) + K + x; \
- temp2 = S2(a) + F0(a,b,c); \
- d += temp1; h = temp1 + temp2; } G_STMT_END
-
- A = buf[0];
- B = buf[1];
- C = buf[2];
- D = buf[3];
- E = buf[4];
- F = buf[5];
- G = buf[6];
- H = buf[7];
-
- P (A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98);
- P (H, A, B, C, D, E, F, G, W[ 1], 0x71374491);
- P (G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF);
- P (F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5);
- P (E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B);
- P (D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1);
- P (C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4);
- P (B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5);
- P (A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98);
- P (H, A, B, C, D, E, F, G, W[ 9], 0x12835B01);
- P (G, H, A, B, C, D, E, F, W[10], 0x243185BE);
- P (F, G, H, A, B, C, D, E, W[11], 0x550C7DC3);
- P (E, F, G, H, A, B, C, D, W[12], 0x72BE5D74);
- P (D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE);
- P (C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7);
- P (B, C, D, E, F, G, H, A, W[15], 0xC19BF174);
- P (A, B, C, D, E, F, G, H, R(16), 0xE49B69C1);
- P (H, A, B, C, D, E, F, G, R(17), 0xEFBE4786);
- P (G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6);
- P (F, G, H, A, B, C, D, E, R(19), 0x240CA1CC);
- P (E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F);
- P (D, E, F, G, H, A, B, C, R(21), 0x4A7484AA);
- P (C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC);
- P (B, C, D, E, F, G, H, A, R(23), 0x76F988DA);
- P (A, B, C, D, E, F, G, H, R(24), 0x983E5152);
- P (H, A, B, C, D, E, F, G, R(25), 0xA831C66D);
- P (G, H, A, B, C, D, E, F, R(26), 0xB00327C8);
- P (F, G, H, A, B, C, D, E, R(27), 0xBF597FC7);
- P (E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3);
- P (D, E, F, G, H, A, B, C, R(29), 0xD5A79147);
- P (C, D, E, F, G, H, A, B, R(30), 0x06CA6351);
- P (B, C, D, E, F, G, H, A, R(31), 0x14292967);
- P (A, B, C, D, E, F, G, H, R(32), 0x27B70A85);
- P (H, A, B, C, D, E, F, G, R(33), 0x2E1B2138);
- P (G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC);
- P (F, G, H, A, B, C, D, E, R(35), 0x53380D13);
- P (E, F, G, H, A, B, C, D, R(36), 0x650A7354);
- P (D, E, F, G, H, A, B, C, R(37), 0x766A0ABB);
- P (C, D, E, F, G, H, A, B, R(38), 0x81C2C92E);
- P (B, C, D, E, F, G, H, A, R(39), 0x92722C85);
- P (A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1);
- P (H, A, B, C, D, E, F, G, R(41), 0xA81A664B);
- P (G, H, A, B, C, D, E, F, R(42), 0xC24B8B70);
- P (F, G, H, A, B, C, D, E, R(43), 0xC76C51A3);
- P (E, F, G, H, A, B, C, D, R(44), 0xD192E819);
- P (D, E, F, G, H, A, B, C, R(45), 0xD6990624);
- P (C, D, E, F, G, H, A, B, R(46), 0xF40E3585);
- P (B, C, D, E, F, G, H, A, R(47), 0x106AA070);
- P (A, B, C, D, E, F, G, H, R(48), 0x19A4C116);
- P (H, A, B, C, D, E, F, G, R(49), 0x1E376C08);
- P (G, H, A, B, C, D, E, F, R(50), 0x2748774C);
- P (F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5);
- P (E, F, G, H, A, B, C, D, R(52), 0x391C0CB3);
- P (D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A);
- P (C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F);
- P (B, C, D, E, F, G, H, A, R(55), 0x682E6FF3);
- P (A, B, C, D, E, F, G, H, R(56), 0x748F82EE);
- P (H, A, B, C, D, E, F, G, R(57), 0x78A5636F);
- P (G, H, A, B, C, D, E, F, R(58), 0x84C87814);
- P (F, G, H, A, B, C, D, E, R(59), 0x8CC70208);
- P (E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA);
- P (D, E, F, G, H, A, B, C, R(61), 0xA4506CEB);
- P (C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7);
- P (B, C, D, E, F, G, H, A, R(63), 0xC67178F2);
-
-#undef SHR
-#undef ROTR
-#undef S0
-#undef S1
-#undef S2
-#undef S3
-#undef F0
-#undef F1
-#undef R
-#undef P
-
- buf[0] += A;
- buf[1] += B;
- buf[2] += C;
- buf[3] += D;
- buf[4] += E;
- buf[5] += F;
- buf[6] += G;
- buf[7] += H;
-}
-
-static void
-sha256_sum_update (Sha256sum *sha256,
- const guchar *buffer,
- gsize length)
-{
- guint32 left, fill;
- const guint8 *input = buffer;
-
- if (length == 0)
- return;
-
- left = sha256->bits[0] & 0x3F;
- fill = 64 - left;
-
- sha256->bits[0] += length;
- sha256->bits[0] &= 0xFFFFFFFF;
-
- if (sha256->bits[0] < length)
- sha256->bits[1]++;
-
- if (left > 0 && length >= fill)
- {
- memcpy ((sha256->data + left), input, fill);
-
- sha256_transform (sha256->buf, sha256->data);
- length -= fill;
- input += fill;
-
- left = 0;
- }
-
- while (length >= SHA256_DATASIZE)
- {
- sha256_transform (sha256->buf, input);
-
- length -= 64;
- input += 64;
- }
-
- if (length)
- memcpy (sha256->data + left, input, length);
-}
-
-static guint8 sha256_padding[64] =
-{
- 0x80, 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
-};
-
-static void
-sha256_sum_close (Sha256sum *sha256)
-{
- guint32 last, padn;
- guint32 high, low;
- guint8 msglen[8];
-
- high = (sha256->bits[0] >> 29)
- | (sha256->bits[1] << 3);
- low = (sha256->bits[0] << 3);
-
- PUT_UINT32 (high, msglen, 0);
- PUT_UINT32 (low, msglen, 4);
-
- last = sha256->bits[0] & 0x3F;
- padn = (last < 56) ? (56 - last) : (120 - last);
-
- sha256_sum_update (sha256, sha256_padding, padn);
- sha256_sum_update (sha256, msglen, 8);
-
- PUT_UINT32 (sha256->buf[0], sha256->digest, 0);
- PUT_UINT32 (sha256->buf[1], sha256->digest, 4);
- PUT_UINT32 (sha256->buf[2], sha256->digest, 8);
- PUT_UINT32 (sha256->buf[3], sha256->digest, 12);
- PUT_UINT32 (sha256->buf[4], sha256->digest, 16);
- PUT_UINT32 (sha256->buf[5], sha256->digest, 20);
- PUT_UINT32 (sha256->buf[6], sha256->digest, 24);
- PUT_UINT32 (sha256->buf[7], sha256->digest, 28);
-}
-
-#undef PUT_UINT32
-#undef GET_UINT32
-
-static gchar *
-sha256_sum_to_string (Sha256sum *sha256)
-{
- return digest_to_string (sha256->digest, SHA256_DIGEST_LEN);
-}
-
-static void
-sha256_sum_digest (Sha256sum *sha256,
- guint8 *digest)
-{
- gint i;
-
- for (i = 0; i < SHA256_DIGEST_LEN; i++)
- digest[i] = sha256->digest[i];
-}
-
-
-/*
- * Public API
- */
-
-/**
- * g_checksum_type_get_length:
- * @checksum_type: a #GChecksumType
- *
- * Gets the length in bytes of digests of type @checksum_type
- *
- * Return value: the checksum length, or -1 if @checksum_type is
- * not supported.
- *
- * Since: 2.16
- */
-gssize
-g_checksum_type_get_length (GChecksumType checksum_type)
-{
- gssize len = -1;
-
- switch (checksum_type)
- {
- case G_CHECKSUM_MD5:
- len = MD5_DIGEST_LEN;
- break;
- case G_CHECKSUM_SHA1:
- len = SHA1_DIGEST_LEN;
- break;
- case G_CHECKSUM_SHA256:
- len = SHA256_DIGEST_LEN;
- break;
- default:
- len = -1;
- break;
- }
-
- return len;
-}
-
-/**
- * g_checksum_new:
- * @checksum_type: the desired type of checksum
- *
- * Creates a new #GChecksum, using the checksum algorithm @checksum_type.
- * If the @checksum_type is not known, %NULL is returned.
- * A #GChecksum can be used to compute the checksum, or digest, of an
- * arbitrary binary blob, using different hashing algorithms.
- *
- * A #GChecksum works by feeding a binary blob through g_checksum_update()
- * until there is data to be checked; the digest can then be extracted
- * using g_checksum_get_string(), which will return the checksum as a
- * hexadecimal string; or g_checksum_get_digest(), which will return a
- * vector of raw bytes. Once either g_checksum_get_string() or
- * g_checksum_get_digest() have been called on a #GChecksum, the checksum
- * will be closed and it won't be possible to call g_checksum_update()
- * on it anymore.
- *
- * Return value: the newly created #GChecksum, or %NULL.
- * Use g_checksum_free() to free the memory allocated by it.
- *
- * Since: 2.16
- */
-GChecksum *
-g_checksum_new (GChecksumType checksum_type)
-{
- GChecksum *checksum;
-
- if (! IS_VALID_TYPE (checksum_type))
- return NULL;
-
- checksum = g_slice_new0 (GChecksum);
- checksum->type = checksum_type;
-
- g_checksum_reset (checksum);
-
- return checksum;
-}
-
-/**
- * g_checksum_reset:
- * @checksum: the #GChecksum to reset
- *
- * Resets the state of the @checksum back to its initial state.
- *
- * Since: 2.18
- **/
-void
-g_checksum_reset (GChecksum *checksum)
-{
- g_return_if_fail (checksum != NULL);
-
- g_free (checksum->digest_str);
- checksum->digest_str = NULL;
-
- switch (checksum->type)
- {
- case G_CHECKSUM_MD5:
- md5_sum_init (&(checksum->sum.md5));
- break;
- case G_CHECKSUM_SHA1:
- sha1_sum_init (&(checksum->sum.sha1));
- break;
- case G_CHECKSUM_SHA256:
- sha256_sum_init (&(checksum->sum.sha256));
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-}
-
-/**
- * g_checksum_copy:
- * @checksum: the #GChecksum to copy
- *
- * Copies a #GChecksum. If @checksum has been closed, by calling
- * g_checksum_get_string() or g_checksum_get_digest(), the copied
- * checksum will be closed as well.
- *
- * Return value: the copy of the passed #GChecksum. Use g_checksum_free()
- * when finished using it.
- *
- * Since: 2.16
- */
-GChecksum *
-g_checksum_copy (const GChecksum *checksum)
-{
- GChecksum *copy;
-
- g_return_val_if_fail (checksum != NULL, NULL);
-
- copy = g_slice_new (GChecksum);
- *copy = *checksum;
-
- copy->digest_str = g_strdup (checksum->digest_str);
-
- return copy;
-}
-
-/**
- * g_checksum_free:
- * @checksum: a #GChecksum
- *
- * Frees the memory allocated for @checksum.
- *
- * Since: 2.16
- */
-void
-g_checksum_free (GChecksum *checksum)
-{
- if (G_LIKELY (checksum))
- {
- g_free (checksum->digest_str);
-
- g_slice_free (GChecksum, checksum);
- }
-}
-
-/**
- * g_checksum_update:
- * @checksum: a #GChecksum
- * @data: buffer used to compute the checksum
- * @length: size of the buffer, or -1 if it is a null-terminated string.
- *
- * Feeds @data into an existing #GChecksum. The checksum must still be
- * open, that is g_checksum_get_string() or g_checksum_get_digest() must
- * not have been called on @checksum.
- *
- * Since: 2.16
- */
-void
-g_checksum_update (GChecksum *checksum,
- const guchar *data,
- gssize length)
-{
- g_return_if_fail (checksum != NULL);
- g_return_if_fail (length == 0 || data != NULL);
-
- if (length < 0)
- length = strlen ((const gchar *) data);
-
- if (checksum->digest_str)
- {
- g_warning ("The checksum `%s' has been closed and cannot be updated "
- "anymore.",
- checksum->digest_str);
- return;
- }
-
- switch (checksum->type)
- {
- case G_CHECKSUM_MD5:
- md5_sum_update (&(checksum->sum.md5), data, length);
- break;
- case G_CHECKSUM_SHA1:
- sha1_sum_update (&(checksum->sum.sha1), data, length);
- break;
- case G_CHECKSUM_SHA256:
- sha256_sum_update (&(checksum->sum.sha256), data, length);
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-}
-
-/**
- * g_checksum_get_string:
- * @checksum: a #GChecksum
- *
- * Gets the digest as an hexadecimal string.
- *
- * Once this function has been called the #GChecksum can no longer be
- * updated with g_checksum_update().
- *
- * The hexadecimal characters will be lower case.
- *
- * Return value: the hexadecimal representation of the checksum. The
- * returned string is owned by the checksum and should not be modified
- * or freed.
- *
- * Since: 2.16
- */
-G_CONST_RETURN gchar *
-g_checksum_get_string (GChecksum *checksum)
-{
- gchar *str = NULL;
-
- g_return_val_if_fail (checksum != NULL, NULL);
-
- if (checksum->digest_str)
- return checksum->digest_str;
-
- switch (checksum->type)
- {
- case G_CHECKSUM_MD5:
- md5_sum_close (&(checksum->sum.md5));
- str = md5_sum_to_string (&(checksum->sum.md5));
- break;
- case G_CHECKSUM_SHA1:
- sha1_sum_close (&(checksum->sum.sha1));
- str = sha1_sum_to_string (&(checksum->sum.sha1));
- break;
- case G_CHECKSUM_SHA256:
- sha256_sum_close (&(checksum->sum.sha256));
- str = sha256_sum_to_string (&(checksum->sum.sha256));
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-
- checksum->digest_str = str;
-
- return checksum->digest_str;
-}
-
-/**
- * g_checksum_get_digest:
- * @checksum: a #GChecksum
- * @buffer: output buffer
- * @digest_len: an inout parameter. The caller initializes it to the size of @buffer.
- * After the call it contains the length of the digest.
- *
- * Gets the digest from @checksum as a raw binary vector and places it
- * into @buffer. The size of the digest depends on the type of checksum.
- *
- * Once this function has been called, the #GChecksum is closed and can
- * no longer be updated with g_checksum_update().
- *
- * Since: 2.16
- */
-void
-g_checksum_get_digest (GChecksum *checksum,
- guint8 *buffer,
- gsize *digest_len)
-{
- gboolean checksum_open = FALSE;
- gchar *str = NULL;
- gsize len;
-
- g_return_if_fail (checksum != NULL);
-
- len = g_checksum_type_get_length (checksum->type);
- g_return_if_fail (*digest_len >= len);
-
- checksum_open = !!(checksum->digest_str == NULL);
-
- switch (checksum->type)
- {
- case G_CHECKSUM_MD5:
- if (checksum_open)
- {
- md5_sum_close (&(checksum->sum.md5));
- str = md5_sum_to_string (&(checksum->sum.md5));
- }
- md5_sum_digest (&(checksum->sum.md5), buffer);
- break;
- case G_CHECKSUM_SHA1:
- if (checksum_open)
- {
- sha1_sum_close (&(checksum->sum.sha1));
- str = sha1_sum_to_string (&(checksum->sum.sha1));
- }
- sha1_sum_digest (&(checksum->sum.sha1), buffer);
- break;
- case G_CHECKSUM_SHA256:
- if (checksum_open)
- {
- sha256_sum_close (&(checksum->sum.sha256));
- str = sha256_sum_to_string (&(checksum->sum.sha256));
- }
- sha256_sum_digest (&(checksum->sum.sha256), buffer);
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-
- if (str)
- checksum->digest_str = str;
-
- *digest_len = len;
-}
-
-/**
- * g_compute_checksum_for_data:
- * @checksum_type: a #GChecksumType
- * @data: binary blob to compute the digest of
- * @length: length of @data
- *
- * Computes the checksum for a binary @data of @length. This is a
- * convenience wrapper for g_checksum_new(), g_checksum_get_string()
- * and g_checksum_free().
- *
- * The hexadecimal string returned will be in lower case.
- *
- * Return value: the digest of the binary data as a string in hexadecimal.
- * The returned string should be freed with g_free() when done using it.
- *
- * Since: 2.16
- */
-gchar *
-g_compute_checksum_for_data (GChecksumType checksum_type,
- const guchar *data,
- gsize length)
-{
- GChecksum *checksum;
- gchar *retval;
-
- g_return_val_if_fail (IS_VALID_TYPE (checksum_type), NULL);
- g_return_val_if_fail (length == 0 || data != NULL, NULL);
-
- checksum = g_checksum_new (checksum_type);
- if (!checksum)
- return NULL;
-
- g_checksum_update (checksum, data, length);
- retval = g_strdup (g_checksum_get_string (checksum));
- g_checksum_free (checksum);
-
- return retval;
-}
-
-/**
- * g_compute_checksum_for_string:
- * @checksum_type: a #GChecksumType
- * @str: the string to compute the checksum of
- * @length: the length of the string, or -1 if the string is null-terminated.
- *
- * Computes the checksum of a string.
- *
- * The hexadecimal string returned will be in lower case.
- *
- * Return value: the checksum as a hexadecimal string. The returned string
- * should be freed with g_free() when done using it.
- *
- * Since: 2.16
- */
-gchar *
-g_compute_checksum_for_string (GChecksumType checksum_type,
- const gchar *str,
- gssize length)
-{
- g_return_val_if_fail (IS_VALID_TYPE (checksum_type), NULL);
- g_return_val_if_fail (length == 0 || str != NULL, NULL);
-
- if (length < 0)
- length = strlen (str);
-
- return g_compute_checksum_for_data (checksum_type, (const guchar *) str, length);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include "gstrfuncs.h"
-#include "gmessages.h"
-#include "gunicode.h"
-
-#undef G_DISABLE_DEPRECATED
-
-#include "gcompletion.h"
-
-/**
- * SECTION: completion
- * @title: Automatic String Completion
- * @short_description: support for automatic completion using a group
- * of target strings
- *
- * #GCompletion provides support for automatic completion of a string
- * using any group of target strings. It is typically used for file
- * name completion as is common in many UNIX shells.
- *
- * A #GCompletion is created using g_completion_new(). Target items are
- * added and removed with g_completion_add_items(),
- * g_completion_remove_items() and g_completion_clear_items(). A
- * completion attempt is requested with g_completion_complete() or
- * g_completion_complete_utf8(). When no longer needed, the
- * #GCompletion is freed with g_completion_free().
- *
- * Items in the completion can be simple strings (e.g. filenames), or
- * pointers to arbitrary data structures. If data structures are used
- * you must provide a #GCompletionFunc in g_completion_new(), which
- * retrieves the item's string from the data structure. You can change
- * the way in which strings are compared by setting a different
- * #GCompletionStrncmpFunc in g_completion_set_compare().
- *
- * GCompletion has been marked as deprecated, since this API is rarely
- * used and not very actively maintained.
- **/
-
-/**
- * GCompletion:
- * @items: list of target items (strings or data structures).
- * @func: function which is called to get the string associated with a
- * target item. It is %NULL if the target items are strings.
- * @prefix: the last prefix passed to g_completion_complete() or
- * g_completion_complete_utf8().
- * @cache: the list of items which begin with @prefix.
- * @strncmp_func: The function to use when comparing strings. Use
- * g_completion_set_compare() to modify this function.
- *
- * The data structure used for automatic completion.
- **/
-
-/**
- * GCompletionFunc:
- * @Param1: the completion item.
- * @Returns: the string corresponding to the item.
- *
- * Specifies the type of the function passed to g_completion_new(). It
- * should return the string corresponding to the given target item.
- * This is used when you use data structures as #GCompletion items.
- **/
-
-/**
- * GCompletionStrncmpFunc:
- * @s1: string to compare with @s2.
- * @s2: string to compare with @s1.
- * @n: maximal number of bytes to compare.
- * @Returns: an integer less than, equal to, or greater than zero if
- * the first @n bytes of @s1 is found, respectively, to be
- * less than, to match, or to be greater than the first @n
- * bytes of @s2.
- *
- * Specifies the type of the function passed to
- * g_completion_set_compare(). This is used when you use strings as
- * #GCompletion items.
- **/
-
-static void completion_check_cache (GCompletion* cmp,
- gchar** new_prefix);
-
-/**
- * g_completion_new:
- * @func: the function to be called to return the string representing
- * an item in the #GCompletion, or %NULL if strings are going to
- * be used as the #GCompletion items.
- * @Returns: the new #GCompletion.
- *
- * Creates a new #GCompletion.
- **/
-GCompletion*
-g_completion_new (GCompletionFunc func)
-{
- GCompletion* gcomp;
-
- gcomp = g_new (GCompletion, 1);
- gcomp->items = NULL;
- gcomp->cache = NULL;
- gcomp->prefix = NULL;
- gcomp->func = func;
- gcomp->strncmp_func = strncmp;
-
- return gcomp;
-}
-
-/**
- * g_completion_add_items:
- * @cmp: the #GCompletion.
- * @items: the list of items to add.
- *
- * Adds items to the #GCompletion.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_completion_add_items (GCompletion* cmp,
- GList* items)
-{
- GList* it;
-
- g_return_if_fail (cmp != NULL);
-
- /* optimize adding to cache? */
- if (cmp->cache)
- {
- g_list_free (cmp->cache);
- cmp->cache = NULL;
- }
-
- if (cmp->prefix)
- {
- g_free (cmp->prefix);
- cmp->prefix = NULL;
- }
-
- it = items;
- while (it)
- {
- cmp->items = g_list_prepend (cmp->items, it->data);
- it = it->next;
- }
-}
-
-/**
- * g_completion_remove_items:
- * @cmp: the #GCompletion.
- * @items: the items to remove.
- *
- * Removes items from a #GCompletion.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_completion_remove_items (GCompletion* cmp,
- GList* items)
-{
- GList* it;
-
- g_return_if_fail (cmp != NULL);
-
- it = items;
- while (cmp->items && it)
- {
- cmp->items = g_list_remove (cmp->items, it->data);
- it = it->next;
- }
-
- it = items;
- while (cmp->cache && it)
- {
- cmp->cache = g_list_remove(cmp->cache, it->data);
- it = it->next;
- }
-}
-
-/**
- * g_completion_clear_items:
- * @cmp: the #GCompletion.
- *
- * Removes all items from the #GCompletion.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_completion_clear_items (GCompletion* cmp)
-{
- g_return_if_fail (cmp != NULL);
-
- g_list_free (cmp->items);
- cmp->items = NULL;
- g_list_free (cmp->cache);
- cmp->cache = NULL;
- g_free (cmp->prefix);
- cmp->prefix = NULL;
-}
-
-static void
-completion_check_cache (GCompletion* cmp,
- gchar** new_prefix)
-{
- register GList* list;
- register gsize len;
- register gsize i;
- register gsize plen;
- gchar* postfix;
- gchar* s;
-
- if (!new_prefix)
- return;
- if (!cmp->cache)
- {
- *new_prefix = NULL;
- return;
- }
-
- len = strlen(cmp->prefix);
- list = cmp->cache;
- s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
- postfix = s + len;
- plen = strlen (postfix);
- list = list->next;
-
- while (list && plen)
- {
- s = cmp->func ? cmp->func (list->data) : (gchar*) list->data;
- s += len;
- for (i = 0; i < plen; ++i)
- {
- if (postfix[i] != s[i])
- break;
- }
- plen = i;
- list = list->next;
- }
-
- *new_prefix = g_new0 (gchar, len + plen + 1);
- strncpy (*new_prefix, cmp->prefix, len);
- strncpy (*new_prefix + len, postfix, plen);
-}
-
-/**
- * g_completion_complete_utf8:
- * @cmp: the #GCompletion
- * @prefix: the prefix string, typically used by the user, which is compared
- * with each of the items
- * @new_prefix: if non-%NULL, returns the longest prefix which is common to all
- * items that matched @prefix, or %NULL if no items matched @prefix.
- * This string should be freed when no longer needed.
- *
- * Attempts to complete the string @prefix using the #GCompletion target items.
- * In contrast to g_completion_complete(), this function returns the largest common
- * prefix that is a valid UTF-8 string, omitting a possible common partial
- * character.
- *
- * You should use this function instead of g_completion_complete() if your
- * items are UTF-8 strings.
- *
- * Return value: the list of items whose strings begin with @prefix. This should
- * not be changed.
- *
- * Since: 2.4
- *
- * Deprecated: 2.26: Rarely used API
- **/
-GList*
-g_completion_complete_utf8 (GCompletion *cmp,
- const gchar *prefix,
- gchar **new_prefix)
-{
- GList *list;
- gchar *p, *q;
-
- list = g_completion_complete (cmp, prefix, new_prefix);
-
- if (new_prefix && *new_prefix)
- {
- p = *new_prefix + strlen (*new_prefix);
- q = g_utf8_find_prev_char (*new_prefix, p);
-
- switch (g_utf8_get_char_validated (q, p - q))
- {
- case (gunichar)-2:
- case (gunichar)-1:
- *q = 0;
- break;
- default: ;
- }
-
- }
-
- return list;
-}
-
-/**
- * g_completion_complete:
- * @cmp: the #GCompletion.
- * @prefix: the prefix string, typically typed by the user, which is
- * compared with each of the items.
- * @new_prefix: if non-%NULL, returns the longest prefix which is
- * common to all items that matched @prefix, or %NULL if
- * no items matched @prefix. This string should be freed
- * when no longer needed.
- * @Returns: the list of items whose strings begin with @prefix. This
- * should not be changed.
- *
- * Attempts to complete the string @prefix using the #GCompletion
- * target items.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-GList*
-g_completion_complete (GCompletion* cmp,
- const gchar* prefix,
- gchar** new_prefix)
-{
- gsize plen, len;
- gboolean done = FALSE;
- GList* list;
-
- g_return_val_if_fail (cmp != NULL, NULL);
- g_return_val_if_fail (prefix != NULL, NULL);
-
- len = strlen (prefix);
- if (cmp->prefix && cmp->cache)
- {
- plen = strlen (cmp->prefix);
- if (plen <= len && ! cmp->strncmp_func (prefix, cmp->prefix, plen))
- {
- /* use the cache */
- list = cmp->cache;
- while (list)
- {
- GList *next = list->next;
-
- if (cmp->strncmp_func (prefix,
- cmp->func ? cmp->func (list->data) : (gchar*) list->data,
- len))
- cmp->cache = g_list_delete_link (cmp->cache, list);
-
- list = next;
- }
- done = TRUE;
- }
- }
-
- if (!done)
- {
- /* normal code */
- g_list_free (cmp->cache);
- cmp->cache = NULL;
- list = cmp->items;
- while (*prefix && list)
- {
- if (!cmp->strncmp_func (prefix,
- cmp->func ? cmp->func (list->data) : (gchar*) list->data,
- len))
- cmp->cache = g_list_prepend (cmp->cache, list->data);
- list = list->next;
- }
- }
- if (cmp->prefix)
- {
- g_free (cmp->prefix);
- cmp->prefix = NULL;
- }
- if (cmp->cache)
- cmp->prefix = g_strdup (prefix);
- completion_check_cache (cmp, new_prefix);
-
- return *prefix ? cmp->cache : cmp->items;
-}
-
-/**
- * g_completion_free:
- * @cmp: the #GCompletion.
- *
- * Frees all memory used by the #GCompletion.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_completion_free (GCompletion* cmp)
-{
- g_return_if_fail (cmp != NULL);
-
- g_completion_clear_items (cmp);
- g_free (cmp);
-}
-
-/**
- * g_completion_set_compare:
- * @cmp: a #GCompletion.
- * @strncmp_func: the string comparison function.
- *
- * Sets the function to use for string comparisons. The default string
- * comparison function is strncmp().
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_completion_set_compare(GCompletion *cmp,
- GCompletionStrncmpFunc strncmp_func)
-{
- cmp->strncmp_func = strncmp_func;
-}
-
-#ifdef TEST_COMPLETION
-#include <stdio.h>
-int
-main (int argc,
- char* argv[])
-{
- FILE *file;
- gchar buf[1024];
- GList *list;
- GList *result;
- GList *tmp;
- GCompletion *cmp;
- gint i;
- gchar *longp = NULL;
-
- if (argc < 3)
- {
- g_warning ("Usage: %s filename prefix1 [prefix2 ...]\n", argv[0]);
- return 1;
- }
-
- file = fopen (argv[1], "r");
- if (!file)
- {
- g_warning ("Cannot open %s\n", argv[1]);
- return 1;
- }
-
- cmp = g_completion_new (NULL);
- list = g_list_alloc ();
- while (fgets (buf, 1024, file))
- {
- list->data = g_strdup (buf);
- g_completion_add_items (cmp, list);
- }
- fclose (file);
-
- for (i = 2; i < argc; ++i)
- {
- printf ("COMPLETING: %s\n", argv[i]);
- result = g_completion_complete (cmp, argv[i], &longp);
- g_list_foreach (result, (GFunc) printf, NULL);
- printf ("LONG MATCH: %s\n", longp);
- g_free (longp);
- longp = NULL;
- }
-
- g_list_foreach (cmp->items, (GFunc) g_free, NULL);
- g_completion_free (cmp);
- g_list_free (list);
-
- return 0;
-}
-#endif
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- *
- * gconvert.c: Convert between character sets using iconv
- * Copyright Red Hat Inc., 2000
- * Authors: Havoc Pennington <hp@redhat.com>, Owen Taylor <otaylor@redhat.com>
- *
- * 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 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 "config.h"
-#include "glibconfig.h"
-
-#ifdef G_OS_WIN32
-#include "win_iconv.c"
-#else
-#ifdef USE_LIBICONV_GNU
-#include <iconv.h>
-#endif
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "gprintfint.h"
-#include "gthreadprivate.h"
-#include "gunicode.h"
-
-#ifdef G_PLATFORM_WIN32
-#define STRICT
-#include <windows.h>
-#undef STRICT
-#endif
-
-#include "gconvert.h"
-
-#include "gprintfint.h"
-#include "gslist.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gthreadprivate.h"
-#include "gunicode.h"
-
-#ifdef NEED_ICONV_CACHE
-#include "glist.h"
-#include "ghash.h"
-#endif
-
-#include "glibintl.h"
-
-#if defined(USE_LIBICONV_GNU) && !defined (_LIBICONV_H)
-#error GNU libiconv in use but included iconv.h not from libiconv
-#endif
-#if !defined(USE_LIBICONV_GNU) && defined (_LIBICONV_H)
-#error GNU libiconv not in use but included iconv.h is from libiconv
-#endif
-
-
-/**
- * SECTION:conversions
- * @title: Character Set Conversion
- * @short_description: Convert strings between different character sets
- *
- * The g_convert() family of function wraps the functionality of iconv(). In
- * addition to pure character set conversions, GLib has functions to deal
- * with the extra complications of encodings for file names.
- *
- * <refsect2 id="file-name-encodings">
- * <title>File Name Encodings</title>
- * <para>
- * Historically, Unix has not had a defined encoding for file
- * names: a file name is valid as long as it does not have path
- * separators in it ("/"). However, displaying file names may
- * require conversion: from the character set in which they were
- * created, to the character set in which the application
- * operates. Consider the Spanish file name
- * "<filename>Presentación.sxi</filename>". If the
- * application which created it uses ISO-8859-1 for its encoding,
- * </para>
- * <programlisting id="filename-iso8859-1">
- * Character: P r e s e n t a c i ó n . s x i
- * Hex code: 50 72 65 73 65 6e 74 61 63 69 f3 6e 2e 73 78 69
- * </programlisting>
- * <para>
- * However, if the application use UTF-8, the actual file name on
- * disk would look like this:
- * </para>
- * <programlisting id="filename-utf-8">
- * Character: P r e s e n t a c i ó n . s x i
- * Hex code: 50 72 65 73 65 6e 74 61 63 69 c3 b3 6e 2e 73 78 69
- * </programlisting>
- * <para>
- * Glib uses UTF-8 for its strings, and GUI toolkits like GTK+
- * that use Glib do the same thing. If you get a file name from
- * the file system, for example, from readdir(3) or from g_dir_read_name(),
- * and you wish to display the file name to the user, you
- * <emphasis>will</emphasis> need to convert it into UTF-8. The
- * opposite case is when the user types the name of a file he
- * wishes to save: the toolkit will give you that string in
- * UTF-8 encoding, and you will need to convert it to the
- * character set used for file names before you can create the
- * file with open(2) or fopen(3).
- * </para>
- * <para>
- * By default, Glib assumes that file names on disk are in UTF-8
- * encoding. This is a valid assumption for file systems which
- * were created relatively recently: most applications use UTF-8
- * encoding for their strings, and that is also what they use for
- * the file names they create. However, older file systems may
- * still contain file names created in "older" encodings, such as
- * ISO-8859-1. In this case, for compatibility reasons, you may
- * want to instruct Glib to use that particular encoding for file
- * names rather than UTF-8. You can do this by specifying the
- * encoding for file names in the <link
- * linkend="G_FILENAME_ENCODING"><envar>G_FILENAME_ENCODING</envar></link>
- * environment variable. For example, if your installation uses
- * ISO-8859-1 for file names, you can put this in your
- * <filename>~/.profile</filename>:
- * </para>
- * <programlisting>
- * export G_FILENAME_ENCODING=ISO-8859-1
- * </programlisting>
- * <para>
- * Glib provides the functions g_filename_to_utf8() and
- * g_filename_from_utf8() to perform the necessary conversions. These
- * functions convert file names from the encoding specified in
- * <envar>G_FILENAME_ENCODING</envar> to UTF-8 and vice-versa.
- * <xref linkend="file-name-encodings-diagram"/> illustrates how
- * these functions are used to convert between UTF-8 and the
- * encoding for file names in the file system.
- * </para>
- * <figure id="file-name-encodings-diagram">
- * <title>Conversion between File Name Encodings</title>
- * <graphic fileref="file-name-encodings.png" format="PNG"/>
- * </figure>
- * <refsect3 id="file-name-encodings-checklist">
- * <title>Checklist for Application Writers</title>
- * <para>
- * This section is a practical summary of the detailed
- * description above. You can use this as a checklist of
- * things to do to make sure your applications process file
- * name encodings correctly.
- * </para>
- * <orderedlist>
- * <listitem><para>
- * If you get a file name from the file system from a function
- * such as readdir(3) or gtk_file_chooser_get_filename(),
- * you do not need to do any conversion to pass that
- * file name to functions like open(2), rename(2), or
- * fopen(3) — those are "raw" file names which the file
- * system understands.
- * </para></listitem>
- * <listitem><para>
- * If you need to display a file name, convert it to UTF-8 first by
- * using g_filename_to_utf8(). If conversion fails, display a string like
- * "<literal>Unknown file name</literal>". <emphasis>Do not</emphasis>
- * convert this string back into the encoding used for file names if you
- * wish to pass it to the file system; use the original file name instead.
- * For example, the document window of a word processor could display
- * "Unknown file name" in its title bar but still let the user save the
- * file, as it would keep the raw file name internally. This can happen
- * if the user has not set the <envar>G_FILENAME_ENCODING</envar>
- * environment variable even though he has files whose names are not
- * encoded in UTF-8.
- * </para></listitem>
- * <listitem><para>
- * If your user interface lets the user type a file name for saving or
- * renaming, convert it to the encoding used for file names in the file
- * system by using g_filename_from_utf8(). Pass the converted file name
- * to functions like fopen(3). If conversion fails, ask the user to enter
- * a different file name. This can happen if the user types Japanese
- * characters when <envar>G_FILENAME_ENCODING</envar> is set to
- * <literal>ISO-8859-1</literal>, for example.
- * </para></listitem>
- * </orderedlist>
- * </refsect3>
- * </refsect2>
- */
-
-/* We try to terminate strings in unknown charsets with this many zero bytes
- * to ensure that multibyte strings really are nul-terminated when we return
- * them from g_convert() and friends.
- */
-#define NUL_TERMINATOR_LENGTH 4
-
-GQuark
-g_convert_error_quark (void)
-{
- return g_quark_from_static_string ("g_convert_error");
-}
-
-#ifdef USE_LIBICONV_GNU
-static gboolean
-try_conversion (const char *to_codeset,
- const char *from_codeset,
- iconv_t *cd)
-{
- *cd = iconv_open (to_codeset, from_codeset);
-
- if (*cd == (iconv_t)-1 && errno == EINVAL)
- return FALSE;
- else
- return TRUE;
-}
-
-static gboolean
-try_to_aliases (const char **to_aliases,
- const char *from_codeset,
- iconv_t *cd)
-{
- if (to_aliases)
- {
- const char **p = to_aliases;
- while (*p)
- {
- if (try_conversion (*p, from_codeset, cd))
- return TRUE;
-
- p++;
- }
- }
-
- return FALSE;
-}
-#endif /* USE_LIBICONV_GNU */
-
-G_GNUC_INTERNAL extern const char **
-_g_charset_get_aliases (const char *canonical_name);
-
-/**
- * g_iconv_open:
- * @to_codeset: destination codeset
- * @from_codeset: source codeset
- *
- * Same as the standard UNIX routine iconv_open(), but
- * may be implemented via libiconv on UNIX flavors that lack
- * a native implementation.
- *
- * GLib provides g_convert() and g_locale_to_utf8() which are likely
- * more convenient than the raw iconv wrappers.
- *
- * Return value: a "conversion descriptor", or (GIConv)-1 if
- * opening the converter failed.
- **/
-GIConv
-g_iconv_open (const gchar *to_codeset,
- const gchar *from_codeset)
-{
-#ifdef USE_LIBICONV_GNU
- iconv_t cd;
-
- if (!try_conversion (to_codeset, from_codeset, &cd))
- {
- const char **to_aliases = _g_charset_get_aliases (to_codeset);
- const char **from_aliases = _g_charset_get_aliases (from_codeset);
-
- if (from_aliases)
- {
- const char **p = from_aliases;
- while (*p)
- {
- if (try_conversion (to_codeset, *p, &cd))
- goto out;
-
- if (try_to_aliases (to_aliases, *p, &cd))
- goto out;
-
- p++;
- }
- }
-
- if (try_to_aliases (to_aliases, from_codeset, &cd))
- goto out;
- }
-
- out:
- return (cd == (iconv_t)-1) ? (GIConv)-1 : (GIConv)cd;
-#else
- return (GIConv)-1;
-#endif /* USE_LIBICONV_GNU */
-}
-
-/**
- * g_iconv:
- * @converter: conversion descriptor from g_iconv_open()
- * @inbuf: bytes to convert
- * @inbytes_left: inout parameter, bytes remaining to convert in @inbuf
- * @outbuf: converted output bytes
- * @outbytes_left: inout parameter, bytes available to fill in @outbuf
- *
- * Same as the standard UNIX routine iconv(), but
- * may be implemented via libiconv on UNIX flavors that lack
- * a native implementation.
- *
- * GLib provides g_convert() and g_locale_to_utf8() which are likely
- * more convenient than the raw iconv wrappers.
- *
- * Return value: count of non-reversible conversions, or -1 on error
- **/
-gsize
-g_iconv (GIConv converter,
- gchar **inbuf,
- gsize *inbytes_left,
- gchar **outbuf,
- gsize *outbytes_left)
-{
-#ifdef USE_LIBICONV_GNU
- iconv_t cd = (iconv_t)converter;
-
- return iconv (cd, inbuf, inbytes_left, outbuf, outbytes_left);
-#else
- return -1;
-#endif /* USE_LIBICONV_GNU */
-}
-
-/**
- * g_iconv_close:
- * @converter: a conversion descriptor from g_iconv_open()
- *
- * Same as the standard UNIX routine iconv_close(), but
- * may be implemented via libiconv on UNIX flavors that lack
- * a native implementation. Should be called to clean up
- * the conversion descriptor from g_iconv_open() when
- * you are done converting things.
- *
- * GLib provides g_convert() and g_locale_to_utf8() which are likely
- * more convenient than the raw iconv wrappers.
- *
- * Return value: -1 on error, 0 on success
- **/
-gint
-g_iconv_close (GIConv converter)
-{
-#ifdef USE_LIBICONV_GNU
- iconv_t cd = (iconv_t)converter;
-
- return iconv_close (cd);
-#else
- return -1;
-#endif /* USE_LIBICONV_GNU */
-}
-
-
-#ifdef NEED_ICONV_CACHE
-#ifdef USE_LIBICONV_GNU
-
-#define ICONV_CACHE_SIZE (16)
-
-struct _iconv_cache_bucket {
- gchar *key;
- guint32 refcount;
- gboolean used;
- GIConv cd;
-};
-
-static GList *iconv_cache_list;
-static GHashTable *iconv_cache;
-static GHashTable *iconv_open_hash;
-static guint iconv_cache_size = 0;
-G_LOCK_DEFINE_STATIC (iconv_cache_lock);
-
-/* caller *must* hold the iconv_cache_lock */
-static void
-iconv_cache_init (void)
-{
- static gboolean initialized = FALSE;
-
- if (initialized)
- return;
-
- iconv_cache_list = NULL;
- iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
- iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
-
- initialized = TRUE;
-}
-
-
-/*
- * iconv_cache_bucket_new:
- * @key: cache key
- * @cd: iconv descriptor
- *
- * Creates a new cache bucket, inserts it into the cache and
- * increments the cache size.
- *
- * This assumes ownership of @key.
- *
- * Returns a pointer to the newly allocated cache bucket.
- **/
-static struct _iconv_cache_bucket *
-iconv_cache_bucket_new (gchar *key, GIConv cd)
-{
- struct _iconv_cache_bucket *bucket;
-
- bucket = g_new (struct _iconv_cache_bucket, 1);
- bucket->key = key;
- bucket->refcount = 1;
- bucket->used = TRUE;
- bucket->cd = cd;
-
- g_hash_table_insert (iconv_cache, bucket->key, bucket);
-
- /* FIXME: if we sorted the list so items with few refcounts were
- first, then we could expire them faster in iconv_cache_expire_unused () */
- iconv_cache_list = g_list_prepend (iconv_cache_list, bucket);
-
- iconv_cache_size++;
-
- return bucket;
-}
-
-
-/*
- * iconv_cache_bucket_expire:
- * @node: cache bucket's node
- * @bucket: cache bucket
- *
- * Expires a single cache bucket @bucket. This should only ever be
- * called on a bucket that currently has no used iconv descriptors
- * open.
- *
- * @node is not a required argument. If @node is not supplied, we
- * search for it ourselves.
- **/
-static void
-iconv_cache_bucket_expire (GList *node, struct _iconv_cache_bucket *bucket)
-{
- g_hash_table_remove (iconv_cache, bucket->key);
-
- if (node == NULL)
- node = g_list_find (iconv_cache_list, bucket);
-
- g_assert (node != NULL);
-
- if (node->prev)
- {
- node->prev->next = node->next;
- if (node->next)
- node->next->prev = node->prev;
- }
- else
- {
- iconv_cache_list = node->next;
- if (node->next)
- node->next->prev = NULL;
- }
-
- g_list_free_1 (node);
-
- g_free (bucket->key);
- g_iconv_close (bucket->cd);
- g_free (bucket);
-
- iconv_cache_size--;
-}
-
-
-/*
- * iconv_cache_expire_unused:
- *
- * Expires as many unused cache buckets as it needs to in order to get
- * the total number of buckets < ICONV_CACHE_SIZE.
- **/
-static void
-iconv_cache_expire_unused (void)
-{
- struct _iconv_cache_bucket *bucket;
- GList *node, *next;
-
- node = iconv_cache_list;
- while (node && iconv_cache_size >= ICONV_CACHE_SIZE)
- {
- next = node->next;
-
- bucket = node->data;
- if (bucket->refcount == 0)
- iconv_cache_bucket_expire (node, bucket);
-
- node = next;
- }
-}
-
-static GIConv
-open_converter (const gchar *to_codeset,
- const gchar *from_codeset,
- GError **error)
-{
- struct _iconv_cache_bucket *bucket;
- gchar *key, *dyn_key, auto_key[80];
- GIConv cd;
- gsize len_from_codeset, len_to_codeset;
-
- /* create our key */
- len_from_codeset = strlen (from_codeset);
- len_to_codeset = strlen (to_codeset);
- if (len_from_codeset + len_to_codeset + 2 < sizeof (auto_key))
- {
- key = auto_key;
- dyn_key = NULL;
- }
- else
- key = dyn_key = g_malloc (len_from_codeset + len_to_codeset + 2);
- memcpy (key, from_codeset, len_from_codeset);
- key[len_from_codeset] = ':';
- strcpy (key + len_from_codeset + 1, to_codeset);
-
- G_LOCK (iconv_cache_lock);
-
- /* make sure the cache has been initialized */
- iconv_cache_init ();
-
- bucket = g_hash_table_lookup (iconv_cache, key);
- if (bucket)
- {
- g_free (dyn_key);
-
- if (bucket->used)
- {
- cd = g_iconv_open (to_codeset, from_codeset);
- if (cd == (GIConv) -1)
- goto error;
- }
- else
- {
- /* Apparently iconv on Solaris <= 7 segfaults if you pass in
- * NULL for anything but inbuf; work around that. (NULL outbuf
- * or NULL *outbuf is allowed by Unix98.)
- */
- gsize inbytes_left = 0;
- gchar *outbuf = NULL;
- gsize outbytes_left = 0;
-
- cd = bucket->cd;
- bucket->used = TRUE;
-
- /* reset the descriptor */
- g_iconv (cd, NULL, &inbytes_left, &outbuf, &outbytes_left);
- }
-
- bucket->refcount++;
- }
- else
- {
- cd = g_iconv_open (to_codeset, from_codeset);
- if (cd == (GIConv) -1)
- {
- g_free (dyn_key);
- goto error;
- }
-
- iconv_cache_expire_unused ();
-
- bucket = iconv_cache_bucket_new (dyn_key ? dyn_key : g_strdup (key), cd);
- }
-
- g_hash_table_insert (iconv_open_hash, cd, bucket->key);
-
- G_UNLOCK (iconv_cache_lock);
-
- return cd;
-
- error:
-
- G_UNLOCK (iconv_cache_lock);
-
- /* Something went wrong. */
- if (error)
- {
- if (errno == EINVAL)
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
- _("Conversion from character set '%s' to '%s' is not supported"),
- from_codeset, to_codeset);
- else
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Could not open converter from '%s' to '%s'"),
- from_codeset, to_codeset);
- }
-
- return cd;
-}
-
-static int
-close_converter (GIConv converter)
-{
- struct _iconv_cache_bucket *bucket;
- const gchar *key;
- GIConv cd;
-
- cd = converter;
-
- if (cd == (GIConv) -1)
- return 0;
-
- G_LOCK (iconv_cache_lock);
-
- key = g_hash_table_lookup (iconv_open_hash, cd);
- if (key)
- {
- g_hash_table_remove (iconv_open_hash, cd);
-
- bucket = g_hash_table_lookup (iconv_cache, key);
- g_assert (bucket);
-
- bucket->refcount--;
-
- if (cd == bucket->cd)
- bucket->used = FALSE;
- else
- g_iconv_close (cd);
-
- if (!bucket->refcount && iconv_cache_size > ICONV_CACHE_SIZE)
- {
- /* expire this cache bucket */
- iconv_cache_bucket_expire (NULL, bucket);
- }
- }
- else
- {
- G_UNLOCK (iconv_cache_lock);
-
- g_warning ("This iconv context wasn't opened using open_converter");
-
- return g_iconv_close (converter);
- }
-
- G_UNLOCK (iconv_cache_lock);
-
- return 0;
-}
-
-#endif /* USE_LIBICONV_GNU */
-#else /* !NEED_ICONV_CACHE */
-
-static GIConv
-open_converter (const gchar *to_codeset,
- const gchar *from_codeset,
- GError **error)
-{
- GIConv cd;
-
- cd = g_iconv_open (to_codeset, from_codeset);
-
- if (cd == (GIConv) -1)
- {
- /* Something went wrong. */
- if (error)
- {
- if (errno == EINVAL)
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
- _("Conversion from character set '%s' to '%s' is not supported"),
- from_codeset, to_codeset);
- else
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Could not open converter from '%s' to '%s'"),
- from_codeset, to_codeset);
- }
- }
-
- return cd;
-}
-
-static int
-close_converter (GIConv cd)
-{
- if (cd == (GIConv) -1)
- return 0;
-
- return g_iconv_close (cd);
-}
-
-#endif /* NEED_ICONV_CACHE */
-
-/**
- * g_convert_with_iconv:
- * @str: the string to convert
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnoteref linkend="nul-unsafe"/>.
- * @converter: conversion descriptor from g_iconv_open()
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from one character set to another.
- *
- * Note that you should use g_iconv() for streaming
- * conversions<footnote id="streaming-state">
- * <para>
- * Despite the fact that @byes_read can return information about partial
- * characters, the <literal>g_convert_...</literal> functions
- * are not generally suitable for streaming. If the underlying converter
- * being used maintains internal state, then this won't be preserved
- * across successive calls to g_convert(), g_convert_with_iconv() or
- * g_convert_with_fallback(). (An example of this is the GNU C converter
- * for CP1255 which does not emit a base character until it knows that
- * the next character is not a mark that could combine with the base
- * character.)
- * </para>
- * </footnote>.
- *
- * Return value: If the conversion was successful, a newly allocated
- * nul-terminated string, which must be freed with
- * g_free(). Otherwise %NULL and @error will be set.
- **/
-gchar*
-g_convert_with_iconv (const gchar *str,
- gssize len,
- GIConv converter,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- gchar *dest;
- gchar *outp;
- const gchar *p;
- gsize inbytes_remaining;
- gsize outbytes_remaining;
- gsize err;
- gsize outbuf_size;
- gboolean have_error = FALSE;
- gboolean done = FALSE;
- gboolean reset = FALSE;
-
- g_return_val_if_fail (converter != (GIConv) -1, NULL);
-
- if (len < 0)
- len = strlen (str);
-
- p = str;
- inbytes_remaining = len;
- outbuf_size = len + NUL_TERMINATOR_LENGTH;
-
- outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
- outp = dest = g_malloc (outbuf_size);
-
- while (!done && !have_error)
- {
- if (reset)
- err = g_iconv (converter, NULL, &inbytes_remaining, &outp, &outbytes_remaining);
- else
- err = g_iconv (converter, (char **)&p, &inbytes_remaining, &outp, &outbytes_remaining);
-
- if (err == (gsize) -1)
- {
- switch (errno)
- {
- case EINVAL:
- /* Incomplete text, do not report an error */
- done = TRUE;
- break;
- case E2BIG:
- {
- gsize used = outp - dest;
-
- outbuf_size *= 2;
- dest = g_realloc (dest, outbuf_size);
-
- outp = dest + used;
- outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
- }
- break;
- case EILSEQ:
- if (error)
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
- have_error = TRUE;
- break;
- default:
- {
- int errsv = errno;
-
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Error during conversion: %s"),
- g_strerror (errsv));
- }
- have_error = TRUE;
- break;
- }
- }
- else
- {
- if (!reset)
- {
- /* call g_iconv with NULL inbuf to cleanup shift state */
- reset = TRUE;
- inbytes_remaining = 0;
- }
- else
- done = TRUE;
- }
- }
-
- memset (outp, 0, NUL_TERMINATOR_LENGTH);
-
- if (bytes_read)
- *bytes_read = p - str;
- else
- {
- if ((p - str) != len)
- {
- if (!have_error)
- {
- if (error)
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Partial character sequence at end of input"));
- have_error = TRUE;
- }
- }
- }
-
- if (bytes_written)
- *bytes_written = outp - dest; /* Doesn't include '\0' */
-
- if (have_error)
- {
- g_free (dest);
- return NULL;
- }
- else
- return dest;
-}
-
-/**
- * g_convert:
- * @str: the string to convert
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnote id="nul-unsafe">
- <para>
- Note that some encodings may allow nul bytes to
- occur inside strings. In that case, using -1 for
- the @len parameter is unsafe.
- </para>
- </footnote>.
- * @to_codeset: name of character set into which to convert @str
- * @from_codeset: character set of @str.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from one character set to another.
- *
- * Note that you should use g_iconv() for streaming
- * conversions<footnoteref linkend="streaming-state"/>.
- *
- * Return value: If the conversion was successful, a newly allocated
- * nul-terminated string, which must be freed with
- * g_free(). Otherwise %NULL and @error will be set.
- **/
-gchar*
-g_convert (const gchar *str,
- gssize len,
- const gchar *to_codeset,
- const gchar *from_codeset,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- gchar *res;
- GIConv cd;
-
- g_return_val_if_fail (str != NULL, NULL);
- g_return_val_if_fail (to_codeset != NULL, NULL);
- g_return_val_if_fail (from_codeset != NULL, NULL);
-
- cd = open_converter (to_codeset, from_codeset, error);
-
- if (cd == (GIConv) -1)
- {
- if (bytes_read)
- *bytes_read = 0;
-
- if (bytes_written)
- *bytes_written = 0;
-
- return NULL;
- }
-
- res = g_convert_with_iconv (str, len, cd,
- bytes_read, bytes_written,
- error);
-
- close_converter (cd);
-
- return res;
-}
-
-/**
- * g_convert_with_fallback:
- * @str: the string to convert
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnoteref linkend="nul-unsafe"/>.
- * @to_codeset: name of character set into which to convert @str
- * @from_codeset: character set of @str.
- * @fallback: UTF-8 string to use in place of character not
- * present in the target encoding. (The string must be
- * representable in the target encoding).
- If %NULL, characters not in the target encoding will
- be represented as Unicode escapes \uxxxx or \Uxxxxyyyy.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from one character set to another, possibly
- * including fallback sequences for characters not representable
- * in the output. Note that it is not guaranteed that the specification
- * for the fallback sequences in @fallback will be honored. Some
- * systems may do an approximate conversion from @from_codeset
- * to @to_codeset in their iconv() functions,
- * in which case GLib will simply return that approximate conversion.
- *
- * Note that you should use g_iconv() for streaming
- * conversions<footnoteref linkend="streaming-state"/>.
- *
- * Return value: If the conversion was successful, a newly allocated
- * nul-terminated string, which must be freed with
- * g_free(). Otherwise %NULL and @error will be set.
- **/
-gchar*
-g_convert_with_fallback (const gchar *str,
- gssize len,
- const gchar *to_codeset,
- const gchar *from_codeset,
- const gchar *fallback,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- gchar *utf8;
- gchar *dest;
- gchar *outp;
- const gchar *insert_str = NULL;
- const gchar *p;
- gsize inbytes_remaining;
- const gchar *save_p = NULL;
- gsize save_inbytes = 0;
- gsize outbytes_remaining;
- gsize err;
- GIConv cd;
- gsize outbuf_size;
- gboolean have_error = FALSE;
- gboolean done = FALSE;
-
- GError *local_error = NULL;
-
- g_return_val_if_fail (str != NULL, NULL);
- g_return_val_if_fail (to_codeset != NULL, NULL);
- g_return_val_if_fail (from_codeset != NULL, NULL);
-
- if (len < 0)
- len = strlen (str);
-
- /* Try an exact conversion; we only proceed if this fails
- * due to an illegal sequence in the input string.
- */
- dest = g_convert (str, len, to_codeset, from_codeset,
- bytes_read, bytes_written, &local_error);
- if (!local_error)
- return dest;
-
- if (!g_error_matches (local_error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
- {
- g_propagate_error (error, local_error);
- return NULL;
- }
- else
- g_error_free (local_error);
-
- local_error = NULL;
-
- /* No go; to proceed, we need a converter from "UTF-8" to
- * to_codeset, and the string as UTF-8.
- */
- cd = open_converter (to_codeset, "UTF-8", error);
- if (cd == (GIConv) -1)
- {
- if (bytes_read)
- *bytes_read = 0;
-
- if (bytes_written)
- *bytes_written = 0;
-
- return NULL;
- }
-
- utf8 = g_convert (str, len, "UTF-8", from_codeset,
- bytes_read, &inbytes_remaining, error);
- if (!utf8)
- {
- close_converter (cd);
- if (bytes_written)
- *bytes_written = 0;
- return NULL;
- }
-
- /* Now the heart of the code. We loop through the UTF-8 string, and
- * whenever we hit an offending character, we form fallback, convert
- * the fallback to the target codeset, and then go back to
- * converting the original string after finishing with the fallback.
- *
- * The variables save_p and save_inbytes store the input state
- * for the original string while we are converting the fallback
- */
- p = utf8;
-
- outbuf_size = len + NUL_TERMINATOR_LENGTH;
- outbytes_remaining = outbuf_size - NUL_TERMINATOR_LENGTH;
- outp = dest = g_malloc (outbuf_size);
-
- while (!done && !have_error)
- {
- gsize inbytes_tmp = inbytes_remaining;
- err = g_iconv (cd, (char **)&p, &inbytes_tmp, &outp, &outbytes_remaining);
- inbytes_remaining = inbytes_tmp;
-
- if (err == (gsize) -1)
- {
- switch (errno)
- {
- case EINVAL:
- g_assert_not_reached();
- break;
- case E2BIG:
- {
- gsize used = outp - dest;
-
- outbuf_size *= 2;
- dest = g_realloc (dest, outbuf_size);
-
- outp = dest + used;
- outbytes_remaining = outbuf_size - used - NUL_TERMINATOR_LENGTH;
-
- break;
- }
- case EILSEQ:
- if (save_p)
- {
- /* Error converting fallback string - fatal
- */
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Cannot convert fallback '%s' to codeset '%s'"),
- insert_str, to_codeset);
- have_error = TRUE;
- break;
- }
- else if (p)
- {
- if (!fallback)
- {
- gunichar ch = g_utf8_get_char (p);
- insert_str = g_strdup_printf (ch < 0x10000 ? "\\u%04x" : "\\U%08x",
- ch);
- }
- else
- insert_str = fallback;
-
- save_p = g_utf8_next_char (p);
- save_inbytes = inbytes_remaining - (save_p - p);
- p = insert_str;
- inbytes_remaining = strlen (p);
- break;
- }
- /* fall thru if p is NULL */
- default:
- {
- int errsv = errno;
-
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Error during conversion: %s"),
- g_strerror (errsv));
- }
-
- have_error = TRUE;
- break;
- }
- }
- else
- {
- if (save_p)
- {
- if (!fallback)
- g_free ((gchar *)insert_str);
- p = save_p;
- inbytes_remaining = save_inbytes;
- save_p = NULL;
- }
- else if (p)
- {
- /* call g_iconv with NULL inbuf to cleanup shift state */
- p = NULL;
- inbytes_remaining = 0;
- }
- else
- done = TRUE;
- }
- }
-
- /* Cleanup
- */
- memset (outp, 0, NUL_TERMINATOR_LENGTH);
-
- close_converter (cd);
-
- if (bytes_written)
- *bytes_written = outp - dest; /* Doesn't include '\0' */
-
- g_free (utf8);
-
- if (have_error)
- {
- if (save_p && !fallback)
- g_free ((gchar *)insert_str);
- g_free (dest);
- return NULL;
- }
- else
- return dest;
-}
-
-/*
- * g_locale_to_utf8
- *
- *
- */
-
-static gchar *
-strdup_len (const gchar *string,
- gssize len,
- gsize *bytes_written,
- gsize *bytes_read,
- GError **error)
-
-{
- gsize real_len;
-
- if (!g_utf8_validate (string, len, NULL))
- {
- if (bytes_read)
- *bytes_read = 0;
- if (bytes_written)
- *bytes_written = 0;
-
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
- return NULL;
- }
-
- if (len < 0)
- real_len = strlen (string);
- else
- {
- real_len = 0;
-
- while (real_len < len && string[real_len])
- real_len++;
- }
-
- if (bytes_read)
- *bytes_read = real_len;
- if (bytes_written)
- *bytes_written = real_len;
-
- return g_strndup (string, real_len);
-}
-
-/**
- * g_locale_to_utf8:
- * @opsysstring: a string in the encoding of the current locale. On Windows
- * this means the system codepage.
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnoteref linkend="nul-unsafe"/>.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string which is in the encoding used for strings by
- * the C runtime (usually the same as that used by the operating
- * system) in the <link linkend="setlocale">current locale</link> into a
- * UTF-8 string.
- *
- * Return value: The converted string, or %NULL on an error.
- **/
-gchar *
-g_locale_to_utf8 (const gchar *opsysstring,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const char *charset;
-
- if (g_get_charset (&charset))
- return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
- else
- return g_convert (opsysstring, len,
- "UTF-8", charset, bytes_read, bytes_written, error);
-}
-
-/**
- * g_locale_from_utf8:
- * @utf8string: a UTF-8 encoded string
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnoteref linkend="nul-unsafe"/>.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from UTF-8 to the encoding used for strings by
- * the C runtime (usually the same as that used by the operating
- * system) in the <link linkend="setlocale">current locale</link>. On
- * Windows this means the system codepage.
- *
- * Return value: The converted string, or %NULL on an error.
- **/
-gchar *
-g_locale_from_utf8 (const gchar *utf8string,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const gchar *charset;
-
- if (g_get_charset (&charset))
- return strdup_len (utf8string, len, bytes_read, bytes_written, error);
- else
- return g_convert (utf8string, len,
- charset, "UTF-8", bytes_read, bytes_written, error);
-}
-
-#ifndef G_PLATFORM_WIN32
-
-typedef struct _GFilenameCharsetCache GFilenameCharsetCache;
-
-struct _GFilenameCharsetCache {
- gboolean is_utf8;
- gchar *charset;
- gchar **filename_charsets;
-};
-
-static void
-filename_charset_cache_free (gpointer data)
-{
- GFilenameCharsetCache *cache = data;
- g_free (cache->charset);
- g_strfreev (cache->filename_charsets);
- g_free (cache);
-}
-
-/**
- * g_get_filename_charsets:
- * @charsets: return location for the %NULL-terminated list of encoding names
- *
- * Determines the preferred character sets used for filenames.
- * The first character set from the @charsets is the filename encoding, the
- * subsequent character sets are used when trying to generate a displayable
- * representation of a filename, see g_filename_display_name().
- *
- * On Unix, the character sets are determined by consulting the
- * environment variables <envar>G_FILENAME_ENCODING</envar> and
- * <envar>G_BROKEN_FILENAMES</envar>. On Windows, the character set
- * used in the GLib API is always UTF-8 and said environment variables
- * have no effect.
- *
- * <envar>G_FILENAME_ENCODING</envar> may be set to a comma-separated list
- * of character set names. The special token "@locale" is taken to
- * mean the character set for the <link linkend="setlocale">current
- * locale</link>. If <envar>G_FILENAME_ENCODING</envar> is not set, but
- * <envar>G_BROKEN_FILENAMES</envar> is, the character set of the current
- * locale is taken as the filename encoding. If neither environment variable
- * is set, UTF-8 is taken as the filename encoding, but the character
- * set of the current locale is also put in the list of encodings.
- *
- * The returned @charsets belong to GLib and must not be freed.
- *
- * Note that on Unix, regardless of the locale character set or
- * <envar>G_FILENAME_ENCODING</envar> value, the actual file names present
- * on a system might be in any random encoding or just gibberish.
- *
- * Return value: %TRUE if the filename encoding is UTF-8.
- *
- * Since: 2.6
- */
-gboolean
-g_get_filename_charsets (G_CONST_RETURN gchar ***filename_charsets)
-{
- static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
- GFilenameCharsetCache *cache = g_static_private_get (&cache_private);
- const gchar *charset;
-
- if (!cache)
- {
- cache = g_new0 (GFilenameCharsetCache, 1);
- g_static_private_set (&cache_private, cache, filename_charset_cache_free);
- }
-
- g_get_charset (&charset);
-
- if (!(cache->charset && strcmp (cache->charset, charset) == 0))
- {
- const gchar *new_charset;
- gchar *p;
- gint i;
-
- g_free (cache->charset);
- g_strfreev (cache->filename_charsets);
- cache->charset = g_strdup (charset);
-
- p = getenv ("G_FILENAME_ENCODING");
- if (p != NULL && p[0] != '\0')
- {
- cache->filename_charsets = g_strsplit (p, ",", 0);
- cache->is_utf8 = (strcmp (cache->filename_charsets[0], "UTF-8") == 0);
-
- for (i = 0; cache->filename_charsets[i]; i++)
- {
- if (strcmp ("@locale", cache->filename_charsets[i]) == 0)
- {
- g_get_charset (&new_charset);
- g_free (cache->filename_charsets[i]);
- cache->filename_charsets[i] = g_strdup (new_charset);
- }
- }
- }
- else if (getenv ("G_BROKEN_FILENAMES") != NULL)
- {
- cache->filename_charsets = g_new0 (gchar *, 2);
- cache->is_utf8 = g_get_charset (&new_charset);
- cache->filename_charsets[0] = g_strdup (new_charset);
- }
- else
- {
- cache->filename_charsets = g_new0 (gchar *, 3);
- cache->is_utf8 = TRUE;
- cache->filename_charsets[0] = g_strdup ("UTF-8");
- if (!g_get_charset (&new_charset))
- cache->filename_charsets[1] = g_strdup (new_charset);
- }
- }
-
- if (filename_charsets)
- *filename_charsets = (const gchar **)cache->filename_charsets;
-
- return cache->is_utf8;
-}
-
-#else /* G_PLATFORM_WIN32 */
-
-gboolean
-g_get_filename_charsets (G_CONST_RETURN gchar ***filename_charsets)
-{
- static const gchar *charsets[] = {
- "UTF-8",
- NULL
- };
-
-#ifdef G_OS_WIN32
- /* On Windows GLib pretends that the filename charset is UTF-8 */
- if (filename_charsets)
- *filename_charsets = charsets;
-
- return TRUE;
-#else
- gboolean result;
-
- /* Cygwin works like before */
- result = g_get_charset (&(charsets[0]));
-
- if (filename_charsets)
- *filename_charsets = charsets;
-
- return result;
-#endif
-}
-
-#endif /* G_PLATFORM_WIN32 */
-
-static gboolean
-get_filename_charset (const gchar **filename_charset)
-{
- const gchar **charsets;
- gboolean is_utf8;
-
- is_utf8 = g_get_filename_charsets (&charsets);
-
- if (filename_charset)
- *filename_charset = charsets[0];
-
- return is_utf8;
-}
-
-/* This is called from g_thread_init(). It's used to
- * initialize some static data in a threadsafe way.
- */
-void
-_g_convert_thread_init (void)
-{
- const gchar **dummy;
- (void) g_get_filename_charsets (&dummy);
-}
-
-/**
- * g_filename_to_utf8:
- * @opsysstring: a string in the encoding for filenames
- * @len: the length of the string, or -1 if the string is
- * nul-terminated<footnoteref linkend="nul-unsafe"/>.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string which is in the encoding used by GLib for
- * filenames into a UTF-8 string. Note that on Windows GLib uses UTF-8
- * for filenames; on other platforms, this function indirectly depends on
- * the <link linkend="setlocale">current locale</link>.
- *
- * Return value: The converted string, or %NULL on an error.
- **/
-gchar*
-g_filename_to_utf8 (const gchar *opsysstring,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const gchar *charset;
-
- if (get_filename_charset (&charset))
- return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
- else
- return g_convert (opsysstring, len,
- "UTF-8", charset, bytes_read, bytes_written, error);
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-#undef g_filename_to_utf8
-
-/* Binary compatibility version. Not for newly compiled code. Also not needed for
- * 64-bit versions as there should be no old deployed binaries that would use
- * the old versions.
- */
-
-gchar*
-g_filename_to_utf8 (const gchar *opsysstring,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const gchar *charset;
-
- if (g_get_charset (&charset))
- return strdup_len (opsysstring, len, bytes_read, bytes_written, error);
- else
- return g_convert (opsysstring, len,
- "UTF-8", charset, bytes_read, bytes_written, error);
-}
-
-#endif
-
-/**
- * g_filename_from_utf8:
- * @utf8string: a UTF-8 encoded string.
- * @len: the length of the string, or -1 if the string is
- * nul-terminated.
- * @bytes_read: location to store the number of bytes in the
- * input string that were successfully converted, or %NULL.
- * Even if the conversion was successful, this may be
- * less than @len if there were partial characters
- * at the end of the input. If the error
- * #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- * stored will the byte offset after the last valid
- * input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not
- * including the terminating nul).
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from UTF-8 to the encoding GLib uses for
- * filenames. Note that on Windows GLib uses UTF-8 for filenames;
- * on other platforms, this function indirectly depends on the
- * <link linkend="setlocale">current locale</link>.
- *
- * Return value: The converted string, or %NULL on an error.
- **/
-gchar*
-g_filename_from_utf8 (const gchar *utf8string,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const gchar *charset;
-
- if (get_filename_charset (&charset))
- return strdup_len (utf8string, len, bytes_read, bytes_written, error);
- else
- return g_convert (utf8string, len,
- charset, "UTF-8", bytes_read, bytes_written, error);
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-#undef g_filename_from_utf8
-
-/* Binary compatibility version. Not for newly compiled code. */
-
-gchar*
-g_filename_from_utf8 (const gchar *utf8string,
- gssize len,
- gsize *bytes_read,
- gsize *bytes_written,
- GError **error)
-{
- const gchar *charset;
-
- if (g_get_charset (&charset))
- return strdup_len (utf8string, len, bytes_read, bytes_written, error);
- else
- return g_convert (utf8string, len,
- charset, "UTF-8", bytes_read, bytes_written, error);
-}
-
-#endif
-
-/* Test of haystack has the needle prefix, comparing case
- * insensitive. haystack may be UTF-8, but needle must
- * contain only ascii. */
-static gboolean
-has_case_prefix (const gchar *haystack, const gchar *needle)
-{
- const gchar *h, *n;
-
- /* Eat one character at a time. */
- h = haystack;
- n = needle;
-
- while (*n && *h &&
- g_ascii_tolower (*n) == g_ascii_tolower (*h))
- {
- n++;
- h++;
- }
-
- return *n == '\0';
-}
-
-typedef enum {
- UNSAFE_ALL = 0x1, /* Escape all unsafe characters */
- UNSAFE_ALLOW_PLUS = 0x2, /* Allows '+' */
- UNSAFE_PATH = 0x8, /* Allows '/', '&', '=', ':', '@', '+', '$' and ',' */
- UNSAFE_HOST = 0x10, /* Allows '/' and ':' and '@' */
- UNSAFE_SLASHES = 0x20 /* Allows all characters except for '/' and '%' */
-} UnsafeCharacterSet;
-
-static const guchar acceptable[96] = {
- /* A table of the ASCII chars from space (32) to DEL (127) */
- /* ! " # $ % & ' ( ) * + , - . / */
- 0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
- /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
- /* @ A B C D E F G H I J K L M N O */
- 0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
- /* P Q R S T U V W X Y Z [ \ ] ^ _ */
- 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
- /* ` a b c d e f g h i j k l m n o */
- 0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
- /* p q r s t u v w x y z { | } ~ DEL */
- 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20
-};
-
-static const gchar hex[16] = "0123456789ABCDEF";
-
-/* Note: This escape function works on file: URIs, but if you want to
- * escape something else, please read RFC-2396 */
-static gchar *
-g_escape_uri_string (const gchar *string,
- UnsafeCharacterSet mask)
-{
-#define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
-
- const gchar *p;
- gchar *q;
- gchar *result;
- int c;
- gint unacceptable;
- UnsafeCharacterSet use_mask;
-
- g_return_val_if_fail (mask == UNSAFE_ALL
- || mask == UNSAFE_ALLOW_PLUS
- || mask == UNSAFE_PATH
- || mask == UNSAFE_HOST
- || mask == UNSAFE_SLASHES, NULL);
-
- unacceptable = 0;
- use_mask = mask;
- for (p = string; *p != '\0'; p++)
- {
- c = (guchar) *p;
- if (!ACCEPTABLE (c))
- unacceptable++;
- }
-
- result = g_malloc (p - string + unacceptable * 2 + 1);
-
- use_mask = mask;
- for (q = result, p = string; *p != '\0'; p++)
- {
- c = (guchar) *p;
-
- if (!ACCEPTABLE (c))
- {
- *q++ = '%'; /* means hex coming */
- *q++ = hex[c >> 4];
- *q++ = hex[c & 15];
- }
- else
- *q++ = *p;
- }
-
- *q = '\0';
-
- return result;
-}
-
-
-static gchar *
-g_escape_file_uri (const gchar *hostname,
- const gchar *pathname)
-{
- char *escaped_hostname = NULL;
- char *escaped_path;
- char *res;
-
-#ifdef G_OS_WIN32
- char *p, *backslash;
-
- /* Turn backslashes into forward slashes. That's what Netscape
- * does, and they are actually more or less equivalent in Windows.
- */
-
- pathname = g_strdup (pathname);
- p = (char *) pathname;
-
- while ((backslash = strchr (p, '\\')) != NULL)
- {
- *backslash = '/';
- p = backslash + 1;
- }
-#endif
-
- if (hostname && *hostname != '\0')
- {
- escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST);
- }
-
- escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH);
-
- res = g_strconcat ("file://",
- (escaped_hostname) ? escaped_hostname : "",
- (*escaped_path != '/') ? "/" : "",
- escaped_path,
- NULL);
-
-#ifdef G_OS_WIN32
- g_free ((char *) pathname);
-#endif
-
- g_free (escaped_hostname);
- g_free (escaped_path);
-
- return res;
-}
-
-static int
-unescape_character (const char *scanner)
-{
- int first_digit;
- int second_digit;
-
- first_digit = g_ascii_xdigit_value (scanner[0]);
- if (first_digit < 0)
- return -1;
-
- second_digit = g_ascii_xdigit_value (scanner[1]);
- if (second_digit < 0)
- return -1;
-
- return (first_digit << 4) | second_digit;
-}
-
-static gchar *
-g_unescape_uri_string (const char *escaped,
- int len,
- const char *illegal_escaped_characters,
- gboolean ascii_must_not_be_escaped)
-{
- const gchar *in, *in_end;
- gchar *out, *result;
- int c;
-
- if (escaped == NULL)
- return NULL;
-
- if (len < 0)
- len = strlen (escaped);
-
- result = g_malloc (len + 1);
-
- out = result;
- for (in = escaped, in_end = escaped + len; in < in_end; in++)
- {
- c = *in;
-
- if (c == '%')
- {
- /* catch partial escape sequences past the end of the substring */
- if (in + 3 > in_end)
- break;
-
- c = unescape_character (in + 1);
-
- /* catch bad escape sequences and NUL characters */
- if (c <= 0)
- break;
-
- /* catch escaped ASCII */
- if (ascii_must_not_be_escaped && c <= 0x7F)
- break;
-
- /* catch other illegal escaped characters */
- if (strchr (illegal_escaped_characters, c) != NULL)
- break;
-
- in += 2;
- }
-
- *out++ = c;
- }
-
- g_assert (out - result <= len);
- *out = '\0';
-
- if (in != in_end)
- {
- g_free (result);
- return NULL;
- }
-
- return result;
-}
-
-static gboolean
-is_asciialphanum (gunichar c)
-{
- return c <= 0x7F && g_ascii_isalnum (c);
-}
-
-static gboolean
-is_asciialpha (gunichar c)
-{
- return c <= 0x7F && g_ascii_isalpha (c);
-}
-
-/* allows an empty string */
-static gboolean
-hostname_validate (const char *hostname)
-{
- const char *p;
- gunichar c, first_char, last_char;
-
- p = hostname;
- if (*p == '\0')
- return TRUE;
- do
- {
- /* read in a label */
- c = g_utf8_get_char (p);
- p = g_utf8_next_char (p);
- if (!is_asciialphanum (c))
- return FALSE;
- first_char = c;
- do
- {
- last_char = c;
- c = g_utf8_get_char (p);
- p = g_utf8_next_char (p);
- }
- while (is_asciialphanum (c) || c == '-');
- if (last_char == '-')
- return FALSE;
-
- /* if that was the last label, check that it was a toplabel */
- if (c == '\0' || (c == '.' && *p == '\0'))
- return is_asciialpha (first_char);
- }
- while (c == '.');
- return FALSE;
-}
-
-/**
- * g_filename_from_uri:
- * @uri: a uri describing a filename (escaped, encoded in ASCII).
- * @hostname: Location to store hostname for the URI, or %NULL.
- * If there is no hostname in the URI, %NULL will be
- * stored in this location.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts an escaped ASCII-encoded URI to a local filename in the
- * encoding used for filenames.
- *
- * Return value: a newly-allocated string holding the resulting
- * filename, or %NULL on an error.
- **/
-gchar *
-g_filename_from_uri (const gchar *uri,
- gchar **hostname,
- GError **error)
-{
- const char *path_part;
- const char *host_part;
- char *unescaped_hostname;
- char *result;
- char *filename;
- int offs;
-#ifdef G_OS_WIN32
- char *p, *slash;
-#endif
-
- if (hostname)
- *hostname = NULL;
-
- if (!has_case_prefix (uri, "file:/"))
- {
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
- _("The URI '%s' is not an absolute URI using the \"file\" scheme"),
- uri);
- return NULL;
- }
-
- path_part = uri + strlen ("file:");
-
- if (strchr (path_part, '#') != NULL)
- {
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
- _("The local file URI '%s' may not include a '#'"),
- uri);
- return NULL;
- }
-
- if (has_case_prefix (path_part, "///"))
- path_part += 2;
- else if (has_case_prefix (path_part, "//"))
- {
- path_part += 2;
- host_part = path_part;
-
- path_part = strchr (path_part, '/');
-
- if (path_part == NULL)
- {
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
- _("The URI '%s' is invalid"),
- uri);
- return NULL;
- }
-
- unescaped_hostname = g_unescape_uri_string (host_part, path_part - host_part, "", TRUE);
-
- if (unescaped_hostname == NULL ||
- !hostname_validate (unescaped_hostname))
- {
- g_free (unescaped_hostname);
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
- _("The hostname of the URI '%s' is invalid"),
- uri);
- return NULL;
- }
-
- if (hostname)
- *hostname = unescaped_hostname;
- else
- g_free (unescaped_hostname);
- }
-
- filename = g_unescape_uri_string (path_part, -1, "/", FALSE);
-
- if (filename == NULL)
- {
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
- _("The URI '%s' contains invalidly escaped characters"),
- uri);
- return NULL;
- }
-
- offs = 0;
-#ifdef G_OS_WIN32
- /* Drop localhost */
- if (hostname && *hostname != NULL &&
- g_ascii_strcasecmp (*hostname, "localhost") == 0)
- {
- g_free (*hostname);
- *hostname = NULL;
- }
-
- /* Turn slashes into backslashes, because that's the canonical spelling */
- p = filename;
- while ((slash = strchr (p, '/')) != NULL)
- {
- *slash = '\\';
- p = slash + 1;
- }
-
- /* Windows URIs with a drive letter can be like "file://host/c:/foo"
- * or "file://host/c|/foo" (some Netscape versions). In those cases, start
- * the filename from the drive letter.
- */
- if (g_ascii_isalpha (filename[1]))
- {
- if (filename[2] == ':')
- offs = 1;
- else if (filename[2] == '|')
- {
- filename[2] = ':';
- offs = 1;
- }
- }
-#endif
-
- result = g_strdup (filename + offs);
- g_free (filename);
-
- return result;
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-#undef g_filename_from_uri
-
-gchar *
-g_filename_from_uri (const gchar *uri,
- gchar **hostname,
- GError **error)
-{
- gchar *utf8_filename;
- gchar *retval = NULL;
-
- utf8_filename = g_filename_from_uri_utf8 (uri, hostname, error);
- if (utf8_filename)
- {
- retval = g_locale_from_utf8 (utf8_filename, -1, NULL, NULL, error);
- g_free (utf8_filename);
- }
- return retval;
-}
-
-#endif
-
-/**
- * g_filename_to_uri:
- * @filename: an absolute filename specified in the GLib file name encoding,
- * which is the on-disk file name bytes on Unix, and UTF-8 on
- * Windows
- * @hostname: A UTF-8 encoded hostname, or %NULL for none.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError may occur.
- *
- * Converts an absolute filename to an escaped ASCII-encoded URI, with the path
- * component following Section 3.3. of RFC 2396.
- *
- * Return value: a newly-allocated string holding the resulting
- * URI, or %NULL on an error.
- **/
-gchar *
-g_filename_to_uri (const gchar *filename,
- const gchar *hostname,
- GError **error)
-{
- char *escaped_uri;
-
- g_return_val_if_fail (filename != NULL, NULL);
-
- if (!g_path_is_absolute (filename))
- {
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NOT_ABSOLUTE_PATH,
- _("The pathname '%s' is not an absolute path"),
- filename);
- return NULL;
- }
-
- if (hostname &&
- !(g_utf8_validate (hostname, -1, NULL)
- && hostname_validate (hostname)))
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid hostname"));
- return NULL;
- }
-
-#ifdef G_OS_WIN32
- /* Don't use localhost unnecessarily */
- if (hostname && g_ascii_strcasecmp (hostname, "localhost") == 0)
- hostname = NULL;
-#endif
-
- escaped_uri = g_escape_file_uri (hostname, filename);
-
- return escaped_uri;
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-#undef g_filename_to_uri
-
-gchar *
-g_filename_to_uri (const gchar *filename,
- const gchar *hostname,
- GError **error)
-{
- gchar *utf8_filename;
- gchar *retval = NULL;
-
- utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
-
- if (utf8_filename)
- {
- retval = g_filename_to_uri_utf8 (utf8_filename, hostname, error);
- g_free (utf8_filename);
- }
-
- return retval;
-}
-
-#endif
-
-/**
- * g_uri_list_extract_uris:
- * @uri_list: an URI list
- *
- * Splits an URI list conforming to the text/uri-list
- * mime type defined in RFC 2483 into individual URIs,
- * discarding any comments. The URIs are not validated.
- *
- * Returns: a newly allocated %NULL-terminated list of
- * strings holding the individual URIs. The array should
- * be freed with g_strfreev().
- *
- * Since: 2.6
- */
-gchar **
-g_uri_list_extract_uris (const gchar *uri_list)
-{
- GSList *uris, *u;
- const gchar *p, *q;
- gchar **result;
- gint n_uris = 0;
-
- uris = NULL;
-
- p = uri_list;
-
- /* We don't actually try to validate the URI according to RFC
- * 2396, or even check for allowed characters - we just ignore
- * comments and trim whitespace off the ends. We also
- * allow LF delimination as well as the specified CRLF.
- *
- * We do allow comments like specified in RFC 2483.
- */
- while (p)
- {
- if (*p != '#')
- {
- while (g_ascii_isspace (*p))
- p++;
-
- q = p;
- while (*q && (*q != '\n') && (*q != '\r'))
- q++;
-
- if (q > p)
- {
- q--;
- while (q > p && g_ascii_isspace (*q))
- q--;
-
- if (q > p)
- {
- uris = g_slist_prepend (uris, g_strndup (p, q - p + 1));
- n_uris++;
- }
- }
- }
- p = strchr (p, '\n');
- if (p)
- p++;
- }
-
- result = g_new (gchar *, n_uris + 1);
-
- result[n_uris--] = NULL;
- for (u = uris; u; u = u->next)
- result[n_uris--] = u->data;
-
- g_slist_free (uris);
-
- return result;
-}
-
-/**
- * g_filename_display_basename:
- * @filename: an absolute pathname in the GLib file name encoding
- *
- * Returns the display basename for the particular filename, guaranteed
- * to be valid UTF-8. The display name might not be identical to the filename,
- * for instance there might be problems converting it to UTF-8, and some files
- * can be translated in the display.
- *
- * If GLib can not make sense of the encoding of @filename, as a last resort it
- * replaces unknown characters with U+FFFD, the Unicode replacement character.
- * You can search the result for the UTF-8 encoding of this character (which is
- * "\357\277\275" in octal notation) to find out if @filename was in an invalid
- * encoding.
- *
- * You must pass the whole absolute pathname to this functions so that
- * translation of well known locations can be done.
- *
- * This function is preferred over g_filename_display_name() if you know the
- * whole path, as it allows translation.
- *
- * Return value: a newly allocated string containing
- * a rendition of the basename of the filename in valid UTF-8
- *
- * Since: 2.6
- **/
-gchar *
-g_filename_display_basename (const gchar *filename)
-{
- char *basename;
- char *display_name;
-
- g_return_val_if_fail (filename != NULL, NULL);
-
- basename = g_path_get_basename (filename);
- display_name = g_filename_display_name (basename);
- g_free (basename);
- return display_name;
-}
-
-/**
- * g_filename_display_name:
- * @filename: a pathname hopefully in the GLib file name encoding
- *
- * Converts a filename into a valid UTF-8 string. The conversion is
- * not necessarily reversible, so you should keep the original around
- * and use the return value of this function only for display purposes.
- * Unlike g_filename_to_utf8(), the result is guaranteed to be non-%NULL
- * even if the filename actually isn't in the GLib file name encoding.
- *
- * If GLib can not make sense of the encoding of @filename, as a last resort it
- * replaces unknown characters with U+FFFD, the Unicode replacement character.
- * You can search the result for the UTF-8 encoding of this character (which is
- * "\357\277\275" in octal notation) to find out if @filename was in an invalid
- * encoding.
- *
- * If you know the whole pathname of the file you should use
- * g_filename_display_basename(), since that allows location-based
- * translation of filenames.
- *
- * Return value: a newly allocated string containing
- * a rendition of the filename in valid UTF-8
- *
- * Since: 2.6
- **/
-gchar *
-g_filename_display_name (const gchar *filename)
-{
- gint i;
- const gchar **charsets;
- gchar *display_name = NULL;
- gboolean is_utf8;
-
- is_utf8 = g_get_filename_charsets (&charsets);
-
- if (is_utf8)
- {
- if (g_utf8_validate (filename, -1, NULL))
- display_name = g_strdup (filename);
- }
-
- if (!display_name)
- {
- /* Try to convert from the filename charsets to UTF-8.
- * Skip the first charset if it is UTF-8.
- */
- for (i = is_utf8 ? 1 : 0; charsets[i]; i++)
- {
- display_name = g_convert (filename, -1, "UTF-8", charsets[i],
- NULL, NULL, NULL);
-
- if (display_name)
- break;
- }
- }
-
- /* if all conversions failed, we replace invalid UTF-8
- * by a question mark
- */
- if (!display_name)
- display_name = _g_utf8_make_valid (filename);
-
- return display_name;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
- * Copyright (C) 1998 Tim Janik
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe ; except for g_data*_foreach()
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include "gdataset.h"
-
-#include "gdatasetprivate.h"
-#include "ghash.h"
-#include "gquark.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "glib_trace.h"
-
-/**
- * SECTION: datasets
- * @title: Datasets
- * @short_description: associate groups of data elements with
- * particular memory locations
- *
- * Datasets associate groups of data elements with particular memory
- * locations. These are useful if you need to associate data with a
- * structure returned from an external library. Since you cannot modify
- * the structure, you use its location in memory as the key into a
- * dataset, where you can associate any number of data elements with it.
- *
- * There are two forms of most of the dataset functions. The first form
- * uses strings to identify the data elements associated with a
- * location. The second form uses #GQuark identifiers, which are
- * created with a call to g_quark_from_string() or
- * g_quark_from_static_string(). The second form is quicker, since it
- * does not require looking up the string in the hash table of #GQuark
- * identifiers.
- *
- * There is no function to create a dataset. It is automatically
- * created as soon as you add elements to it.
- *
- * To add data elements to a dataset use g_dataset_id_set_data(),
- * g_dataset_id_set_data_full(), g_dataset_set_data() and
- * g_dataset_set_data_full().
- *
- * To get data elements from a dataset use g_dataset_id_get_data() and
- * g_dataset_get_data().
- *
- * To iterate over all data elements in a dataset use
- * g_dataset_foreach() (not thread-safe).
- *
- * To remove data elements from a dataset use
- * g_dataset_id_remove_data() and g_dataset_remove_data().
- *
- * To destroy a dataset, use g_dataset_destroy().
- **/
-
-/**
- * SECTION: datalist
- * @title: Keyed Data Lists
- * @short_description: lists of data elements which are accessible by a
- * string or GQuark identifier
- *
- * Keyed data lists provide lists of arbitrary data elements which can
- * be accessed either with a string or with a #GQuark corresponding to
- * the string.
- *
- * The #GQuark methods are quicker, since the strings have to be
- * converted to #GQuarks anyway.
- *
- * Data lists are used for associating arbitrary data with #GObjects,
- * using g_object_set_data() and related functions.
- *
- * To create a datalist, use g_datalist_init().
- *
- * To add data elements to a datalist use g_datalist_id_set_data(),
- * g_datalist_id_set_data_full(), g_datalist_set_data() and
- * g_datalist_set_data_full().
- *
- * To get data elements from a datalist use g_datalist_id_get_data()
- * and g_datalist_get_data().
- *
- * To iterate over all data elements in a datalist use
- * g_datalist_foreach() (not thread-safe).
- *
- * To remove data elements from a datalist use
- * g_datalist_id_remove_data() and g_datalist_remove_data().
- *
- * To remove all data elements from a datalist, use g_datalist_clear().
- **/
-
-/**
- * GData:
- *
- * The #GData struct is an opaque data structure to represent a <link
- * linkend="glib-Keyed-Data-Lists">Keyed Data List</link>. It should
- * only be accessed via the following functions.
- **/
-
-/**
- * GDestroyNotify:
- * @data: the data element.
- *
- * Specifies the type of function which is called when a data element
- * is destroyed. It is passed the pointer to the data element and
- * should free any memory and resources allocated for it.
- **/
-
-/* --- defines --- */
-#define G_QUARK_BLOCK_SIZE (512)
-
-/* datalist pointer accesses have to be carried out atomically */
-#define G_DATALIST_GET_POINTER(datalist) \
- ((GData*) ((gsize) g_atomic_pointer_get (datalist) & ~(gsize) G_DATALIST_FLAGS_MASK))
-
-#define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START { \
- gpointer _oldv, _newv; \
- do { \
- _oldv = g_atomic_pointer_get (datalist); \
- _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK) | (gsize) pointer); \
- } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv)); \
-} G_STMT_END
-
-/* --- structures --- */
-typedef struct _GDataset GDataset;
-struct _GData
-{
- GData *next;
- GQuark id;
- gpointer data;
- GDestroyNotify destroy_func;
-};
-
-struct _GDataset
-{
- gconstpointer location;
- GData *datalist;
-};
-
-
-/* --- prototypes --- */
-static inline GDataset* g_dataset_lookup (gconstpointer dataset_location);
-static inline void g_datalist_clear_i (GData **datalist);
-static void g_dataset_destroy_internal (GDataset *dataset);
-static inline gpointer g_data_set_internal (GData **datalist,
- GQuark key_id,
- gpointer data,
- GDestroyNotify destroy_func,
- GDataset *dataset);
-static void g_data_initialize (void);
-static inline GQuark g_quark_new (gchar *string);
-
-
-/* --- variables --- */
-G_LOCK_DEFINE_STATIC (g_dataset_global);
-static GHashTable *g_dataset_location_ht = NULL;
-static GDataset *g_dataset_cached = NULL; /* should this be
- threadspecific? */
-G_LOCK_DEFINE_STATIC (g_quark_global);
-static GHashTable *g_quark_ht = NULL;
-static gchar **g_quarks = NULL;
-static GQuark g_quark_seq_id = 0;
-
-/* --- functions --- */
-
-/* HOLDS: g_dataset_global_lock */
-static inline void
-g_datalist_clear_i (GData **datalist)
-{
- register GData *list;
-
- /* unlink *all* items before walking their destructors
- */
- list = G_DATALIST_GET_POINTER (datalist);
- G_DATALIST_SET_POINTER (datalist, NULL);
-
- while (list)
- {
- register GData *prev;
-
- prev = list;
- list = prev->next;
-
- if (prev->destroy_func)
- {
- G_UNLOCK (g_dataset_global);
- prev->destroy_func (prev->data);
- G_LOCK (g_dataset_global);
- }
-
- g_slice_free (GData, prev);
- }
-}
-
-/**
- * g_datalist_clear:
- * @datalist: a datalist.
- *
- * Frees all the data elements of the datalist. The data elements'
- * destroy functions are called if they have been set.
- **/
-void
-g_datalist_clear (GData **datalist)
-{
- g_return_if_fail (datalist != NULL);
-
- G_LOCK (g_dataset_global);
- if (!g_dataset_location_ht)
- g_data_initialize ();
-
- while (G_DATALIST_GET_POINTER (datalist))
- g_datalist_clear_i (datalist);
- G_UNLOCK (g_dataset_global);
-}
-
-/* HOLDS: g_dataset_global_lock */
-static inline GDataset*
-g_dataset_lookup (gconstpointer dataset_location)
-{
- register GDataset *dataset;
-
- if (g_dataset_cached && g_dataset_cached->location == dataset_location)
- return g_dataset_cached;
-
- dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
- if (dataset)
- g_dataset_cached = dataset;
-
- return dataset;
-}
-
-/* HOLDS: g_dataset_global_lock */
-static void
-g_dataset_destroy_internal (GDataset *dataset)
-{
- register gconstpointer dataset_location;
-
- dataset_location = dataset->location;
- while (dataset)
- {
- if (!dataset->datalist)
- {
- if (dataset == g_dataset_cached)
- g_dataset_cached = NULL;
- g_hash_table_remove (g_dataset_location_ht, dataset_location);
- g_slice_free (GDataset, dataset);
- break;
- }
-
- g_datalist_clear_i (&dataset->datalist);
- dataset = g_dataset_lookup (dataset_location);
- }
-}
-
-/**
- * g_dataset_destroy:
- * @dataset_location: the location identifying the dataset.
- *
- * Destroys the dataset, freeing all memory allocated, and calling any
- * destroy functions set for data elements.
- **/
-void
-g_dataset_destroy (gconstpointer dataset_location)
-{
- g_return_if_fail (dataset_location != NULL);
-
- G_LOCK (g_dataset_global);
- if (g_dataset_location_ht)
- {
- register GDataset *dataset;
-
- dataset = g_dataset_lookup (dataset_location);
- if (dataset)
- g_dataset_destroy_internal (dataset);
- }
- G_UNLOCK (g_dataset_global);
-}
-
-/* HOLDS: g_dataset_global_lock */
-static inline gpointer
-g_data_set_internal (GData **datalist,
- GQuark key_id,
- gpointer data,
- GDestroyNotify destroy_func,
- GDataset *dataset)
-{
- register GData *list;
-
- list = G_DATALIST_GET_POINTER (datalist);
- if (!data)
- {
- register GData *prev;
-
- prev = NULL;
- while (list)
- {
- if (list->id == key_id)
- {
- gpointer ret_data = NULL;
-
- if (prev)
- prev->next = list->next;
- else
- {
- G_DATALIST_SET_POINTER (datalist, list->next);
-
- /* the dataset destruction *must* be done
- * prior to invocation of the data destroy function
- */
- if (!list->next && dataset)
- g_dataset_destroy_internal (dataset);
- }
-
- /* the GData struct *must* already be unlinked
- * when invoking the destroy function.
- * we use (data==NULL && destroy_func!=NULL) as
- * a special hint combination to "steal"
- * data without destroy notification
- */
- if (list->destroy_func && !destroy_func)
- {
- G_UNLOCK (g_dataset_global);
- list->destroy_func (list->data);
- G_LOCK (g_dataset_global);
- }
- else
- ret_data = list->data;
-
- g_slice_free (GData, list);
-
- return ret_data;
- }
-
- prev = list;
- list = list->next;
- }
- }
- else
- {
- while (list)
- {
- if (list->id == key_id)
- {
- if (!list->destroy_func)
- {
- list->data = data;
- list->destroy_func = destroy_func;
- }
- else
- {
- register GDestroyNotify dfunc;
- register gpointer ddata;
-
- dfunc = list->destroy_func;
- ddata = list->data;
- list->data = data;
- list->destroy_func = destroy_func;
-
- /* we need to have updated all structures prior to
- * invocation of the destroy function
- */
- G_UNLOCK (g_dataset_global);
- dfunc (ddata);
- G_LOCK (g_dataset_global);
- }
-
- return NULL;
- }
-
- list = list->next;
- }
-
- list = g_slice_new (GData);
- list->next = G_DATALIST_GET_POINTER (datalist);
- list->id = key_id;
- list->data = data;
- list->destroy_func = destroy_func;
- G_DATALIST_SET_POINTER (datalist, list);
- }
-
- return NULL;
-}
-
-/**
- * g_dataset_id_set_data_full:
- * @dataset_location: the location identifying the dataset.
- * @key_id: the #GQuark id to identify the data element.
- * @data: the data element.
- * @destroy_func: the function to call when the data element is
- * removed. This function will be called with the data
- * element and can be used to free any memory allocated
- * for it.
- *
- * Sets the data element associated with the given #GQuark id, and also
- * the function to call when the data element is destroyed. Any
- * previous data with the same key is removed, and its destroy function
- * is called.
- **/
-/**
- * g_dataset_set_data_full:
- * @l: the location identifying the dataset.
- * @k: the string to identify the data element.
- * @d: the data element.
- * @f: the function to call when the data element is removed. This
- * function will be called with the data element and can be used to
- * free any memory allocated for it.
- *
- * Sets the data corresponding to the given string identifier, and the
- * function to call when the data element is destroyed.
- **/
-/**
- * g_dataset_id_set_data:
- * @l: the location identifying the dataset.
- * @k: the #GQuark id to identify the data element.
- * @d: the data element.
- *
- * Sets the data element associated with the given #GQuark id. Any
- * previous data with the same key is removed, and its destroy function
- * is called.
- **/
-/**
- * g_dataset_set_data:
- * @l: the location identifying the dataset.
- * @k: the string to identify the data element.
- * @d: the data element.
- *
- * Sets the data corresponding to the given string identifier.
- **/
-/**
- * g_dataset_id_remove_data:
- * @l: the location identifying the dataset.
- * @k: the #GQuark id identifying the data element.
- *
- * Removes a data element from a dataset. The data element's destroy
- * function is called if it has been set.
- **/
-/**
- * g_dataset_remove_data:
- * @l: the location identifying the dataset.
- * @k: the string identifying the data element.
- *
- * Removes a data element corresponding to a string. Its destroy
- * function is called if it has been set.
- **/
-void
-g_dataset_id_set_data_full (gconstpointer dataset_location,
- GQuark key_id,
- gpointer data,
- GDestroyNotify destroy_func)
-{
- register GDataset *dataset;
-
- g_return_if_fail (dataset_location != NULL);
- if (!data)
- g_return_if_fail (destroy_func == NULL);
- if (!key_id)
- {
- if (data)
- g_return_if_fail (key_id > 0);
- else
- return;
- }
-
- G_LOCK (g_dataset_global);
- if (!g_dataset_location_ht)
- g_data_initialize ();
-
- dataset = g_dataset_lookup (dataset_location);
- if (!dataset)
- {
- dataset = g_slice_new (GDataset);
- dataset->location = dataset_location;
- g_datalist_init (&dataset->datalist);
- g_hash_table_insert (g_dataset_location_ht,
- (gpointer) dataset->location,
- dataset);
- }
-
- g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
- G_UNLOCK (g_dataset_global);
-}
-
-/**
- * g_datalist_id_set_data_full:
- * @datalist: a datalist.
- * @key_id: the #GQuark to identify the data element.
- * @data: the data element or %NULL to remove any previous element
- * corresponding to @key_id.
- * @destroy_func: the function to call when the data element is
- * removed. This function will be called with the data
- * element and can be used to free any memory allocated
- * for it. If @data is %NULL, then @destroy_func must
- * also be %NULL.
- *
- * Sets the data corresponding to the given #GQuark id, and the
- * function to be called when the element is removed from the datalist.
- * Any previous data with the same key is removed, and its destroy
- * function is called.
- **/
-/**
- * g_datalist_set_data_full:
- * @dl: a datalist.
- * @k: the string to identify the data element.
- * @d: the data element, or %NULL to remove any previous element
- * corresponding to @k.
- * @f: the function to call when the data element is removed. This
- * function will be called with the data element and can be used to
- * free any memory allocated for it. If @d is %NULL, then @f must
- * also be %NULL.
- *
- * Sets the data element corresponding to the given string identifier,
- * and the function to be called when the data element is removed.
- **/
-/**
- * g_datalist_id_set_data:
- * @dl: a datalist.
- * @q: the #GQuark to identify the data element.
- * @d: the data element, or %NULL to remove any previous element
- * corresponding to @q.
- *
- * Sets the data corresponding to the given #GQuark id. Any previous
- * data with the same key is removed, and its destroy function is
- * called.
- **/
-/**
- * g_datalist_set_data:
- * @dl: a datalist.
- * @k: the string to identify the data element.
- * @d: the data element, or %NULL to remove any previous element
- * corresponding to @k.
- *
- * Sets the data element corresponding to the given string identifier.
- **/
-/**
- * g_datalist_id_remove_data:
- * @dl: a datalist.
- * @q: the #GQuark identifying the data element.
- *
- * Removes an element, using its #GQuark identifier.
- **/
-/**
- * g_datalist_remove_data:
- * @dl: a datalist.
- * @k: the string identifying the data element.
- *
- * Removes an element using its string identifier. The data element's
- * destroy function is called if it has been set.
- **/
-void
-g_datalist_id_set_data_full (GData **datalist,
- GQuark key_id,
- gpointer data,
- GDestroyNotify destroy_func)
-{
- g_return_if_fail (datalist != NULL);
- if (!data)
- g_return_if_fail (destroy_func == NULL);
- if (!key_id)
- {
- if (data)
- g_return_if_fail (key_id > 0);
- else
- return;
- }
-
- G_LOCK (g_dataset_global);
- if (!g_dataset_location_ht)
- g_data_initialize ();
-
- g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
- G_UNLOCK (g_dataset_global);
-}
-
-/**
- * g_dataset_id_remove_no_notify:
- * @dataset_location: the location identifying the dataset.
- * @key_id: the #GQuark ID identifying the data element.
- * @Returns: the data previously stored at @key_id, or %NULL if none.
- *
- * Removes an element, without calling its destroy notification
- * function.
- **/
-/**
- * g_dataset_remove_no_notify:
- * @l: the location identifying the dataset.
- * @k: the string identifying the data element.
- *
- * Removes an element, without calling its destroy notifier.
- **/
-gpointer
-g_dataset_id_remove_no_notify (gconstpointer dataset_location,
- GQuark key_id)
-{
- gpointer ret_data = NULL;
-
- g_return_val_if_fail (dataset_location != NULL, NULL);
-
- G_LOCK (g_dataset_global);
- if (key_id && g_dataset_location_ht)
- {
- GDataset *dataset;
-
- dataset = g_dataset_lookup (dataset_location);
- if (dataset)
- ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
- }
- G_UNLOCK (g_dataset_global);
-
- return ret_data;
-}
-
-/**
- * g_datalist_id_remove_no_notify:
- * @datalist: a datalist.
- * @key_id: the #GQuark identifying a data element.
- * @Returns: the data previously stored at @key_id, or %NULL if none.
- *
- * Removes an element, without calling its destroy notification
- * function.
- **/
-/**
- * g_datalist_remove_no_notify:
- * @dl: a datalist.
- * @k: the string identifying the data element.
- *
- * Removes an element, without calling its destroy notifier.
- **/
-gpointer
-g_datalist_id_remove_no_notify (GData **datalist,
- GQuark key_id)
-{
- gpointer ret_data = NULL;
-
- g_return_val_if_fail (datalist != NULL, NULL);
-
- G_LOCK (g_dataset_global);
- if (key_id && g_dataset_location_ht)
- ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
- G_UNLOCK (g_dataset_global);
-
- return ret_data;
-}
-
-/**
- * g_dataset_id_get_data:
- * @dataset_location: the location identifying the dataset.
- * @key_id: the #GQuark id to identify the data element.
- * @Returns: the data element corresponding to the #GQuark, or %NULL if
- * it is not found.
- *
- * Gets the data element corresponding to a #GQuark.
- **/
-/**
- * g_dataset_get_data:
- * @l: the location identifying the dataset.
- * @k: the string identifying the data element.
- * @Returns: the data element corresponding to the string, or %NULL if
- * it is not found.
- *
- * Gets the data element corresponding to a string.
- **/
-gpointer
-g_dataset_id_get_data (gconstpointer dataset_location,
- GQuark key_id)
-{
- g_return_val_if_fail (dataset_location != NULL, NULL);
-
- G_LOCK (g_dataset_global);
- if (key_id && g_dataset_location_ht)
- {
- register GDataset *dataset;
-
- dataset = g_dataset_lookup (dataset_location);
- if (dataset)
- {
- register GData *list;
-
- for (list = dataset->datalist; list; list = list->next)
- if (list->id == key_id)
- {
- G_UNLOCK (g_dataset_global);
- return list->data;
- }
- }
- }
- G_UNLOCK (g_dataset_global);
-
- return NULL;
-}
-
-/**
- * g_datalist_id_get_data:
- * @datalist: a datalist.
- * @key_id: the #GQuark identifying a data element.
- * @Returns: the data element, or %NULL if it is not found.
- *
- * Retrieves the data element corresponding to @key_id.
- **/
-/**
- * g_datalist_get_data:
- * @dl: a datalist.
- * @k: the string identifying a data element.
- * @Returns: the data element, or %NULL if it is not found.
- *
- * Gets a data element, using its string identifer. This is slower than
- * g_datalist_id_get_data() because the string is first converted to a
- * #GQuark.
- **/
-gpointer
-g_datalist_id_get_data (GData **datalist,
- GQuark key_id)
-{
- gpointer data = NULL;
- g_return_val_if_fail (datalist != NULL, NULL);
- if (key_id)
- {
- register GData *list;
- G_LOCK (g_dataset_global);
- for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next)
- if (list->id == key_id)
- {
- data = list->data;
- break;
- }
- G_UNLOCK (g_dataset_global);
- }
- return data;
-}
-
-/**
- * GDataForeachFunc:
- * @key_id: the #GQuark id to identifying the data element.
- * @data: the data element.
- * @user_data: user data passed to g_dataset_foreach().
- *
- * Specifies the type of function passed to g_dataset_foreach(). It is
- * called with each #GQuark id and associated data element, together
- * with the @user_data parameter supplied to g_dataset_foreach().
- **/
-
-/**
- * g_dataset_foreach:
- * @dataset_location: the location identifying the dataset.
- * @func: the function to call for each data element.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each data element which is associated
- * with the given location. Note that this function is NOT thread-safe.
- * So unless @datalist can be protected from any modifications during
- * invocation of this function, it should not be called.
- **/
-void
-g_dataset_foreach (gconstpointer dataset_location,
- GDataForeachFunc func,
- gpointer user_data)
-{
- register GDataset *dataset;
-
- g_return_if_fail (dataset_location != NULL);
- g_return_if_fail (func != NULL);
-
- G_LOCK (g_dataset_global);
- if (g_dataset_location_ht)
- {
- dataset = g_dataset_lookup (dataset_location);
- G_UNLOCK (g_dataset_global);
- if (dataset)
- {
- register GData *list, *next;
-
- for (list = dataset->datalist; list; list = next)
- {
- next = list->next;
- func (list->id, list->data, user_data);
- }
- }
- }
- else
- {
- G_UNLOCK (g_dataset_global);
- }
-}
-
-/**
- * g_datalist_foreach:
- * @datalist: a datalist.
- * @func: the function to call for each data element.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each data element of the datalist. The
- * function is called with each data element's #GQuark id and data,
- * together with the given @user_data parameter. Note that this
- * function is NOT thread-safe. So unless @datalist can be protected
- * from any modifications during invocation of this function, it should
- * not be called.
- **/
-void
-g_datalist_foreach (GData **datalist,
- GDataForeachFunc func,
- gpointer user_data)
-{
- register GData *list, *next;
-
- g_return_if_fail (datalist != NULL);
- g_return_if_fail (func != NULL);
-
- for (list = G_DATALIST_GET_POINTER (datalist); list; list = next)
- {
- next = list->next;
- func (list->id, list->data, user_data);
- }
-}
-
-/**
- * g_datalist_init:
- * @datalist: a pointer to a pointer to a datalist.
- *
- * Resets the datalist to %NULL. It does not free any memory or call
- * any destroy functions.
- **/
-void
-g_datalist_init (GData **datalist)
-{
- g_return_if_fail (datalist != NULL);
-
- g_atomic_pointer_set (datalist, NULL);
-}
-
-/**
- * g_datalist_set_flags:
- * @datalist: pointer to the location that holds a list
- * @flags: the flags to turn on. The values of the flags are
- * restricted by %G_DATALIST_FLAGS_MASK (currently
- * 3; giving two possible boolean flags).
- * A value for @flags that doesn't fit within the mask is
- * an error.
- *
- * Turns on flag values for a data list. This function is used
- * to keep a small number of boolean flags in an object with
- * a data list without using any additional space. It is
- * not generally useful except in circumstances where space
- * is very tight. (It is used in the base #GObject type, for
- * example.)
- *
- * Since: 2.8
- **/
-void
-g_datalist_set_flags (GData **datalist,
- guint flags)
-{
- gpointer oldvalue;
- g_return_if_fail (datalist != NULL);
- g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
-
- do
- {
- oldvalue = g_atomic_pointer_get (datalist);
- }
- while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue,
- (gpointer) ((gsize) oldvalue | flags)));
-}
-
-/**
- * g_datalist_unset_flags:
- * @datalist: pointer to the location that holds a list
- * @flags: the flags to turn off. The values of the flags are
- * restricted by %G_DATALIST_FLAGS_MASK (currently
- * 3: giving two possible boolean flags).
- * A value for @flags that doesn't fit within the mask is
- * an error.
- *
- * Turns off flag values for a data list. See g_datalist_unset_flags()
- *
- * Since: 2.8
- **/
-void
-g_datalist_unset_flags (GData **datalist,
- guint flags)
-{
- gpointer oldvalue;
- g_return_if_fail (datalist != NULL);
- g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
-
- do
- {
- oldvalue = g_atomic_pointer_get (datalist);
- }
- while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue,
- (gpointer) ((gsize) oldvalue & ~(gsize) flags)));
-}
-
-/**
- * g_datalist_get_flags:
- * @datalist: pointer to the location that holds a list
- *
- * Gets flags values packed in together with the datalist.
- * See g_datalist_set_flags().
- *
- * Return value: the flags of the datalist
- *
- * Since: 2.8
- **/
-guint
-g_datalist_get_flags (GData **datalist)
-{
- g_return_val_if_fail (datalist != NULL, 0);
-
- return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
-}
-
-/* HOLDS: g_dataset_global_lock */
-static void
-g_data_initialize (void)
-{
- g_return_if_fail (g_dataset_location_ht == NULL);
-
- g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
- g_dataset_cached = NULL;
-}
-
-/**
- * SECTION: quarks
- * @title: Quarks
- * @short_description: a 2-way association between a string and a
- * unique integer identifier
- *
- * Quarks are associations between strings and integer identifiers.
- * Given either the string or the #GQuark identifier it is possible to
- * retrieve the other.
- *
- * Quarks are used for both <link
- * linkend="glib-datasets">Datasets</link> and <link
- * linkend="glib-keyed-data-lists">Keyed Data Lists</link>.
- *
- * To create a new quark from a string, use g_quark_from_string() or
- * g_quark_from_static_string().
- *
- * To find the string corresponding to a given #GQuark, use
- * g_quark_to_string().
- *
- * To find the #GQuark corresponding to a given string, use
- * g_quark_try_string().
- *
- * Another use for the string pool maintained for the quark functions
- * is string interning, using g_intern_string() or
- * g_intern_static_string(). An interned string is a canonical
- * representation for a string. One important advantage of interned
- * strings is that they can be compared for equality by a simple
- * pointer comparision, rather than using strcmp().
- **/
-
-/**
- * GQuark:
- *
- * A GQuark is a non-zero integer which uniquely identifies a
- * particular string. A GQuark value of zero is associated to %NULL.
- **/
-
-/**
- * g_quark_try_string:
- * @string: a string.
- * @Returns: the #GQuark associated with the string, or 0 if @string is
- * %NULL or there is no #GQuark associated with it.
- *
- * Gets the #GQuark associated with the given string, or 0 if string is
- * %NULL or it has no associated #GQuark.
- *
- * If you want the GQuark to be created if it doesn't already exist,
- * use g_quark_from_string() or g_quark_from_static_string().
- **/
-GQuark
-g_quark_try_string (const gchar *string)
-{
- GQuark quark = 0;
-
- if (string == NULL)
- return 0;
-
- G_LOCK (g_quark_global);
- if (g_quark_ht)
- quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
- G_UNLOCK (g_quark_global);
-
- return quark;
-}
-
-#define QUARK_STRING_BLOCK_SIZE (4096 - sizeof (gsize))
-static char *quark_block = NULL;
-static int quark_block_offset = 0;
-
-/* HOLDS: g_quark_global_lock */
-static char *
-quark_strdup(const gchar *string)
-{
- gchar *copy;
- gsize len;
-
- len = strlen (string) + 1;
-
- /* For strings longer than half the block size, fall back
- to strdup so that we fill our blocks at least 50%. */
- if (len > QUARK_STRING_BLOCK_SIZE / 2)
- return g_strdup (string);
-
- if (quark_block == NULL ||
- QUARK_STRING_BLOCK_SIZE - quark_block_offset < len)
- {
- quark_block = g_malloc (QUARK_STRING_BLOCK_SIZE);
- quark_block_offset = 0;
- }
-
- copy = quark_block + quark_block_offset;
- memcpy (copy, string, len);
- quark_block_offset += len;
-
- return copy;
-}
-
-/* HOLDS: g_quark_global_lock */
-static inline GQuark
-g_quark_from_string_internal (const gchar *string,
- gboolean duplicate)
-{
- GQuark quark = 0;
-
- if (g_quark_ht)
- quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
-
- if (!quark)
- {
- quark = g_quark_new (duplicate ? quark_strdup (string) : (gchar *)string);
- TRACE(GLIB_QUARK_NEW(string, quark));
- }
-
- return quark;
-}
-
-/**
- * g_quark_from_string:
- * @string: a string.
- * @Returns: the #GQuark identifying the string, or 0 if @string is
- * %NULL.
- *
- * Gets the #GQuark identifying the given string. If the string does
- * not currently have an associated #GQuark, a new #GQuark is created,
- * using a copy of the string.
- **/
-GQuark
-g_quark_from_string (const gchar *string)
-{
- GQuark quark;
-
- if (!string)
- return 0;
-
- G_LOCK (g_quark_global);
- quark = g_quark_from_string_internal (string, TRUE);
- G_UNLOCK (g_quark_global);
-
- return quark;
-}
-
-/**
- * g_quark_from_static_string:
- * @string: a string.
- * @Returns: the #GQuark identifying the string, or 0 if @string is
- * %NULL.
- *
- * Gets the #GQuark identifying the given (static) string. If the
- * string does not currently have an associated #GQuark, a new #GQuark
- * is created, linked to the given string.
- *
- * Note that this function is identical to g_quark_from_string() except
- * that if a new #GQuark is created the string itself is used rather
- * than a copy. This saves memory, but can only be used if the string
- * will <emphasis>always</emphasis> exist. It can be used with
- * statically allocated strings in the main program, but not with
- * statically allocated memory in dynamically loaded modules, if you
- * expect to ever unload the module again (e.g. do not use this
- * function in GTK+ theme engines).
- **/
-GQuark
-g_quark_from_static_string (const gchar *string)
-{
- GQuark quark;
-
- if (!string)
- return 0;
-
- G_LOCK (g_quark_global);
- quark = g_quark_from_string_internal (string, FALSE);
- G_UNLOCK (g_quark_global);
-
- return quark;
-}
-
-/**
- * g_quark_to_string:
- * @quark: a #GQuark.
- * @Returns: the string associated with the #GQuark.
- *
- * Gets the string associated with the given #GQuark.
- **/
-G_CONST_RETURN gchar*
-g_quark_to_string (GQuark quark)
-{
- gchar* result = NULL;
-
- G_LOCK (g_quark_global);
- if (quark < g_quark_seq_id)
- result = g_quarks[quark];
- G_UNLOCK (g_quark_global);
-
- return result;
-}
-
-/* HOLDS: g_quark_global_lock */
-static inline GQuark
-g_quark_new (gchar *string)
-{
- GQuark quark;
-
- if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
- g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
- if (!g_quark_ht)
- {
- g_assert (g_quark_seq_id == 0);
- g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
- g_quarks[g_quark_seq_id++] = NULL;
- }
-
- quark = g_quark_seq_id++;
- g_quarks[quark] = string;
- g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
-
- return quark;
-}
-
-/**
- * g_intern_string:
- * @string: a string
- *
- * Returns a canonical representation for @string. Interned strings can
- * be compared for equality by comparing the pointers, instead of using strcmp().
- *
- * Returns: a canonical representation for the string
- *
- * Since: 2.10
- */
-G_CONST_RETURN gchar*
-g_intern_string (const gchar *string)
-{
- const gchar *result;
- GQuark quark;
-
- if (!string)
- return NULL;
-
- G_LOCK (g_quark_global);
- quark = g_quark_from_string_internal (string, TRUE);
- result = g_quarks[quark];
- G_UNLOCK (g_quark_global);
-
- return result;
-}
-
-/**
- * g_intern_static_string:
- * @string: a static string
- *
- * Returns a canonical representation for @string. Interned strings can
- * be compared for equality by comparing the pointers, instead of using strcmp().
- * g_intern_static_string() does not copy the string, therefore @string must
- * not be freed or modified.
- *
- * Returns: a canonical representation for the string
- *
- * Since: 2.10
- */
-G_CONST_RETURN gchar*
-g_intern_static_string (const gchar *string)
-{
- GQuark quark;
- const gchar *result;
-
- if (!string)
- return NULL;
-
- G_LOCK (g_quark_global);
- quark = g_quark_from_string_internal (string, FALSE);
- result = g_quarks[quark];
- G_UNLOCK (g_quark_global);
-
- return result;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#define DEBUG_MSG(x) /* */
-#ifdef G_ENABLE_DEBUG
-/* #define DEBUG_MSG(args) g_message args ; */
-#endif
-
-#include <time.h>
-#include <string.h>
-#include <stdlib.h>
-#include <locale.h>
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif
-
-#include "gdate.h"
-
-#include "gconvert.h"
-#include "gmem.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gunicode.h"
-
-#ifdef G_OS_WIN32
-#include "garray.h"
-#endif
-
-GDate*
-g_date_new (void)
-{
- GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
-
- return d;
-}
-
-GDate*
-g_date_new_dmy (GDateDay day,
- GDateMonth m,
- GDateYear y)
-{
- GDate *d;
- g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
-
- d = g_new (GDate, 1);
-
- d->julian = FALSE;
- d->dmy = TRUE;
-
- d->month = m;
- d->day = day;
- d->year = y;
-
- g_assert (g_date_valid (d));
-
- return d;
-}
-
-GDate*
-g_date_new_julian (guint32 j)
-{
- GDate *d;
- g_return_val_if_fail (g_date_valid_julian (j), NULL);
-
- d = g_new (GDate, 1);
-
- d->julian = TRUE;
- d->dmy = FALSE;
-
- d->julian_days = j;
-
- g_assert (g_date_valid (d));
-
- return d;
-}
-
-void
-g_date_free (GDate *d)
-{
- g_return_if_fail (d != NULL);
-
- g_free (d);
-}
-
-gboolean
-g_date_valid (const GDate *d)
-{
- g_return_val_if_fail (d != NULL, FALSE);
-
- return (d->julian || d->dmy);
-}
-
-static const guint8 days_in_months[2][13] =
-{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */
- { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
-};
-
-static const guint16 days_in_year[2][14] =
-{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */
- { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-};
-
-gboolean
-g_date_valid_month (GDateMonth m)
-{
- return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
-}
-
-gboolean
-g_date_valid_year (GDateYear y)
-{
- return ( y > G_DATE_BAD_YEAR );
-}
-
-gboolean
-g_date_valid_day (GDateDay d)
-{
- return ( (d > G_DATE_BAD_DAY) && (d < 32) );
-}
-
-gboolean
-g_date_valid_weekday (GDateWeekday w)
-{
- return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
-}
-
-gboolean
-g_date_valid_julian (guint32 j)
-{
- return (j > G_DATE_BAD_JULIAN);
-}
-
-gboolean
-g_date_valid_dmy (GDateDay d,
- GDateMonth m,
- GDateYear y)
-{
- return ( (m > G_DATE_BAD_MONTH) &&
- (m < 13) &&
- (d > G_DATE_BAD_DAY) &&
- (y > G_DATE_BAD_YEAR) && /* must check before using g_date_is_leap_year */
- (d <= (g_date_is_leap_year (y) ?
- days_in_months[1][m] : days_in_months[0][m])) );
-}
-
-
-/* "Julian days" just means an absolute number of days, where Day 1 ==
- * Jan 1, Year 1
- */
-static void
-g_date_update_julian (const GDate *const_d)
-{
- GDate *d = (GDate *) const_d;
- GDateYear year;
- gint idx;
-
- g_return_if_fail (d != NULL);
- g_return_if_fail (d->dmy);
- g_return_if_fail (!d->julian);
- g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
-
- /* What we actually do is: multiply years * 365 days in the year,
- * add the number of years divided by 4, subtract the number of
- * years divided by 100 and add the number of years divided by 400,
- * which accounts for leap year stuff. Code from Steffen Beyer's
- * DateCalc.
- */
-
- year = d->year - 1; /* we know d->year > 0 since it's valid */
-
- d->julian_days = year * 365U;
- d->julian_days += (year >>= 2); /* divide by 4 and add */
- d->julian_days -= (year /= 25); /* divides original # years by 100 */
- d->julian_days += year >> 2; /* divides by 4, which divides original by 400 */
-
- idx = g_date_is_leap_year (d->year) ? 1 : 0;
-
- d->julian_days += days_in_year[idx][d->month] + d->day;
-
- g_return_if_fail (g_date_valid_julian (d->julian_days));
-
- d->julian = TRUE;
-}
-
-static void
-g_date_update_dmy (const GDate *const_d)
-{
- GDate *d = (GDate *) const_d;
- GDateYear y;
- GDateMonth m;
- GDateDay day;
-
- guint32 A, B, C, D, E, M;
-
- g_return_if_fail (d != NULL);
- g_return_if_fail (d->julian);
- g_return_if_fail (!d->dmy);
- g_return_if_fail (g_date_valid_julian (d->julian_days));
-
- /* Formula taken from the Calendar FAQ; the formula was for the
- * Julian Period which starts on 1 January 4713 BC, so we add
- * 1,721,425 to the number of days before doing the formula.
- *
- * I'm sure this can be simplified for our 1 January 1 AD period
- * start, but I can't figure out how to unpack the formula.
- */
-
- A = d->julian_days + 1721425 + 32045;
- B = ( 4 *(A + 36524) )/ 146097 - 1;
- C = A - (146097 * B)/4;
- D = ( 4 * (C + 365) ) / 1461 - 1;
- E = C - ((1461*D) / 4);
- M = (5 * (E - 1) + 2)/153;
-
- m = M + 3 - (12*(M/10));
- day = E - (153*M + 2)/5;
- y = 100 * B + D - 4800 + (M/10);
-
-#ifdef G_ENABLE_DEBUG
- if (!g_date_valid_dmy (day, m, y))
- g_warning ("\nOOPS julian: %u computed dmy: %u %u %u\n",
- d->julian_days, day, m, y);
-#endif
-
- d->month = m;
- d->day = day;
- d->year = y;
-
- d->dmy = TRUE;
-}
-
-GDateWeekday
-g_date_get_weekday (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
-
- if (!d->julian)
- g_date_update_julian (d);
-
- g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
-
- return ((d->julian_days - 1) % 7) + 1;
-}
-
-GDateMonth
-g_date_get_month (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
-
- return d->month;
-}
-
-GDateYear
-g_date_get_year (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);
-
- return d->year;
-}
-
-GDateDay
-g_date_get_day (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);
-
- return d->day;
-}
-
-guint32
-g_date_get_julian (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
-
- if (!d->julian)
- g_date_update_julian (d);
-
- g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);
-
- return d->julian_days;
-}
-
-guint
-g_date_get_day_of_year (const GDate *d)
-{
- gint idx;
-
- g_return_val_if_fail (g_date_valid (d), 0);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, 0);
-
- idx = g_date_is_leap_year (d->year) ? 1 : 0;
-
- return (days_in_year[idx][d->month] + d->day);
-}
-
-guint
-g_date_get_monday_week_of_year (const GDate *d)
-{
- GDateWeekday wd;
- guint day;
- GDate first;
-
- g_return_val_if_fail (g_date_valid (d), 0);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, 0);
-
- g_date_clear (&first, 1);
-
- g_date_set_dmy (&first, 1, 1, d->year);
-
- wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
- day = g_date_get_day_of_year (d) - 1;
-
- return ((day + wd)/7U + (wd == 0 ? 1 : 0));
-}
-
-guint
-g_date_get_sunday_week_of_year (const GDate *d)
-{
- GDateWeekday wd;
- guint day;
- GDate first;
-
- g_return_val_if_fail (g_date_valid (d), 0);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, 0);
-
- g_date_clear (&first, 1);
-
- g_date_set_dmy (&first, 1, 1, d->year);
-
- wd = g_date_get_weekday (&first);
- if (wd == 7) wd = 0; /* make Sunday day 0 */
- day = g_date_get_day_of_year (d) - 1;
-
- return ((day + wd)/7U + (wd == 0 ? 1 : 0));
-}
-
-/**
- * g_date_get_iso8601_week_of_year:
- * @date: a valid #GDate
- *
- * Returns the week of the year, where weeks are interpreted according
- * to ISO 8601.
- *
- * Returns: ISO 8601 week number of the year.
- *
- * Since: 2.6
- **/
-guint
-g_date_get_iso8601_week_of_year (const GDate *d)
-{
- guint j, d4, L, d1, w;
-
- g_return_val_if_fail (g_date_valid (d), 0);
-
- if (!d->julian)
- g_date_update_julian (d);
-
- g_return_val_if_fail (d->julian, 0);
-
- /* Formula taken from the Calendar FAQ; the formula was for the
- * Julian Period which starts on 1 January 4713 BC, so we add
- * 1,721,425 to the number of days before doing the formula.
- */
- j = d->julian_days + 1721425;
- d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
- L = d4 / 1460;
- d1 = ((d4 - L) % 365) + L;
- w = d1 / 7 + 1;
-
- return w;
-}
-
-gint
-g_date_days_between (const GDate *d1,
- const GDate *d2)
-{
- g_return_val_if_fail (g_date_valid (d1), 0);
- g_return_val_if_fail (g_date_valid (d2), 0);
-
- return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
-}
-
-void
-g_date_clear (GDate *d, guint ndates)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (ndates != 0);
-
- memset (d, 0x0, ndates*sizeof (GDate));
-}
-
-G_LOCK_DEFINE_STATIC (g_date_global);
-
-/* These are for the parser, output to the user should use *
- * g_date_strftime () - this creates more never-freed memory to annoy
- * all those memory debugger users. :-)
- */
-
-static gchar *long_month_names[13] =
-{
- NULL,
-};
-
-static gchar *short_month_names[13] =
-{
- NULL,
-};
-
-/* This tells us if we need to update the parse info */
-static gchar *current_locale = NULL;
-
-/* order of these in the current locale */
-static GDateDMY dmy_order[3] =
-{
- G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
-};
-
-/* Where to chop two-digit years: i.e., for the 1930 default, numbers
- * 29 and below are counted as in the year 2000, numbers 30 and above
- * are counted as in the year 1900.
- */
-
-static const GDateYear twodigit_start_year = 1930;
-
-/* It is impossible to enter a year between 1 AD and 99 AD with this
- * in effect.
- */
-static gboolean using_twodigit_years = FALSE;
-
-/* Adjustment of locale era to AD, non-zero means using locale era
- */
-static gint locale_era_adjust = 0;
-
-struct _GDateParseTokens {
- gint num_ints;
- gint n[3];
- guint month;
-};
-
-typedef struct _GDateParseTokens GDateParseTokens;
-
-#define NUM_LEN 10
-
-/* HOLDS: g_date_global_lock */
-static void
-g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
-{
- gchar num[4][NUM_LEN+1];
- gint i;
- const guchar *s;
-
- /* We count 4, but store 3; so we can give an error
- * if there are 4.
- */
- num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
-
- s = (const guchar *) str;
- pt->num_ints = 0;
- while (*s && pt->num_ints < 4)
- {
-
- i = 0;
- while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
- {
- num[pt->num_ints][i] = *s;
- ++s;
- ++i;
- }
-
- if (i > 0)
- {
- num[pt->num_ints][i] = '\0';
- ++(pt->num_ints);
- }
-
- if (*s == '\0') break;
-
- ++s;
- }
-
- pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
- pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
- pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
-
- pt->month = G_DATE_BAD_MONTH;
-
- if (pt->num_ints < 3)
- {
- gchar *casefold;
- gchar *normalized;
-
- casefold = g_utf8_casefold (str, -1);
- normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
- g_free (casefold);
-
- i = 1;
- while (i < 13)
- {
- if (long_month_names[i] != NULL)
- {
- const gchar *found = strstr (normalized, long_month_names[i]);
-
- if (found != NULL)
- {
- pt->month = i;
- break;
- }
- }
-
- if (short_month_names[i] != NULL)
- {
- const gchar *found = strstr (normalized, short_month_names[i]);
-
- if (found != NULL)
- {
- pt->month = i;
- break;
- }
- }
-
- ++i;
- }
-
- g_free (normalized);
- }
-}
-
-/* HOLDS: g_date_global_lock */
-static void
-g_date_prepare_to_parse (const gchar *str,
- GDateParseTokens *pt)
-{
- const gchar *locale = setlocale (LC_TIME, NULL);
- gboolean recompute_localeinfo = FALSE;
- GDate d;
-
- g_return_if_fail (locale != NULL); /* should not happen */
-
- g_date_clear (&d, 1); /* clear for scratch use */
-
- if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) )
- recompute_localeinfo = TRUE; /* Uh, there used to be a reason for the temporary */
-
- if (recompute_localeinfo)
- {
- int i = 1;
- GDateParseTokens testpt;
- gchar buf[128];
-
- g_free (current_locale); /* still works if current_locale == NULL */
-
- current_locale = g_strdup (locale);
-
- short_month_names[0] = "Error";
- long_month_names[0] = "Error";
-
- while (i < 13)
- {
- gchar *casefold;
-
- g_date_set_dmy (&d, 1, i, 1);
-
- g_return_if_fail (g_date_valid (&d));
-
- g_date_strftime (buf, 127, "%b", &d);
-
- casefold = g_utf8_casefold (buf, -1);
- g_free (short_month_names[i]);
- short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
- g_free (casefold);
-
- g_date_strftime (buf, 127, "%B", &d);
- casefold = g_utf8_casefold (buf, -1);
- g_free (long_month_names[i]);
- long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
- g_free (casefold);
-
- ++i;
- }
-
- /* Determine DMY order */
-
- /* had to pick a random day - don't change this, some strftimes
- * are broken on some days, and this one is good so far. */
- g_date_set_dmy (&d, 4, 7, 1976);
-
- g_date_strftime (buf, 127, "%x", &d);
-
- g_date_fill_parse_tokens (buf, &testpt);
-
- i = 0;
- while (i < testpt.num_ints)
- {
- switch (testpt.n[i])
- {
- case 7:
- dmy_order[i] = G_DATE_MONTH;
- break;
- case 4:
- dmy_order[i] = G_DATE_DAY;
- break;
- case 76:
- using_twodigit_years = TRUE; /* FALL THRU */
- case 1976:
- dmy_order[i] = G_DATE_YEAR;
- break;
- default:
- /* assume locale era */
- locale_era_adjust = 1976 - testpt.n[i];
- dmy_order[i] = G_DATE_YEAR;
- break;
- }
- ++i;
- }
-
-#ifdef G_ENABLE_DEBUG
- DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
- i = 1;
- while (i < 13)
- {
- DEBUG_MSG ((" %s %s", long_month_names[i], short_month_names[i]));
- ++i;
- }
- if (using_twodigit_years)
- {
- DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
- }
- {
- gchar *strings[3];
- i = 0;
- while (i < 3)
- {
- switch (dmy_order[i])
- {
- case G_DATE_MONTH:
- strings[i] = "Month";
- break;
- case G_DATE_YEAR:
- strings[i] = "Year";
- break;
- case G_DATE_DAY:
- strings[i] = "Day";
- break;
- default:
- strings[i] = NULL;
- break;
- }
- ++i;
- }
- DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
- DEBUG_MSG (("**Sample date in this locale: `%s'", buf));
- }
-#endif
- }
-
- g_date_fill_parse_tokens (str, pt);
-}
-
-void
-g_date_set_parse (GDate *d,
- const gchar *str)
-{
- GDateParseTokens pt;
- guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
-
- g_return_if_fail (d != NULL);
-
- /* set invalid */
- g_date_clear (d, 1);
-
- G_LOCK (g_date_global);
-
- g_date_prepare_to_parse (str, &pt);
-
- DEBUG_MSG (("Found %d ints, `%d' `%d' `%d' and written out month %d",
- pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
-
-
- if (pt.num_ints == 4)
- {
- G_UNLOCK (g_date_global);
- return; /* presumably a typo; bail out. */
- }
-
- if (pt.num_ints > 1)
- {
- int i = 0;
- int j = 0;
-
- g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
-
- while (i < pt.num_ints && j < 3)
- {
- switch (dmy_order[j])
- {
- case G_DATE_MONTH:
- {
- if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
- {
- m = pt.month;
- ++j; /* skip months, but don't skip this number */
- continue;
- }
- else
- m = pt.n[i];
- }
- break;
- case G_DATE_DAY:
- {
- if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
- {
- day = 1;
- ++j; /* skip days, since we may have month/year */
- continue;
- }
- day = pt.n[i];
- }
- break;
- case G_DATE_YEAR:
- {
- y = pt.n[i];
-
- if (locale_era_adjust != 0)
- {
- y += locale_era_adjust;
- }
- else if (using_twodigit_years && y < 100)
- {
- guint two = twodigit_start_year % 100;
- guint century = (twodigit_start_year / 100) * 100;
-
- if (y < two)
- century += 100;
-
- y += century;
- }
- }
- break;
- default:
- break;
- }
-
- ++i;
- ++j;
- }
-
-
- if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
- {
- /* Try YYYY MM DD */
- y = pt.n[0];
- m = pt.n[1];
- day = pt.n[2];
-
- if (using_twodigit_years && y < 100)
- y = G_DATE_BAD_YEAR; /* avoids ambiguity */
- }
- else if (pt.num_ints == 2)
- {
- if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
- m = pt.month;
- }
- }
- else if (pt.num_ints == 1)
- {
- if (pt.month != G_DATE_BAD_MONTH)
- {
- /* Month name and year? */
- m = pt.month;
- day = 1;
- y = pt.n[0];
- }
- else
- {
- /* Try yyyymmdd and yymmdd */
-
- m = (pt.n[0]/100) % 100;
- day = pt.n[0] % 100;
- y = pt.n[0]/10000;
-
- /* FIXME move this into a separate function */
- if (using_twodigit_years && y < 100)
- {
- guint two = twodigit_start_year % 100;
- guint century = (twodigit_start_year / 100) * 100;
-
- if (y < two)
- century += 100;
-
- y += century;
- }
- }
- }
-
- /* See if we got anything valid out of all this. */
- /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
- if (y < 8000 && g_date_valid_dmy (day, m, y))
- {
- d->month = m;
- d->day = day;
- d->year = y;
- d->dmy = TRUE;
- }
-#ifdef G_ENABLE_DEBUG
- else
- {
- DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
- }
-#endif
- G_UNLOCK (g_date_global);
-}
-
-/**
- * g_date_set_time_t:
- * @date: a #GDate
- * @timet: <type>time_t</type> value to set
- *
- * Sets the value of a date to the date corresponding to a time
- * specified as a time_t. The time to date conversion is done using
- * the user's current timezone.
- *
- * To set the value of a date to the current day, you could write:
- * |[
- * g_date_set_time_t (date, time (NULL));
- * ]|
- *
- * Since: 2.10
- */
-void
-g_date_set_time_t (GDate *date,
- time_t timet)
-{
- struct tm tm;
-
- g_return_if_fail (date != NULL);
-
-#ifdef HAVE_LOCALTIME_R
- localtime_r (&timet, &tm);
-#else
- {
- struct tm *ptm = localtime (&timet);
-
- if (ptm == NULL)
- {
- /* Happens at least in Microsoft's C library if you pass a
- * negative time_t. Use 2000-01-01 as default date.
- */
-#ifndef G_DISABLE_CHECKS
- g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
-#endif
-
- tm.tm_mon = 0;
- tm.tm_mday = 1;
- tm.tm_year = 100;
- }
- else
- memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
- }
-#endif
-
- date->julian = FALSE;
-
- date->month = tm.tm_mon + 1;
- date->day = tm.tm_mday;
- date->year = tm.tm_year + 1900;
-
- g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
-
- date->dmy = TRUE;
-}
-
-
-/**
- * g_date_set_time:
- * @date: a #GDate.
- * @time_: #GTime value to set.
- *
- * Sets the value of a date from a #GTime value.
- * The time to date conversion is done using the user's current timezone.
- *
- * Deprecated: 2.10: Use g_date_set_time_t() instead.
- */
-void
-g_date_set_time (GDate *date,
- GTime time_)
-{
- g_date_set_time_t (date, (time_t) time_);
-}
-
-/**
- * g_date_set_time_val:
- * @date: a #GDate
- * @timeval: #GTimeVal value to set
- *
- * Sets the value of a date from a #GTimeVal value. Note that the
- * @tv_usec member is ignored, because #GDate can't make use of the
- * additional precision.
- *
- * The time to date conversion is done using the user's current timezone.
- *
- * Since: 2.10
- */
-void
-g_date_set_time_val (GDate *date,
- GTimeVal *timeval)
-{
- g_date_set_time_t (date, (time_t) timeval->tv_sec);
-}
-
-void
-g_date_set_month (GDate *d,
- GDateMonth m)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (g_date_valid_month (m));
-
- if (d->julian && !d->dmy) g_date_update_dmy(d);
- d->julian = FALSE;
-
- d->month = m;
-
- if (g_date_valid_dmy (d->day, d->month, d->year))
- d->dmy = TRUE;
- else
- d->dmy = FALSE;
-}
-
-void
-g_date_set_day (GDate *d,
- GDateDay day)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (g_date_valid_day (day));
-
- if (d->julian && !d->dmy) g_date_update_dmy(d);
- d->julian = FALSE;
-
- d->day = day;
-
- if (g_date_valid_dmy (d->day, d->month, d->year))
- d->dmy = TRUE;
- else
- d->dmy = FALSE;
-}
-
-void
-g_date_set_year (GDate *d,
- GDateYear y)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (g_date_valid_year (y));
-
- if (d->julian && !d->dmy) g_date_update_dmy(d);
- d->julian = FALSE;
-
- d->year = y;
-
- if (g_date_valid_dmy (d->day, d->month, d->year))
- d->dmy = TRUE;
- else
- d->dmy = FALSE;
-}
-
-void
-g_date_set_dmy (GDate *d,
- GDateDay day,
- GDateMonth m,
- GDateYear y)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (g_date_valid_dmy (day, m, y));
-
- d->julian = FALSE;
-
- d->month = m;
- d->day = day;
- d->year = y;
-
- d->dmy = TRUE;
-}
-
-void
-g_date_set_julian (GDate *d,
- guint32 j)
-{
- g_return_if_fail (d != NULL);
- g_return_if_fail (g_date_valid_julian (j));
-
- d->julian_days = j;
- d->julian = TRUE;
- d->dmy = FALSE;
-}
-
-
-gboolean
-g_date_is_first_of_month (const GDate *d)
-{
- g_return_val_if_fail (g_date_valid (d), FALSE);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, FALSE);
-
- if (d->day == 1) return TRUE;
- else return FALSE;
-}
-
-gboolean
-g_date_is_last_of_month (const GDate *d)
-{
- gint idx;
-
- g_return_val_if_fail (g_date_valid (d), FALSE);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_val_if_fail (d->dmy, FALSE);
-
- idx = g_date_is_leap_year (d->year) ? 1 : 0;
-
- if (d->day == days_in_months[idx][d->month]) return TRUE;
- else return FALSE;
-}
-
-void
-g_date_add_days (GDate *d,
- guint ndays)
-{
- g_return_if_fail (g_date_valid (d));
-
- if (!d->julian)
- g_date_update_julian (d);
-
- g_return_if_fail (d->julian);
-
- d->julian_days += ndays;
- d->dmy = FALSE;
-}
-
-void
-g_date_subtract_days (GDate *d,
- guint ndays)
-{
- g_return_if_fail (g_date_valid (d));
-
- if (!d->julian)
- g_date_update_julian (d);
-
- g_return_if_fail (d->julian);
- g_return_if_fail (d->julian_days > ndays);
-
- d->julian_days -= ndays;
- d->dmy = FALSE;
-}
-
-void
-g_date_add_months (GDate *d,
- guint nmonths)
-{
- guint years, months;
- gint idx;
-
- g_return_if_fail (g_date_valid (d));
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_if_fail (d->dmy);
-
- nmonths += d->month - 1;
-
- years = nmonths/12;
- months = nmonths%12;
-
- d->month = months + 1;
- d->year += years;
-
- idx = g_date_is_leap_year (d->year) ? 1 : 0;
-
- if (d->day > days_in_months[idx][d->month])
- d->day = days_in_months[idx][d->month];
-
- d->julian = FALSE;
-
- g_return_if_fail (g_date_valid (d));
-}
-
-void
-g_date_subtract_months (GDate *d,
- guint nmonths)
-{
- guint years, months;
- gint idx;
-
- g_return_if_fail (g_date_valid (d));
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_if_fail (d->dmy);
-
- years = nmonths/12;
- months = nmonths%12;
-
- g_return_if_fail (d->year > years);
-
- d->year -= years;
-
- if (d->month > months) d->month -= months;
- else
- {
- months -= d->month;
- d->month = 12 - months;
- d->year -= 1;
- }
-
- idx = g_date_is_leap_year (d->year) ? 1 : 0;
-
- if (d->day > days_in_months[idx][d->month])
- d->day = days_in_months[idx][d->month];
-
- d->julian = FALSE;
-
- g_return_if_fail (g_date_valid (d));
-}
-
-void
-g_date_add_years (GDate *d,
- guint nyears)
-{
- g_return_if_fail (g_date_valid (d));
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_if_fail (d->dmy);
-
- d->year += nyears;
-
- if (d->month == 2 && d->day == 29)
- {
- if (!g_date_is_leap_year (d->year))
- d->day = 28;
- }
-
- d->julian = FALSE;
-}
-
-void
-g_date_subtract_years (GDate *d,
- guint nyears)
-{
- g_return_if_fail (g_date_valid (d));
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_if_fail (d->dmy);
- g_return_if_fail (d->year > nyears);
-
- d->year -= nyears;
-
- if (d->month == 2 && d->day == 29)
- {
- if (!g_date_is_leap_year (d->year))
- d->day = 28;
- }
-
- d->julian = FALSE;
-}
-
-gboolean
-g_date_is_leap_year (GDateYear year)
-{
- g_return_val_if_fail (g_date_valid_year (year), FALSE);
-
- return ( (((year % 4) == 0) && ((year % 100) != 0)) ||
- (year % 400) == 0 );
-}
-
-guint8
-g_date_get_days_in_month (GDateMonth month,
- GDateYear year)
-{
- gint idx;
-
- g_return_val_if_fail (g_date_valid_year (year), 0);
- g_return_val_if_fail (g_date_valid_month (month), 0);
-
- idx = g_date_is_leap_year (year) ? 1 : 0;
-
- return days_in_months[idx][month];
-}
-
-guint8
-g_date_get_monday_weeks_in_year (GDateYear year)
-{
- GDate d;
-
- g_return_val_if_fail (g_date_valid_year (year), 0);
-
- g_date_clear (&d, 1);
- g_date_set_dmy (&d, 1, 1, year);
- if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
- g_date_set_dmy (&d, 31, 12, year);
- if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
- if (g_date_is_leap_year (year))
- {
- g_date_set_dmy (&d, 2, 1, year);
- if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
- g_date_set_dmy (&d, 30, 12, year);
- if (g_date_get_weekday (&d) == G_DATE_MONDAY) return 53;
- }
- return 52;
-}
-
-guint8
-g_date_get_sunday_weeks_in_year (GDateYear year)
-{
- GDate d;
-
- g_return_val_if_fail (g_date_valid_year (year), 0);
-
- g_date_clear (&d, 1);
- g_date_set_dmy (&d, 1, 1, year);
- if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
- g_date_set_dmy (&d, 31, 12, year);
- if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
- if (g_date_is_leap_year (year))
- {
- g_date_set_dmy (&d, 2, 1, year);
- if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
- g_date_set_dmy (&d, 30, 12, year);
- if (g_date_get_weekday (&d) == G_DATE_SUNDAY) return 53;
- }
- return 52;
-}
-
-gint
-g_date_compare (const GDate *lhs,
- const GDate *rhs)
-{
- g_return_val_if_fail (lhs != NULL, 0);
- g_return_val_if_fail (rhs != NULL, 0);
- g_return_val_if_fail (g_date_valid (lhs), 0);
- g_return_val_if_fail (g_date_valid (rhs), 0);
-
- /* Remember the self-comparison case! I think it works right now. */
-
- while (TRUE)
- {
- if (lhs->julian && rhs->julian)
- {
- if (lhs->julian_days < rhs->julian_days) return -1;
- else if (lhs->julian_days > rhs->julian_days) return 1;
- else return 0;
- }
- else if (lhs->dmy && rhs->dmy)
- {
- if (lhs->year < rhs->year) return -1;
- else if (lhs->year > rhs->year) return 1;
- else
- {
- if (lhs->month < rhs->month) return -1;
- else if (lhs->month > rhs->month) return 1;
- else
- {
- if (lhs->day < rhs->day) return -1;
- else if (lhs->day > rhs->day) return 1;
- else return 0;
- }
-
- }
-
- }
- else
- {
- if (!lhs->julian) g_date_update_julian (lhs);
- if (!rhs->julian) g_date_update_julian (rhs);
- g_return_val_if_fail (lhs->julian, 0);
- g_return_val_if_fail (rhs->julian, 0);
- }
-
- }
- return 0; /* warnings */
-}
-
-
-void
-g_date_to_struct_tm (const GDate *d,
- struct tm *tm)
-{
- GDateWeekday day;
-
- g_return_if_fail (g_date_valid (d));
- g_return_if_fail (tm != NULL);
-
- if (!d->dmy)
- g_date_update_dmy (d);
-
- g_return_if_fail (d->dmy);
-
- /* zero all the irrelevant fields to be sure they're valid */
-
- /* On Linux and maybe other systems, there are weird non-POSIX
- * fields on the end of struct tm that choke strftime if they
- * contain garbage. So we need to 0 the entire struct, not just the
- * fields we know to exist.
- */
-
- memset (tm, 0x0, sizeof (struct tm));
-
- tm->tm_mday = d->day;
- tm->tm_mon = d->month - 1; /* 0-11 goes in tm */
- tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */
-
- day = g_date_get_weekday (d);
- if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */
-
- tm->tm_wday = (int)day;
-
- tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */
- tm->tm_isdst = -1; /* -1 means "information not available" */
-}
-
-void
-g_date_clamp (GDate *date,
- const GDate *min_date,
- const GDate *max_date)
-{
- g_return_if_fail (g_date_valid (date));
-
- if (min_date != NULL)
- g_return_if_fail (g_date_valid (min_date));
-
- if (max_date != NULL)
- g_return_if_fail (g_date_valid (max_date));
-
- if (min_date != NULL && max_date != NULL)
- g_return_if_fail (g_date_compare (min_date, max_date) <= 0);
-
- if (min_date && g_date_compare (date, min_date) < 0)
- *date = *min_date;
-
- if (max_date && g_date_compare (max_date, date) < 0)
- *date = *max_date;
-}
-
-void
-g_date_order (GDate *date1,
- GDate *date2)
-{
- g_return_if_fail (g_date_valid (date1));
- g_return_if_fail (g_date_valid (date2));
-
- if (g_date_compare (date1, date2) > 0)
- {
- GDate tmp = *date1;
- *date1 = *date2;
- *date2 = tmp;
- }
-}
-
-#ifdef G_OS_WIN32
-static gsize
-win32_strftime_helper (const GDate *d,
- const gchar *format,
- const struct tm *tm,
- gchar *s,
- gsize slen)
-{
- SYSTEMTIME systemtime;
- TIME_ZONE_INFORMATION tzinfo;
- LCID lcid;
- int n, k;
- GArray *result;
- const gchar *p;
- gunichar c;
- const wchar_t digits[] = L"0123456789";
- gchar *convbuf;
- glong convlen = 0;
- gsize retval;
-
- systemtime.wYear = tm->tm_year + 1900;
- systemtime.wMonth = tm->tm_mon + 1;
- systemtime.wDayOfWeek = tm->tm_wday;
- systemtime.wDay = tm->tm_mday;
- systemtime.wHour = tm->tm_hour;
- systemtime.wMinute = tm->tm_min;
- systemtime.wSecond = tm->tm_sec;
- systemtime.wMilliseconds = 0;
-
- lcid = GetThreadLocale ();
- result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2));
-
- p = format;
- while (*p)
- {
- c = g_utf8_get_char (p);
- if (c == '%')
- {
- p = g_utf8_next_char (p);
- if (!*p)
- {
- s[0] = '\0';
- g_array_free (result, TRUE);
-
- return 0;
- }
-
- c = g_utf8_get_char (p);
- if (c == 'E' || c == 'O')
- {
- /* Ignore modified conversion specifiers for now. */
- p = g_utf8_next_char (p);
- if (!*p)
- {
- s[0] = '\0';
- g_array_free (result, TRUE);
-
- return 0;
- }
-
- c = g_utf8_get_char (p);
- }
-
- switch (c)
- {
- case 'a':
- if (systemtime.wDayOfWeek == 0)
- k = 6;
- else
- k = systemtime.wDayOfWeek - 1;
- n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0);
- g_array_set_size (result, result->len + n);
- GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- break;
- case 'A':
- if (systemtime.wDayOfWeek == 0)
- k = 6;
- else
- k = systemtime.wDayOfWeek - 1;
- n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0);
- g_array_set_size (result, result->len + n);
- GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- break;
- case 'b':
- case 'h':
- n = GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, NULL, 0);
- g_array_set_size (result, result->len + n);
- GetLocaleInfoW (lcid, LOCALE_SABBREVMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- break;
- case 'B':
- n = GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, NULL, 0);
- g_array_set_size (result, result->len + n);
- GetLocaleInfoW (lcid, LOCALE_SMONTHNAME1+systemtime.wMonth-1, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- break;
- case 'c':
- n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- g_array_append_vals (result, L" ", 1);
- n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- break;
- case 'C':
- g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
- g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1);
- break;
- case 'd':
- g_array_append_vals (result, digits + systemtime.wDay/10, 1);
- g_array_append_vals (result, digits + systemtime.wDay%10, 1);
- break;
- case 'D':
- g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
- g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
- g_array_append_vals (result, L"/", 1);
- g_array_append_vals (result, digits + systemtime.wDay/10, 1);
- g_array_append_vals (result, digits + systemtime.wDay%10, 1);
- g_array_append_vals (result, L"/", 1);
- g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
- g_array_append_vals (result, digits + systemtime.wYear%10, 1);
- break;
- case 'e':
- if (systemtime.wDay >= 10)
- g_array_append_vals (result, digits + systemtime.wDay/10, 1);
- else
- g_array_append_vals (result, L" ", 1);
- g_array_append_vals (result, digits + systemtime.wDay%10, 1);
- break;
-
- /* A GDate has no time fields, so for now we can
- * hardcode all time conversions into zeros (or 12 for
- * %I). The alternative code snippets in the #else
- * branches are here ready to be taken into use when
- * needed by a g_strftime() or g_date_and_time_format()
- * or whatever.
- */
- case 'H':
-#if 1
- g_array_append_vals (result, L"00", 2);
-#else
- g_array_append_vals (result, digits + systemtime.wHour/10, 1);
- g_array_append_vals (result, digits + systemtime.wHour%10, 1);
-#endif
- break;
- case 'I':
-#if 1
- g_array_append_vals (result, L"12", 2);
-#else
- if (systemtime.wHour == 0)
- g_array_append_vals (result, L"12", 2);
- else
- {
- g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
- g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
- }
-#endif
- break;
- case 'j':
- g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1);
- g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1);
- g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1);
- break;
- case 'm':
- g_array_append_vals (result, digits + systemtime.wMonth/10, 1);
- g_array_append_vals (result, digits + systemtime.wMonth%10, 1);
- break;
- case 'M':
-#if 1
- g_array_append_vals (result, L"00", 2);
-#else
- g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
- g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
-#endif
- break;
- case 'n':
- g_array_append_vals (result, L"\n", 1);
- break;
- case 'p':
- n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- break;
- case 'r':
- /* This is a rather odd format. Hard to say what to do.
- * Let's always use the POSIX %I:%M:%S %p
- */
-#if 1
- g_array_append_vals (result, L"12:00:00", 8);
-#else
- if (systemtime.wHour == 0)
- g_array_append_vals (result, L"12", 2);
- else
- {
- g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1);
- g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1);
- }
- g_array_append_vals (result, L":", 1);
- g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
- g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
- g_array_append_vals (result, L":", 1);
- g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
- g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
- g_array_append_vals (result, L" ", 1);
-#endif
- n = GetTimeFormatW (lcid, 0, &systemtime, L"tt", NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetTimeFormatW (lcid, 0, &systemtime, L"tt", ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- break;
- case 'R':
-#if 1
- g_array_append_vals (result, L"00:00", 5);
-#else
- g_array_append_vals (result, digits + systemtime.wHour/10, 1);
- g_array_append_vals (result, digits + systemtime.wHour%10, 1);
- g_array_append_vals (result, L":", 1);
- g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
- g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
-#endif
- break;
- case 'S':
-#if 1
- g_array_append_vals (result, L"00", 2);
-#else
- g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
- g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
-#endif
- break;
- case 't':
- g_array_append_vals (result, L"\t", 1);
- break;
- case 'T':
-#if 1
- g_array_append_vals (result, L"00:00:00", 8);
-#else
- g_array_append_vals (result, digits + systemtime.wHour/10, 1);
- g_array_append_vals (result, digits + systemtime.wHour%10, 1);
- g_array_append_vals (result, L":", 1);
- g_array_append_vals (result, digits + systemtime.wMinute/10, 1);
- g_array_append_vals (result, digits + systemtime.wMinute%10, 1);
- g_array_append_vals (result, L":", 1);
- g_array_append_vals (result, digits + systemtime.wSecond/10, 1);
- g_array_append_vals (result, digits + systemtime.wSecond%10, 1);
-#endif
- break;
- case 'u':
- if (systemtime.wDayOfWeek == 0)
- g_array_append_vals (result, L"7", 1);
- else
- g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
- break;
- case 'U':
- n = g_date_get_sunday_week_of_year (d);
- g_array_append_vals (result, digits + n/10, 1);
- g_array_append_vals (result, digits + n%10, 1);
- break;
- case 'V':
- n = g_date_get_iso8601_week_of_year (d);
- g_array_append_vals (result, digits + n/10, 1);
- g_array_append_vals (result, digits + n%10, 1);
- break;
- case 'w':
- g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1);
- break;
- case 'W':
- n = g_date_get_monday_week_of_year (d);
- g_array_append_vals (result, digits + n/10, 1);
- g_array_append_vals (result, digits + n%10, 1);
- break;
- case 'x':
- n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- break;
- case 'X':
- n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0);
- if (n > 0)
- {
- g_array_set_size (result, result->len + n);
- GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n);
- g_array_set_size (result, result->len - 1);
- }
- break;
- case 'y':
- g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
- g_array_append_vals (result, digits + systemtime.wYear%10, 1);
- break;
- case 'Y':
- g_array_append_vals (result, digits + systemtime.wYear/1000, 1);
- g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1);
- g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1);
- g_array_append_vals (result, digits + systemtime.wYear%10, 1);
- break;
- case 'Z':
- n = GetTimeZoneInformation (&tzinfo);
- if (n == TIME_ZONE_ID_UNKNOWN)
- ;
- else if (n == TIME_ZONE_ID_STANDARD)
- g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName));
- else if (n == TIME_ZONE_ID_DAYLIGHT)
- g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName));
- break;
- case '%':
- g_array_append_vals (result, L"%", 1);
- break;
- }
- }
- else if (c <= 0xFFFF)
- {
- wchar_t wc = c;
- g_array_append_vals (result, &wc, 1);
- }
- else
- {
- glong nwc;
- wchar_t *ws;
-
- ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL);
- g_array_append_vals (result, ws, nwc);
- g_free (ws);
- }
- p = g_utf8_next_char (p);
- }
-
- convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL);
- g_array_free (result, TRUE);
-
- if (!convbuf)
- {
- s[0] = '\0';
- return 0;
- }
-
- if (slen <= convlen)
- {
- /* Ensure only whole characters are copied into the buffer. */
- gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
- g_assert (end != NULL);
- convlen = end - convbuf;
-
- /* Return 0 because the buffer isn't large enough. */
- retval = 0;
- }
- else
- retval = convlen;
-
- memcpy (s, convbuf, convlen);
- s[convlen] = '\0';
- g_free (convbuf);
-
- return retval;
-}
-
-#endif
-
-gsize
-g_date_strftime (gchar *s,
- gsize slen,
- const gchar *format,
- const GDate *d)
-{
- struct tm tm;
-#ifndef G_OS_WIN32
- gsize locale_format_len = 0;
- gchar *locale_format;
- gsize tmplen;
- gchar *tmpbuf;
- gsize tmpbufsize;
- gsize convlen = 0;
- gchar *convbuf;
- GError *error = NULL;
- gsize retval;
-#endif
-
- g_return_val_if_fail (g_date_valid (d), 0);
- g_return_val_if_fail (slen > 0, 0);
- g_return_val_if_fail (format != NULL, 0);
- g_return_val_if_fail (s != NULL, 0);
-
- g_date_to_struct_tm (d, &tm);
-
-#ifdef G_OS_WIN32
- if (!g_utf8_validate (format, -1, NULL))
- {
- s[0] = '\0';
- return 0;
- }
- return win32_strftime_helper (d, format, &tm, s, slen);
-#else
-
- locale_format = g_locale_from_utf8 (format, -1, NULL, &locale_format_len, &error);
-
- if (error)
- {
- g_warning (G_STRLOC "Error converting format to locale encoding: %s\n", error->message);
- g_error_free (error);
-
- s[0] = '\0';
- return 0;
- }
-
- tmpbufsize = MAX (128, locale_format_len * 2);
- while (TRUE)
- {
- tmpbuf = g_malloc (tmpbufsize);
-
- /* Set the first byte to something other than '\0', to be able to
- * recognize whether strftime actually failed or just returned "".
- */
- tmpbuf[0] = '\1';
- tmplen = strftime (tmpbuf, tmpbufsize, locale_format, &tm);
-
- if (tmplen == 0 && tmpbuf[0] != '\0')
- {
- g_free (tmpbuf);
- tmpbufsize *= 2;
-
- if (tmpbufsize > 65536)
- {
- g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up\n");
- g_free (locale_format);
-
- s[0] = '\0';
- return 0;
- }
- }
- else
- break;
- }
- g_free (locale_format);
-
- convbuf = g_locale_to_utf8 (tmpbuf, tmplen, NULL, &convlen, &error);
- g_free (tmpbuf);
-
- if (error)
- {
- g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s\n", error->message);
- g_error_free (error);
-
- s[0] = '\0';
- return 0;
- }
-
- if (slen <= convlen)
- {
- /* Ensure only whole characters are copied into the buffer.
- */
- gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen);
- g_assert (end != NULL);
- convlen = end - convbuf;
-
- /* Return 0 because the buffer isn't large enough.
- */
- retval = 0;
- }
- else
- retval = convlen;
-
- memcpy (s, convbuf, convlen);
- s[convlen] = '\0';
- g_free (convbuf);
-
- return retval;
-#endif
-}
+++ /dev/null
-/* gdatetime.c
- *
- * Copyright (C) 2009-2010 Christian Hergert <chris@dronelabs.com>
- * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
- * Copyright (C) 2010 Emmanuele Bassi <ebassi@linux.intel.com>
- * Copyright © 2010 Codethink Limited
- *
- * 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
- * licence, or (at your option) any later version.
- *
- * This 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 St, Fifth Floor, Boston, MA 02110-1301
- * USA.
- *
- * Authors: Christian Hergert <chris@dronelabs.com>
- * Thiago Santos <thiago.sousa.santos@collabora.co.uk>
- * Emmanuele Bassi <ebassi@linux.intel.com>
- * Ryan Lortie <desrt@desrt.ca>
- */
-
-/* Algorithms within this file are based on the Calendar FAQ by
- * Claus Tondering. It can be found at
- * http://www.tondering.dk/claus/cal/calendar29.txt
- *
- * Copyright and disclaimer
- * ------------------------
- * This document is Copyright (C) 2008 by Claus Tondering.
- * E-mail: claus@tondering.dk. (Please include the word
- * "calendar" in the subject line.)
- * The document may be freely distributed, provided this
- * copyright notice is included and no money is charged for
- * the document.
- *
- * This document is provided "as is". No warranties are made as
- * to its correctness.
- */
-
-/* Prologue {{{1 */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifndef G_OS_WIN32
-#include <sys/time.h>
-#include <time.h>
-#endif /* !G_OS_WIN32 */
-
-#include "gdatetime.h"
-
-#include "gatomic.h"
-#include "gfileutils.h"
-#include "ghash.h"
-#include "gmain.h"
-#include "gmappedfile.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gtimezoneprivate.h"
-
-#include "glibintl.h"
-
-/**
- * SECTION:date-time
- * @title: GDateTime
- * @short_description: A structure representing Date and Time
- * @see_also: #GTimeZone
- *
- * #GDateTime is a structure that combines a Gregorian date and time
- * into a single structure. It provides many conversion and methods to
- * manipulate dates and times. Time precision is provided down to
- * microseconds and the time can range (proleptically) from 0001-01-01
- * 00:00:00 to 9999-12-31 23:59:59.999999. #GDateTime follows POSIX
- * time in the sense that it is oblivious to leap seconds.
- *
- * #GDateTime is an immutable object; once it has been created it cannot
- * be modified further. All modifiers will create a new #GDateTime.
- * Nearly all such functions can fail due to the date or time going out
- * of range, in which case %NULL will be returned.
- *
- * #GDateTime is reference counted: the reference count is increased by calling
- * g_date_time_ref() and decreased by calling g_date_time_unref(). When the
- * reference count drops to 0, the resources allocated by the #GDateTime
- * structure are released.
- *
- * Many parts of the API may produce non-obvious results. As an
- * example, adding two months to January 31st will yield March 31st
- * whereas adding one month and then one month again will yield either
- * March 28th or March 29th. Also note that adding 24 hours is not
- * always the same as adding one day (since days containing daylight
- * savings time transitions are either 23 or 25 hours in length).
- *
- * #GDateTime is available since GLib 2.26.
- */
-
-struct _GDateTime
-{
- /* 1 is 0001-01-01 in Proleptic Gregorian */
- gint32 days;
-
- /* Microsecond timekeeping within Day */
- guint64 usec;
-
- /* TimeZone information */
- GTimeZone *tz;
- gint interval;
-
- volatile gint ref_count;
-};
-
-/* Time conversion {{{1 */
-
-#define UNIX_EPOCH_START 719163
-#define INSTANT_TO_UNIX(instant) \
- ((instant)/USEC_PER_SECOND - UNIX_EPOCH_START * SEC_PER_DAY)
-#define UNIX_TO_INSTANT(unix) \
- (((unix) + UNIX_EPOCH_START * SEC_PER_DAY) * USEC_PER_SECOND)
-
-#define DAYS_IN_4YEARS 1461 /* days in 4 years */
-#define DAYS_IN_100YEARS 36524 /* days in 100 years */
-#define DAYS_IN_400YEARS 146097 /* days in 400 years */
-
-#define USEC_PER_SECOND (G_GINT64_CONSTANT (1000000))
-#define USEC_PER_MINUTE (G_GINT64_CONSTANT (60000000))
-#define USEC_PER_HOUR (G_GINT64_CONSTANT (3600000000))
-#define USEC_PER_MILLISECOND (G_GINT64_CONSTANT (1000))
-#define USEC_PER_DAY (G_GINT64_CONSTANT (86400000000))
-#define SEC_PER_DAY (G_GINT64_CONSTANT (86400))
-
-#define GREGORIAN_LEAP(y) ((((y) % 4) == 0) && (!((((y) % 100) == 0) && (((y) % 400) != 0))))
-#define JULIAN_YEAR(d) ((d)->julian / 365.25)
-#define DAYS_PER_PERIOD (G_GINT64_CONSTANT (2914695))
-
-#define GET_AMPM(d,l) ((g_date_time_get_hour (d) < 12) \
- /* Translators: 'before midday' indicator */ \
- ? (l ? C_("GDateTime", "am") \
- /* Translators: 'before midday' indicator */ \
- : C_("GDateTime", "AM")) \
- /* Translators: 'after midday' indicator */ \
- : (l ? C_("GDateTime", "pm") \
- /* Translators: 'after midday' indicator */ \
- : C_("GDateTime", "PM")))
-
-#define WEEKDAY_ABBR(d) (get_weekday_name_abbr (g_date_time_get_day_of_week (datetime)))
-#define WEEKDAY_FULL(d) (get_weekday_name (g_date_time_get_day_of_week (datetime)))
-
-#define MONTH_ABBR(d) (get_month_name_abbr (g_date_time_get_month (datetime)))
-#define MONTH_FULL(d) (get_month_name (g_date_time_get_month (datetime)))
-
-/* Translators: this is the preferred format for expressing the date */
-#define GET_PREFERRED_DATE(d) (g_date_time_format ((d), C_("GDateTime", "%m/%d/%y")))
-
-/* Translators: this is the preferred format for expressing the time */
-#define GET_PREFERRED_TIME(d) (g_date_time_format ((d), C_("GDateTime", "%H:%M:%S")))
-
-#define SECS_PER_MINUTE (60)
-#define SECS_PER_HOUR (60 * SECS_PER_MINUTE)
-#define SECS_PER_DAY (24 * SECS_PER_HOUR)
-#define SECS_PER_YEAR (365 * SECS_PER_DAY)
-#define SECS_PER_JULIAN (DAYS_PER_PERIOD * SECS_PER_DAY)
-
-static const guint16 days_in_months[2][13] =
-{
- { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
- { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
-};
-
-static const guint16 days_in_year[2][13] =
-{
- { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
- { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
-};
-
-static const gchar *
-get_month_name (gint month)
-{
- switch (month)
- {
- case 1:
- return C_("full month name", "January");
- case 2:
- return C_("full month name", "February");
- case 3:
- return C_("full month name", "March");
- case 4:
- return C_("full month name", "April");
- case 5:
- return C_("full month name", "May");
- case 6:
- return C_("full month name", "June");
- case 7:
- return C_("full month name", "July");
- case 8:
- return C_("full month name", "August");
- case 9:
- return C_("full month name", "September");
- case 10:
- return C_("full month name", "October");
- case 11:
- return C_("full month name", "November");
- case 12:
- return C_("full month name", "December");
-
- default:
- g_warning ("Invalid month number %d", month);
- }
-
- return NULL;
-}
-
-static const gchar *
-get_month_name_abbr (gint month)
-{
- switch (month)
- {
- case 1:
- return C_("abbreviated month name", "Jan");
- case 2:
- return C_("abbreviated month name", "Feb");
- case 3:
- return C_("abbreviated month name", "Mar");
- case 4:
- return C_("abbreviated month name", "Apr");
- case 5:
- return C_("abbreviated month name", "May");
- case 6:
- return C_("abbreviated month name", "Jun");
- case 7:
- return C_("abbreviated month name", "Jul");
- case 8:
- return C_("abbreviated month name", "Aug");
- case 9:
- return C_("abbreviated month name", "Sep");
- case 10:
- return C_("abbreviated month name", "Oct");
- case 11:
- return C_("abbreviated month name", "Nov");
- case 12:
- return C_("abbreviated month name", "Dec");
-
- default:
- g_warning ("Invalid month number %d", month);
- }
-
- return NULL;
-}
-
-static const gchar *
-get_weekday_name (gint day)
-{
- switch (day)
- {
- case 1:
- return C_("full weekday name", "Monday");
- case 2:
- return C_("full weekday name", "Tuesday");
- case 3:
- return C_("full weekday name", "Wednesday");
- case 4:
- return C_("full weekday name", "Thursday");
- case 5:
- return C_("full weekday name", "Friday");
- case 6:
- return C_("full weekday name", "Saturday");
- case 7:
- return C_("full weekday name", "Sunday");
-
- default:
- g_warning ("Invalid week day number %d", day);
- }
-
- return NULL;
-}
-
-static const gchar *
-get_weekday_name_abbr (gint day)
-{
- switch (day)
- {
- case 1:
- return C_("abbreviated weekday name", "Mon");
- case 2:
- return C_("abbreviated weekday name", "Tue");
- case 3:
- return C_("abbreviated weekday name", "Wed");
- case 4:
- return C_("abbreviated weekday name", "Thu");
- case 5:
- return C_("abbreviated weekday name", "Fri");
- case 6:
- return C_("abbreviated weekday name", "Sat");
- case 7:
- return C_("abbreviated weekday name", "Sun");
-
- default:
- g_warning ("Invalid week day number %d", day);
- }
-
- return NULL;
-}
-
-static inline gint
-ymd_to_days (gint year,
- gint month,
- gint day)
-{
- gint64 days;
-
- days = (year - 1) * 365 + ((year - 1) / 4) - ((year - 1) / 100)
- + ((year - 1) / 400);
-
- days += days_in_year[0][month - 1];
- if (GREGORIAN_LEAP (year) && month > 2)
- day++;
-
- days += day;
-
- return days;
-}
-
-static void
-g_date_time_get_week_number (GDateTime *datetime,
- gint *week_number,
- gint *day_of_week,
- gint *day_of_year)
-{
- gint a, b, c, d, e, f, g, n, s, month, day, year;
-
- g_date_time_get_ymd (datetime, &year, &month, &day);
-
- if (month <= 2)
- {
- a = g_date_time_get_year (datetime) - 1;
- b = (a / 4) - (a / 100) + (a / 400);
- c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
- s = b - c;
- e = 0;
- f = day - 1 + (31 * (month - 1));
- }
- else
- {
- a = year;
- b = (a / 4) - (a / 100) + (a / 400);
- c = ((a - 1) / 4) - ((a - 1) / 100) + ((a - 1) / 400);
- s = b - c;
- e = s + 1;
- f = day + (((153 * (month - 3)) + 2) / 5) + 58 + s;
- }
-
- g = (a + b) % 7;
- d = (f + g - e) % 7;
- n = f + 3 - d;
-
- if (week_number)
- {
- if (n < 0)
- *week_number = 53 - ((g - s) / 5);
- else if (n > 364 + s)
- *week_number = 1;
- else
- *week_number = (n / 7) + 1;
- }
-
- if (day_of_week)
- *day_of_week = d + 1;
-
- if (day_of_year)
- *day_of_year = f + 1;
-}
-
-/* Lifecycle {{{1 */
-
-static GDateTime *
-g_date_time_alloc (GTimeZone *tz)
-{
- GDateTime *datetime;
-
- datetime = g_slice_new0 (GDateTime);
- datetime->tz = g_time_zone_ref (tz);
- datetime->ref_count = 1;
-
- return datetime;
-}
-
-/**
- * g_date_time_ref:
- * @datetime: a #GDateTime
- *
- * Atomically increments the reference count of @datetime by one.
- *
- * Return value: the #GDateTime with the reference count increased
- *
- * Since: 2.26
- */
-GDateTime *
-g_date_time_ref (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, NULL);
- g_return_val_if_fail (datetime->ref_count > 0, NULL);
-
- g_atomic_int_inc (&datetime->ref_count);
-
- return datetime;
-}
-
-/**
- * g_date_time_unref:
- * @datetime: a #GDateTime
- *
- * Atomically decrements the reference count of @datetime by one.
- *
- * When the reference count reaches zero, the resources allocated by
- * @datetime are freed
- *
- * Since: 2.26
- */
-void
-g_date_time_unref (GDateTime *datetime)
-{
- g_return_if_fail (datetime != NULL);
- g_return_if_fail (datetime->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&datetime->ref_count))
- {
- g_time_zone_unref (datetime->tz);
- g_slice_free (GDateTime, datetime);
- }
-}
-
-/* Internal state transformers {{{1 */
-/*< internal >
- * g_date_time_to_instant:
- * @datetime: a #GDateTime
- *
- * Convert a @datetime into an instant.
- *
- * An instant is a number that uniquely describes a particular
- * microsecond in time, taking time zone considerations into account.
- * (ie: "03:00 -0400" is the same instant as "02:00 -0500").
- *
- * An instant is always positive but we use a signed return value to
- * avoid troubles with C.
- */
-static gint64
-g_date_time_to_instant (GDateTime *datetime)
-{
- gint64 offset;
-
- offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
- offset *= USEC_PER_SECOND;
-
- return datetime->days * USEC_PER_DAY + datetime->usec - offset;
-}
-
-/*< internal >
- * g_date_time_from_instant:
- * @tz: a #GTimeZone
- * @instant: a instant in time
- *
- * Creates a #GDateTime from a time zone and an instant.
- *
- * This might fail if the time ends up being out of range.
- */
-static GDateTime *
-g_date_time_from_instant (GTimeZone *tz,
- gint64 instant)
-{
- GDateTime *datetime;
- gint64 offset;
-
- if (instant < 0 || instant > 1000000000000000000)
- return NULL;
-
- datetime = g_date_time_alloc (tz);
- datetime->interval = g_time_zone_find_interval (tz,
- G_TIME_TYPE_UNIVERSAL,
- INSTANT_TO_UNIX (instant));
- offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
- offset *= USEC_PER_SECOND;
-
- instant += offset;
-
- datetime->days = instant / USEC_PER_DAY;
- datetime->usec = instant % USEC_PER_DAY;
-
- if (datetime->days < 1 || 3652059 < datetime->days)
- {
- g_date_time_unref (datetime);
- datetime = NULL;
- }
-
- return datetime;
-}
-
-
-/*< internal >
- * g_date_time_deal_with_date_change:
- * @datetime: a #GDateTime
- *
- * This function should be called whenever the date changes by adding
- * days, months or years. It does three things.
- *
- * First, we ensure that the date falls between 0001-01-01 and
- * 9999-12-31 and return %FALSE if it does not.
- *
- * Next we update the ->interval field.
- *
- * Finally, we ensure that the resulting date and time pair exists (by
- * ensuring that our time zone has an interval containing it) and
- * adjusting as required. For example, if we have the time 02:30:00 on
- * March 13 2010 in Toronto and we add 1 day to it, we would end up with
- * 2:30am on March 14th, which doesn't exist. In that case, we bump the
- * time up to 3:00am.
- */
-static gboolean
-g_date_time_deal_with_date_change (GDateTime *datetime)
-{
- GTimeType was_dst;
- gint64 full_time;
- gint64 usec;
-
- if (datetime->days < 1 || datetime->days > 3652059)
- return FALSE;
-
- was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval);
-
- full_time = datetime->days * USEC_PER_DAY + datetime->usec;
-
-
- usec = full_time % USEC_PER_SECOND;
- full_time /= USEC_PER_SECOND;
- full_time -= UNIX_EPOCH_START * SEC_PER_DAY;
-
- datetime->interval = g_time_zone_adjust_time (datetime->tz,
- was_dst,
- &full_time);
- full_time += UNIX_EPOCH_START * SEC_PER_DAY;
- full_time *= USEC_PER_SECOND;
- full_time += usec;
-
- datetime->days = full_time / USEC_PER_DAY;
- datetime->usec = full_time % USEC_PER_DAY;
-
- /* maybe daylight time caused us to shift to a different day,
- * but it definitely didn't push us into a different year */
- return TRUE;
-}
-
-static GDateTime *
-g_date_time_replace_days (GDateTime *datetime,
- gint days)
-{
- GDateTime *new;
-
- new = g_date_time_alloc (datetime->tz);
- new->interval = datetime->interval;
- new->usec = datetime->usec;
- new->days = days;
-
- if (!g_date_time_deal_with_date_change (new))
- {
- g_date_time_unref (new);
- new = NULL;
- }
-
- return new;
-}
-
-/* now/unix/timeval Constructors {{{1 */
-
-/*< internal >
- * g_date_time_new_from_timeval:
- * @tz: a #GTimeZone
- * @tv: a #GTimeVal
- *
- * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
- * given time zone @tz.
- *
- * The time contained in a #GTimeVal is always stored in the form of
- * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
- * given time zone.
- *
- * This call can fail (returning %NULL) if @tv represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-static GDateTime *
-g_date_time_new_from_timeval (GTimeZone *tz,
- const GTimeVal *tv)
-{
- return g_date_time_from_instant (tz, tv->tv_usec +
- UNIX_TO_INSTANT (tv->tv_sec));
-}
-
-/*< internal >
- * g_date_time_new_from_unix:
- * @tz: a #GTimeZone
- * @t: the Unix time
- *
- * Creates a #GDateTime corresponding to the given Unix time @t in the
- * given time zone @tz.
- *
- * Unix time is the number of seconds that have elapsed since 1970-01-01
- * 00:00:00 UTC, regardless of the time zone given.
- *
- * This call can fail (returning %NULL) if @t represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-static GDateTime *
-g_date_time_new_from_unix (GTimeZone *tz,
- gint64 secs)
-{
- return g_date_time_from_instant (tz, UNIX_TO_INSTANT (secs));
-}
-
-/**
- * g_date_time_new_now:
- * @tz: a #GTimeZone
- *
- * Creates a #GDateTime corresponding to this exact instant in the given
- * time zone @tz. The time is as accurate as the system allows, to a
- * maximum accuracy of 1 microsecond.
- *
- * This function will always succeed unless the system clock is set to
- * truly insane values (or unless GLib is still being used after the
- * year 9999).
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_now (GTimeZone *tz)
-{
- GTimeVal tv;
-
- g_get_current_time (&tv);
-
- return g_date_time_new_from_timeval (tz, &tv);
-}
-
-/**
- * g_date_time_new_now_local:
- *
- * Creates a #GDateTime corresponding to this exact instant in the local
- * time zone.
- *
- * This is equivalent to calling g_date_time_new_now() with the time
- * zone returned by g_time_zone_new_local().
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_now_local (void)
-{
- GDateTime *datetime;
- GTimeZone *local;
-
- local = g_time_zone_new_local ();
- datetime = g_date_time_new_now (local);
- g_time_zone_unref (local);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_now_utc:
- *
- * Creates a #GDateTime corresponding to this exact instant in UTC.
- *
- * This is equivalent to calling g_date_time_new_now() with the time
- * zone returned by g_time_zone_new_utc().
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_now_utc (void)
-{
- GDateTime *datetime;
- GTimeZone *utc;
-
- utc = g_time_zone_new_utc ();
- datetime = g_date_time_new_now (utc);
- g_time_zone_unref (utc);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_from_unix_local:
- * @t: the Unix time
- *
- * Creates a #GDateTime corresponding to the given Unix time @t in the
- * local time zone.
- *
- * Unix time is the number of seconds that have elapsed since 1970-01-01
- * 00:00:00 UTC, regardless of the local time offset.
- *
- * This call can fail (returning %NULL) if @t represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_from_unix_local (gint64 t)
-{
- GDateTime *datetime;
- GTimeZone *local;
-
- local = g_time_zone_new_local ();
- datetime = g_date_time_new_from_unix (local, t);
- g_time_zone_unref (local);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_from_unix_utc:
- * @t: the Unix time
- *
- * Creates a #GDateTime corresponding to the given Unix time @t in UTC.
- *
- * Unix time is the number of seconds that have elapsed since 1970-01-01
- * 00:00:00 UTC.
- *
- * This call can fail (returning %NULL) if @t represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_from_unix_utc (gint64 t)
-{
- GDateTime *datetime;
- GTimeZone *utc;
-
- utc = g_time_zone_new_utc ();
- datetime = g_date_time_new_from_unix (utc, t);
- g_time_zone_unref (utc);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_from_timeval_local:
- * @tv: a #GTimeVal
- *
- * Creates a #GDateTime corresponding to the given #GTimeVal @tv in the
- * local time zone.
- *
- * The time contained in a #GTimeVal is always stored in the form of
- * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the
- * local time offset.
- *
- * This call can fail (returning %NULL) if @tv represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_from_timeval_local (const GTimeVal *tv)
-{
- GDateTime *datetime;
- GTimeZone *local;
-
- local = g_time_zone_new_local ();
- datetime = g_date_time_new_from_timeval (local, tv);
- g_time_zone_unref (local);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_from_timeval_utc:
- * @tv: a #GTimeVal
- *
- * Creates a #GDateTime corresponding to the given #GTimeVal @tv in UTC.
- *
- * The time contained in a #GTimeVal is always stored in the form of
- * seconds elapsed since 1970-01-01 00:00:00 UTC.
- *
- * This call can fail (returning %NULL) if @tv represents a time outside
- * of the supported range of #GDateTime.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new_from_timeval_utc (const GTimeVal *tv)
-{
- GDateTime *datetime;
- GTimeZone *utc;
-
- utc = g_time_zone_new_utc ();
- datetime = g_date_time_new_from_timeval (utc, tv);
- g_time_zone_unref (utc);
-
- return datetime;
-}
-
-/* full new functions {{{1 */
-
-/**
- * g_date_time_new:
- * @tz: a #GTimeZone
- * @year: the year component of the date
- * @month: the month component of the date
- * @day: the day component of the date
- * @hour: the hour component of the date
- * @minute: the minute component of the date
- * @seconds: the number of seconds past the minute
- *
- * Creates a new #GDateTime corresponding to the given date and time in
- * the time zone @tz.
- *
- * The @year must be between 1 and 9999, @month between 1 and 12 and @day
- * between 1 and 28, 29, 30 or 31 depending on the month and the year.
- *
- * @hour must be between 0 and 23 and @minute must be between 0 and 59.
- *
- * @seconds must be at least 0.0 and must be strictly less than 60.0.
- * It will be rounded down to the nearest microsecond.
- *
- * If the given time is not representable in the given time zone (for
- * example, 02:30 on March 14th 2010 in Toronto, due to daylight savings
- * time) then the time will be rounded up to the nearest existing time
- * (in this case, 03:00). If this matters to you then you should verify
- * the return value for containing the same as the numbers you gave.
- *
- * In the case that the given time is ambiguous in the given time zone
- * (for example, 01:30 on November 7th 2010 in Toronto, due to daylight
- * savings time) then the time falling within standard (ie:
- * non-daylight) time is taken.
- *
- * It not considered a programmer error for the values to this function
- * to be out of range, but in the case that they are, the function will
- * return %NULL.
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_new (GTimeZone *tz,
- gint year,
- gint month,
- gint day,
- gint hour,
- gint minute,
- gdouble seconds)
-{
- GDateTime *datetime;
- gint64 full_time;
-
- datetime = g_date_time_alloc (tz);
- datetime->days = ymd_to_days (year, month, day);
- datetime->usec = (hour * USEC_PER_HOUR)
- + (minute * USEC_PER_MINUTE)
- + (gint64) (seconds * USEC_PER_SECOND);
-
- full_time = SEC_PER_DAY *
- (ymd_to_days (year, month, day) - UNIX_EPOCH_START) +
- SECS_PER_HOUR * hour +
- SECS_PER_MINUTE * minute +
- (int) seconds;
-
- datetime->interval = g_time_zone_adjust_time (datetime->tz,
- G_TIME_TYPE_STANDARD,
- &full_time);
-
- full_time += UNIX_EPOCH_START * SEC_PER_DAY;
- datetime->days = full_time / SEC_PER_DAY;
- datetime->usec = (full_time % SEC_PER_DAY) * USEC_PER_SECOND;
- datetime->usec += ((int) (seconds * USEC_PER_SECOND)) % USEC_PER_SECOND;
-
- return datetime;
-}
-
-/**
- * g_date_time_new_local:
- * @year: the year component of the date
- * @month: the month component of the date
- * @day: the day component of the date
- * @hour: the hour component of the date
- * @minute: the minute component of the date
- * @seconds: the number of seconds past the minute
- *
- * Creates a new #GDateTime corresponding to the given date and time in
- * the local time zone.
- *
- * This call is equivalent to calling g_date_time_new() with the time
- * zone returned by g_time_zone_new_local().
- *
- * Returns: a #GDateTime, or %NULL
- *
- * Since: 2.26.
- **/
-GDateTime *
-g_date_time_new_local (gint year,
- gint month,
- gint day,
- gint hour,
- gint minute,
- gdouble seconds)
-{
- GDateTime *datetime;
- GTimeZone *local;
-
- local = g_time_zone_new_local ();
- datetime = g_date_time_new (local, year, month, day, hour, minute, seconds);
- g_time_zone_unref (local);
-
- return datetime;
-}
-
-/**
- * g_date_time_new_utc:
- * @year: the year component of the date
- * @month: the month component of the date
- * @day: the day component of the date
- * @hour: the hour component of the date
- * @minute: the minute component of the date
- * @seconds: the number of seconds past the minute
- *
- * Creates a new #GDateTime corresponding to the given date and time in
- * UTC.
- *
- * This call is equivalent to calling g_date_time_new() with the time
- * zone returned by g_time_zone_new_utc().
- *
- * Returns: a #GDateTime, or %NULL
- *
- * Since: 2.26.
- **/
-GDateTime *
-g_date_time_new_utc (gint year,
- gint month,
- gint day,
- gint hour,
- gint minute,
- gdouble seconds)
-{
- GDateTime *datetime;
- GTimeZone *utc;
-
- utc = g_time_zone_new_utc ();
- datetime = g_date_time_new (utc, year, month, day, hour, minute, seconds);
- g_time_zone_unref (utc);
-
- return datetime;
-}
-
-/* Adders {{{1 */
-
-/**
- * g_date_time_add:
- * @datetime: a #GDateTime
- * @timespan: a #GTimeSpan
- *
- * Creates a copy of @datetime and adds the specified timespan to the copy.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add (GDateTime *datetime,
- GTimeSpan timespan)
-{
- return g_date_time_from_instant (datetime->tz, timespan +
- g_date_time_to_instant (datetime));
-}
-
-/**
- * g_date_time_add_years:
- * @datetime: a #GDateTime
- * @years: the number of years
- *
- * Creates a copy of @datetime and adds the specified number of years to the
- * copy.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime *
-g_date_time_add_years (GDateTime *datetime,
- gint years)
-{
- gint year, month, day;
-
- g_return_val_if_fail (datetime != NULL, NULL);
-
- if (years < -10000 || years > 10000)
- return NULL;
-
- g_date_time_get_ymd (datetime, &year, &month, &day);
- year += years;
-
- /* only possible issue is if we've entered a year with no February 29
- */
- if (month == 2 && day == 29 && !GREGORIAN_LEAP (year))
- day = 28;
-
- return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
-}
-
-/**
- * g_date_time_add_months:
- * @datetime: a #GDateTime
- * @months: the number of months
- *
- * Creates a copy of @datetime and adds the specified number of months to the
- * copy.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_months (GDateTime *datetime,
- gint months)
-{
- gint year, month, day;
-
- g_return_val_if_fail (datetime != NULL, NULL);
- g_date_time_get_ymd (datetime, &year, &month, &day);
-
- if (months < -120000 || months > 120000)
- return NULL;
-
- year += months / 12;
- month += months % 12;
- if (month < 1)
- {
- month += 12;
- year--;
- }
- else if (month > 12)
- {
- month -= 12;
- year++;
- }
-
- day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
-
- return g_date_time_replace_days (datetime, ymd_to_days (year, month, day));
-}
-
-/**
- * g_date_time_add_weeks:
- * @datetime: a #GDateTime
- * @weeks: the number of weeks
- *
- * Creates a copy of @datetime and adds the specified number of weeks to the
- * copy.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_weeks (GDateTime *datetime,
- gint weeks)
-{
- g_return_val_if_fail (datetime != NULL, NULL);
-
- return g_date_time_add_days (datetime, weeks * 7);
-}
-
-/**
- * g_date_time_add_days:
- * @datetime: a #GDateTime
- * @days: the number of days
- *
- * Creates a copy of @datetime and adds the specified number of days to the
- * copy.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_days (GDateTime *datetime,
- gint days)
-{
- g_return_val_if_fail (datetime != NULL, NULL);
-
- if (days < -3660000 || days > 3660000)
- return NULL;
-
- return g_date_time_replace_days (datetime, datetime->days + days);
-}
-
-/**
- * g_date_time_add_hours:
- * @datetime: a #GDateTime
- * @hours: the number of hours to add
- *
- * Creates a copy of @datetime and adds the specified number of hours
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_hours (GDateTime *datetime,
- gint hours)
-{
- return g_date_time_add (datetime, hours * USEC_PER_HOUR);
-}
-
-/**
- * g_date_time_add_minutes:
- * @datetime: a #GDateTime
- * @minutes: the number of minutes to add
- *
- * Creates a copy of @datetime adding the specified number of minutes.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_minutes (GDateTime *datetime,
- gint minutes)
-{
- return g_date_time_add (datetime, minutes * USEC_PER_MINUTE);
-}
-
-
-/**
- * g_date_time_add_seconds:
- * @datetime: a #GDateTime
- * @seconds: the number of seconds to add
- *
- * Creates a copy of @datetime and adds the specified number of seconds.
- *
- * Return value: the newly created #GDateTime which should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime*
-g_date_time_add_seconds (GDateTime *datetime,
- gdouble seconds)
-{
- return g_date_time_add (datetime, seconds * USEC_PER_SECOND);
-}
-
-/**
- * g_date_time_add_full:
- * @datetime: a #GDateTime
- * @years: the number of years to add
- * @months: the number of months to add
- * @days: the number of days to add
- * @hours: the number of hours to add
- * @minutes: the number of minutes to add
- * @seconds: the number of seconds to add
- *
- * Creates a new #GDateTime adding the specified values to the current date and
- * time in @datetime.
- *
- * Return value: the newly created #GDateTime that should be freed with
- * g_date_time_unref().
- *
- * Since: 2.26
- */
-GDateTime *
-g_date_time_add_full (GDateTime *datetime,
- gint years,
- gint months,
- gint days,
- gint hours,
- gint minutes,
- gdouble seconds)
-{
- gint year, month, day;
- gint64 full_time;
- GDateTime *new;
- gint interval;
-
- g_return_val_if_fail (datetime != NULL, NULL);
- g_date_time_get_ymd (datetime, &year, &month, &day);
-
- months += years * 12;
-
- if (months < -120000 || months > 120000)
- return NULL;
-
- if (days < -3660000 || days > 3660000)
- return NULL;
-
- year += months / 12;
- month += months % 12;
- if (month < 1)
- {
- month += 12;
- year--;
- }
- else if (month > 12)
- {
- month -= 12;
- year++;
- }
-
- day = MIN (day, days_in_months[GREGORIAN_LEAP (year)][month]);
-
- /* full_time is now in unix (local) time */
- full_time = datetime->usec / USEC_PER_SECOND + SEC_PER_DAY *
- (ymd_to_days (year, month, day) + days - UNIX_EPOCH_START);
-
- interval = g_time_zone_adjust_time (datetime->tz,
- g_time_zone_is_dst (datetime->tz,
- datetime->interval),
- &full_time);
-
- /* move to UTC unix time */
- full_time -= g_time_zone_get_offset (datetime->tz, interval);
-
- /* convert back to an instant, add back fractional seconds */
- full_time += UNIX_EPOCH_START * SEC_PER_DAY;
- full_time = full_time * USEC_PER_SECOND +
- datetime->usec % USEC_PER_SECOND;
-
- /* do the actual addition now */
- full_time += (hours * USEC_PER_HOUR) +
- (minutes * USEC_PER_MINUTE) +
- (gint64) (seconds * USEC_PER_SECOND);
-
- /* find the new interval */
- interval = g_time_zone_find_interval (datetime->tz,
- G_TIME_TYPE_UNIVERSAL,
- INSTANT_TO_UNIX (full_time));
-
- /* convert back into local time */
- full_time += USEC_PER_SECOND *
- g_time_zone_get_offset (datetime->tz, interval);
-
- /* split into days and usec of a new datetime */
- new = g_date_time_alloc (datetime->tz);
- new->interval = interval;
- new->days = full_time / USEC_PER_DAY;
- new->usec = full_time % USEC_PER_DAY;
-
- /* XXX validate */
-
- return new;
-}
-
-/* Compare, difference, hash, equal {{{1 */
-/**
- * g_date_time_compare:
- * @dt1: first #GDateTime to compare
- * @dt2: second #GDateTime to compare
- *
- * #GCompareFunc-compatible comparison for #GDateTime<!-- -->'s. Both
- * #GDateTime<-- -->'s must be non-%NULL.
- *
- * Return value: 0 for equal, less than zero if dt1 is less than dt2, greater
- * than zero if dt2 is greator than dt1.
- *
- * Since: 2.26
- */
-gint
-g_date_time_compare (gconstpointer dt1,
- gconstpointer dt2)
-{
- gint64 difference;
-
- difference = g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2);
-
- if (difference < 0)
- return -1;
-
- else if (difference > 0)
- return 1;
-
- else
- return 0;
-}
-
-/**
- * g_date_time_difference:
- * @end: a #GDateTime
- * @begin: a #GDateTime
- *
- * Calculates the difference in time between @end and @begin. The
- * #GTimeSpan that is returned is effectively @end - @begin (ie:
- * positive if the first simparameter is larger).
- *
- * Return value: the difference between the two #GDateTime, as a time
- * span expressed in microseconds.
- *
- * Since: 2.26
- */
-GTimeSpan
-g_date_time_difference (GDateTime *end,
- GDateTime *begin)
-{
- g_return_val_if_fail (begin != NULL, 0);
- g_return_val_if_fail (end != NULL, 0);
-
- return g_date_time_to_instant (end) -
- g_date_time_to_instant (begin);
-}
-
-/**
- * g_date_time_hash:
- * @datetime: a #GDateTime
- *
- * Hashes @datetime into a #guint, suitable for use within #GHashTable.
- *
- * Return value: a #guint containing the hash
- *
- * Since: 2.26
- */
-guint
-g_date_time_hash (gconstpointer datetime)
-{
- return g_date_time_to_instant ((GDateTime *) datetime);
-}
-
-/**
- * g_date_time_equal:
- * @dt1: a #GDateTime
- * @dt2: a #GDateTime
- *
- * Checks to see if @dt1 and @dt2 are equal.
- *
- * Equal here means that they represent the same moment after converting
- * them to the same time zone.
- *
- * Return value: %TRUE if @dt1 and @dt2 are equal
- *
- * Since: 2.26
- */
-gboolean
-g_date_time_equal (gconstpointer dt1,
- gconstpointer dt2)
-{
- return g_date_time_difference ((GDateTime *) dt1, (GDateTime *) dt2) == 0;
-}
-
-/* Year, Month, Day Getters {{{1 */
-/**
- * g_date_time_get_ymd:
- * @datetime: a #GDateTime.
- * @year: (out): the return location for the gregorian year, or %NULL.
- * @month: (out): the return location for the monty of the year, or %NULL.
- * @day: (out): the return location for the day of the month, or %NULL.
- *
- * Retrieves the Gregorian day, month, and year of a given #GDateTime.
- *
- * Since: 2.26
- **/
-void
-g_date_time_get_ymd (GDateTime *datetime,
- gint *year,
- gint *month,
- gint *day)
-{
- gint the_year;
- gint the_month;
- gint the_day;
- gint remaining_days;
- gint y100_cycles;
- gint y4_cycles;
- gint y1_cycles;
- gint preceding;
- gboolean leap;
-
- g_return_if_fail (datetime != NULL);
-
- remaining_days = datetime->days;
-
- /*
- * We need to convert an offset in days to its year/month/day representation.
- * Leap years makes this a little trickier than it should be, so we use
- * 400, 100 and 4 years cycles here to get to the correct year.
- */
-
- /* Our days offset starts sets 0001-01-01 as day 1, if it was day 0 our
- * math would be simpler, so let's do it */
- remaining_days--;
-
- the_year = (remaining_days / DAYS_IN_400YEARS) * 400 + 1;
- remaining_days = remaining_days % DAYS_IN_400YEARS;
-
- y100_cycles = remaining_days / DAYS_IN_100YEARS;
- remaining_days = remaining_days % DAYS_IN_100YEARS;
- the_year += y100_cycles * 100;
-
- y4_cycles = remaining_days / DAYS_IN_4YEARS;
- remaining_days = remaining_days % DAYS_IN_4YEARS;
- the_year += y4_cycles * 4;
-
- y1_cycles = remaining_days / 365;
- the_year += y1_cycles;
- remaining_days = remaining_days % 365;
-
- if (y1_cycles == 4 || y100_cycles == 4) {
- g_assert (remaining_days == 0);
-
- /* special case that indicates that the date is actually one year before,
- * in the 31th of December */
- the_year--;
- the_month = 12;
- the_day = 31;
- goto end;
- }
-
- /* now get the month and the day */
- leap = y1_cycles == 3 && (y4_cycles != 24 || y100_cycles == 3);
-
- g_assert (leap == GREGORIAN_LEAP(the_year));
-
- the_month = (remaining_days + 50) >> 5;
- preceding = (days_in_year[0][the_month - 1] + (the_month > 2 && leap));
- if (preceding > remaining_days)
- {
- /* estimate is too large */
- the_month -= 1;
- preceding -= leap ? days_in_months[1][the_month]
- : days_in_months[0][the_month];
- }
-
- remaining_days -= preceding;
- g_assert(0 <= remaining_days);
-
- the_day = remaining_days + 1;
-
-end:
- if (year)
- *year = the_year;
- if (month)
- *month = the_month;
- if (day)
- *day = the_day;
-}
-
-/**
- * g_date_time_get_year:
- * @datetime: A #GDateTime
- *
- * Retrieves the year represented by @datetime in the Gregorian calendar.
- *
- * Return value: the year represented by @datetime
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_year (GDateTime *datetime)
-{
- gint year;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- g_date_time_get_ymd (datetime, &year, NULL, NULL);
-
- return year;
-}
-
-/**
- * g_date_time_get_month:
- * @datetime: a #GDateTime
- *
- * Retrieves the month of the year represented by @datetime in the Gregorian
- * calendar.
- *
- * Return value: the month represented by @datetime
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_month (GDateTime *datetime)
-{
- gint month;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- g_date_time_get_ymd (datetime, NULL, &month, NULL);
-
- return month;
-}
-
-/**
- * g_date_time_get_day_of_month:
- * @datetime: a #GDateTime
- *
- * Retrieves the day of the month represented by @datetime in the gregorian
- * calendar.
- *
- * Return value: the day of the month
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_day_of_month (GDateTime *datetime)
-{
- gint day_of_year,
- i;
- const guint16 *days;
- guint16 last = 0;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- days = days_in_year[GREGORIAN_LEAP (g_date_time_get_year (datetime)) ? 1 : 0];
- g_date_time_get_week_number (datetime, NULL, NULL, &day_of_year);
-
- for (i = 1; i <= 12; i++)
- {
- if (days [i] >= day_of_year)
- return day_of_year - last;
- last = days [i];
- }
-
- g_warn_if_reached ();
- return 0;
-}
-
-/* Week of year / day of week getters {{{1 */
-/**
- * g_date_time_get_week_numbering_year:
- * @date: a #GDateTime
- *
- * Returns the ISO 8601 week-numbering year in which the week containing
- * @datetime falls.
- *
- * This function, taken together with g_date_time_get_week_of_year() and
- * g_date_time_get_day_of_week() can be used to determine the full ISO
- * week date on which @datetime falls.
- *
- * This is usually equal to the normal Gregorian year (as returned by
- * g_date_time_get_year()), except as detailed below:
- *
- * For Thursday, the week-numbering year is always equal to the usual
- * calendar year. For other days, the number is such that every day
- * within a complete week (Monday to Sunday) is contained within the
- * same week-numbering year.
- *
- * For Monday, Tuesday and Wednesday occuring near the end of the year,
- * this may mean that the week-numbering year is one greater than the
- * calendar year (so that these days have the same week-numbering year
- * as the Thursday occuring early in the next year).
- *
- * For Friday, Saturaday and Sunday occuring near the start of the year,
- * this may mean that the week-numbering year is one less than the
- * calendar year (so that these days have the same week-numbering year
- * as the Thursday occuring late in the previous year).
- *
- * An equivalent description is that the week-numbering year is equal to
- * the calendar year containing the majority of the days in the current
- * week (Monday to Sunday).
- *
- * Note that January 1 0001 in the proleptic Gregorian calendar is a
- * Monday, so this function never returns 0.
- *
- * Returns: the ISO 8601 week-numbering year for @datetime
- *
- * Since: 2.26
- **/
-gint
-g_date_time_get_week_numbering_year (GDateTime *datetime)
-{
- gint year, month, day, weekday;
-
- g_date_time_get_ymd (datetime, &year, &month, &day);
- weekday = g_date_time_get_day_of_week (datetime);
-
- /* January 1, 2, 3 might be in the previous year if they occur after
- * Thursday.
- *
- * Jan 1: Friday, Saturday, Sunday => day 1: weekday 5, 6, 7
- * Jan 2: Saturday, Sunday => day 2: weekday 6, 7
- * Jan 3: Sunday => day 3: weekday 7
- *
- * So we have a special case if (day - weekday) <= -4
- */
- if (month == 1 && (day - weekday) <= -4)
- return year - 1;
-
- /* December 29, 30, 31 might be in the next year if they occur before
- * Thursday.
- *
- * Dec 31: Monday, Tuesday, Wednesday => day 31: weekday 1, 2, 3
- * Dec 30: Monday, Tuesday => day 30: weekday 1, 2
- * Dec 29: Monday => day 29: weekday 1
- *
- * So we have a special case if (day - weekday) >= 28
- */
- else if (month == 12 && (day - weekday) >= 28)
- return year + 1;
-
- else
- return year;
-}
-
-/**
- * g_date_time_get_week_of_year:
- * @datetime: a #GDateTime
- *
- * Returns the ISO 8601 week number for the week containing @datetime.
- * The ISO 8601 week number is the same for every day of the week (from
- * Moday through Sunday). That can produce some unusual results
- * (described below).
- *
- * The first week of the year is week 1. This is the week that contains
- * the first Thursday of the year. Equivalently, this is the first week
- * that has more than 4 of its days falling within the calendar year.
- *
- * The value 0 is never returned by this function. Days contained
- * within a year but occuring before the first ISO 8601 week of that
- * year are considered as being contained in the last week of the
- * previous year. Similarly, the final days of a calendar year may be
- * considered as being part of the first ISO 8601 week of the next year
- * if 4 or more days of that week are contained within the new year.
- *
- * Returns: the ISO 8601 week number for @datetime.
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_week_of_year (GDateTime *datetime)
-{
- gint weeknum;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- g_date_time_get_week_number (datetime, &weeknum, NULL, NULL);
-
- return weeknum;
-}
-
-/**
- * g_date_time_get_day_of_week:
- * @datetime: a #GDateTime
- *
- * Retrieves the ISO 8601 day of the week on which @datetime falls (1 is
- * Monday, 2 is Tuesday... 7 is Sunday).
- *
- * Return value: the day of the week
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_day_of_week (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->days - 1) % 7 + 1;
-}
-
-/* Day of year getter {{{1 */
-/**
- * g_date_time_get_day_of_year:
- * @datetime: a #GDateTime
- *
- * Retrieves the day of the year represented by @datetime in the Gregorian
- * calendar.
- *
- * Return value: the day of the year
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_day_of_year (GDateTime *datetime)
-{
- gint doy = 0;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- g_date_time_get_week_number (datetime, NULL, NULL, &doy);
- return doy;
-}
-
-/* Time component getters {{{1 */
-
-/**
- * g_date_time_get_hour:
- * @datetime: a #GDateTime
- *
- * Retrieves the hour of the day represented by @datetime
- *
- * Return value: the hour of the day
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_hour (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec / USEC_PER_HOUR);
-}
-
-/**
- * g_date_time_get_minute:
- * @datetime: a #GDateTime
- *
- * Retrieves the minute of the hour represented by @datetime
- *
- * Return value: the minute of the hour
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_minute (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_HOUR) / USEC_PER_MINUTE;
-}
-
-/**
- * g_date_time_get_second:
- * @datetime: a #GDateTime
- *
- * Retrieves the second of the minute represented by @datetime
- *
- * Return value: the second represented by @datetime
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_second (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_MINUTE) / USEC_PER_SECOND;
-}
-
-/**
- * g_date_time_get_microsecond:
- * @datetime: a #GDateTime
- *
- * Retrieves the microsecond of the date represented by @datetime
- *
- * Return value: the microsecond of the second
- *
- * Since: 2.26
- */
-gint
-g_date_time_get_microsecond (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_SECOND);
-}
-
-/**
- * g_date_time_get_seconds:
- * @datetime: a #GDateTime
- *
- * Retrieves the number of seconds since the start of the last minute,
- * including the fractional part.
- *
- * Returns: the number of seconds
- *
- * Since: 2.26
- **/
-gdouble
-g_date_time_get_seconds (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, 0);
-
- return (datetime->usec % USEC_PER_MINUTE) / 1000000.0;
-}
-
-/* Exporters {{{1 */
-/**
- * g_date_time_to_unix:
- * @datetime: a #GDateTime
- *
- * Gives the Unix time corresponding to @datetime, rounding down to the
- * nearest second.
- *
- * Unix time is the number of seconds that have elapsed since 1970-01-01
- * 00:00:00 UTC, regardless of the time zone associated with @datetime.
- *
- * Returns: the Unix time corresponding to @datetime
- *
- * Since: 2.26
- **/
-gint64
-g_date_time_to_unix (GDateTime *datetime)
-{
- return INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
-}
-
-/**
- * g_date_time_to_timeval:
- * @datetime: a #GDateTime
- * @tv: a #GTimeVal to modify
- *
- * Stores the instant in time that @datetime represents into @tv.
- *
- * The time contained in a #GTimeVal is always stored in the form of
- * seconds elapsed since 1970-01-01 00:00:00 UTC, regardless of the time
- * zone associated with @datetime.
- *
- * On systems where 'long' is 32bit (ie: all 32bit systems and all
- * Windows systems), a #GTimeVal is incapable of storing the entire
- * range of values that #GDateTime is capable of expressing. On those
- * systems, this function returns %FALSE to indicate that the time is
- * out of range.
- *
- * On systems where 'long' is 64bit, this function never fails.
- *
- * Returns: %TRUE if successful, else %FALSE
- *
- * Since: 2.26
- **/
-gboolean
-g_date_time_to_timeval (GDateTime *datetime,
- GTimeVal *tv)
-{
- tv->tv_sec = INSTANT_TO_UNIX (g_date_time_to_instant (datetime));
- tv->tv_usec = datetime->usec % USEC_PER_SECOND;
-
- return TRUE;
-}
-
-/* Timezone queries {{{1 */
-/**
- * g_date_time_get_utc_offset:
- * @datetime: a #GDateTime
- *
- * Determines the offset to UTC in effect at the time and in the time
- * zone of @datetime.
- *
- * The offset is the number of microseconds that you add to UTC time to
- * arrive at local time for the time zone (ie: negative numbers for time
- * zones west of GMT, positive numbers for east).
- *
- * If @datetime represents UTC time, then the offset is always zero.
- *
- * Returns: the number of microseconds that should be added to UTC to
- * get the local time
- *
- * Since: 2.26
- **/
-GTimeSpan
-g_date_time_get_utc_offset (GDateTime *datetime)
-{
- gint offset;
-
- g_return_val_if_fail (datetime != NULL, 0);
-
- offset = g_time_zone_get_offset (datetime->tz, datetime->interval);
-
- return (gint64) offset * USEC_PER_SECOND;
-}
-
-/**
- * g_date_time_get_timezone_abbreviation:
- * @datetime: a #GDateTime
- *
- * Determines the time zone abbreviation to be used at the time and in
- * the time zone of @datetime.
- *
- * For example, in Toronto this is currently "EST" during the winter
- * months and "EDT" during the summer months when daylight savings
- * time is in effect.
- *
- * Returns: (transfer none): the time zone abbreviation. The returned
- * string is owned by the #GDateTime and it should not be
- * modified or freed
- *
- * Since: 2.26
- **/
-const gchar *
-g_date_time_get_timezone_abbreviation (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, NULL);
-
- return g_time_zone_get_abbreviation (datetime->tz, datetime->interval);
-}
-
-/**
- * g_date_time_is_daylight_savings:
- * @datetime: a #GDateTime
- *
- * Determines if daylight savings time is in effect at the time and in
- * the time zone of @datetime.
- *
- * Returns: %TRUE if daylight savings time is in effect
- *
- * Since: 2.26
- **/
-gboolean
-g_date_time_is_daylight_savings (GDateTime *datetime)
-{
- g_return_val_if_fail (datetime != NULL, FALSE);
-
- return g_time_zone_is_dst (datetime->tz, datetime->interval);
-}
-
-/* Timezone convert {{{1 */
-/**
- * g_date_time_to_timezone:
- * @datetime: a #GDateTime
- * @tz: the new #GTimeZone
- *
- * Create a new #GDateTime corresponding to the same instant in time as
- * @datetime, but in the time zone @tz.
- *
- * This call can fail in the case that the time goes out of bounds. For
- * example, converting 0001-01-01 00:00:00 UTC to a time zone west of
- * Greenwich will fail (due to the year 0 being out of range).
- *
- * You should release the return value by calling g_date_time_unref()
- * when you are done with it.
- *
- * Returns: a new #GDateTime, or %NULL
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_to_timezone (GDateTime *datetime,
- GTimeZone *tz)
-{
- return g_date_time_from_instant (tz, g_date_time_to_instant (datetime));
-}
-
-/**
- * g_date_time_to_local:
- * @datetime: a #GDateTime
- *
- * Creates a new #GDateTime corresponding to the same instant in time as
- * @datetime, but in the local time zone.
- *
- * This call is equivalent to calling g_date_time_to_timezone() with the
- * time zone returned by g_time_zone_new_local().
- *
- * Returns: the newly created #GDateTime
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_to_local (GDateTime *datetime)
-{
- GDateTime *new;
- GTimeZone *local;
-
- local = g_time_zone_new_local ();
- new = g_date_time_to_timezone (datetime, local);
- g_time_zone_unref (local);
-
- return new;
-}
-
-/**
- * g_date_time_to_utc:
- * @datetime: a #GDateTime
- *
- * Creates a new #GDateTime corresponding to the same instant in time as
- * @datetime, but in UTC.
- *
- * This call is equivalent to calling g_date_time_to_timezone() with the
- * time zone returned by g_time_zone_new_utc().
- *
- * Returns: the newly created #GDateTime
- *
- * Since: 2.26
- **/
-GDateTime *
-g_date_time_to_utc (GDateTime *datetime)
-{
- GDateTime *new;
- GTimeZone *utc;
-
- utc = g_time_zone_new_utc ();
- new = g_date_time_to_timezone (datetime, utc);
- g_time_zone_unref (utc);
-
- return new;
-}
-
-/* Format {{{1 */
-/**
- * g_date_time_format:
- * @datetime: A #GDateTime
- * @format: a valid UTF-8 string, containing the format for the
- * #GDateTime
- *
- * Creates a newly allocated string representing the requested @format.
- *
- * The following format specifiers are supported:
- *
- * <variablelist>
- * <varlistentry><term>
- * <literal>%%a</literal>:
- * </term><listitem><simpara>
- * the abbreviated weekday name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%A</literal>:
- * </term><listitem><simpara>
- * the full weekday name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%b</literal>:
- * </term><listitem><simpara>
- * the abbreviated month name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%B</literal>:
- * </term><listitem><simpara>
- * the full month name according to the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%d</literal>:
- * </term><listitem><simpara>
- * the day of the month as a decimal number (range 01 to 31)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%e</literal>:
- * </term><listitem><simpara>
- * the day of the month as a decimal number (range 1 to 31)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%F</literal>:
- * </term><listitem><simpara>
- * equivalent to <literal>%%Y-%%m-%%d</literal> (the ISO 8601 date
- * format)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%h</literal>:
- * </term><listitem><simpara>
- * equivalent to <literal>%%b</literal>
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%H</literal>:
- * </term><listitem><simpara>
- * the hour as a decimal number using a 24-hour clock (range 00 to
- * 23)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%I</literal>:
- * </term><listitem><simpara>
- * the hour as a decimal number using a 12-hour clock (range 01 to
- * 12)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%j</literal>:
- * </term><listitem><simpara>
- * the day of the year as a decimal number (range 001 to 366)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%k</literal>:
- * </term><listitem><simpara>
- * the hour (24-hour clock) as a decimal number (range 0 to 23);
- * single digits are preceded by a blank
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%l</literal>:
- * </term><listitem><simpara>
- * the hour (12-hour clock) as a decimal number (range 1 to 12);
- * single digits are preceded by a blank
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%m</literal>:
- * </term><listitem><simpara>
- * the month as a decimal number (range 01 to 12)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%M</literal>:
- * </term><listitem><simpara>
- * the minute as a decimal number (range 00 to 59)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%N</literal>:
- * </term><listitem><simpara>
- * the micro-seconds as a decimal number
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%p</literal>:
- * </term><listitem><simpara>
- * either "AM" or "PM" according to the given time value, or the
- * corresponding strings for the current locale. Noon is treated as
- * "PM" and midnight as "AM".
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%P</literal>:
- * </term><listitem><simpara>
- * like %%p but lowercase: "am" or "pm" or a corresponding string for
- * the current locale
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%r</literal>:
- * </term><listitem><simpara>
- * the time in a.m. or p.m. notation
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%R</literal>:
- * </term><listitem><simpara>
- * the time in 24-hour notation (<literal>%%H:%%M</literal>)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%s</literal>:
- * </term><listitem><simpara>
- * the number of seconds since the Epoch, that is, since 1970-01-01
- * 00:00:00 UTC
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%S</literal>:
- * </term><listitem><simpara>
- * the second as a decimal number (range 00 to 60)
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%t</literal>:
- * </term><listitem><simpara>
- * a tab character
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%u</literal>:
- * </term><listitem><simpara>
- * the day of the week as a decimal, range 1 to 7, Monday being 1
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%W</literal>:
- * </term><listitem><simpara>
- * the week number of the current year as a decimal number
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%x</literal>:
- * </term><listitem><simpara>
- * the preferred date representation for the current locale without
- * the time
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%X</literal>:
- * </term><listitem><simpara>
- * the preferred time representation for the current locale without
- * the date
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%y</literal>:
- * </term><listitem><simpara>
- * the year as a decimal number without the century
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%Y</literal>:
- * </term><listitem><simpara>
- * the year as a decimal number including the century
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%z</literal>:
- * </term><listitem><simpara>
- * the time-zone as hour offset from UTC
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%Z</literal>:
- * </term><listitem><simpara>
- * the time zone or name or abbreviation
- * </simpara></listitem></varlistentry>
- * <varlistentry><term>
- * <literal>%%%</literal>:
- * </term><listitem><simpara>
- * a literal <literal>%%</literal> character
- * </simpara></listitem></varlistentry>
- * </variablelist>
- *
- * Returns: a newly allocated string formatted to the requested format
- * or %NULL in the case that there was an error. The string
- * should be freed with g_free().
- *
- * Since: 2.26
- */
-gchar *
-g_date_time_format (GDateTime *datetime,
- const gchar *format)
-{
- GString *outstr;
- gchar *tmp;
- gunichar c;
- glong utf8len;
- gboolean in_mod;
-
- g_return_val_if_fail (datetime != NULL, NULL);
- g_return_val_if_fail (format != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (format, -1, NULL), NULL);
-
- outstr = g_string_sized_new (strlen (format) * 2);
- utf8len = g_utf8_strlen (format, -1);
- in_mod = FALSE;
-
- for (; *format; format = g_utf8_next_char(format))
- {
- c = g_utf8_get_char (format);
-
- switch (c)
- {
- case '%':
- if (!in_mod)
- {
- in_mod = TRUE;
- break;
- }
- /* Fall through */
- default:
- if (in_mod)
- {
- switch (c)
- {
- case 'a':
- g_string_append (outstr, WEEKDAY_ABBR (datetime));
- break;
- case 'A':
- g_string_append (outstr, WEEKDAY_FULL (datetime));
- break;
- case 'b':
- g_string_append (outstr, MONTH_ABBR (datetime));
- break;
- case 'B':
- g_string_append (outstr, MONTH_FULL (datetime));
- break;
- case 'd':
- g_string_append_printf (outstr, "%02d", g_date_time_get_day_of_month (datetime));
- break;
- case 'e':
- g_string_append_printf (outstr, "%2d", g_date_time_get_day_of_month (datetime));
- break;
- case 'F':
- g_string_append_printf (outstr, "%d-%02d-%02d",
- g_date_time_get_year (datetime),
- g_date_time_get_month (datetime),
- g_date_time_get_day_of_month (datetime));
- break;
- case 'h':
- g_string_append (outstr, MONTH_ABBR (datetime));
- break;
- case 'H':
- g_string_append_printf (outstr, "%02d", g_date_time_get_hour (datetime));
- break;
- case 'I':
- if (g_date_time_get_hour (datetime) == 0)
- g_string_append (outstr, "12");
- else
- g_string_append_printf (outstr, "%02d", g_date_time_get_hour (datetime) % 12);
- break;
- case 'j':
- g_string_append_printf (outstr, "%03d", g_date_time_get_day_of_year (datetime));
- break;
- case 'k':
- g_string_append_printf (outstr, "%2d", g_date_time_get_hour (datetime));
- break;
- case 'l':
- if (g_date_time_get_hour (datetime) == 0)
- g_string_append (outstr, "12");
- else
- g_string_append_printf (outstr, "%2d", g_date_time_get_hour (datetime) % 12);
- break;
- case 'm':
- g_string_append_printf (outstr, "%02d", g_date_time_get_month (datetime));
- break;
- case 'M':
- g_string_append_printf (outstr, "%02d", g_date_time_get_minute (datetime));
- break;
- case 'N':
- g_string_append_printf (outstr, "%"G_GUINT64_FORMAT, datetime->usec % USEC_PER_SECOND);
- break;
- case 'p':
- g_string_append (outstr, GET_AMPM (datetime, FALSE));
- break;
- case 'P':
- g_string_append (outstr, GET_AMPM (datetime, TRUE));
- break;
- case 'r':
- {
- gint hour = g_date_time_get_hour (datetime) % 12;
- if (hour == 0)
- hour = 12;
- g_string_append_printf (outstr, "%02d:%02d:%02d %s",
- hour,
- g_date_time_get_minute (datetime),
- g_date_time_get_second (datetime),
- GET_AMPM (datetime, FALSE));
- }
- break;
- case 'R':
- g_string_append_printf (outstr, "%02d:%02d",
- g_date_time_get_hour (datetime),
- g_date_time_get_minute (datetime));
- break;
- case 's':
- g_string_append_printf (outstr, "%" G_GINT64_FORMAT, g_date_time_to_unix (datetime));
- break;
- case 'S':
- g_string_append_printf (outstr, "%02d", g_date_time_get_second (datetime));
- break;
- case 't':
- g_string_append_c (outstr, '\t');
- break;
- case 'u':
- g_string_append_printf (outstr, "%d", g_date_time_get_day_of_week (datetime));
- break;
- case 'W':
- g_string_append_printf (outstr, "%d", g_date_time_get_day_of_year (datetime) / 7);
- break;
- case 'x':
- {
- tmp = GET_PREFERRED_DATE (datetime);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- break;
- case 'X':
- {
- tmp = GET_PREFERRED_TIME (datetime);
- g_string_append (outstr, tmp);
- g_free (tmp);
- }
- break;
- case 'y':
- g_string_append_printf (outstr, "%02d", g_date_time_get_year (datetime) % 100);
- break;
- case 'Y':
- g_string_append_printf (outstr, "%d", g_date_time_get_year (datetime));
- break;
- case 'z':
- if (datetime->tz != NULL)
- {
- gint64 offset = g_date_time_get_utc_offset (datetime)
- / USEC_PER_SECOND;
-
- g_string_append_printf (outstr, "%c%02d%02d",
- offset >= 0 ? '+' : '-',
- (int) offset / 3600,
- (int) offset / 60 % 60);
- }
- else
- g_string_append (outstr, "+0000");
- break;
- case 'Z':
- g_string_append (outstr, g_date_time_get_timezone_abbreviation (datetime));
- break;
- case '%':
- g_string_append_c (outstr, '%');
- break;
- case 'n':
- g_string_append_c (outstr, '\n');
- break;
- default:
- goto bad_format;
- }
- in_mod = FALSE;
- }
- else
- g_string_append_unichar (outstr, c);
- }
- }
-
- return g_string_free (outstr, FALSE);
-
-bad_format:
- g_string_free (outstr, TRUE);
- return NULL;
-}
-
-
-/* Epilogue {{{1 */
-/* vim:set foldmethod=marker: */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gdir.c: Simplified wrapper around the DIRENT functions.
- *
- * Copyright 2001 Hans Breuer
- * Copyright 2004 Tor Lillqvist
- *
- * 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 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 "config.h"
-
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_DIRENT_H
-#include <sys/types.h>
-#include <dirent.h>
-#endif
-
-#include "gdir.h"
-
-#include "gconvert.h"
-#include "gfileutils.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "glibintl.h"
-
-
-#if defined (_MSC_VER) && !defined (HAVE_DIRENT_H)
-#include "../build/win32/dirent/dirent.h"
-#include "../build/win32/dirent/wdirent.c"
-#endif
-
-struct _GDir
-{
-#ifdef G_OS_WIN32
- _WDIR *wdirp;
-#else
- DIR *dirp;
-#endif
-#ifdef G_OS_WIN32
- gchar utf8_buf[FILENAME_MAX*4];
-#endif
-};
-
-/**
- * g_dir_open:
- * @path: the path to the directory you are interested in. On Unix
- * in the on-disk encoding. On Windows in UTF-8
- * @flags: Currently must be set to 0. Reserved for future use.
- * @error: return location for a #GError, or %NULL.
- * If non-%NULL, an error will be set if and only if
- * g_dir_open() fails.
- *
- * Opens a directory for reading. The names of the files in the
- * directory can then be retrieved using g_dir_read_name(). Note
- * that the ordering is not defined.
- *
- * Return value: a newly allocated #GDir on success, %NULL on failure.
- * If non-%NULL, you must free the result with g_dir_close()
- * when you are finished with it.
- **/
-GDir *
-g_dir_open (const gchar *path,
- guint flags,
- GError **error)
-{
- GDir *dir;
- int errsv;
-#ifdef G_OS_WIN32
- wchar_t *wpath;
-#else
- gchar *utf8_path;
-#endif
-
- g_return_val_if_fail (path != NULL, NULL);
-
-#ifdef G_OS_WIN32
- wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, error);
-
- if (wpath == NULL)
- return NULL;
-
- dir = g_new (GDir, 1);
-
- dir->wdirp = _wopendir (wpath);
- g_free (wpath);
-
- if (dir->wdirp)
- return dir;
-
- /* error case */
- errsv = errno;
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errsv),
- _("Error opening directory '%s': %s"),
- path, g_strerror (errsv));
-
- g_free (dir);
-
- return NULL;
-#else
- dir = g_new (GDir, 1);
-
- dir->dirp = opendir (path);
-
- if (dir->dirp)
- return dir;
-
- /* error case */
- errsv = errno;
-
- utf8_path = g_filename_to_utf8 (path, -1,
- NULL, NULL, NULL);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (errsv),
- _("Error opening directory '%s': %s"),
- utf8_path, g_strerror (errsv));
-
- g_free (utf8_path);
- g_free (dir);
-
- return NULL;
-#endif
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-/* The above function actually is called g_dir_open_utf8, and it's
- * that what applications compiled with this GLib version will
- * use.
- */
-
-#undef g_dir_open
-
-/* Binary compatibility version. Not for newly compiled code. */
-
-GDir *
-g_dir_open (const gchar *path,
- guint flags,
- GError **error)
-{
- gchar *utf8_path = g_locale_to_utf8 (path, -1, NULL, NULL, error);
- GDir *retval;
-
- if (utf8_path == NULL)
- return NULL;
-
- retval = g_dir_open_utf8 (utf8_path, flags, error);
-
- g_free (utf8_path);
-
- return retval;
-}
-#endif
-
-/**
- * g_dir_read_name:
- * @dir: a #GDir* created by g_dir_open()
- *
- * Retrieves the name of another entry in the directory, or %NULL.
- * The order of entries returned from this function is not defined,
- * and may vary by file system or other operating-system dependent
- * factors.
- *
- * On Unix, the '.' and '..' entries are omitted, and the returned
- * name is in the on-disk encoding.
- *
- * On Windows, as is true of all GLib functions which operate on
- * filenames, the returned name is in UTF-8.
- *
- * Return value: The entry's name or %NULL if there are no
- * more entries. The return value is owned by GLib and
- * must not be modified or freed.
- **/
-G_CONST_RETURN gchar*
-g_dir_read_name (GDir *dir)
-{
-#ifdef G_OS_WIN32
- gchar *utf8_name;
- struct _wdirent *wentry;
-#else
- struct dirent *entry;
-#endif
-
- g_return_val_if_fail (dir != NULL, NULL);
-
-#ifdef G_OS_WIN32
- while (1)
- {
- wentry = _wreaddir (dir->wdirp);
- while (wentry
- && (0 == wcscmp (wentry->d_name, L".") ||
- 0 == wcscmp (wentry->d_name, L"..")))
- wentry = _wreaddir (dir->wdirp);
-
- if (wentry == NULL)
- return NULL;
-
- utf8_name = g_utf16_to_utf8 (wentry->d_name, -1, NULL, NULL, NULL);
-
- if (utf8_name == NULL)
- continue; /* Huh, impossible? Skip it anyway */
-
- strcpy (dir->utf8_buf, utf8_name);
- g_free (utf8_name);
-
- return dir->utf8_buf;
- }
-#else
- entry = readdir (dir->dirp);
- while (entry
- && (0 == strcmp (entry->d_name, ".") ||
- 0 == strcmp (entry->d_name, "..")))
- entry = readdir (dir->dirp);
-
- if (entry)
- return entry->d_name;
- else
- return NULL;
-#endif
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-/* Ditto for g_dir_read_name */
-
-#undef g_dir_read_name
-
-/* Binary compatibility version. Not for newly compiled code. */
-
-G_CONST_RETURN gchar*
-g_dir_read_name (GDir *dir)
-{
- while (1)
- {
- const gchar *utf8_name = g_dir_read_name_utf8 (dir);
- gchar *retval;
-
- if (utf8_name == NULL)
- return NULL;
-
- retval = g_locale_from_utf8 (utf8_name, -1, NULL, NULL, NULL);
-
- if (retval != NULL)
- {
- strcpy (dir->utf8_buf, retval);
- g_free (retval);
-
- return dir->utf8_buf;
- }
- }
-}
-
-#endif
-
-/**
- * g_dir_rewind:
- * @dir: a #GDir* created by g_dir_open()
- *
- * Resets the given directory. The next call to g_dir_read_name()
- * will return the first entry again.
- **/
-void
-g_dir_rewind (GDir *dir)
-{
- g_return_if_fail (dir != NULL);
-
-#ifdef G_OS_WIN32
- _wrewinddir (dir->wdirp);
-#else
- rewinddir (dir->dirp);
-#endif
-}
-
-/**
- * g_dir_close:
- * @dir: a #GDir* created by g_dir_open()
- *
- * Closes the directory and deallocates all related resources.
- **/
-void
-g_dir_close (GDir *dir)
-{
- g_return_if_fail (dir != NULL);
-
-#ifdef G_OS_WIN32
- _wclosedir (dir->wdirp);
-#else
- closedir (dir->dirp);
-#endif
- g_free (dir);
-}
+++ /dev/null
-#!/usr/bin/perl -w
-#
-# Script to convert http://www.unicode.org/Public/UNIDATA/Scripts.txt
-# into a machine-readable table.
-#
-######################################################################
-
-if (@ARGV != 1) {
- die "Usage: gen-script-table.pl Scripts.txt > gscripttable.h\n";
-}
-
-open IN, $ARGV[0] || die "Cannot open $ARGV[0]: $!\n";
-
-my @ranges;
-my $file;
-my $easy_range;
-my $i;
-my $start;
-my $end;
-my $script;
-
-
-while (<IN>) {
- if (/^\#\s+(Scripts-.*.txt)/) {
- $file = $1;
- }
-
- s/#.*//;
- next if /^\s*$/;
- if (!/^([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s*;\s*([A-Za-z_]+)\s*$/) {
- die "Cannot parse line: '$_'\n";
- }
-
- if (defined $2) {
- push @ranges, [ hex $1, hex $2, uc $3 ];
- } else {
- push @ranges, [ hex $1, hex $1, uc $3 ];
- }
-}
-
-@ranges = sort { $a->[0] <=> $b->[0] } @ranges;
-$date = gmtime;
-
-print <<"EOT";
-/* gscripttable.h: Generated by gen-script-table.pl
- *
- * Date: $date
- * Source: $file
- *
- * Do not edit.
- */
-
-EOT
-
-$easy_range = 0x2000;
-
-print <<"EOT";
-#define G_EASY_SCRIPTS_RANGE $easy_range
-
-static const guchar g_script_easy_table[$easy_range] = {
-EOT
-
-$i = 0;
-$end = -1;
-
-for (my $c = 0; $c < $easy_range; $c++) {
-
- if ($c % 3 == 0) {
- printf "\n ";
- }
-
- if ($c > $end) {
- $start = $ranges[$i]->[0];
- $end = $ranges[$i]->[1];
- $script = $ranges[$i]->[2];
- $i++;
- }
-
- if ($c < $start) {
- printf " G_UNICODE_SCRIPT_UNKNOWN,";
- } else {
- printf " G_UNICODE_SCRIPT_%s,", $script;
- }
-}
-
-if ($end >= $easy_range) {
- $i--;
- $ranges[$i]->[0] = $easy_range;
-}
-
-
-print <<"EOT";
-
-};
-
-static const struct {
- gunichar start;
- guint16 chars;
- guint16 script;
-} g_script_table[] = {
-EOT
-
-for (; $i <= $#ranges; $i++) {
- $start = $ranges[$i]->[0];
- $end = $ranges[$i]->[1];
- $script = $ranges[$i]->[2];
-
- while ($i <= $#ranges - 1 &&
- $ranges[$i + 1]->[0] == $end + 1 &&
- $ranges[$i + 1]->[2] eq $script) {
- $i++;
- $end = $ranges[$i]->[1];
- }
-
- printf " { %#06x, %5d, G_UNICODE_SCRIPT_%s },\n", $start, $end - $start + 1, $script;
-}
-
-printf "};\n";
-
+++ /dev/null
-#! /usr/bin/perl -w
-
-# Copyright (C) 1998, 1999 Tom Tromey
-# Copyright (C) 2001 Red Hat Software
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-# 02111-1307, USA.
-
-# Contributer(s):
-# Andrew Taylor <andrew.taylor@montage.ca>
-
-# gen-unicode-tables.pl - Generate tables for libunicode from Unicode data.
-# See http://www.unicode.org/Public/UNIDATA/UnicodeCharacterDatabase.html
-# I consider the output of this program to be unrestricted. Use it as
-# you will.
-
-# FIXME:
-# * For decomp table it might make sense to use a shift count other
-# than 8. We could easily compute the perfect shift count.
-
-# we use some perl unicode features
-require 5.006;
-
-use vars qw($CODE $NAME $CATEGORY $COMBINING_CLASSES $BIDI_CATEGORY $DECOMPOSITION $DECIMAL_VALUE $DIGIT_VALUE $NUMERIC_VALUE $MIRRORED $OLD_NAME $COMMENT $UPPER $LOWER $TITLE $BREAK_CODE $BREAK_CATEGORY $BREAK_NAME $CASE_CODE $CASE_LOWER $CASE_TITLE $CASE_UPPER $CASE_CONDITION);
-
-
-# Names of fields in Unicode data table.
-$CODE = 0;
-$NAME = 1;
-$CATEGORY = 2;
-$COMBINING_CLASSES = 3;
-$BIDI_CATEGORY = 4;
-$DECOMPOSITION = 5;
-$DECIMAL_VALUE = 6;
-$DIGIT_VALUE = 7;
-$NUMERIC_VALUE = 8;
-$MIRRORED = 9;
-$OLD_NAME = 10;
-$COMMENT = 11;
-$UPPER = 12;
-$LOWER = 13;
-$TITLE = 14;
-
-# Names of fields in the line break table
-$BREAK_CODE = 0;
-$BREAK_PROPERTY = 1;
-
-# Names of fields in the SpecialCasing table
-$CASE_CODE = 0;
-$CASE_LOWER = 1;
-$CASE_TITLE = 2;
-$CASE_UPPER = 3;
-$CASE_CONDITION = 4;
-
-# Names of fields in the CaseFolding table
-$FOLDING_CODE = 0;
-$FOLDING_STATUS = 1;
-$FOLDING_MAPPING = 2;
-
-# Map general category code onto symbolic name.
-%mappings =
- (
- # Normative.
- 'Lu' => "G_UNICODE_UPPERCASE_LETTER",
- 'Ll' => "G_UNICODE_LOWERCASE_LETTER",
- 'Lt' => "G_UNICODE_TITLECASE_LETTER",
- 'Mn' => "G_UNICODE_NON_SPACING_MARK",
- 'Mc' => "G_UNICODE_COMBINING_MARK",
- 'Me' => "G_UNICODE_ENCLOSING_MARK",
- 'Nd' => "G_UNICODE_DECIMAL_NUMBER",
- 'Nl' => "G_UNICODE_LETTER_NUMBER",
- 'No' => "G_UNICODE_OTHER_NUMBER",
- 'Zs' => "G_UNICODE_SPACE_SEPARATOR",
- 'Zl' => "G_UNICODE_LINE_SEPARATOR",
- 'Zp' => "G_UNICODE_PARAGRAPH_SEPARATOR",
- 'Cc' => "G_UNICODE_CONTROL",
- 'Cf' => "G_UNICODE_FORMAT",
- 'Cs' => "G_UNICODE_SURROGATE",
- 'Co' => "G_UNICODE_PRIVATE_USE",
- 'Cn' => "G_UNICODE_UNASSIGNED",
-
- # Informative.
- 'Lm' => "G_UNICODE_MODIFIER_LETTER",
- 'Lo' => "G_UNICODE_OTHER_LETTER",
- 'Pc' => "G_UNICODE_CONNECT_PUNCTUATION",
- 'Pd' => "G_UNICODE_DASH_PUNCTUATION",
- 'Ps' => "G_UNICODE_OPEN_PUNCTUATION",
- 'Pe' => "G_UNICODE_CLOSE_PUNCTUATION",
- 'Pi' => "G_UNICODE_INITIAL_PUNCTUATION",
- 'Pf' => "G_UNICODE_FINAL_PUNCTUATION",
- 'Po' => "G_UNICODE_OTHER_PUNCTUATION",
- 'Sm' => "G_UNICODE_MATH_SYMBOL",
- 'Sc' => "G_UNICODE_CURRENCY_SYMBOL",
- 'Sk' => "G_UNICODE_MODIFIER_SYMBOL",
- 'So' => "G_UNICODE_OTHER_SYMBOL"
- );
-
-%break_mappings =
- (
- 'BK' => "G_UNICODE_BREAK_MANDATORY",
- 'CR' => "G_UNICODE_BREAK_CARRIAGE_RETURN",
- 'LF' => "G_UNICODE_BREAK_LINE_FEED",
- 'CM' => "G_UNICODE_BREAK_COMBINING_MARK",
- 'SG' => "G_UNICODE_BREAK_SURROGATE",
- 'ZW' => "G_UNICODE_BREAK_ZERO_WIDTH_SPACE",
- 'IN' => "G_UNICODE_BREAK_INSEPARABLE",
- 'GL' => "G_UNICODE_BREAK_NON_BREAKING_GLUE",
- 'CB' => "G_UNICODE_BREAK_CONTINGENT",
- 'SP' => "G_UNICODE_BREAK_SPACE",
- 'BA' => "G_UNICODE_BREAK_AFTER",
- 'BB' => "G_UNICODE_BREAK_BEFORE",
- 'B2' => "G_UNICODE_BREAK_BEFORE_AND_AFTER",
- 'HY' => "G_UNICODE_BREAK_HYPHEN",
- 'NS' => "G_UNICODE_BREAK_NON_STARTER",
- 'OP' => "G_UNICODE_BREAK_OPEN_PUNCTUATION",
- 'CL' => "G_UNICODE_BREAK_CLOSE_PUNCTUATION",
- 'QU' => "G_UNICODE_BREAK_QUOTATION",
- 'EX' => "G_UNICODE_BREAK_EXCLAMATION",
- 'ID' => "G_UNICODE_BREAK_IDEOGRAPHIC",
- 'NU' => "G_UNICODE_BREAK_NUMERIC",
- 'IS' => "G_UNICODE_BREAK_INFIX_SEPARATOR",
- 'SY' => "G_UNICODE_BREAK_SYMBOL",
- 'AL' => "G_UNICODE_BREAK_ALPHABETIC",
- 'PR' => "G_UNICODE_BREAK_PREFIX",
- 'PO' => "G_UNICODE_BREAK_POSTFIX",
- 'SA' => "G_UNICODE_BREAK_COMPLEX_CONTEXT",
- 'AI' => "G_UNICODE_BREAK_AMBIGUOUS",
- 'NL' => "G_UNICODE_BREAK_NEXT_LINE",
- 'WJ' => "G_UNICODE_BREAK_WORD_JOINER",
- 'XX' => "G_UNICODE_BREAK_UNKNOWN",
- 'JL' => "G_UNICODE_BREAK_HANGUL_L_JAMO",
- 'JV' => "G_UNICODE_BREAK_HANGUL_V_JAMO",
- 'JT' => "G_UNICODE_BREAK_HANGUL_T_JAMO",
- 'H2' => "G_UNICODE_BREAK_HANGUL_LV_SYLLABLE",
- 'H3' => "G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE"
- );
-
-# Title case mappings.
-%title_to_lower = ();
-%title_to_upper = ();
-
-# Maximum length of special-case strings
-
-my @special_cases;
-my @special_case_offsets;
-my $special_case_offset = 0;
-
-$do_decomp = 0;
-$do_props = 1;
-if (@ARGV && $ARGV[0] eq '-decomp')
-{
- $do_decomp = 1;
- $do_props = 0;
- shift @ARGV;
-}
-elsif (@ARGV && $ARGV[0] eq '-both')
-{
- $do_decomp = 1;
- shift @ARGV;
-}
-
-if (@ARGV != 2) {
- $0 =~ s@.*/@@;
- die "\nUsage: $0 [-decomp | -both] UNICODE-VERSION DIRECTORY\n\n DIRECTORY should contain the following Unicode data files:\n UnicodeData.txt, LineBreak.txt, SpecialCasing.txt, CaseFolding.txt,\n CompositionExclusions.txt, BidiMirroring.txt\n\n";
-}
-
-my ($unicodedatatxt, $linebreaktxt, $specialcasingtxt, $casefoldingtxt, $compositionexclusionstxt);
-
-my $d = $ARGV[1];
-opendir (my $dir, $d) or die "Cannot open Unicode data dir $d: $!\n";
-for my $f (readdir ($dir))
-{
- $unicodedatatxt = "$d/$f" if ($f =~ /UnicodeData.*\.txt/);
- $linebreaktxt = "$d/$f" if ($f =~ /LineBreak.*\.txt/);
- $specialcasingtxt = "$d/$f" if ($f =~ /SpecialCasing.*\.txt/);
- $casefoldingtxt = "$d/$f" if ($f =~ /CaseFolding.*\.txt/);
- $compositionexclusionstxt = "$d/$f" if ($f =~ /CompositionExclusions.*\.txt/);
-}
-
-defined $unicodedatatxt or die "Did not find UnicodeData file";
-defined $linebreaktxt or die "Did not find LineBreak file";
-defined $specialcasingtxt or die "Did not find SpecialCasing file";
-defined $casefoldingtxt or die "Did not find CaseFolding file";
-defined $compositionexclusionstxt or die "Did not find CompositionExclusions file";
-
-print "Creating decomp table\n" if ($do_decomp);
-print "Creating property table\n" if ($do_props);
-
-print "Composition exlusions from $compositionexclusionstxt\n";
-
-open (INPUT, "< $compositionexclusionstxt") || exit 1;
-
-while (<INPUT>) {
-
- chop;
-
- next if /^#/;
- next if /^\s*$/;
-
- s/\s*#.*//;
-
- s/^\s*//;
- s/\s*$//;
-
- $composition_exclusions{hex($_)} = 1;
-}
-
-close INPUT;
-
-print "Unicode data from $unicodedatatxt\n";
-
-open (INPUT, "< $unicodedatatxt") || exit 1;
-
-# we save memory by skipping the huge empty area before U+E0000
-my $pages_before_e0000;
-
-$last_code = -1;
-while (<INPUT>)
-{
- chop;
- @fields = split (';', $_, 30);
- if ($#fields != 14)
- {
- printf STDERR ("Entry for $fields[$CODE] has wrong number of fields (%d)\n", $#fields);
- }
-
- $code = hex ($fields[$CODE]);
-
- if ($code >= 0xE0000 and $last_code < 0xE0000)
- {
- $pages_before_e0000 = ($last_code >> 8) + 1;
- }
-
- if ($code > $last_code + 1)
- {
- # Found a gap.
- if ($fields[$NAME] =~ /Last>/)
- {
- # Fill the gap with the last character read,
- # since this was a range specified in the char database
- @gfields = @fields;
- }
- else
- {
- # The gap represents undefined characters. Only the type
- # matters.
- @gfields = ('', '', 'Cn', '0', '', '', '', '', '', '', '',
- '', '', '', '');
- }
- for (++$last_code; $last_code < $code; ++$last_code)
- {
- $gfields{$CODE} = sprintf ("%04x", $last_code);
- &process_one ($last_code, @gfields);
- }
- }
- &process_one ($code, @fields);
- $last_code = $code;
-}
-
-close INPUT;
-
-@gfields = ('', '', 'Cn', '0', '', '', '', '', '', '', '',
- '', '', '', '');
-for (++$last_code; $last_code <= 0x10FFFF; ++$last_code)
-{
- $gfields{$CODE} = sprintf ("%04x", $last_code);
- &process_one ($last_code, @gfields);
-}
---$last_code; # Want last to be 0x10FFFF.
-
-print "Creating line break table\n";
-
-print "Line break data from $linebreaktxt\n";
-
-open (INPUT, "< $linebreaktxt") || exit 1;
-
-$last_code = -1;
-while (<INPUT>)
-{
- my ($start_code, $end_code);
-
- chop;
-
- next if /^#/;
-
- s/\s*#.*//;
-
- @fields = split (';', $_, 30);
- if ($#fields != 1)
- {
- printf STDERR ("Entry for $fields[$CODE] has wrong number of fields (%d)\n", $#fields);
- next;
- }
-
- if ($fields[$CODE] =~ /([A-F0-9]{4,6})\.\.([A-F0-9]{4,6})/)
- {
- $start_code = hex ($1);
- $end_code = hex ($2);
- } else {
- $start_code = $end_code = hex ($fields[$CODE]);
-
- }
-
- if ($start_code > $last_code + 1)
- {
- # The gap represents undefined characters. If assigned,
- # they are AL, if not assigned, XX
- for (++$last_code; $last_code < $start_code; ++$last_code)
- {
- if ($type[$last_code] eq 'Cn')
- {
- $break_props[$last_code] = 'XX';
- }
- else
- {
- $break_props[$last_code] = 'AL';
- }
- }
- }
-
- for ($last_code = $start_code; $last_code <= $end_code; $last_code++)
- {
- $break_props[$last_code] = $fields[$BREAK_PROPERTY];
- }
-
- $last_code = $end_code;
-}
-
-close INPUT;
-
-for (++$last_code; $last_code <= 0x10FFFF; ++$last_code)
-{
- if ($type[$last_code] eq 'Cn')
- {
- $break_props[$last_code] = 'XX';
- }
- else
- {
- $break_props[$last_code] = 'AL';
- }
-}
---$last_code; # Want last to be 0x10FFFF.
-
-print STDERR "Last code is not 0x10FFFF" if ($last_code != 0x10FFFF);
-
-print "Reading special-casing table for case conversion\n";
-
-open (INPUT, "< $specialcasingtxt") || exit 1;
-
-while (<INPUT>)
-{
- my $code;
-
- chop;
-
- next if /^#/;
- next if /^\s*$/;
-
- s/\s*#.*//;
-
- @fields = split ('\s*;\s*', $_, 30);
-
- $raw_code = $fields[$CASE_CODE];
- $code = hex ($raw_code);
-
- if ($#fields != 4 && $#fields != 5)
- {
- printf STDERR ("Entry for $raw_code has wrong number of fields (%d)\n", $#fields);
- next;
- }
-
- if (!defined $type[$code])
- {
- printf STDERR "Special case for code point: $code, which has no defined type\n";
- next;
- }
-
- if (defined $fields[5]) {
- # Ignore conditional special cases - we'll handle them in code
- next;
- }
-
- if ($type[$code] eq 'Lu')
- {
- (hex $fields[$CASE_UPPER] == $code) || die "$raw_code is Lu and UCD_Upper($raw_code) != $raw_code";
-
- &add_special_case ($code, $value[$code], $fields[$CASE_LOWER], $fields[$CASE_TITLE]);
-
- } elsif ($type[$code] eq 'Lt')
- {
- (hex $fields[$CASE_TITLE] == $code) || die "$raw_code is Lt and UCD_Title($raw_code) != $raw_code";
-
- &add_special_case ($code, undef, $fields[$CASE_LOWER], $fields[$CASE_UPPER]);
- } elsif ($type[$code] eq 'Ll')
- {
- (hex $fields[$CASE_LOWER] == $code) || die "$raw_code is Ll and UCD_Lower($raw_code) != $raw_code";
-
- &add_special_case ($code, $value[$code], $fields[$CASE_UPPER], $fields[$CASE_TITLE]);
- } else {
- printf STDERR "Special case for non-alphabetic code point: $raw_code\n";
- next;
- }
-}
-
-close INPUT;
-
-open (INPUT, "< $casefoldingtxt") || exit 1;
-
-my $casefoldlen = 0;
-my @casefold;
-
-while (<INPUT>)
-{
- my $code;
-
- chop;
-
- next if /^#/;
- next if /^\s*$/;
-
- s/\s*#.*//;
-
- @fields = split ('\s*;\s*', $_, 30);
-
- $raw_code = $fields[$FOLDING_CODE];
- $code = hex ($raw_code);
-
- if ($#fields != 3)
- {
- printf STDERR ("Entry for $raw_code has wrong number of fields (%d)\n", $#fields);
- next;
- }
-
- # we don't use Simple or Turkic rules here
- next if ($fields[$FOLDING_STATUS] =~ /^[ST]$/);
-
- @values = map { hex ($_) } split /\s+/, $fields[$FOLDING_MAPPING];
-
- # Check simple case
-
- if (@values == 1 &&
- !(defined $value[$code] && $value[$code] >= 0x1000000) &&
- defined $type[$code]) {
-
- my $lower;
- if ($type[$code] eq 'Ll')
- {
- $lower = $code;
- } elsif ($type[$code] eq 'Lt')
- {
- $lower = $title_to_lower{$code};
- } elsif ($type[$code] eq 'Lu')
- {
- $lower = $value[$code];
- } else {
- $lower = $code;
- }
-
- if ($lower == $values[0]) {
- next;
- }
- }
-
- my $string = pack ("U*", @values);
-
- if (1 + &length_in_bytes ($string) > $casefoldlen) {
- $casefoldlen = 1 + &length_in_bytes ($string);
- }
-
- push @casefold, [ $code, &escape ($string) ];
-}
-
-close INPUT;
-
-if ($do_props) {
- &print_tables ($last_code)
-}
-if ($do_decomp) {
- &print_decomp ($last_code);
- &output_composition_table;
-}
-
-&print_line_break ($last_code);
-
-exit 0;
-
-
-# perl "length" returns the length in characters
-sub length_in_bytes
-{
- my ($string) = @_;
-
- use bytes;
- return length $string;
-}
-
-# Process a single character.
-sub process_one
-{
- my ($code, @fields) = @_;
-
- $type[$code] = $fields[$CATEGORY];
- if ($type[$code] eq 'Nd')
- {
- $value[$code] = int ($fields[$DECIMAL_VALUE]);
- }
- elsif ($type[$code] eq 'Ll')
- {
- $value[$code] = hex ($fields[$UPPER]);
- }
- elsif ($type[$code] eq 'Lu')
- {
- $value[$code] = hex ($fields[$LOWER]);
- }
-
- if ($type[$code] eq 'Lt')
- {
- $title_to_lower{$code} = hex ($fields[$LOWER]);
- $title_to_upper{$code} = hex ($fields[$UPPER]);
- }
-
- $cclass[$code] = $fields[$COMBINING_CLASSES];
-
- # Handle decompositions.
- if ($fields[$DECOMPOSITION] ne '')
- {
- if ($fields[$DECOMPOSITION] =~ s/\<.*\>\s*//) {
- $decompose_compat[$code] = 1;
- } else {
- $decompose_compat[$code] = 0;
-
- if (!exists $composition_exclusions{$code}) {
- $compositions{$code} = $fields[$DECOMPOSITION];
- }
- }
- $decompositions[$code] = $fields[$DECOMPOSITION];
- }
-}
-
-sub print_tables
-{
- my ($last) = @_;
- my ($outfile) = "gunichartables.h";
-
- local ($bytes_out) = 0;
-
- print "Writing $outfile...\n";
-
- open (OUT, "> $outfile");
-
- print OUT "/* This file is automatically generated. DO NOT EDIT!\n";
- print OUT " Instead, edit gen-unicode-tables.pl and re-run. */\n\n";
-
- print OUT "#ifndef CHARTABLES_H\n";
- print OUT "#define CHARTABLES_H\n\n";
-
- print OUT "#define G_UNICODE_DATA_VERSION \"$ARGV[0]\"\n\n";
-
- printf OUT "#define G_UNICODE_LAST_CHAR 0x%04x\n\n", $last;
-
- printf OUT "#define G_UNICODE_MAX_TABLE_INDEX 10000\n\n";
-
- my $last_part1 = ($pages_before_e0000 * 256) - 1;
- printf OUT "#define G_UNICODE_LAST_CHAR_PART1 0x%04X\n\n", $last_part1;
- printf OUT "#define G_UNICODE_LAST_PAGE_PART1 %d\n\n", $pages_before_e0000 - 1;
-
- $table_index = 0;
- printf OUT "static const char type_data[][256] = {\n";
- for ($count = 0; $count <= $last; $count += 256)
- {
- $row[$count / 256] = &print_row ($count, 1, \&fetch_type);
- }
- printf OUT "\n};\n\n";
-
- printf OUT "/* U+0000 through U+%04X */\n", $last_part1;
- print OUT "static const gint16 type_table_part1[$pages_before_e0000] = {\n";
- for ($count = 0; $count <= $last_part1; $count += 256)
- {
- print OUT ",\n" if $count > 0;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- printf OUT "/* U+E0000 through U+%04X */\n", $last;
- print OUT "static const gint16 type_table_part2[768] = {\n";
- for ($count = 0xE0000; $count <= $last; $count += 256)
- {
- print OUT ",\n" if $count > 0xE0000;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
-
- #
- # Now print attribute table.
- #
-
- $table_index = 0;
- printf OUT "static const gunichar attr_data[][256] = {\n";
- for ($count = 0; $count <= $last; $count += 256)
- {
- $row[$count / 256] = &print_row ($count, 4, \&fetch_attr);
- }
- printf OUT "\n};\n\n";
-
- printf OUT "/* U+0000 through U+%04X */\n", $last_part1;
- print OUT "static const gint16 attr_table_part1[$pages_before_e0000] = {\n";
- for ($count = 0; $count <= $last_part1; $count += 256)
- {
- print OUT ",\n" if $count > 0;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- printf OUT "/* U+E0000 through U+%04X */\n", $last;
- print OUT "static const gint16 attr_table_part2[768] = {\n";
- for ($count = 0xE0000; $count <= $last; $count += 256)
- {
- print OUT ",\n" if $count > 0xE0000;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- #
- # print title case table
- #
-
- print OUT "static const gunichar title_table[][3] = {\n";
- my ($item);
- my ($first) = 1;
- foreach $item (sort keys %title_to_lower)
- {
- print OUT ",\n"
- unless $first;
- $first = 0;
- printf OUT " { 0x%04x, 0x%04x, 0x%04x }", $item, $title_to_upper{$item}, $title_to_lower{$item};
- $bytes_out += 12;
- }
- print OUT "\n};\n\n";
-
- #
- # And special case conversion table -- conversions that change length
- #
- &output_special_case_table (\*OUT);
- &output_casefold_table (\*OUT);
-
- print OUT "#endif /* CHARTABLES_H */\n";
-
- close (OUT);
-
- printf STDERR "Generated %d bytes in tables\n", $bytes_out;
-}
-
-# A fetch function for the type table.
-sub fetch_type
-{
- my ($index) = @_;
- return $mappings{$type[$index]};
-}
-
-# A fetch function for the attribute table.
-sub fetch_attr
-{
- my ($index) = @_;
- if (defined $value[$index])
- {
- return sprintf ("0x%04x", $value[$index]);
- }
- else
- {
- return "0x0000";
- }
-}
-
-sub print_row
-{
- my ($start, $typsize, $fetcher) = @_;
-
- my ($i);
- my (@values);
- my ($flag) = 1;
- my ($off);
-
- for ($off = 0; $off < 256; ++$off)
- {
- $values[$off] = $fetcher->($off + $start);
- if ($values[$off] ne $values[0])
- {
- $flag = 0;
- }
- }
- if ($flag)
- {
- return $values[0] . " + G_UNICODE_MAX_TABLE_INDEX";
- }
-
- printf OUT ",\n" if ($table_index != 0);
- printf OUT " { /* page %d, index %d */\n ", $start / 256, $table_index;
- my ($column) = 4;
- for ($i = $start; $i < $start + 256; ++$i)
- {
- print OUT ", "
- if $i > $start;
- my ($text) = $values[$i - $start];
- if (length ($text) + $column + 2 > 78)
- {
- print OUT "\n ";
- $column = 4;
- }
- print OUT $text;
- $column += length ($text) + 2;
- }
- print OUT "\n }";
-
- $bytes_out += 256 * $typsize;
-
- return sprintf "%d /* page %d */", $table_index++, $start / 256;
-}
-
-sub escape
-{
- my ($string) = @_;
-
- my $escaped = unpack("H*", $string);
- $escaped =~ s/(.{2})/\\x$1/g;
-
- return $escaped;
-}
-
-# Returns the offset of $decomp in the offset string. Updates the
-# referenced variables as appropriate.
-sub handle_decomp ($$$$)
-{
- my ($decomp, $decomp_offsets_ref, $decomp_string_ref, $decomp_string_offset_ref) = @_;
- my $offset = "G_UNICODE_NOT_PRESENT_OFFSET";
-
- if (defined $decomp)
- {
- if (defined $decomp_offsets_ref->{$decomp})
- {
- $offset = $decomp_offsets_ref->{$decomp};
- }
- else
- {
- $offset = ${$decomp_string_offset_ref};
- $decomp_offsets_ref->{$decomp} = $offset;
- ${$decomp_string_ref} .= "\n \"" . &escape ($decomp) . "\\0\" /* offset ${$decomp_string_offset_ref} */";
- ${$decomp_string_offset_ref} += &length_in_bytes ($decomp) + 1;
- }
- }
-
- return $offset;
-}
-
-# Generate the character decomposition header.
-sub print_decomp
-{
- my ($last) = @_;
- my ($outfile) = "gunidecomp.h";
-
- local ($bytes_out) = 0;
-
- print "Writing $outfile...\n";
-
- open (OUT, "> $outfile") || exit 1;
-
- print OUT "/* This file is automatically generated. DO NOT EDIT! */\n\n";
- print OUT "#ifndef DECOMP_H\n";
- print OUT "#define DECOMP_H\n\n";
-
- printf OUT "#define G_UNICODE_LAST_CHAR 0x%04x\n\n", $last;
-
- printf OUT "#define G_UNICODE_MAX_TABLE_INDEX (0x110000 / 256)\n\n";
-
- my $last_part1 = ($pages_before_e0000 * 256) - 1;
- printf OUT "#define G_UNICODE_LAST_CHAR_PART1 0x%04X\n\n", $last_part1;
- printf OUT "#define G_UNICODE_LAST_PAGE_PART1 %d\n\n", $pages_before_e0000 - 1;
-
- $NOT_PRESENT_OFFSET = 65535;
- print OUT "#define G_UNICODE_NOT_PRESENT_OFFSET $NOT_PRESENT_OFFSET\n\n";
-
- my ($count, @row);
- $table_index = 0;
- printf OUT "static const guchar cclass_data[][256] = {\n";
- for ($count = 0; $count <= $last; $count += 256)
- {
- $row[$count / 256] = &print_row ($count, 1, \&fetch_cclass);
- }
- printf OUT "\n};\n\n";
-
- print OUT "static const gint16 combining_class_table_part1[$pages_before_e0000] = {\n";
- for ($count = 0; $count <= $last_part1; $count += 256)
- {
- print OUT ",\n" if $count > 0;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- print OUT "static const gint16 combining_class_table_part2[768] = {\n";
- for ($count = 0xE0000; $count <= $last; $count += 256)
- {
- print OUT ",\n" if $count > 0xE0000;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- print OUT "typedef struct\n{\n";
- print OUT " gunichar ch;\n";
- print OUT " guint16 canon_offset;\n";
- print OUT " guint16 compat_offset;\n";
- print OUT "} decomposition;\n\n";
-
- print OUT "static const decomposition decomp_table[] =\n{\n";
- my ($iter);
- my ($first) = 1;
- my ($decomp_string) = "";
- my ($decomp_string_offset) = 0;
- for ($count = 0; $count <= $last; ++$count)
- {
- if (defined $decompositions[$count])
- {
- print OUT ",\n"
- if ! $first;
- $first = 0;
-
- my $canon_decomp;
- my $compat_decomp;
-
- if (!$decompose_compat[$count]) {
- $canon_decomp = make_decomp ($count, 0);
- }
- $compat_decomp = make_decomp ($count, 1);
-
- if (defined $canon_decomp && $compat_decomp eq $canon_decomp) {
- undef $compat_decomp;
- }
-
- my $canon_offset = handle_decomp ($canon_decomp, \%decomp_offsets, \$decomp_string, \$decomp_string_offset);
- my $compat_offset = handle_decomp ($compat_decomp, \%decomp_offsets, \$decomp_string, \$decomp_string_offset);
-
- die if $decomp_string_offset > $NOT_PRESENT_OFFSET;
-
- printf OUT qq( { 0x%04x, $canon_offset, $compat_offset }), $count;
- $bytes_out += 8;
- }
- }
- print OUT "\n};\n\n";
- $bytes_out += $decomp_string_offset + 1;
-
- printf OUT "static const gchar decomp_expansion_string[] = %s;\n\n", $decomp_string;
-
- print OUT "#endif /* DECOMP_H */\n";
-
- printf STDERR "Generated %d bytes in decomp tables\n", $bytes_out;
-}
-
-sub print_line_break
-{
- my ($last) = @_;
- my ($outfile) = "gunibreak.h";
-
- local ($bytes_out) = 0;
-
- print "Writing $outfile...\n";
-
- open (OUT, "> $outfile");
-
- print OUT "/* This file is automatically generated. DO NOT EDIT!\n";
- print OUT " Instead, edit gen-unicode-tables.pl and re-run. */\n\n";
-
- print OUT "#ifndef BREAKTABLES_H\n";
- print OUT "#define BREAKTABLES_H\n\n";
-
- print OUT "#define G_UNICODE_DATA_VERSION \"$ARGV[0]\"\n\n";
-
- printf OUT "#define G_UNICODE_LAST_CHAR 0x%04X\n\n", $last;
-
- printf OUT "#define G_UNICODE_MAX_TABLE_INDEX 10000\n\n";
-
- my $last_part1 = ($pages_before_e0000 * 256) - 1;
- printf OUT "/* the last code point that should be looked up in break_property_table_part1 */\n";
- printf OUT "#define G_UNICODE_LAST_CHAR_PART1 0x%04X\n\n", $last_part1;
-
- $table_index = 0;
- printf OUT "static const gint8 break_property_data[][256] = {\n";
- for ($count = 0; $count <= $last; $count += 256)
- {
- $row[$count / 256] = &print_row ($count, 1, \&fetch_break_type);
- }
- printf OUT "\n};\n\n";
-
- printf OUT "/* U+0000 through U+%04X */\n", $last_part1;
- print OUT "static const gint16 break_property_table_part1[$pages_before_e0000] = {\n";
- for ($count = 0; $count <= $last_part1; $count += 256)
- {
- print OUT ",\n" if $count > 0;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- printf OUT "/* U+E0000 through U+%04X */\n", $last;
- print OUT "static const gint16 break_property_table_part2[768] = {\n";
- for ($count = 0xE0000; $count <= $last; $count += 256)
- {
- print OUT ",\n" if $count > 0xE0000;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
-
- print OUT "#endif /* BREAKTABLES_H */\n";
-
- close (OUT);
-
- printf STDERR "Generated %d bytes in break tables\n", $bytes_out;
-}
-
-
-# A fetch function for the break properties table.
-sub fetch_break_type
-{
- my ($index) = @_;
- return $break_mappings{$break_props[$index]};
-}
-
-# Fetcher for combining class.
-sub fetch_cclass
-{
- my ($i) = @_;
- return $cclass[$i];
-}
-
-# Expand a character decomposition recursively.
-sub expand_decomp
-{
- my ($code, $compat) = @_;
-
- my ($iter, $val);
- my (@result) = ();
- foreach $iter (split (' ', $decompositions[$code]))
- {
- $val = hex ($iter);
- if (defined $decompositions[$val] &&
- ($compat || !$decompose_compat[$val]))
- {
- push (@result, &expand_decomp ($val, $compat));
- }
- else
- {
- push (@result, $val);
- }
- }
-
- return @result;
-}
-
-sub make_decomp
-{
- my ($code, $compat) = @_;
-
- my $result = "";
- foreach $iter (&expand_decomp ($code, $compat))
- {
- $result .= pack ("U", $iter); # to utf-8
- }
-
- $result;
-}
-# Generate special case data string from two fields
-sub add_special_case
-{
- my ($code, $single, $field1, $field2) = @_;
-
- @values = (defined $single ? $single : (),
- (map { hex ($_) } split /\s+/, $field1),
- 0,
- (map { hex ($_) } split /\s+/, $field2));
- $result = "";
-
-
- for $value (@values) {
- $result .= pack ("U", $value); # to utf-8
- }
-
- push @special_case_offsets, $special_case_offset;
-
- # We encode special cases up in the 0x1000000 space
- $value[$code] = 0x1000000 + $special_case_offset;
-
- $special_case_offset += 1 + &length_in_bytes ($result);
-
- push @special_cases, &escape ($result);
-}
-
-sub output_special_case_table
-{
- my $out = shift;
-
- print $out <<EOT;
-
-/* Table of special cases for case conversion; each record contains
- * First, the best single character mapping to lowercase if Lu,
- * and to uppercase if Ll, followed by the output mapping for the two cases
- * other than the case of the codepoint, in the order [Ll],[Lu],[Lt],
- * encoded in UTF-8, separated and terminated by a null character.
- */
-static const gchar special_case_table[] = {
-EOT
-
- my $i = 0;
- for $case (@special_cases) {
- print $out qq( "$case\\0" /* offset ${special_case_offsets[$i]} */\n);
- $i++;
- }
-
- print $out <<EOT;
-};
-
-EOT
-
- print STDERR "Generated " . ($special_case_offset + 1) . " bytes in special case table\n";
-}
-
-sub enumerate_ordered
-{
- my ($array) = @_;
-
- my $n = 0;
- for my $code (sort { $a <=> $b } keys %$array) {
- if ($array->{$code} == 1) {
- delete $array->{$code};
- next;
- }
- $array->{$code} = $n++;
- }
-
- return $n;
-}
-
-sub output_composition_table
-{
- print STDERR "Generating composition table\n";
-
- local ($bytes_out) = 0;
-
- my %first;
- my %second;
-
- # First we need to go through and remove decompositions
- # starting with a non-starter, and single-character
- # decompositions. At the same time, record
- # the first and second character of each decomposition
-
- for $code (keys %compositions)
- {
- @values = map { hex ($_) } split /\s+/, $compositions{$code};
-
- # non-starters
- if ($cclass[$values[0]]) {
- delete $compositions{$code};
- next;
- }
-
- # single-character decompositions
- if (@values == 1) {
- delete $compositions{$code};
- next;
- }
-
- if (@values != 2) {
- die "$code has more than two elements in its decomposition!\n";
- }
-
- if (exists $first{$values[0]}) {
- $first{$values[0]}++;
- } else {
- $first{$values[0]} = 1;
- }
- }
-
- # Assign integer indices, removing singletons
- my $n_first = enumerate_ordered (\%first);
-
- # Now record the second character of each (non-singleton) decomposition
- for $code (keys %compositions) {
- @values = map { hex ($_) } split /\s+/, $compositions{$code};
-
- if (exists $first{$values[0]}) {
- if (exists $second{$values[1]}) {
- $second{$values[1]}++;
- } else {
- $second{$values[1]} = 1;
- }
- }
- }
-
- # Assign integer indices, removing duplicate
- my $n_second = enumerate_ordered (\%second);
-
- # Build reverse table
-
- my @first_singletons;
- my @second_singletons;
- my %reverse;
- for $code (keys %compositions) {
- @values = map { hex ($_) } split /\s+/, $compositions{$code};
-
- my $first = $first{$values[0]};
- my $second = $second{$values[1]};
-
- if (defined $first && defined $second) {
- $reverse{"$first|$second"} = $code;
- } elsif (!defined $first) {
- push @first_singletons, [ $values[0], $values[1], $code ];
- } else {
- push @second_singletons, [ $values[1], $values[0], $code ];
- }
- }
-
- @first_singletons = sort { $a->[0] <=> $b->[0] } @first_singletons;
- @second_singletons = sort { $a->[0] <=> $b->[0] } @second_singletons;
-
- my %vals;
-
- open OUT, ">gunicomp.h" or die "Cannot open gunicomp.h: $!\n";
-
- # Assign values in lookup table for all code points involved
-
- my $total = 1;
- my $last = 0;
- printf OUT "#define COMPOSE_FIRST_START %d\n", $total;
- for $code (keys %first) {
- $vals{$code} = $first{$code} + $total;
- $last = $code if $code > $last;
- }
- $total += $n_first;
- $i = 0;
- printf OUT "#define COMPOSE_FIRST_SINGLE_START %d\n", $total;
- for $record (@first_singletons) {
- my $code = $record->[0];
- $vals{$code} = $i++ + $total;
- $last = $code if $code > $last;
- }
- $total += @first_singletons;
- printf OUT "#define COMPOSE_SECOND_START %d\n", $total;
- for $code (keys %second) {
- $vals{$code} = $second{$code} + $total;
- $last = $code if $code > $last;
- }
- $total += $n_second;
- $i = 0;
- printf OUT "#define COMPOSE_SECOND_SINGLE_START %d\n\n", $total;
- for $record (@second_singletons) {
- my $code = $record->[0];
- $vals{$code} = $i++ + $total;
- $last = $code if $code > $last;
- }
-
- printf OUT "#define COMPOSE_TABLE_LAST %d\n\n", $last / 256;
-
- # Output lookup table
-
- my @row;
- $table_index = 0;
- printf OUT "static const guint16 compose_data[][256] = {\n";
- for (my $count = 0; $count <= $last; $count += 256)
- {
- $row[$count / 256] = &print_row ($count, 2, sub { exists $vals{$_[0]} ? $vals{$_[0]} : 0; });
- }
- printf OUT "\n};\n\n";
-
- print OUT "static const gint16 compose_table[COMPOSE_TABLE_LAST + 1] = {\n";
- for (my $count = 0; $count <= $last; $count += 256)
- {
- print OUT ",\n" if $count > 0;
- print OUT " ", $row[$count / 256];
- $bytes_out += 2;
- }
- print OUT "\n};\n\n";
-
- # Output first singletons
-
- print OUT "static const guint16 compose_first_single[][2] = {\n";
- $i = 0;
- for $record (@first_singletons) {
- if ($record->[1] > 0xFFFF or $record->[2] > 0xFFFF) {
- die "time to switch compose_first_single to gunichar" ;
- }
- print OUT ",\n" if $i++ > 0;
- printf OUT " { %#06x, %#06x }", $record->[1], $record->[2];
- }
- print OUT "\n};\n";
-
- $bytes_out += @first_singletons * 4;
-
- # Output second singletons
-
- print OUT "static const guint16 compose_second_single[][2] = {\n";
- $i = 0;
- for $record (@second_singletons) {
- if ($record->[1] > 0xFFFF or $record->[2] > 0xFFFF) {
- die "time to switch compose_second_single to gunichar";
- }
- print OUT ",\n" if $i++ > 0;
- printf OUT " { %#06x, %#06x }", $record->[1], $record->[2];
- }
- print OUT "\n};\n";
-
- $bytes_out += @second_singletons * 4;
-
- # Output array of composition pairs
-
- print OUT <<EOT;
-static const guint16 compose_array[$n_first][$n_second] = {
-EOT
-
- for (my $i = 0; $i < $n_first; $i++) {
- print OUT ",\n" if $i;
- print OUT " { ";
- for (my $j = 0; $j < $n_second; $j++) {
- print OUT ", " if $j;
- if (exists $reverse{"$i|$j"}) {
- if ($reverse{"$i|$j"} > 0xFFFF) {
- die "time to switch compose_array to gunichar" ;
- }
- printf OUT "0x%04x", $reverse{"$i|$j"};
- } else {
- print OUT " 0";
- }
- }
- print OUT " }";
- }
- print OUT "\n";
-
- print OUT <<EOT;
-};
-EOT
-
- $bytes_out += $n_first * $n_second * 2;
-
- printf STDERR "Generated %d bytes in compose tables\n", $bytes_out;
-}
-
-sub output_casefold_table
-{
- my $out = shift;
-
- print $out <<EOT;
-
-/* Table of casefolding cases that can't be derived by lowercasing
- */
-static const struct {
- guint16 ch;
- gchar data[$casefoldlen];
-} casefold_table[] = {
-EOT
-
- @casefold = sort { $a->[0] <=> $b->[0] } @casefold;
-
- for $case (@casefold)
- {
- $code = $case->[0];
- $string = $case->[1];
-
- if ($code > 0xFFFF) {
- die "time to switch casefold_table to gunichar" ;
- }
-
- print $out sprintf(qq( { 0x%04x, "$string" },\n), $code);
-
- }
-
- print $out <<EOT;
-};
-
-EOT
-
- my $recordlen = (2+$casefoldlen+1) & ~1;
- printf "Generated %d bytes for casefold table\n", $recordlen * @casefold;
-}
-
-
-
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include "config.h"
-
-#include "gerror.h"
-
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-
-/**
- * g_error_new_valist:
- * @domain: error domain
- * @code: error code
- * @format: printf()-style format for error message
- * @args: #va_list of parameters for the message format
- *
- * Creates a new #GError with the given @domain and @code,
- * and a message formatted with @format.
- *
- * Returns: a new #GError
- *
- * Since: 2.22
- */
-GError*
-g_error_new_valist (GQuark domain,
- gint code,
- const gchar *format,
- va_list args)
-{
- GError *error;
-
- error = g_slice_new (GError);
-
- error->domain = domain;
- error->code = code;
- error->message = g_strdup_vprintf (format, args);
-
- return error;
-}
-
-/**
- * g_error_new:
- * @domain: error domain
- * @code: error code
- * @format: printf()-style format for error message
- * @Varargs: parameters for message format
- *
- * Creates a new #GError with the given @domain and @code,
- * and a message formatted with @format.
- *
- * Return value: a new #GError
- */
-GError*
-g_error_new (GQuark domain,
- gint code,
- const gchar *format,
- ...)
-{
- GError* error;
- va_list args;
-
- g_return_val_if_fail (format != NULL, NULL);
- g_return_val_if_fail (domain != 0, NULL);
-
- va_start (args, format);
- error = g_error_new_valist (domain, code, format, args);
- va_end (args);
-
- return error;
-}
-
-/**
- * g_error_new_literal:
- * @domain: error domain
- * @code: error code
- * @message: error message
- *
- * Creates a new #GError; unlike g_error_new(), @message is
- * not a printf()-style format string. Use this function if
- * @message contains text you don't have control over,
- * that could include printf() escape sequences.
- *
- * Return value: a new #GError
- **/
-GError*
-g_error_new_literal (GQuark domain,
- gint code,
- const gchar *message)
-{
- GError* err;
-
- g_return_val_if_fail (message != NULL, NULL);
- g_return_val_if_fail (domain != 0, NULL);
-
- err = g_slice_new (GError);
-
- err->domain = domain;
- err->code = code;
- err->message = g_strdup (message);
-
- return err;
-}
-
-/**
- * g_error_free:
- * @error: a #GError
- *
- * Frees a #GError and associated resources.
- */
-void
-g_error_free (GError *error)
-{
- g_return_if_fail (error != NULL);
-
- g_free (error->message);
-
- g_slice_free (GError, error);
-}
-
-/**
- * g_error_copy:
- * @error: a #GError
- *
- * Makes a copy of @error.
- *
- * Return value: a new #GError
- */
-GError*
-g_error_copy (const GError *error)
-{
- GError *copy;
-
- g_return_val_if_fail (error != NULL, NULL);
-
- copy = g_slice_new (GError);
-
- *copy = *error;
-
- copy->message = g_strdup (error->message);
-
- return copy;
-}
-
-/**
- * g_error_matches:
- * @error: a #GError or %NULL
- * @domain: an error domain
- * @code: an error code
- *
- * Returns %TRUE if @error matches @domain and @code, %FALSE
- * otherwise. In particular, when @error is %NULL, %FALSE will
- * be returned.
- *
- * Return value: whether @error has @domain and @code
- */
-gboolean
-g_error_matches (const GError *error,
- GQuark domain,
- gint code)
-{
- return error &&
- error->domain == domain &&
- error->code == code;
-}
-
-#define ERROR_OVERWRITTEN_WARNING "GError set over the top of a previous GError or uninitialized memory.\n" \
- "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n" \
- "The overwriting error message was: %s"
-
-/**
- * g_set_error:
- * @err: a return location for a #GError, or %NULL
- * @domain: error domain
- * @code: error code
- * @format: printf()-style format
- * @Varargs: args for @format
- *
- * Does nothing if @err is %NULL; if @err is non-%NULL, then *@err
- * must be %NULL. A new #GError is created and assigned to *@err.
- */
-void
-g_set_error (GError **err,
- GQuark domain,
- gint code,
- const gchar *format,
- ...)
-{
- GError *new;
-
- va_list args;
-
- if (err == NULL)
- return;
-
- va_start (args, format);
- new = g_error_new_valist (domain, code, format, args);
- va_end (args);
-
- if (*err == NULL)
- *err = new;
- else
- g_warning (ERROR_OVERWRITTEN_WARNING, new->message);
-}
-
-/**
- * g_set_error_literal:
- * @err: a return location for a #GError, or %NULL
- * @domain: error domain
- * @code: error code
- * @message: error message
- *
- * Does nothing if @err is %NULL; if @err is non-%NULL, then *@err
- * must be %NULL. A new #GError is created and assigned to *@err.
- * Unlike g_set_error(), @message is not a printf()-style format string.
- * Use this function if @message contains text you don't have control over,
- * that could include printf() escape sequences.
- *
- * Since: 2.18
- */
-void
-g_set_error_literal (GError **err,
- GQuark domain,
- gint code,
- const gchar *message)
-{
- GError *new;
-
- if (err == NULL)
- return;
-
- new = g_error_new_literal (domain, code, message);
- if (*err == NULL)
- *err = new;
- else
- g_warning (ERROR_OVERWRITTEN_WARNING, new->message);
-}
-
-/**
- * g_propagate_error:
- * @dest: error return location
- * @src: error to move into the return location
- *
- * If @dest is %NULL, free @src; otherwise, moves @src into *@dest.
- * The error variable @dest points to must be %NULL.
- */
-void
-g_propagate_error (GError **dest,
- GError *src)
-{
- g_return_if_fail (src != NULL);
-
- if (dest == NULL)
- {
- if (src)
- g_error_free (src);
- return;
- }
- else
- {
- if (*dest != NULL)
- g_warning (ERROR_OVERWRITTEN_WARNING, src->message);
- else
- *dest = src;
- }
-}
-
-/**
- * g_clear_error:
- * @err: a #GError return location
- *
- * If @err is %NULL, does nothing. If @err is non-%NULL,
- * calls g_error_free() on *@err and sets *@err to %NULL.
- */
-void
-g_clear_error (GError **err)
-{
- if (err && *err)
- {
- g_error_free (*err);
- *err = NULL;
- }
-}
-
-static void
-g_error_add_prefix (gchar **string,
- const gchar *format,
- va_list ap)
-{
- gchar *oldstring;
- gchar *prefix;
-
- prefix = g_strdup_vprintf (format, ap);
- oldstring = *string;
- *string = g_strconcat (prefix, oldstring, NULL);
- g_free (oldstring);
- g_free (prefix);
-}
-
-/**
- * g_prefix_error:
- * @err: a return location for a #GError, or %NULL
- * @format: printf()-style format string
- * @...: arguments to @format
- *
- * Formats a string according to @format and
- * prefix it to an existing error message. If
- * @err is %NULL (ie: no error variable) then do
- * nothing.
- *
- * If *@err is %NULL (ie: an error variable is
- * present but there is no error condition) then
- * also do nothing. Whether or not it makes
- * sense to take advantage of this feature is up
- * to you.
- *
- * Since: 2.16
- */
-void
-g_prefix_error (GError **err,
- const gchar *format,
- ...)
-{
- if (err && *err)
- {
- va_list ap;
-
- va_start (ap, format);
- g_error_add_prefix (&(*err)->message, format, ap);
- va_end (ap);
- }
-}
-
-/**
- * g_propagate_prefixed_error:
- * @dest: error return location
- * @src: error to move into the return location
- * @format: printf()-style format string
- * @...: arguments to @format
- *
- * If @dest is %NULL, free @src; otherwise,
- * moves @src into *@dest. *@dest must be %NULL.
- * After the move, add a prefix as with
- * g_prefix_error().
- *
- * Since: 2.16
- **/
-void
-g_propagate_prefixed_error (GError **dest,
- GError *src,
- const gchar *format,
- ...)
-{
- g_propagate_error (dest, src);
-
- if (dest && *dest)
- {
- va_list ap;
-
- va_start (ap, format);
- g_error_add_prefix (&(*dest)->message, format, ap);
- va_end (ap);
- }
-}
+++ /dev/null
-/* gfileutils.c - File utility functions
- *
- * Copyright 2000 Red Hat, Inc.
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#include <io.h>
-#endif /* G_OS_WIN32 */
-
-#ifndef S_ISLNK
-#define S_ISLNK(x) 0
-#endif
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-#include "gfileutils.h"
-
-#include "gstdio.h"
-#include "glibintl.h"
-
-
-/**
- * g_mkdir_with_parents:
- * @pathname: a pathname in the GLib file name encoding
- * @mode: permissions to use for newly created directories
- *
- * Create a directory if it doesn't already exist. Create intermediate
- * parent directories as needed, too.
- *
- * Returns: 0 if the directory already exists, or was successfully
- * created. Returns -1 if an error occurred, with errno set.
- *
- * Since: 2.8
- */
-int
-g_mkdir_with_parents (const gchar *pathname,
- int mode)
-{
- gchar *fn, *p;
-
- if (pathname == NULL || *pathname == '\0')
- {
- errno = EINVAL;
- return -1;
- }
-
- fn = g_strdup (pathname);
-
- if (g_path_is_absolute (fn))
- p = (gchar *) g_path_skip_root (fn);
- else
- p = fn;
-
- do
- {
- while (*p && !G_IS_DIR_SEPARATOR (*p))
- p++;
-
- if (!*p)
- p = NULL;
- else
- *p = '\0';
-
- if (!g_file_test (fn, G_FILE_TEST_EXISTS))
- {
- if (g_mkdir (fn, mode) == -1)
- {
- int errno_save = errno;
- g_free (fn);
- errno = errno_save;
- return -1;
- }
- }
- else if (!g_file_test (fn, G_FILE_TEST_IS_DIR))
- {
- g_free (fn);
- errno = ENOTDIR;
- return -1;
- }
- if (p)
- {
- *p++ = G_DIR_SEPARATOR;
- while (*p && G_IS_DIR_SEPARATOR (*p))
- p++;
- }
- }
- while (p);
-
- g_free (fn);
-
- return 0;
-}
-
-/**
- * g_file_test:
- * @filename: a filename to test in the GLib file name encoding
- * @test: bitfield of #GFileTest flags
- *
- * Returns %TRUE if any of the tests in the bitfield @test are
- * %TRUE. For example, <literal>(G_FILE_TEST_EXISTS |
- * G_FILE_TEST_IS_DIR)</literal> will return %TRUE if the file exists;
- * the check whether it's a directory doesn't matter since the existence
- * test is %TRUE. With the current set of available tests, there's no point
- * passing in more than one test at a time.
- *
- * Apart from %G_FILE_TEST_IS_SYMLINK all tests follow symbolic links,
- * so for a symbolic link to a regular file g_file_test() will return
- * %TRUE for both %G_FILE_TEST_IS_SYMLINK and %G_FILE_TEST_IS_REGULAR.
- *
- * Note, that for a dangling symbolic link g_file_test() will return
- * %TRUE for %G_FILE_TEST_IS_SYMLINK and %FALSE for all other flags.
- *
- * You should never use g_file_test() to test whether it is safe
- * to perform an operation, because there is always the possibility
- * of the condition changing before you actually perform the operation.
- * For example, you might think you could use %G_FILE_TEST_IS_SYMLINK
- * to know whether it is safe to write to a file without being
- * tricked into writing into a different location. It doesn't work!
- * |[
- * /* DON'T DO THIS */
- * if (!g_file_test (filename, G_FILE_TEST_IS_SYMLINK))
- * {
- * fd = g_open (filename, O_WRONLY);
- * /* write to fd */
- * }
- * ]|
- *
- * Another thing to note is that %G_FILE_TEST_EXISTS and
- * %G_FILE_TEST_IS_EXECUTABLE are implemented using the access()
- * system call. This usually doesn't matter, but if your program
- * is setuid or setgid it means that these tests will give you
- * the answer for the real user ID and group ID, rather than the
- * effective user ID and group ID.
- *
- * On Windows, there are no symlinks, so testing for
- * %G_FILE_TEST_IS_SYMLINK will always return %FALSE. Testing for
- * %G_FILE_TEST_IS_EXECUTABLE will just check that the file exists and
- * its name indicates that it is executable, checking for well-known
- * extensions and those listed in the %PATHEXT environment variable.
- *
- * Return value: whether a test was %TRUE
- **/
-gboolean
-g_file_test (const gchar *filename,
- GFileTest test)
-{
-#ifdef G_OS_WIN32
-/* stuff missing in std vc6 api */
-# ifndef INVALID_FILE_ATTRIBUTES
-# define INVALID_FILE_ATTRIBUTES -1
-# endif
-# ifndef FILE_ATTRIBUTE_DEVICE
-# define FILE_ATTRIBUTE_DEVICE 64
-# endif
- int attributes;
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
-
- if (wfilename == NULL)
- return FALSE;
-
- attributes = GetFileAttributesW (wfilename);
-
- g_free (wfilename);
-
- if (attributes == INVALID_FILE_ATTRIBUTES)
- return FALSE;
-
- if (test & G_FILE_TEST_EXISTS)
- return TRUE;
-
- if (test & G_FILE_TEST_IS_REGULAR)
- {
- if ((attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0)
- return TRUE;
- }
-
- if (test & G_FILE_TEST_IS_DIR)
- {
- if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
- return TRUE;
- }
-
- /* "while" so that we can exit this "loop" with a simple "break" */
- while (test & G_FILE_TEST_IS_EXECUTABLE)
- {
- const gchar *lastdot = strrchr (filename, '.');
- const gchar *pathext = NULL, *p;
- int extlen;
-
- if (lastdot == NULL)
- break;
-
- if (_stricmp (lastdot, ".exe") == 0 ||
- _stricmp (lastdot, ".cmd") == 0 ||
- _stricmp (lastdot, ".bat") == 0 ||
- _stricmp (lastdot, ".com") == 0)
- return TRUE;
-
- /* Check if it is one of the types listed in %PATHEXT% */
-
- pathext = g_getenv ("PATHEXT");
- if (pathext == NULL)
- break;
-
- pathext = g_utf8_casefold (pathext, -1);
-
- lastdot = g_utf8_casefold (lastdot, -1);
- extlen = strlen (lastdot);
-
- p = pathext;
- while (TRUE)
- {
- const gchar *q = strchr (p, ';');
- if (q == NULL)
- q = p + strlen (p);
- if (extlen == q - p &&
- memcmp (lastdot, p, extlen) == 0)
- {
- g_free ((gchar *) pathext);
- g_free ((gchar *) lastdot);
- return TRUE;
- }
- if (*q)
- p = q + 1;
- else
- break;
- }
-
- g_free ((gchar *) pathext);
- g_free ((gchar *) lastdot);
- break;
- }
-
- return FALSE;
-#else
- if ((test & G_FILE_TEST_EXISTS) && (access (filename, F_OK) == 0))
- return TRUE;
-
- if ((test & G_FILE_TEST_IS_EXECUTABLE) && (access (filename, X_OK) == 0))
- {
- if (getuid () != 0)
- return TRUE;
-
- /* For root, on some POSIX systems, access (filename, X_OK)
- * will succeed even if no executable bits are set on the
- * file. We fall through to a stat test to avoid that.
- */
- }
- else
- test &= ~G_FILE_TEST_IS_EXECUTABLE;
-
- if (test & G_FILE_TEST_IS_SYMLINK)
- {
- struct stat s;
-
- if ((lstat (filename, &s) == 0) && S_ISLNK (s.st_mode))
- return TRUE;
- }
-
- if (test & (G_FILE_TEST_IS_REGULAR |
- G_FILE_TEST_IS_DIR |
- G_FILE_TEST_IS_EXECUTABLE))
- {
- struct stat s;
-
- if (stat (filename, &s) == 0)
- {
- if ((test & G_FILE_TEST_IS_REGULAR) && S_ISREG (s.st_mode))
- return TRUE;
-
- if ((test & G_FILE_TEST_IS_DIR) && S_ISDIR (s.st_mode))
- return TRUE;
-
- /* The extra test for root when access (file, X_OK) succeeds.
- */
- if ((test & G_FILE_TEST_IS_EXECUTABLE) &&
- ((s.st_mode & S_IXOTH) ||
- (s.st_mode & S_IXUSR) ||
- (s.st_mode & S_IXGRP)))
- return TRUE;
- }
- }
-
- return FALSE;
-#endif
-}
-
-GQuark
-g_file_error_quark (void)
-{
- return g_quark_from_static_string ("g-file-error-quark");
-}
-
-/**
- * g_file_error_from_errno:
- * @err_no: an "errno" value
- *
- * Gets a #GFileError constant based on the passed-in @errno.
- * For example, if you pass in %EEXIST this function returns
- * #G_FILE_ERROR_EXIST. Unlike @errno values, you can portably
- * assume that all #GFileError values will exist.
- *
- * Normally a #GFileError value goes into a #GError returned
- * from a function that manipulates files. So you would use
- * g_file_error_from_errno() when constructing a #GError.
- *
- * Return value: #GFileError corresponding to the given @errno
- **/
-GFileError
-g_file_error_from_errno (gint err_no)
-{
- switch (err_no)
- {
-#ifdef EEXIST
- case EEXIST:
- return G_FILE_ERROR_EXIST;
- break;
-#endif
-
-#ifdef EISDIR
- case EISDIR:
- return G_FILE_ERROR_ISDIR;
- break;
-#endif
-
-#ifdef EACCES
- case EACCES:
- return G_FILE_ERROR_ACCES;
- break;
-#endif
-
-#ifdef ENAMETOOLONG
- case ENAMETOOLONG:
- return G_FILE_ERROR_NAMETOOLONG;
- break;
-#endif
-
-#ifdef ENOENT
- case ENOENT:
- return G_FILE_ERROR_NOENT;
- break;
-#endif
-
-#ifdef ENOTDIR
- case ENOTDIR:
- return G_FILE_ERROR_NOTDIR;
- break;
-#endif
-
-#ifdef ENXIO
- case ENXIO:
- return G_FILE_ERROR_NXIO;
- break;
-#endif
-
-#ifdef ENODEV
- case ENODEV:
- return G_FILE_ERROR_NODEV;
- break;
-#endif
-
-#ifdef EROFS
- case EROFS:
- return G_FILE_ERROR_ROFS;
- break;
-#endif
-
-#ifdef ETXTBSY
- case ETXTBSY:
- return G_FILE_ERROR_TXTBSY;
- break;
-#endif
-
-#ifdef EFAULT
- case EFAULT:
- return G_FILE_ERROR_FAULT;
- break;
-#endif
-
-#ifdef ELOOP
- case ELOOP:
- return G_FILE_ERROR_LOOP;
- break;
-#endif
-
-#ifdef ENOSPC
- case ENOSPC:
- return G_FILE_ERROR_NOSPC;
- break;
-#endif
-
-#ifdef ENOMEM
- case ENOMEM:
- return G_FILE_ERROR_NOMEM;
- break;
-#endif
-
-#ifdef EMFILE
- case EMFILE:
- return G_FILE_ERROR_MFILE;
- break;
-#endif
-
-#ifdef ENFILE
- case ENFILE:
- return G_FILE_ERROR_NFILE;
- break;
-#endif
-
-#ifdef EBADF
- case EBADF:
- return G_FILE_ERROR_BADF;
- break;
-#endif
-
-#ifdef EINVAL
- case EINVAL:
- return G_FILE_ERROR_INVAL;
- break;
-#endif
-
-#ifdef EPIPE
- case EPIPE:
- return G_FILE_ERROR_PIPE;
- break;
-#endif
-
-#ifdef EAGAIN
- case EAGAIN:
- return G_FILE_ERROR_AGAIN;
- break;
-#endif
-
-#ifdef EINTR
- case EINTR:
- return G_FILE_ERROR_INTR;
- break;
-#endif
-
-#ifdef EIO
- case EIO:
- return G_FILE_ERROR_IO;
- break;
-#endif
-
-#ifdef EPERM
- case EPERM:
- return G_FILE_ERROR_PERM;
- break;
-#endif
-
-#ifdef ENOSYS
- case ENOSYS:
- return G_FILE_ERROR_NOSYS;
- break;
-#endif
-
- default:
- return G_FILE_ERROR_FAILED;
- break;
- }
-}
-
-static gboolean
-get_contents_stdio (const gchar *display_filename,
- FILE *f,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- gchar buf[4096];
- gsize bytes;
- gchar *str = NULL;
- gsize total_bytes = 0;
- gsize total_allocated = 0;
- gchar *tmp;
-
- g_assert (f != NULL);
-
- while (!feof (f))
- {
- gint save_errno;
-
- bytes = fread (buf, 1, sizeof (buf), f);
- save_errno = errno;
-
- while ((total_bytes + bytes + 1) > total_allocated)
- {
- if (str)
- total_allocated *= 2;
- else
- total_allocated = MIN (bytes + 1, sizeof (buf));
-
- tmp = g_try_realloc (str, total_allocated);
-
- if (tmp == NULL)
- {
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_NOMEM,
- _("Could not allocate %lu bytes to read file \"%s\""),
- (gulong) total_allocated,
- display_filename);
-
- goto error;
- }
-
- str = tmp;
- }
-
- if (ferror (f))
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Error reading file '%s': %s"),
- display_filename,
- g_strerror (save_errno));
-
- goto error;
- }
-
- memcpy (str + total_bytes, buf, bytes);
-
- if (total_bytes + bytes < total_bytes)
- {
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- _("File \"%s\" is too large"),
- display_filename);
-
- goto error;
- }
-
- total_bytes += bytes;
- }
-
- fclose (f);
-
- if (total_allocated == 0)
- {
- str = g_new (gchar, 1);
- total_bytes = 0;
- }
-
- str[total_bytes] = '\0';
-
- if (length)
- *length = total_bytes;
-
- *contents = str;
-
- return TRUE;
-
- error:
-
- g_free (str);
- fclose (f);
-
- return FALSE;
-}
-
-#ifndef G_OS_WIN32
-
-static gboolean
-get_contents_regfile (const gchar *display_filename,
- struct stat *stat_buf,
- gint fd,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- gchar *buf;
- gsize bytes_read;
- gsize size;
- gsize alloc_size;
-
- size = stat_buf->st_size;
-
- alloc_size = size + 1;
- buf = g_try_malloc (alloc_size);
-
- if (buf == NULL)
- {
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_NOMEM,
- _("Could not allocate %lu bytes to read file \"%s\""),
- (gulong) alloc_size,
- display_filename);
-
- goto error;
- }
-
- bytes_read = 0;
- while (bytes_read < size)
- {
- gssize rc;
-
- rc = read (fd, buf + bytes_read, size - bytes_read);
-
- if (rc < 0)
- {
- if (errno != EINTR)
- {
- int save_errno = errno;
-
- g_free (buf);
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to read from file '%s': %s"),
- display_filename,
- g_strerror (save_errno));
-
- goto error;
- }
- }
- else if (rc == 0)
- break;
- else
- bytes_read += rc;
- }
-
- buf[bytes_read] = '\0';
-
- if (length)
- *length = bytes_read;
-
- *contents = buf;
-
- close (fd);
-
- return TRUE;
-
- error:
-
- close (fd);
-
- return FALSE;
-}
-
-static gboolean
-get_contents_posix (const gchar *filename,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- struct stat stat_buf;
- gint fd;
- gchar *display_filename = g_filename_display_name (filename);
-
- /* O_BINARY useful on Cygwin */
- fd = open (filename, O_RDONLY|O_BINARY);
-
- if (fd < 0)
- {
- int save_errno = errno;
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to open file '%s': %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
-
- return FALSE;
- }
-
- /* I don't think this will ever fail, aside from ENOMEM, but. */
- if (fstat (fd, &stat_buf) < 0)
- {
- int save_errno = errno;
-
- close (fd);
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to get attributes of file '%s': fstat() failed: %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
-
- return FALSE;
- }
-
- if (stat_buf.st_size > 0 && S_ISREG (stat_buf.st_mode))
- {
- gboolean retval = get_contents_regfile (display_filename,
- &stat_buf,
- fd,
- contents,
- length,
- error);
- g_free (display_filename);
-
- return retval;
- }
- else
- {
- FILE *f;
- gboolean retval;
-
- f = fdopen (fd, "r");
-
- if (f == NULL)
- {
- int save_errno = errno;
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to open file '%s': fdopen() failed: %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
-
- return FALSE;
- }
-
- retval = get_contents_stdio (display_filename, f, contents, length, error);
- g_free (display_filename);
-
- return retval;
- }
-}
-
-#else /* G_OS_WIN32 */
-
-static gboolean
-get_contents_win32 (const gchar *filename,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- FILE *f;
- gboolean retval;
- gchar *display_filename = g_filename_display_name (filename);
- int save_errno;
-
- f = g_fopen (filename, "rb");
- save_errno = errno;
-
- if (f == NULL)
- {
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to open file '%s': %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
-
- return FALSE;
- }
-
- retval = get_contents_stdio (display_filename, f, contents, length, error);
- g_free (display_filename);
-
- return retval;
-}
-
-#endif
-
-/**
- * g_file_get_contents:
- * @filename: name of a file to read contents from, in the GLib file name encoding
- * @contents: location to store an allocated string, use g_free() to free
- * the returned string
- * @length: location to store length in bytes of the contents, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Reads an entire file into allocated memory, with good error
- * checking.
- *
- * If the call was successful, it returns %TRUE and sets @contents to the file
- * contents and @length to the length of the file contents in bytes. The string
- * stored in @contents will be nul-terminated, so for text files you can pass
- * %NULL for the @length argument. If the call was not successful, it returns
- * %FALSE and sets @error. The error domain is #G_FILE_ERROR. Possible error
- * codes are those in the #GFileError enumeration. In the error case,
- * @contents is set to %NULL and @length is set to zero.
- *
- * Return value: %TRUE on success, %FALSE if an error occurred
- **/
-gboolean
-g_file_get_contents (const gchar *filename,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- g_return_val_if_fail (filename != NULL, FALSE);
- g_return_val_if_fail (contents != NULL, FALSE);
-
- *contents = NULL;
- if (length)
- *length = 0;
-
-#ifdef G_OS_WIN32
- return get_contents_win32 (filename, contents, length, error);
-#else
- return get_contents_posix (filename, contents, length, error);
-#endif
-}
-
-static gboolean
-rename_file (const char *old_name,
- const char *new_name,
- GError **err)
-{
- errno = 0;
- if (g_rename (old_name, new_name) == -1)
- {
- int save_errno = errno;
- gchar *display_old_name = g_filename_display_name (old_name);
- gchar *display_new_name = g_filename_display_name (new_name);
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to rename file '%s' to '%s': g_rename() failed: %s"),
- display_old_name,
- display_new_name,
- g_strerror (save_errno));
-
- g_free (display_old_name);
- g_free (display_new_name);
-
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gchar *
-write_to_temp_file (const gchar *contents,
- gssize length,
- const gchar *dest_file,
- GError **err)
-{
- gchar *tmp_name;
- gchar *display_name;
- gchar *retval;
- FILE *file;
- gint fd;
- int save_errno;
-
- retval = NULL;
-
- tmp_name = g_strdup_printf ("%s.XXXXXX", dest_file);
-
- errno = 0;
- fd = g_mkstemp_full (tmp_name, O_RDWR | O_BINARY, 0666);
- save_errno = errno;
-
- display_name = g_filename_display_name (tmp_name);
-
- if (fd == -1)
- {
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to create file '%s': %s"),
- display_name, g_strerror (save_errno));
-
- goto out;
- }
-
- errno = 0;
- file = fdopen (fd, "wb");
- if (!file)
- {
- save_errno = errno;
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to open file '%s' for writing: fdopen() failed: %s"),
- display_name,
- g_strerror (save_errno));
-
- close (fd);
- g_unlink (tmp_name);
-
- goto out;
- }
-
- if (length > 0)
- {
- gsize n_written;
-
- errno = 0;
-
- n_written = fwrite (contents, 1, length, file);
-
- if (n_written < length)
- {
- save_errno = errno;
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to write file '%s': fwrite() failed: %s"),
- display_name,
- g_strerror (save_errno));
-
- fclose (file);
- g_unlink (tmp_name);
-
- goto out;
- }
- }
-
- errno = 0;
- if (fflush (file) != 0)
- {
- save_errno = errno;
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to write file '%s': fflush() failed: %s"),
- display_name,
- g_strerror (save_errno));
-
- g_unlink (tmp_name);
-
- goto out;
- }
-
-#ifdef HAVE_FSYNC
- {
- struct stat statbuf;
-
- errno = 0;
- /* If the final destination exists and is > 0 bytes, we want to sync the
- * newly written file to ensure the data is on disk when we rename over
- * the destination. Otherwise if we get a system crash we can lose both
- * the new and the old file on some filesystems. (I.E. those that don't
- * guarantee the data is written to the disk before the metadata.)
- */
- if (g_lstat (dest_file, &statbuf) == 0 &&
- statbuf.st_size > 0 &&
- fsync (fileno (file)) != 0)
- {
- save_errno = errno;
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to write file '%s': fsync() failed: %s"),
- display_name,
- g_strerror (save_errno));
-
- g_unlink (tmp_name);
-
- goto out;
- }
- }
-#endif
-
- errno = 0;
- if (fclose (file) == EOF)
- {
- save_errno = errno;
-
- g_set_error (err,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to close file '%s': fclose() failed: %s"),
- display_name,
- g_strerror (save_errno));
-
- g_unlink (tmp_name);
-
- goto out;
- }
-
- retval = g_strdup (tmp_name);
-
- out:
- g_free (tmp_name);
- g_free (display_name);
-
- return retval;
-}
-
-/**
- * g_file_set_contents:
- * @filename: name of a file to write @contents to, in the GLib file name
- * encoding
- * @contents: string to write to the file
- * @length: length of @contents, or -1 if @contents is a nul-terminated string
- * @error: return location for a #GError, or %NULL
- *
- * Writes all of @contents to a file named @filename, with good error checking.
- * If a file called @filename already exists it will be overwritten.
- *
- * This write is atomic in the sense that it is first written to a temporary
- * file which is then renamed to the final name. Notes:
- * <itemizedlist>
- * <listitem>
- * On Unix, if @filename already exists hard links to @filename will break.
- * Also since the file is recreated, existing permissions, access control
- * lists, metadata etc. may be lost. If @filename is a symbolic link,
- * the link itself will be replaced, not the linked file.
- * </listitem>
- * <listitem>
- * On Windows renaming a file will not remove an existing file with the
- * new name, so on Windows there is a race condition between the existing
- * file being removed and the temporary file being renamed.
- * </listitem>
- * <listitem>
- * On Windows there is no way to remove a file that is open to some
- * process, or mapped into memory. Thus, this function will fail if
- * @filename already exists and is open.
- * </listitem>
- * </itemizedlist>
- *
- * If the call was sucessful, it returns %TRUE. If the call was not successful,
- * it returns %FALSE and sets @error. The error domain is #G_FILE_ERROR.
- * Possible error codes are those in the #GFileError enumeration.
- *
- * Note that the name for the temporary file is constructed by appending up
- * to 7 characters to @filename.
- *
- * Return value: %TRUE on success, %FALSE if an error occurred
- *
- * Since: 2.8
- **/
-gboolean
-g_file_set_contents (const gchar *filename,
- const gchar *contents,
- gssize length,
- GError **error)
-{
- gchar *tmp_filename;
- gboolean retval;
- GError *rename_error = NULL;
-
- g_return_val_if_fail (filename != NULL, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (contents != NULL || length == 0, FALSE);
- g_return_val_if_fail (length >= -1, FALSE);
-
- if (length == -1)
- length = strlen (contents);
-
- tmp_filename = write_to_temp_file (contents, length, filename, error);
-
- if (!tmp_filename)
- {
- retval = FALSE;
- goto out;
- }
-
- if (!rename_file (tmp_filename, filename, &rename_error))
- {
-#ifndef G_OS_WIN32
-
- g_unlink (tmp_filename);
- g_propagate_error (error, rename_error);
- retval = FALSE;
- goto out;
-
-#else /* G_OS_WIN32 */
-
- /* Renaming failed, but on Windows this may just mean
- * the file already exists. So if the target file
- * exists, try deleting it and do the rename again.
- */
- if (!g_file_test (filename, G_FILE_TEST_EXISTS))
- {
- g_unlink (tmp_filename);
- g_propagate_error (error, rename_error);
- retval = FALSE;
- goto out;
- }
-
- g_error_free (rename_error);
-
- if (g_unlink (filename) == -1)
- {
- gchar *display_filename = g_filename_display_name (filename);
-
- int save_errno = errno;
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Existing file '%s' could not be removed: g_unlink() failed: %s"),
- display_filename,
- g_strerror (save_errno));
-
- g_free (display_filename);
- g_unlink (tmp_filename);
- retval = FALSE;
- goto out;
- }
-
- if (!rename_file (tmp_filename, filename, error))
- {
- g_unlink (tmp_filename);
- retval = FALSE;
- goto out;
- }
-
-#endif
- }
-
- retval = TRUE;
-
- out:
- g_free (tmp_filename);
- return retval;
-}
-
-/**
- * g_mkstemp_full:
- * @tmpl: template filename
- * @flags: flags to pass to an open() call in addition to O_EXCL and
- * O_CREAT, which are passed automatically
- * @mode: permissios to create the temporary file with
- *
- * Opens a temporary file. See the mkstemp() documentation
- * on most UNIX-like systems.
- *
- * The parameter is a string that should follow the rules for
- * mkstemp() templates, i.e. contain the string "XXXXXX".
- * g_mkstemp_full() is slightly more flexible than mkstemp()
- * in that the sequence does not have to occur at the very end of the
- * template and you can pass a @mode and additional @flags. The X
- * string will be modified to form the name of a file that didn't exist.
- * The string should be in the GLib file name encoding. Most importantly,
- * on Windows it should be in UTF-8.
- *
- * Return value: A file handle (as from open()) to the file
- * opened for reading and writing. The file handle should be
- * closed with close(). In case of errors, -1 is returned.
- *
- * Since: 2.22
- */
-/*
- * g_mkstemp_full based on the mkstemp implementation from the GNU C library.
- * Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
- */
-gint
-g_mkstemp_full (gchar *tmpl,
- int flags,
- int mode)
-{
- char *XXXXXX;
- int count, fd;
- static const char letters[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- static const int NLETTERS = sizeof (letters) - 1;
- glong value;
- GTimeVal tv;
- static int counter = 0;
-
- g_return_val_if_fail (tmpl != NULL, -1);
-
-
- /* find the last occurrence of "XXXXXX" */
- XXXXXX = g_strrstr (tmpl, "XXXXXX");
-
- if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
- {
- errno = EINVAL;
- return -1;
- }
-
- /* Get some more or less random data. */
- g_get_current_time (&tv);
- value = (tv.tv_usec ^ tv.tv_sec) + counter++;
-
- for (count = 0; count < 100; value += 7777, ++count)
- {
- glong v = value;
-
- /* Fill in the random bits. */
- XXXXXX[0] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[1] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[2] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[3] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[4] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[5] = letters[v % NLETTERS];
-
- /* tmpl is in UTF-8 on Windows, thus use g_open() */
- fd = g_open (tmpl, flags | O_CREAT | O_EXCL, mode);
-
- if (fd >= 0)
- return fd;
- else if (errno != EEXIST)
- /* Any other error will apply also to other names we might
- * try, and there are 2^32 or so of them, so give up now.
- */
- return -1;
- }
-
- /* We got out of the loop because we ran out of combinations to try. */
- errno = EEXIST;
- return -1;
-}
-
-/**
- * g_mkstemp:
- * @tmpl: template filename
- *
- * Opens a temporary file. See the mkstemp() documentation
- * on most UNIX-like systems.
- *
- * The parameter is a string that should follow the rules for
- * mkstemp() templates, i.e. contain the string "XXXXXX".
- * g_mkstemp() is slightly more flexible than mkstemp()
- * in that the sequence does not have to occur at the very end of the
- * template. The X string will
- * be modified to form the name of a file that didn't exist.
- * The string should be in the GLib file name encoding. Most importantly,
- * on Windows it should be in UTF-8.
- *
- * Return value: A file handle (as from open()) to the file
- * opened for reading and writing. The file is opened in binary mode
- * on platforms where there is a difference. The file handle should be
- * closed with close(). In case of errors, -1 is returned.
- */
-gint
-g_mkstemp (gchar *tmpl)
-{
- return g_mkstemp_full (tmpl, O_RDWR | O_BINARY, 0600);
-}
-
-/**
- * g_file_open_tmp:
- * @tmpl: Template for file name, as in g_mkstemp(), basename only,
- * or %NULL, to a default template
- * @name_used: location to store actual name used, or %NULL
- * @error: return location for a #GError
- *
- * Opens a file for writing in the preferred directory for temporary
- * files (as returned by g_get_tmp_dir()).
- *
- * @tmpl should be a string in the GLib file name encoding containing
- * a sequence of six 'X' characters, as the parameter to g_mkstemp().
- * However, unlike these functions, the template should only be a
- * basename, no directory components are allowed. If template is
- * %NULL, a default template is used.
- *
- * Note that in contrast to g_mkstemp() (and mkstemp())
- * @tmpl is not modified, and might thus be a read-only literal string.
- *
- * The actual name used is returned in @name_used if non-%NULL. This
- * string should be freed with g_free() when not needed any longer.
- * The returned name is in the GLib file name encoding.
- *
- * Return value: A file handle (as from open()) to
- * the file opened for reading and writing. The file is opened in binary
- * mode on platforms where there is a difference. The file handle should be
- * closed with close(). In case of errors, -1 is returned
- * and @error will be set.
- **/
-gint
-g_file_open_tmp (const gchar *tmpl,
- gchar **name_used,
- GError **error)
-{
- int retval;
- const char *tmpdir;
- const char *sep;
- char *fulltemplate;
- const char *slash;
-
- if (tmpl == NULL)
- tmpl = ".XXXXXX";
-
- if ((slash = strchr (tmpl, G_DIR_SEPARATOR)) != NULL
-#ifdef G_OS_WIN32
- || (strchr (tmpl, '/') != NULL && (slash = "/"))
-#endif
- )
- {
- gchar *display_tmpl = g_filename_display_name (tmpl);
- char c[2];
- c[0] = *slash;
- c[1] = '\0';
-
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- _("Template '%s' invalid, should not contain a '%s'"),
- display_tmpl, c);
- g_free (display_tmpl);
-
- return -1;
- }
-
- if (strstr (tmpl, "XXXXXX") == NULL)
- {
- gchar *display_tmpl = g_filename_display_name (tmpl);
- g_set_error (error,
- G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- _("Template '%s' doesn't contain XXXXXX"),
- display_tmpl);
- g_free (display_tmpl);
- return -1;
- }
-
- tmpdir = g_get_tmp_dir ();
-
- if (G_IS_DIR_SEPARATOR (tmpdir [strlen (tmpdir) - 1]))
- sep = "";
- else
- sep = G_DIR_SEPARATOR_S;
-
- fulltemplate = g_strconcat (tmpdir, sep, tmpl, NULL);
-
- retval = g_mkstemp (fulltemplate);
-
- if (retval == -1)
- {
- int save_errno = errno;
- gchar *display_fulltemplate = g_filename_display_name (fulltemplate);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to create file '%s': %s"),
- display_fulltemplate, g_strerror (save_errno));
- g_free (display_fulltemplate);
- g_free (fulltemplate);
- return -1;
- }
-
- if (name_used)
- *name_used = fulltemplate;
- else
- g_free (fulltemplate);
-
- return retval;
-}
-
-static gchar *
-g_build_path_va (const gchar *separator,
- const gchar *first_element,
- va_list *args,
- gchar **str_array)
-{
- GString *result;
- gint separator_len = strlen (separator);
- gboolean is_first = TRUE;
- gboolean have_leading = FALSE;
- const gchar *single_element = NULL;
- const gchar *next_element;
- const gchar *last_trailing = NULL;
- gint i = 0;
-
- result = g_string_new (NULL);
-
- if (str_array)
- next_element = str_array[i++];
- else
- next_element = first_element;
-
- while (TRUE)
- {
- const gchar *element;
- const gchar *start;
- const gchar *end;
-
- if (next_element)
- {
- element = next_element;
- if (str_array)
- next_element = str_array[i++];
- else
- next_element = va_arg (*args, gchar *);
- }
- else
- break;
-
- /* Ignore empty elements */
- if (!*element)
- continue;
-
- start = element;
-
- if (separator_len)
- {
- while (strncmp (start, separator, separator_len) == 0)
- start += separator_len;
- }
-
- end = start + strlen (start);
-
- if (separator_len)
- {
- while (end >= start + separator_len &&
- strncmp (end - separator_len, separator, separator_len) == 0)
- end -= separator_len;
-
- last_trailing = end;
- while (last_trailing >= element + separator_len &&
- strncmp (last_trailing - separator_len, separator, separator_len) == 0)
- last_trailing -= separator_len;
-
- if (!have_leading)
- {
- /* If the leading and trailing separator strings are in the
- * same element and overlap, the result is exactly that element
- */
- if (last_trailing <= start)
- single_element = element;
-
- g_string_append_len (result, element, start - element);
- have_leading = TRUE;
- }
- else
- single_element = NULL;
- }
-
- if (end == start)
- continue;
-
- if (!is_first)
- g_string_append (result, separator);
-
- g_string_append_len (result, start, end - start);
- is_first = FALSE;
- }
-
- if (single_element)
- {
- g_string_free (result, TRUE);
- return g_strdup (single_element);
- }
- else
- {
- if (last_trailing)
- g_string_append (result, last_trailing);
-
- return g_string_free (result, FALSE);
- }
-}
-
-/**
- * g_build_pathv:
- * @separator: a string used to separator the elements of the path.
- * @args: %NULL-terminated array of strings containing the path elements.
- *
- * Behaves exactly like g_build_path(), but takes the path elements
- * as a string array, instead of varargs. This function is mainly
- * meant for language bindings.
- *
- * Return value: a newly-allocated string that must be freed with g_free().
- *
- * Since: 2.8
- */
-gchar *
-g_build_pathv (const gchar *separator,
- gchar **args)
-{
- if (!args)
- return NULL;
-
- return g_build_path_va (separator, NULL, NULL, args);
-}
-
-
-/**
- * g_build_path:
- * @separator: a string used to separator the elements of the path.
- * @first_element: the first element in the path
- * @Varargs: remaining elements in path, terminated by %NULL
- *
- * Creates a path from a series of elements using @separator as the
- * separator between elements. At the boundary between two elements,
- * any trailing occurrences of separator in the first element, or
- * leading occurrences of separator in the second element are removed
- * and exactly one copy of the separator is inserted.
- *
- * Empty elements are ignored.
- *
- * The number of leading copies of the separator on the result is
- * the same as the number of leading copies of the separator on
- * the first non-empty element.
- *
- * The number of trailing copies of the separator on the result is
- * the same as the number of trailing copies of the separator on
- * the last non-empty element. (Determination of the number of
- * trailing copies is done without stripping leading copies, so
- * if the separator is <literal>ABA</literal>, <literal>ABABA</literal>
- * has 1 trailing copy.)
- *
- * However, if there is only a single non-empty element, and there
- * are no characters in that element not part of the leading or
- * trailing separators, then the result is exactly the original value
- * of that element.
- *
- * Other than for determination of the number of leading and trailing
- * copies of the separator, elements consisting only of copies
- * of the separator are ignored.
- *
- * Return value: a newly-allocated string that must be freed with g_free().
- **/
-gchar *
-g_build_path (const gchar *separator,
- const gchar *first_element,
- ...)
-{
- gchar *str;
- va_list args;
-
- g_return_val_if_fail (separator != NULL, NULL);
-
- va_start (args, first_element);
- str = g_build_path_va (separator, first_element, &args, NULL);
- va_end (args);
-
- return str;
-}
-
-#ifdef G_OS_WIN32
-
-static gchar *
-g_build_pathname_va (const gchar *first_element,
- va_list *args,
- gchar **str_array)
-{
- /* Code copied from g_build_pathv(), and modified to use two
- * alternative single-character separators.
- */
- GString *result;
- gboolean is_first = TRUE;
- gboolean have_leading = FALSE;
- const gchar *single_element = NULL;
- const gchar *next_element;
- const gchar *last_trailing = NULL;
- gchar current_separator = '\\';
- gint i = 0;
-
- result = g_string_new (NULL);
-
- if (str_array)
- next_element = str_array[i++];
- else
- next_element = first_element;
-
- while (TRUE)
- {
- const gchar *element;
- const gchar *start;
- const gchar *end;
-
- if (next_element)
- {
- element = next_element;
- if (str_array)
- next_element = str_array[i++];
- else
- next_element = va_arg (*args, gchar *);
- }
- else
- break;
-
- /* Ignore empty elements */
- if (!*element)
- continue;
-
- start = element;
-
- if (TRUE)
- {
- while (start &&
- (*start == '\\' || *start == '/'))
- {
- current_separator = *start;
- start++;
- }
- }
-
- end = start + strlen (start);
-
- if (TRUE)
- {
- while (end >= start + 1 &&
- (end[-1] == '\\' || end[-1] == '/'))
- {
- current_separator = end[-1];
- end--;
- }
-
- last_trailing = end;
- while (last_trailing >= element + 1 &&
- (last_trailing[-1] == '\\' || last_trailing[-1] == '/'))
- last_trailing--;
-
- if (!have_leading)
- {
- /* If the leading and trailing separator strings are in the
- * same element and overlap, the result is exactly that element
- */
- if (last_trailing <= start)
- single_element = element;
-
- g_string_append_len (result, element, start - element);
- have_leading = TRUE;
- }
- else
- single_element = NULL;
- }
-
- if (end == start)
- continue;
-
- if (!is_first)
- g_string_append_len (result, ¤t_separator, 1);
-
- g_string_append_len (result, start, end - start);
- is_first = FALSE;
- }
-
- if (single_element)
- {
- g_string_free (result, TRUE);
- return g_strdup (single_element);
- }
- else
- {
- if (last_trailing)
- g_string_append (result, last_trailing);
-
- return g_string_free (result, FALSE);
- }
-}
-
-#endif
-
-/**
- * g_build_filenamev:
- * @args: %NULL-terminated array of strings containing the path elements.
- *
- * Behaves exactly like g_build_filename(), but takes the path elements
- * as a string array, instead of varargs. This function is mainly
- * meant for language bindings.
- *
- * Return value: a newly-allocated string that must be freed with g_free().
- *
- * Since: 2.8
- */
-gchar *
-g_build_filenamev (gchar **args)
-{
- gchar *str;
-
-#ifndef G_OS_WIN32
- str = g_build_path_va (G_DIR_SEPARATOR_S, NULL, NULL, args);
-#else
- str = g_build_pathname_va (NULL, NULL, args);
-#endif
-
- return str;
-}
-
-/**
- * g_build_filename:
- * @first_element: the first element in the path
- * @Varargs: remaining elements in path, terminated by %NULL
- *
- * Creates a filename from a series of elements using the correct
- * separator for filenames.
- *
- * On Unix, this function behaves identically to <literal>g_build_path
- * (G_DIR_SEPARATOR_S, first_element, ....)</literal>.
- *
- * On Windows, it takes into account that either the backslash
- * (<literal>\</literal> or slash (<literal>/</literal>) can be used
- * as separator in filenames, but otherwise behaves as on Unix. When
- * file pathname separators need to be inserted, the one that last
- * previously occurred in the parameters (reading from left to right)
- * is used.
- *
- * No attempt is made to force the resulting filename to be an absolute
- * path. If the first element is a relative path, the result will
- * be a relative path.
- *
- * Return value: a newly-allocated string that must be freed with g_free().
- **/
-gchar *
-g_build_filename (const gchar *first_element,
- ...)
-{
- gchar *str;
- va_list args;
-
- va_start (args, first_element);
-#ifndef G_OS_WIN32
- str = g_build_path_va (G_DIR_SEPARATOR_S, first_element, &args, NULL);
-#else
- str = g_build_pathname_va (first_element, &args, NULL);
-#endif
- va_end (args);
-
- return str;
-}
-
-#define KILOBYTE_FACTOR (G_GOFFSET_CONSTANT (1024))
-#define MEGABYTE_FACTOR (KILOBYTE_FACTOR * KILOBYTE_FACTOR)
-#define GIGABYTE_FACTOR (MEGABYTE_FACTOR * KILOBYTE_FACTOR)
-#define TERABYTE_FACTOR (GIGABYTE_FACTOR * KILOBYTE_FACTOR)
-#define PETABYTE_FACTOR (TERABYTE_FACTOR * KILOBYTE_FACTOR)
-#define EXABYTE_FACTOR (PETABYTE_FACTOR * KILOBYTE_FACTOR)
-
-/**
- * g_format_size_for_display:
- * @size: a size in bytes.
- *
- * Formats a size (for example the size of a file) into a human readable string.
- * Sizes are rounded to the nearest size prefix (KB, MB, GB) and are displayed
- * rounded to the nearest tenth. E.g. the file size 3292528 bytes will be
- * converted into the string "3.1 MB".
- *
- * The prefix units base is 1024 (i.e. 1 KB is 1024 bytes).
- *
- * This string should be freed with g_free() when not needed any longer.
- *
- * Returns: a newly-allocated formatted string containing a human readable
- * file size.
- *
- * Since: 2.16
- **/
-char *
-g_format_size_for_display (goffset size)
-{
- if (size < (goffset) KILOBYTE_FACTOR)
- return g_strdup_printf (g_dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size), (guint) size);
- else
- {
- gdouble displayed_size;
-
- if (size < (goffset) MEGABYTE_FACTOR)
- {
- displayed_size = (gdouble) size / (gdouble) KILOBYTE_FACTOR;
- return g_strdup_printf (_("%.1f KB"), displayed_size);
- }
- else if (size < (goffset) GIGABYTE_FACTOR)
- {
- displayed_size = (gdouble) size / (gdouble) MEGABYTE_FACTOR;
- return g_strdup_printf (_("%.1f MB"), displayed_size);
- }
- else if (size < (goffset) TERABYTE_FACTOR)
- {
- displayed_size = (gdouble) size / (gdouble) GIGABYTE_FACTOR;
- return g_strdup_printf (_("%.1f GB"), displayed_size);
- }
- else if (size < (goffset) PETABYTE_FACTOR)
- {
- displayed_size = (gdouble) size / (gdouble) TERABYTE_FACTOR;
- return g_strdup_printf (_("%.1f TB"), displayed_size);
- }
- else if (size < (goffset) EXABYTE_FACTOR)
- {
- displayed_size = (gdouble) size / (gdouble) PETABYTE_FACTOR;
- return g_strdup_printf (_("%.1f PB"), displayed_size);
- }
- else
- {
- displayed_size = (gdouble) size / (gdouble) EXABYTE_FACTOR;
- return g_strdup_printf (_("%.1f EB"), displayed_size);
- }
- }
-}
-
-
-/**
- * g_file_read_link:
- * @filename: the symbolic link
- * @error: return location for a #GError
- *
- * Reads the contents of the symbolic link @filename like the POSIX
- * readlink() function. The returned string is in the encoding used
- * for filenames. Use g_filename_to_utf8() to convert it to UTF-8.
- *
- * Returns: A newly-allocated string with the contents of the symbolic link,
- * or %NULL if an error occurred.
- *
- * Since: 2.4
- */
-gchar *
-g_file_read_link (const gchar *filename,
- GError **error)
-{
-#ifdef HAVE_READLINK
- gchar *buffer;
- guint size;
- gint read_size;
-
- size = 256;
- buffer = g_malloc (size);
-
- while (TRUE)
- {
- read_size = readlink (filename, buffer, size);
- if (read_size < 0) {
- int save_errno = errno;
- gchar *display_filename = g_filename_display_name (filename);
-
- g_free (buffer);
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to read the symbolic link '%s': %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
-
- return NULL;
- }
-
- if (read_size < size)
- {
- buffer[read_size] = 0;
- return buffer;
- }
-
- size *= 2;
- buffer = g_realloc (buffer, size);
- }
-#else
- g_set_error_literal (error,
- G_FILE_ERROR,
- G_FILE_ERROR_INVAL,
- _("Symbolic links not supported"));
-
- return NULL;
-#endif
-}
-
-/* NOTE : Keep this part last to ensure nothing in this file uses the
- * below binary compatibility versions.
- */
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-/* Binary compatibility versions. Will be called by code compiled
- * against quite old (pre-2.8, I think) headers only, not from more
- * recently compiled code.
- */
-
-#undef g_file_test
-
-gboolean
-g_file_test (const gchar *filename,
- GFileTest test)
-{
- gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
- gboolean retval;
-
- if (utf8_filename == NULL)
- return FALSE;
-
- retval = g_file_test_utf8 (utf8_filename, test);
-
- g_free (utf8_filename);
-
- return retval;
-}
-
-#undef g_file_get_contents
-
-gboolean
-g_file_get_contents (const gchar *filename,
- gchar **contents,
- gsize *length,
- GError **error)
-{
- gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
- gboolean retval;
-
- if (utf8_filename == NULL)
- return FALSE;
-
- retval = g_file_get_contents_utf8 (utf8_filename, contents, length, error);
-
- g_free (utf8_filename);
-
- return retval;
-}
-
-#undef g_mkstemp
-
-gint
-g_mkstemp (gchar *tmpl)
-{
- char *XXXXXX;
- int count, fd;
- static const char letters[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- static const int NLETTERS = sizeof (letters) - 1;
- glong value;
- GTimeVal tv;
- static int counter = 0;
-
- /* find the last occurrence of 'XXXXXX' */
- XXXXXX = g_strrstr (tmpl, "XXXXXX");
-
- if (!XXXXXX)
- {
- errno = EINVAL;
- return -1;
- }
-
- /* Get some more or less random data. */
- g_get_current_time (&tv);
- value = (tv.tv_usec ^ tv.tv_sec) + counter++;
-
- for (count = 0; count < 100; value += 7777, ++count)
- {
- glong v = value;
-
- /* Fill in the random bits. */
- XXXXXX[0] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[1] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[2] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[3] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[4] = letters[v % NLETTERS];
- v /= NLETTERS;
- XXXXXX[5] = letters[v % NLETTERS];
-
- /* This is the backward compatibility system codepage version,
- * thus use normal open().
- */
- fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
-
- if (fd >= 0)
- return fd;
- else if (errno != EEXIST)
- /* Any other error will apply also to other names we might
- * try, and there are 2^32 or so of them, so give up now.
- */
- return -1;
- }
-
- /* We got out of the loop because we ran out of combinations to try. */
- errno = EEXIST;
- return -1;
-}
-
-#undef g_file_open_tmp
-
-gint
-g_file_open_tmp (const gchar *tmpl,
- gchar **name_used,
- GError **error)
-{
- gchar *utf8_tmpl = g_locale_to_utf8 (tmpl, -1, NULL, NULL, error);
- gchar *utf8_name_used;
- gint retval;
-
- if (utf8_tmpl == NULL)
- return -1;
-
- retval = g_file_open_tmp_utf8 (utf8_tmpl, &utf8_name_used, error);
-
- if (retval == -1)
- return -1;
-
- if (name_used)
- *name_used = g_locale_from_utf8 (utf8_name_used, -1, NULL, NULL, NULL);
-
- g_free (utf8_name_used);
-
- return retval;
-}
-
-#endif
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <string.h> /* memset */
-
-#include "ghash.h"
-
-#include "gatomic.h"
-#include "gtestutils.h"
-
-
-/**
- * SECTION: hash_tables
- * @title: Hash Tables
- * @short_description: associations between keys and values so that
- * given a key the value can be found quickly
- *
- * A #GHashTable provides associations between keys and values which is
- * optimized so that given a key, the associated value can be found
- * very quickly.
- *
- * Note that neither keys nor values are copied when inserted into the
- * #GHashTable, so they must exist for the lifetime of the #GHashTable.
- * This means that the use of static strings is OK, but temporary
- * strings (i.e. those created in buffers and those returned by GTK+
- * widgets) should be copied with g_strdup() before being inserted.
- *
- * If keys or values are dynamically allocated, you must be careful to
- * ensure that they are freed when they are removed from the
- * #GHashTable, and also when they are overwritten by new insertions
- * into the #GHashTable. It is also not advisable to mix static strings
- * and dynamically-allocated strings in a #GHashTable, because it then
- * becomes difficult to determine whether the string should be freed.
- *
- * To create a #GHashTable, use g_hash_table_new().
- *
- * To insert a key and value into a #GHashTable, use
- * g_hash_table_insert().
- *
- * To lookup a value corresponding to a given key, use
- * g_hash_table_lookup() and g_hash_table_lookup_extended().
- *
- * To remove a key and value, use g_hash_table_remove().
- *
- * To call a function for each key and value pair use
- * g_hash_table_foreach() or use a iterator to iterate over the
- * key/value pairs in the hash table, see #GHashTableIter.
- *
- * To destroy a #GHashTable use g_hash_table_destroy().
- **/
-
-/**
- * GHashTable:
- *
- * The #GHashTable struct is an opaque data structure to represent a
- * <link linkend="glib-Hash-Tables">Hash Table</link>. It should only be
- * accessed via the following functions.
- **/
-
-/**
- * GHashFunc:
- * @key: a key.
- * @Returns: the hash value corresponding to the key.
- *
- * Specifies the type of the hash function which is passed to
- * g_hash_table_new() when a #GHashTable is created.
- *
- * The function is passed a key and should return a #guint hash value.
- * The functions g_direct_hash(), g_int_hash() and g_str_hash() provide
- * hash functions which can be used when the key is a #gpointer, #gint,
- * and #gchar* respectively.
- *
- * <!-- FIXME: Need more here. --> The hash values should be evenly
- * distributed over a fairly large range? The modulus is taken with the
- * hash table size (a prime number) to find the 'bucket' to place each
- * key into. The function should also be very fast, since it is called
- * for each key lookup.
- **/
-
-/**
- * GHFunc:
- * @key: a key.
- * @value: the value corresponding to the key.
- * @user_data: user data passed to g_hash_table_foreach().
- *
- * Specifies the type of the function passed to g_hash_table_foreach().
- * It is called with each key/value pair, together with the @user_data
- * parameter which is passed to g_hash_table_foreach().
- **/
-
-/**
- * GHRFunc:
- * @key: a key.
- * @value: the value associated with the key.
- * @user_data: user data passed to g_hash_table_remove().
- * @Returns: %TRUE if the key/value pair should be removed from the
- * #GHashTable.
- *
- * Specifies the type of the function passed to
- * g_hash_table_foreach_remove(). It is called with each key/value
- * pair, together with the @user_data parameter passed to
- * g_hash_table_foreach_remove(). It should return %TRUE if the
- * key/value pair should be removed from the #GHashTable.
- **/
-
-/**
- * GEqualFunc:
- * @a: a value.
- * @b: a value to compare with.
- * @Returns: %TRUE if @a = @b; %FALSE otherwise.
- *
- * Specifies the type of a function used to test two values for
- * equality. The function should return %TRUE if both values are equal
- * and %FALSE otherwise.
- **/
-
-/**
- * GHashTableIter:
- *
- * A GHashTableIter structure represents an iterator that can be used
- * to iterate over the elements of a #GHashTable. GHashTableIter
- * structures are typically allocated on the stack and then initialized
- * with g_hash_table_iter_init().
- **/
-
-#define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */
-
-typedef struct _GHashNode GHashNode;
-
-struct _GHashNode
-{
- gpointer key;
- gpointer value;
-
- /* If key_hash == 0, node is not in use
- * If key_hash == 1, node is a tombstone
- * If key_hash >= 2, node contains data */
- guint key_hash;
-};
-
-struct _GHashTable
-{
- gint size;
- gint mod;
- guint mask;
- gint nnodes;
- gint noccupied; /* nnodes + tombstones */
- GHashNode *nodes;
- GHashFunc hash_func;
- GEqualFunc key_equal_func;
- volatile gint ref_count;
-#ifndef G_DISABLE_ASSERT
- /*
- * Tracks the structure of the hash table, not its contents: is only
- * incremented when a node is added or removed (is not incremented
- * when the key or data of a node is modified).
- */
- int version;
-#endif
- GDestroyNotify key_destroy_func;
- GDestroyNotify value_destroy_func;
-};
-
-typedef struct
-{
- GHashTable *hash_table;
- gpointer dummy1;
- gpointer dummy2;
- int position;
- gboolean dummy3;
- int version;
-} RealIter;
-
-/* Each table size has an associated prime modulo (the first prime
- * lower than the table size) used to find the initial bucket. Probing
- * then works modulo 2^n. The prime modulo is necessary to get a
- * good distribution with poor hash functions. */
-static const gint prime_mod [] =
-{
- 1, /* For 1 << 0 */
- 2,
- 3,
- 7,
- 13,
- 31,
- 61,
- 127,
- 251,
- 509,
- 1021,
- 2039,
- 4093,
- 8191,
- 16381,
- 32749,
- 65521, /* For 1 << 16 */
- 131071,
- 262139,
- 524287,
- 1048573,
- 2097143,
- 4194301,
- 8388593,
- 16777213,
- 33554393,
- 67108859,
- 134217689,
- 268435399,
- 536870909,
- 1073741789,
- 2147483647 /* For 1 << 31 */
-};
-
-static void
-g_hash_table_set_shift (GHashTable *hash_table, gint shift)
-{
- gint i;
- guint mask = 0;
-
- hash_table->size = 1 << shift;
- hash_table->mod = prime_mod [shift];
-
- for (i = 0; i < shift; i++)
- {
- mask <<= 1;
- mask |= 1;
- }
-
- hash_table->mask = mask;
-}
-
-static gint
-g_hash_table_find_closest_shift (gint n)
-{
- gint i;
-
- for (i = 0; n; i++)
- n >>= 1;
-
- return i;
-}
-
-static void
-g_hash_table_set_shift_from_size (GHashTable *hash_table, gint size)
-{
- gint shift;
-
- shift = g_hash_table_find_closest_shift (size);
- shift = MAX (shift, HASH_TABLE_MIN_SHIFT);
-
- g_hash_table_set_shift (hash_table, shift);
-}
-
-/*
- * g_hash_table_lookup_node:
- * @hash_table: our #GHashTable
- * @key: the key to lookup against
- * @hash_return: optional key hash return location
- * Return value: index of the described #GHashNode
- *
- * Performs a lookup in the hash table. Virtually all hash operations
- * will use this function internally.
- *
- * This function first computes the hash value of the key using the
- * user's hash function.
- *
- * If an entry in the table matching @key is found then this function
- * returns the index of that entry in the table, and if not, the
- * index of an empty node (never a tombstone).
- */
-static inline guint
-g_hash_table_lookup_node (GHashTable *hash_table,
- gconstpointer key)
-{
- GHashNode *node;
- guint node_index;
- guint hash_value;
- guint step = 0;
-
- /* Empty buckets have hash_value set to 0, and for tombstones, it's 1.
- * We need to make sure our hash value is not one of these. */
-
- hash_value = (* hash_table->hash_func) (key);
- if (G_UNLIKELY (hash_value <= 1))
- hash_value = 2;
-
- node_index = hash_value % hash_table->mod;
- node = &hash_table->nodes [node_index];
-
- while (node->key_hash)
- {
- /* We first check if our full hash values
- * are equal so we can avoid calling the full-blown
- * key equality function in most cases.
- */
-
- if (node->key_hash == hash_value)
- {
- if (hash_table->key_equal_func)
- {
- if (hash_table->key_equal_func (node->key, key))
- break;
- }
- else if (node->key == key)
- {
- break;
- }
- }
-
- step++;
- node_index += step;
- node_index &= hash_table->mask;
- node = &hash_table->nodes [node_index];
- }
-
- return node_index;
-}
-
-/*
- * g_hash_table_lookup_node_for_insertion:
- * @hash_table: our #GHashTable
- * @key: the key to lookup against
- * @hash_return: key hash return location
- * Return value: index of the described #GHashNode
- *
- * Performs a lookup in the hash table, preserving extra information
- * usually needed for insertion.
- *
- * This function first computes the hash value of the key using the
- * user's hash function.
- *
- * If an entry in the table matching @key is found then this function
- * returns the index of that entry in the table, and if not, the
- * index of an unused node (empty or tombstone) where the key can be
- * inserted.
- *
- * The computed hash value is returned in the variable pointed to
- * by @hash_return. This is to save insertions from having to compute
- * the hash record again for the new record.
- */
-static inline guint
-g_hash_table_lookup_node_for_insertion (GHashTable *hash_table,
- gconstpointer key,
- guint *hash_return)
-{
- GHashNode *node;
- guint node_index;
- guint hash_value;
- guint first_tombstone;
- gboolean have_tombstone = FALSE;
- guint step = 0;
-
- /* Empty buckets have hash_value set to 0, and for tombstones, it's 1.
- * We need to make sure our hash value is not one of these. */
-
- hash_value = (* hash_table->hash_func) (key);
- if (G_UNLIKELY (hash_value <= 1))
- hash_value = 2;
-
- *hash_return = hash_value;
-
- node_index = hash_value % hash_table->mod;
- node = &hash_table->nodes [node_index];
-
- while (node->key_hash)
- {
- /* We first check if our full hash values
- * are equal so we can avoid calling the full-blown
- * key equality function in most cases.
- */
-
- if (node->key_hash == hash_value)
- {
- if (hash_table->key_equal_func)
- {
- if (hash_table->key_equal_func (node->key, key))
- return node_index;
- }
- else if (node->key == key)
- {
- return node_index;
- }
- }
- else if (node->key_hash == 1 && !have_tombstone)
- {
- first_tombstone = node_index;
- have_tombstone = TRUE;
- }
-
- step++;
- node_index += step;
- node_index &= hash_table->mask;
- node = &hash_table->nodes [node_index];
- }
-
- if (have_tombstone)
- return first_tombstone;
-
- return node_index;
-}
-
-/*
- * g_hash_table_remove_node:
- * @hash_table: our #GHashTable
- * @node: pointer to node to remove
- * @notify: %TRUE if the destroy notify handlers are to be called
- *
- * Removes a node from the hash table and updates the node count.
- * The node is replaced by a tombstone. No table resize is performed.
- *
- * If @notify is %TRUE then the destroy notify functions are called
- * for the key and value of the hash node.
- */
-static void
-g_hash_table_remove_node (GHashTable *hash_table,
- GHashNode *node,
- gboolean notify)
-{
- if (notify && hash_table->key_destroy_func)
- hash_table->key_destroy_func (node->key);
-
- if (notify && hash_table->value_destroy_func)
- hash_table->value_destroy_func (node->value);
-
- /* Erect tombstone */
- node->key_hash = 1;
-
- /* Be GC friendly */
- node->key = NULL;
- node->value = NULL;
-
- hash_table->nnodes--;
-}
-
-/*
- * g_hash_table_remove_all_nodes:
- * @hash_table: our #GHashTable
- * @notify: %TRUE if the destroy notify handlers are to be called
- *
- * Removes all nodes from the table. Since this may be a precursor to
- * freeing the table entirely, no resize is performed.
- *
- * If @notify is %TRUE then the destroy notify functions are called
- * for the key and value of the hash node.
- */
-static void
-g_hash_table_remove_all_nodes (GHashTable *hash_table,
- gboolean notify)
-{
- int i;
-
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1)
- {
- if (notify && hash_table->key_destroy_func)
- hash_table->key_destroy_func (node->key);
-
- if (notify && hash_table->value_destroy_func)
- hash_table->value_destroy_func (node->value);
- }
- }
-
- /* We need to set node->key_hash = 0 for all nodes - might as well be GC
- * friendly and clear everything */
- memset (hash_table->nodes, 0, hash_table->size * sizeof (GHashNode));
-
- hash_table->nnodes = 0;
- hash_table->noccupied = 0;
-}
-
-/*
- * g_hash_table_resize:
- * @hash_table: our #GHashTable
- *
- * Resizes the hash table to the optimal size based on the number of
- * nodes currently held. If you call this function then a resize will
- * occur, even if one does not need to occur. Use
- * g_hash_table_maybe_resize() instead.
- *
- * This function may "resize" the hash table to its current size, with
- * the side effect of cleaning up tombstones and otherwise optimizing
- * the probe sequences.
- */
-static void
-g_hash_table_resize (GHashTable *hash_table)
-{
- GHashNode *new_nodes;
- gint old_size;
- gint i;
-
- old_size = hash_table->size;
- g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2);
-
- new_nodes = g_new0 (GHashNode, hash_table->size);
-
- for (i = 0; i < old_size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
- GHashNode *new_node;
- guint hash_val;
- guint step = 0;
-
- if (node->key_hash <= 1)
- continue;
-
- hash_val = node->key_hash % hash_table->mod;
- new_node = &new_nodes [hash_val];
-
- while (new_node->key_hash)
- {
- step++;
- hash_val += step;
- hash_val &= hash_table->mask;
- new_node = &new_nodes [hash_val];
- }
-
- *new_node = *node;
- }
-
- g_free (hash_table->nodes);
- hash_table->nodes = new_nodes;
- hash_table->noccupied = hash_table->nnodes;
-}
-
-/*
- * g_hash_table_maybe_resize:
- * @hash_table: our #GHashTable
- *
- * Resizes the hash table, if needed.
- *
- * Essentially, calls g_hash_table_resize() if the table has strayed
- * too far from its ideal size for its number of nodes.
- */
-static inline void
-g_hash_table_maybe_resize (GHashTable *hash_table)
-{
- gint noccupied = hash_table->noccupied;
- gint size = hash_table->size;
-
- if ((size > hash_table->nnodes * 4 && size > 1 << HASH_TABLE_MIN_SHIFT) ||
- (size <= noccupied + (noccupied / 16)))
- g_hash_table_resize (hash_table);
-}
-
-/**
- * g_hash_table_new:
- * @hash_func: a function to create a hash value from a key.
- * Hash values are used to determine where keys are stored within the
- * #GHashTable data structure. The g_direct_hash(), g_int_hash(),
- * g_int64_hash(), g_double_hash() and g_str_hash() functions are provided
- * for some common types of keys.
- * If hash_func is %NULL, g_direct_hash() is used.
- * @key_equal_func: a function to check two keys for equality. This is
- * used when looking up keys in the #GHashTable. The g_direct_equal(),
- * g_int_equal(), g_int64_equal(), g_double_equal() and g_str_equal()
- * functions are provided for the most common types of keys.
- * If @key_equal_func is %NULL, keys are compared directly in a similar
- * fashion to g_direct_equal(), but without the overhead of a function call.
- *
- * Creates a new #GHashTable with a reference count of 1.
- *
- * Return value: a new #GHashTable.
- **/
-GHashTable*
-g_hash_table_new (GHashFunc hash_func,
- GEqualFunc key_equal_func)
-{
- return g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
-}
-
-
-/**
- * g_hash_table_new_full:
- * @hash_func: a function to create a hash value from a key.
- * @key_equal_func: a function to check two keys for equality.
- * @key_destroy_func: a function to free the memory allocated for the key
- * used when removing the entry from the #GHashTable or %NULL if you
- * don't want to supply such a function.
- * @value_destroy_func: a function to free the memory allocated for the
- * value used when removing the entry from the #GHashTable or %NULL if
- * you don't want to supply such a function.
- *
- * Creates a new #GHashTable like g_hash_table_new() with a reference count
- * of 1 and allows to specify functions to free the memory allocated for the
- * key and value that get called when removing the entry from the #GHashTable.
- *
- * Return value: a new #GHashTable.
- **/
-GHashTable*
-g_hash_table_new_full (GHashFunc hash_func,
- GEqualFunc key_equal_func,
- GDestroyNotify key_destroy_func,
- GDestroyNotify value_destroy_func)
-{
- GHashTable *hash_table;
-
- hash_table = g_slice_new (GHashTable);
- g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT);
- hash_table->nnodes = 0;
- hash_table->noccupied = 0;
- hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
- hash_table->key_equal_func = key_equal_func;
- hash_table->ref_count = 1;
-#ifndef G_DISABLE_ASSERT
- hash_table->version = 0;
-#endif
- hash_table->key_destroy_func = key_destroy_func;
- hash_table->value_destroy_func = value_destroy_func;
- hash_table->nodes = g_new0 (GHashNode, hash_table->size);
-
- return hash_table;
-}
-
-/**
- * g_hash_table_iter_init:
- * @iter: an uninitialized #GHashTableIter.
- * @hash_table: a #GHashTable.
- *
- * Initializes a key/value pair iterator and associates it with
- * @hash_table. Modifying the hash table after calling this function
- * invalidates the returned iterator.
- * |[
- * GHashTableIter iter;
- * gpointer key, value;
- *
- * g_hash_table_iter_init (&iter, hash_table);
- * while (g_hash_table_iter_next (&iter, &key, &value))
- * {
- * /* do something with key and value */
- * }
- * ]|
- *
- * Since: 2.16
- **/
-void
-g_hash_table_iter_init (GHashTableIter *iter,
- GHashTable *hash_table)
-{
- RealIter *ri = (RealIter *) iter;
-
- g_return_if_fail (iter != NULL);
- g_return_if_fail (hash_table != NULL);
-
- ri->hash_table = hash_table;
- ri->position = -1;
-#ifndef G_DISABLE_ASSERT
- ri->version = hash_table->version;
-#endif
-}
-
-/**
- * g_hash_table_iter_next:
- * @iter: an initialized #GHashTableIter.
- * @key: a location to store the key, or %NULL.
- * @value: a location to store the value, or %NULL.
- *
- * Advances @iter and retrieves the key and/or value that are now
- * pointed to as a result of this advancement. If %FALSE is returned,
- * @key and @value are not set, and the iterator becomes invalid.
- *
- * Return value: %FALSE if the end of the #GHashTable has been reached.
- *
- * Since: 2.16
- **/
-gboolean
-g_hash_table_iter_next (GHashTableIter *iter,
- gpointer *key,
- gpointer *value)
-{
- RealIter *ri = (RealIter *) iter;
- GHashNode *node;
- gint position;
-
- g_return_val_if_fail (iter != NULL, FALSE);
-#ifndef G_DISABLE_ASSERT
- g_return_val_if_fail (ri->version == ri->hash_table->version, FALSE);
-#endif
- g_return_val_if_fail (ri->position < ri->hash_table->size, FALSE);
-
- position = ri->position;
-
- do
- {
- position++;
- if (position >= ri->hash_table->size)
- {
- ri->position = position;
- return FALSE;
- }
-
- node = &ri->hash_table->nodes [position];
- }
- while (node->key_hash <= 1);
-
- if (key != NULL)
- *key = node->key;
- if (value != NULL)
- *value = node->value;
-
- ri->position = position;
- return TRUE;
-}
-
-/**
- * g_hash_table_iter_get_hash_table:
- * @iter: an initialized #GHashTableIter.
- *
- * Returns the #GHashTable associated with @iter.
- *
- * Return value: the #GHashTable associated with @iter.
- *
- * Since: 2.16
- **/
-GHashTable *
-g_hash_table_iter_get_hash_table (GHashTableIter *iter)
-{
- g_return_val_if_fail (iter != NULL, NULL);
-
- return ((RealIter *) iter)->hash_table;
-}
-
-static void
-iter_remove_or_steal (RealIter *ri, gboolean notify)
-{
- g_return_if_fail (ri != NULL);
-#ifndef G_DISABLE_ASSERT
- g_return_if_fail (ri->version == ri->hash_table->version);
-#endif
- g_return_if_fail (ri->position >= 0);
- g_return_if_fail (ri->position < ri->hash_table->size);
-
- g_hash_table_remove_node (ri->hash_table, &ri->hash_table->nodes [ri->position], notify);
-
-#ifndef G_DISABLE_ASSERT
- ri->version++;
- ri->hash_table->version++;
-#endif
-}
-
-/**
- * g_hash_table_iter_remove:
- * @iter: an initialized #GHashTableIter.
- *
- * Removes the key/value pair currently pointed to by the iterator
- * from its associated #GHashTable. Can only be called after
- * g_hash_table_iter_next() returned %TRUE, and cannot be called more
- * than once for the same key/value pair.
- *
- * If the #GHashTable was created using g_hash_table_new_full(), the
- * key and value are freed using the supplied destroy functions, otherwise
- * you have to make sure that any dynamically allocated values are freed
- * yourself.
- *
- * Since: 2.16
- **/
-void
-g_hash_table_iter_remove (GHashTableIter *iter)
-{
- iter_remove_or_steal ((RealIter *) iter, TRUE);
-}
-
-/**
- * g_hash_table_iter_steal:
- * @iter: an initialized #GHashTableIter.
- *
- * Removes the key/value pair currently pointed to by the iterator
- * from its associated #GHashTable, without calling the key and value
- * destroy functions. Can only be called after
- * g_hash_table_iter_next() returned %TRUE, and cannot be called more
- * than once for the same key/value pair.
- *
- * Since: 2.16
- **/
-void
-g_hash_table_iter_steal (GHashTableIter *iter)
-{
- iter_remove_or_steal ((RealIter *) iter, FALSE);
-}
-
-
-/**
- * g_hash_table_ref:
- * @hash_table: a valid #GHashTable.
- *
- * Atomically increments the reference count of @hash_table by one.
- * This function is MT-safe and may be called from any thread.
- *
- * Return value: the passed in #GHashTable.
- *
- * Since: 2.10
- **/
-GHashTable*
-g_hash_table_ref (GHashTable *hash_table)
-{
- g_return_val_if_fail (hash_table != NULL, NULL);
- g_return_val_if_fail (hash_table->ref_count > 0, hash_table);
-
- g_atomic_int_add (&hash_table->ref_count, 1);
- return hash_table;
-}
-
-/**
- * g_hash_table_unref:
- * @hash_table: a valid #GHashTable.
- *
- * Atomically decrements the reference count of @hash_table by one.
- * If the reference count drops to 0, all keys and values will be
- * destroyed, and all memory allocated by the hash table is released.
- * This function is MT-safe and may be called from any thread.
- *
- * Since: 2.10
- **/
-void
-g_hash_table_unref (GHashTable *hash_table)
-{
- g_return_if_fail (hash_table != NULL);
- g_return_if_fail (hash_table->ref_count > 0);
-
- if (g_atomic_int_exchange_and_add (&hash_table->ref_count, -1) - 1 == 0)
- {
- g_hash_table_remove_all_nodes (hash_table, TRUE);
- g_free (hash_table->nodes);
- g_slice_free (GHashTable, hash_table);
- }
-}
-
-/**
- * g_hash_table_destroy:
- * @hash_table: a #GHashTable.
- *
- * Destroys all keys and values in the #GHashTable and decrements its
- * reference count by 1. If keys and/or values are dynamically allocated,
- * you should either free them first or create the #GHashTable with destroy
- * notifiers using g_hash_table_new_full(). In the latter case the destroy
- * functions you supplied will be called on all keys and values during the
- * destruction phase.
- **/
-void
-g_hash_table_destroy (GHashTable *hash_table)
-{
- g_return_if_fail (hash_table != NULL);
- g_return_if_fail (hash_table->ref_count > 0);
-
- g_hash_table_remove_all (hash_table);
- g_hash_table_unref (hash_table);
-}
-
-/**
- * g_hash_table_lookup:
- * @hash_table: a #GHashTable.
- * @key: the key to look up.
- *
- * Looks up a key in a #GHashTable. Note that this function cannot
- * distinguish between a key that is not present and one which is present
- * and has the value %NULL. If you need this distinction, use
- * g_hash_table_lookup_extended().
- *
- * Return value: the associated value, or %NULL if the key is not found.
- **/
-gpointer
-g_hash_table_lookup (GHashTable *hash_table,
- gconstpointer key)
-{
- GHashNode *node;
- guint node_index;
-
- g_return_val_if_fail (hash_table != NULL, NULL);
-
- node_index = g_hash_table_lookup_node (hash_table, key);
- node = &hash_table->nodes [node_index];
-
- return node->key_hash ? node->value : NULL;
-}
-
-/**
- * g_hash_table_lookup_extended:
- * @hash_table: a #GHashTable
- * @lookup_key: the key to look up
- * @orig_key: return location for the original key, or %NULL
- * @value: return location for the value associated with the key, or %NULL
- *
- * Looks up a key in the #GHashTable, returning the original key and the
- * associated value and a #gboolean which is %TRUE if the key was found. This
- * is useful if you need to free the memory allocated for the original key,
- * for example before calling g_hash_table_remove().
- *
- * You can actually pass %NULL for @lookup_key to test
- * whether the %NULL key exists.
- *
- * Return value: %TRUE if the key was found in the #GHashTable.
- **/
-gboolean
-g_hash_table_lookup_extended (GHashTable *hash_table,
- gconstpointer lookup_key,
- gpointer *orig_key,
- gpointer *value)
-{
- GHashNode *node;
- guint node_index;
-
- g_return_val_if_fail (hash_table != NULL, FALSE);
-
- node_index = g_hash_table_lookup_node (hash_table, lookup_key);
- node = &hash_table->nodes [node_index];
-
- if (!node->key_hash)
- return FALSE;
-
- if (orig_key)
- *orig_key = node->key;
-
- if (value)
- *value = node->value;
-
- return TRUE;
-}
-
-/*
- * g_hash_table_insert_internal:
- * @hash_table: our #GHashTable
- * @key: the key to insert
- * @value: the value to insert
- * @keep_new_key: if %TRUE and this key already exists in the table
- * then call the destroy notify function on the old key. If %FALSE
- * then call the destroy notify function on the new key.
- *
- * Implements the common logic for the g_hash_table_insert() and
- * g_hash_table_replace() functions.
- *
- * Do a lookup of @key. If it is found, replace it with the new
- * @value (and perhaps the new @key). If it is not found, create a
- * new node.
- */
-static void
-g_hash_table_insert_internal (GHashTable *hash_table,
- gpointer key,
- gpointer value,
- gboolean keep_new_key)
-{
- GHashNode *node;
- guint node_index;
- guint key_hash;
- guint old_hash;
-
- g_return_if_fail (hash_table != NULL);
- g_return_if_fail (hash_table->ref_count > 0);
-
- node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash);
- node = &hash_table->nodes [node_index];
-
- old_hash = node->key_hash;
-
- if (old_hash > 1)
- {
- if (keep_new_key)
- {
- if (hash_table->key_destroy_func)
- hash_table->key_destroy_func (node->key);
- node->key = key;
- }
- else
- {
- if (hash_table->key_destroy_func)
- hash_table->key_destroy_func (key);
- }
-
- if (hash_table->value_destroy_func)
- hash_table->value_destroy_func (node->value);
-
- node->value = value;
- }
- else
- {
- node->key = key;
- node->value = value;
- node->key_hash = key_hash;
-
- hash_table->nnodes++;
-
- if (old_hash == 0)
- {
- /* We replaced an empty node, and not a tombstone */
- hash_table->noccupied++;
- g_hash_table_maybe_resize (hash_table);
- }
-
-#ifndef G_DISABLE_ASSERT
- hash_table->version++;
-#endif
- }
-}
-
-/**
- * g_hash_table_insert:
- * @hash_table: a #GHashTable.
- * @key: a key to insert.
- * @value: the value to associate with the key.
- *
- * Inserts a new key and value into a #GHashTable.
- *
- * If the key already exists in the #GHashTable its current value is replaced
- * with the new value. If you supplied a @value_destroy_func when creating the
- * #GHashTable, the old value is freed using that function. If you supplied
- * a @key_destroy_func when creating the #GHashTable, the passed key is freed
- * using that function.
- **/
-void
-g_hash_table_insert (GHashTable *hash_table,
- gpointer key,
- gpointer value)
-{
- g_hash_table_insert_internal (hash_table, key, value, FALSE);
-}
-
-/**
- * g_hash_table_replace:
- * @hash_table: a #GHashTable.
- * @key: a key to insert.
- * @value: the value to associate with the key.
- *
- * Inserts a new key and value into a #GHashTable similar to
- * g_hash_table_insert(). The difference is that if the key already exists
- * in the #GHashTable, it gets replaced by the new key. If you supplied a
- * @value_destroy_func when creating the #GHashTable, the old value is freed
- * using that function. If you supplied a @key_destroy_func when creating the
- * #GHashTable, the old key is freed using that function.
- **/
-void
-g_hash_table_replace (GHashTable *hash_table,
- gpointer key,
- gpointer value)
-{
- g_hash_table_insert_internal (hash_table, key, value, TRUE);
-}
-
-/*
- * g_hash_table_remove_internal:
- * @hash_table: our #GHashTable
- * @key: the key to remove
- * @notify: %TRUE if the destroy notify handlers are to be called
- * Return value: %TRUE if a node was found and removed, else %FALSE
- *
- * Implements the common logic for the g_hash_table_remove() and
- * g_hash_table_steal() functions.
- *
- * Do a lookup of @key and remove it if it is found, calling the
- * destroy notify handlers only if @notify is %TRUE.
- */
-static gboolean
-g_hash_table_remove_internal (GHashTable *hash_table,
- gconstpointer key,
- gboolean notify)
-{
- GHashNode *node;
- guint node_index;
-
- g_return_val_if_fail (hash_table != NULL, FALSE);
-
- node_index = g_hash_table_lookup_node (hash_table, key);
- node = &hash_table->nodes [node_index];
-
- /* g_hash_table_lookup_node() never returns a tombstone, so this is safe */
- if (!node->key_hash)
- return FALSE;
-
- g_hash_table_remove_node (hash_table, node, notify);
- g_hash_table_maybe_resize (hash_table);
-
-#ifndef G_DISABLE_ASSERT
- hash_table->version++;
-#endif
-
- return TRUE;
-}
-
-/**
- * g_hash_table_remove:
- * @hash_table: a #GHashTable.
- * @key: the key to remove.
- *
- * Removes a key and its associated value from a #GHashTable.
- *
- * If the #GHashTable was created using g_hash_table_new_full(), the
- * key and value are freed using the supplied destroy functions, otherwise
- * you have to make sure that any dynamically allocated values are freed
- * yourself.
- *
- * Return value: %TRUE if the key was found and removed from the #GHashTable.
- **/
-gboolean
-g_hash_table_remove (GHashTable *hash_table,
- gconstpointer key)
-{
- return g_hash_table_remove_internal (hash_table, key, TRUE);
-}
-
-/**
- * g_hash_table_steal:
- * @hash_table: a #GHashTable.
- * @key: the key to remove.
- *
- * Removes a key and its associated value from a #GHashTable without
- * calling the key and value destroy functions.
- *
- * Return value: %TRUE if the key was found and removed from the #GHashTable.
- **/
-gboolean
-g_hash_table_steal (GHashTable *hash_table,
- gconstpointer key)
-{
- return g_hash_table_remove_internal (hash_table, key, FALSE);
-}
-
-/**
- * g_hash_table_remove_all:
- * @hash_table: a #GHashTable
- *
- * Removes all keys and their associated values from a #GHashTable.
- *
- * If the #GHashTable was created using g_hash_table_new_full(), the keys
- * and values are freed using the supplied destroy functions, otherwise you
- * have to make sure that any dynamically allocated values are freed
- * yourself.
- *
- * Since: 2.12
- **/
-void
-g_hash_table_remove_all (GHashTable *hash_table)
-{
- g_return_if_fail (hash_table != NULL);
-
-#ifndef G_DISABLE_ASSERT
- if (hash_table->nnodes != 0)
- hash_table->version++;
-#endif
-
- g_hash_table_remove_all_nodes (hash_table, TRUE);
- g_hash_table_maybe_resize (hash_table);
-}
-
-/**
- * g_hash_table_steal_all:
- * @hash_table: a #GHashTable.
- *
- * Removes all keys and their associated values from a #GHashTable
- * without calling the key and value destroy functions.
- *
- * Since: 2.12
- **/
-void
-g_hash_table_steal_all (GHashTable *hash_table)
-{
- g_return_if_fail (hash_table != NULL);
-
-#ifndef G_DISABLE_ASSERT
- if (hash_table->nnodes != 0)
- hash_table->version++;
-#endif
-
- g_hash_table_remove_all_nodes (hash_table, FALSE);
- g_hash_table_maybe_resize (hash_table);
-}
-
-/*
- * g_hash_table_foreach_remove_or_steal:
- * @hash_table: our #GHashTable
- * @func: the user's callback function
- * @user_data: data for @func
- * @notify: %TRUE if the destroy notify handlers are to be called
- *
- * Implements the common logic for g_hash_table_foreach_remove() and
- * g_hash_table_foreach_steal().
- *
- * Iterates over every node in the table, calling @func with the key
- * and value of the node (and @user_data). If @func returns %TRUE the
- * node is removed from the table.
- *
- * If @notify is true then the destroy notify handlers will be called
- * for each removed node.
- */
-static guint
-g_hash_table_foreach_remove_or_steal (GHashTable *hash_table,
- GHRFunc func,
- gpointer user_data,
- gboolean notify)
-{
- guint deleted = 0;
- gint i;
-
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1 && (* func) (node->key, node->value, user_data))
- {
- g_hash_table_remove_node (hash_table, node, notify);
- deleted++;
- }
- }
-
- g_hash_table_maybe_resize (hash_table);
-
-#ifndef G_DISABLE_ASSERT
- if (deleted > 0)
- hash_table->version++;
-#endif
-
- return deleted;
-}
-
-/**
- * g_hash_table_foreach_remove:
- * @hash_table: a #GHashTable.
- * @func: the function to call for each key/value pair.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each key/value pair in the #GHashTable.
- * If the function returns %TRUE, then the key/value pair is removed from the
- * #GHashTable. If you supplied key or value destroy functions when creating
- * the #GHashTable, they are used to free the memory allocated for the removed
- * keys and values.
- *
- * See #GHashTableIter for an alternative way to loop over the
- * key/value pairs in the hash table.
- *
- * Return value: the number of key/value pairs removed.
- **/
-guint
-g_hash_table_foreach_remove (GHashTable *hash_table,
- GHRFunc func,
- gpointer user_data)
-{
- g_return_val_if_fail (hash_table != NULL, 0);
- g_return_val_if_fail (func != NULL, 0);
-
- return g_hash_table_foreach_remove_or_steal (hash_table, func, user_data, TRUE);
-}
-
-/**
- * g_hash_table_foreach_steal:
- * @hash_table: a #GHashTable.
- * @func: the function to call for each key/value pair.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each key/value pair in the #GHashTable.
- * If the function returns %TRUE, then the key/value pair is removed from the
- * #GHashTable, but no key or value destroy functions are called.
- *
- * See #GHashTableIter for an alternative way to loop over the
- * key/value pairs in the hash table.
- *
- * Return value: the number of key/value pairs removed.
- **/
-guint
-g_hash_table_foreach_steal (GHashTable *hash_table,
- GHRFunc func,
- gpointer user_data)
-{
- g_return_val_if_fail (hash_table != NULL, 0);
- g_return_val_if_fail (func != NULL, 0);
-
- return g_hash_table_foreach_remove_or_steal (hash_table, func, user_data, FALSE);
-}
-
-/**
- * g_hash_table_foreach:
- * @hash_table: a #GHashTable.
- * @func: the function to call for each key/value pair.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each of the key/value pairs in the
- * #GHashTable. The function is passed the key and value of each
- * pair, and the given @user_data parameter. The hash table may not
- * be modified while iterating over it (you can't add/remove
- * items). To remove all items matching a predicate, use
- * g_hash_table_foreach_remove().
- *
- * See g_hash_table_find() for performance caveats for linear
- * order searches in contrast to g_hash_table_lookup().
- **/
-void
-g_hash_table_foreach (GHashTable *hash_table,
- GHFunc func,
- gpointer user_data)
-{
- gint i;
-
- g_return_if_fail (hash_table != NULL);
- g_return_if_fail (func != NULL);
-
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1)
- (* func) (node->key, node->value, user_data);
- }
-}
-
-/**
- * g_hash_table_find:
- * @hash_table: a #GHashTable.
- * @predicate: function to test the key/value pairs for a certain property.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for key/value pairs in the #GHashTable until
- * @predicate returns %TRUE. The function is passed the key and value of
- * each pair, and the given @user_data parameter. The hash table may not
- * be modified while iterating over it (you can't add/remove items).
- *
- * Note, that hash tables are really only optimized for forward lookups,
- * i.e. g_hash_table_lookup().
- * So code that frequently issues g_hash_table_find() or
- * g_hash_table_foreach() (e.g. in the order of once per every entry in a
- * hash table) should probably be reworked to use additional or different
- * data structures for reverse lookups (keep in mind that an O(n) find/foreach
- * operation issued for all n values in a hash table ends up needing O(n*n)
- * operations).
- *
- * Return value: The value of the first key/value pair is returned, for which
- * func evaluates to %TRUE. If no pair with the requested property is found,
- * %NULL is returned.
- *
- * Since: 2.4
- **/
-gpointer
-g_hash_table_find (GHashTable *hash_table,
- GHRFunc predicate,
- gpointer user_data)
-{
- gint i;
-
- g_return_val_if_fail (hash_table != NULL, NULL);
- g_return_val_if_fail (predicate != NULL, NULL);
-
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1 && predicate (node->key, node->value, user_data))
- return node->value;
- }
-
- return NULL;
-}
-
-/**
- * g_hash_table_size:
- * @hash_table: a #GHashTable.
- *
- * Returns the number of elements contained in the #GHashTable.
- *
- * Return value: the number of key/value pairs in the #GHashTable.
- **/
-guint
-g_hash_table_size (GHashTable *hash_table)
-{
- g_return_val_if_fail (hash_table != NULL, 0);
-
- return hash_table->nnodes;
-}
-
-/**
- * g_hash_table_get_keys:
- * @hash_table: a #GHashTable
- *
- * Retrieves every key inside @hash_table. The returned data is valid
- * until @hash_table is modified.
- *
- * Return value: a #GList containing all the keys inside the hash
- * table. The content of the list is owned by the hash table and
- * should not be modified or freed. Use g_list_free() when done
- * using the list.
- *
- * Since: 2.14
- */
-GList *
-g_hash_table_get_keys (GHashTable *hash_table)
-{
- gint i;
- GList *retval;
-
- g_return_val_if_fail (hash_table != NULL, NULL);
-
- retval = NULL;
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1)
- retval = g_list_prepend (retval, node->key);
- }
-
- return retval;
-}
-
-/**
- * g_hash_table_get_values:
- * @hash_table: a #GHashTable
- *
- * Retrieves every value inside @hash_table. The returned data is
- * valid until @hash_table is modified.
- *
- * Return value: a #GList containing all the values inside the hash
- * table. The content of the list is owned by the hash table and
- * should not be modified or freed. Use g_list_free() when done
- * using the list.
- *
- * Since: 2.14
- */
-GList *
-g_hash_table_get_values (GHashTable *hash_table)
-{
- gint i;
- GList *retval;
-
- g_return_val_if_fail (hash_table != NULL, NULL);
-
- retval = NULL;
- for (i = 0; i < hash_table->size; i++)
- {
- GHashNode *node = &hash_table->nodes [i];
-
- if (node->key_hash > 1)
- retval = g_list_prepend (retval, node->value);
- }
-
- return retval;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GHook: Callback maintenance functions
- * Copyright (C) 1998 Tim Janik
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "ghook.h"
-
-#include "gtestutils.h"
-
-
-/* --- functions --- */
-static void
-default_finalize_hook (GHookList *hook_list,
- GHook *hook)
-{
- GDestroyNotify destroy = hook->destroy;
-
- if (destroy)
- {
- hook->destroy = NULL;
- destroy (hook->data);
- }
-}
-
-void
-g_hook_list_init (GHookList *hook_list,
- guint hook_size)
-{
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_size >= sizeof (GHook));
-
- hook_list->seq_id = 1;
- hook_list->hook_size = hook_size;
- hook_list->is_setup = TRUE;
- hook_list->hooks = NULL;
- hook_list->dummy3 = NULL;
- hook_list->finalize_hook = default_finalize_hook;
- hook_list->dummy[0] = NULL;
- hook_list->dummy[1] = NULL;
-}
-
-void
-g_hook_list_clear (GHookList *hook_list)
-{
- g_return_if_fail (hook_list != NULL);
-
- if (hook_list->is_setup)
- {
- GHook *hook;
-
- hook_list->is_setup = FALSE;
-
- hook = hook_list->hooks;
- if (!hook)
- {
- /* destroy hook_list->hook_memchunk */
- }
- else
- do
- {
- GHook *tmp;
-
- g_hook_ref (hook_list, hook);
- g_hook_destroy_link (hook_list, hook);
- tmp = hook->next;
- g_hook_unref (hook_list, hook);
- hook = tmp;
- }
- while (hook);
- }
-}
-
-GHook*
-g_hook_alloc (GHookList *hook_list)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (hook_list->is_setup, NULL);
-
- hook = g_slice_alloc0 (hook_list->hook_size);
- hook->data = NULL;
- hook->next = NULL;
- hook->prev = NULL;
- hook->flags = G_HOOK_FLAG_ACTIVE;
- hook->ref_count = 0;
- hook->hook_id = 0;
- hook->func = NULL;
- hook->destroy = NULL;
-
- return hook;
-}
-
-void
-g_hook_free (GHookList *hook_list,
- GHook *hook)
-{
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
- g_return_if_fail (hook != NULL);
- g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
- g_return_if_fail (!G_HOOK_IN_CALL (hook));
-
- if(hook_list->finalize_hook != NULL)
- hook_list->finalize_hook (hook_list, hook);
- g_slice_free1 (hook_list->hook_size, hook);
-}
-
-void
-g_hook_destroy_link (GHookList *hook_list,
- GHook *hook)
-{
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook != NULL);
-
- hook->flags &= ~G_HOOK_FLAG_ACTIVE;
- if (hook->hook_id)
- {
- hook->hook_id = 0;
- g_hook_unref (hook_list, hook); /* counterpart to g_hook_insert_before */
- }
-}
-
-gboolean
-g_hook_destroy (GHookList *hook_list,
- gulong hook_id)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, FALSE);
- g_return_val_if_fail (hook_id > 0, FALSE);
-
- hook = g_hook_get (hook_list, hook_id);
- if (hook)
- {
- g_hook_destroy_link (hook_list, hook);
- return TRUE;
- }
-
- return FALSE;
-}
-
-void
-g_hook_unref (GHookList *hook_list,
- GHook *hook)
-{
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook != NULL);
- g_return_if_fail (hook->ref_count > 0);
-
- hook->ref_count--;
- if (!hook->ref_count)
- {
- g_return_if_fail (hook->hook_id == 0);
- g_return_if_fail (!G_HOOK_IN_CALL (hook));
-
- if (hook->prev)
- hook->prev->next = hook->next;
- else
- hook_list->hooks = hook->next;
- if (hook->next)
- {
- hook->next->prev = hook->prev;
- hook->next = NULL;
- }
- hook->prev = NULL;
-
- if (!hook_list->is_setup)
- {
- hook_list->is_setup = TRUE;
- g_hook_free (hook_list, hook);
- hook_list->is_setup = FALSE;
-
- if (!hook_list->hooks)
- {
- /* destroy hook_list->hook_memchunk */
- }
- }
- else
- g_hook_free (hook_list, hook);
- }
-}
-
-GHook *
-g_hook_ref (GHookList *hook_list,
- GHook *hook)
-{
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (hook != NULL, NULL);
- g_return_val_if_fail (hook->ref_count > 0, NULL);
-
- hook->ref_count++;
-
- return hook;
-}
-
-void
-g_hook_prepend (GHookList *hook_list,
- GHook *hook)
-{
- g_return_if_fail (hook_list != NULL);
-
- g_hook_insert_before (hook_list, hook_list->hooks, hook);
-}
-
-void
-g_hook_insert_before (GHookList *hook_list,
- GHook *sibling,
- GHook *hook)
-{
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
- g_return_if_fail (hook != NULL);
- g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
- g_return_if_fail (hook->ref_count == 0);
-
- hook->hook_id = hook_list->seq_id++;
- hook->ref_count = 1; /* counterpart to g_hook_destroy_link */
-
- if (sibling)
- {
- if (sibling->prev)
- {
- hook->prev = sibling->prev;
- hook->prev->next = hook;
- hook->next = sibling;
- sibling->prev = hook;
- }
- else
- {
- hook_list->hooks = hook;
- hook->next = sibling;
- sibling->prev = hook;
- }
- }
- else
- {
- if (hook_list->hooks)
- {
- sibling = hook_list->hooks;
- while (sibling->next)
- sibling = sibling->next;
- hook->prev = sibling;
- sibling->next = hook;
- }
- else
- hook_list->hooks = hook;
- }
-}
-
-void
-g_hook_list_invoke (GHookList *hook_list,
- gboolean may_recurse)
-{
- GHook *hook;
-
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
-
- hook = g_hook_first_valid (hook_list, may_recurse);
- while (hook)
- {
- GHookFunc func;
- gboolean was_in_call;
-
- func = (GHookFunc) hook->func;
-
- was_in_call = G_HOOK_IN_CALL (hook);
- hook->flags |= G_HOOK_FLAG_IN_CALL;
- func (hook->data);
- if (!was_in_call)
- hook->flags &= ~G_HOOK_FLAG_IN_CALL;
-
- hook = g_hook_next_valid (hook_list, hook, may_recurse);
- }
-}
-
-void
-g_hook_list_invoke_check (GHookList *hook_list,
- gboolean may_recurse)
-{
- GHook *hook;
-
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
-
- hook = g_hook_first_valid (hook_list, may_recurse);
- while (hook)
- {
- GHookCheckFunc func;
- gboolean was_in_call;
- gboolean need_destroy;
-
- func = (GHookCheckFunc) hook->func;
-
- was_in_call = G_HOOK_IN_CALL (hook);
- hook->flags |= G_HOOK_FLAG_IN_CALL;
- need_destroy = !func (hook->data);
- if (!was_in_call)
- hook->flags &= ~G_HOOK_FLAG_IN_CALL;
- if (need_destroy)
- g_hook_destroy_link (hook_list, hook);
-
- hook = g_hook_next_valid (hook_list, hook, may_recurse);
- }
-}
-
-void
-g_hook_list_marshal_check (GHookList *hook_list,
- gboolean may_recurse,
- GHookCheckMarshaller marshaller,
- gpointer data)
-{
- GHook *hook;
-
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
- g_return_if_fail (marshaller != NULL);
-
- hook = g_hook_first_valid (hook_list, may_recurse);
- while (hook)
- {
- gboolean was_in_call;
- gboolean need_destroy;
-
- was_in_call = G_HOOK_IN_CALL (hook);
- hook->flags |= G_HOOK_FLAG_IN_CALL;
- need_destroy = !marshaller (hook, data);
- if (!was_in_call)
- hook->flags &= ~G_HOOK_FLAG_IN_CALL;
- if (need_destroy)
- g_hook_destroy_link (hook_list, hook);
-
- hook = g_hook_next_valid (hook_list, hook, may_recurse);
- }
-}
-
-void
-g_hook_list_marshal (GHookList *hook_list,
- gboolean may_recurse,
- GHookMarshaller marshaller,
- gpointer data)
-{
- GHook *hook;
-
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
- g_return_if_fail (marshaller != NULL);
-
- hook = g_hook_first_valid (hook_list, may_recurse);
- while (hook)
- {
- gboolean was_in_call;
-
- was_in_call = G_HOOK_IN_CALL (hook);
- hook->flags |= G_HOOK_FLAG_IN_CALL;
- marshaller (hook, data);
- if (!was_in_call)
- hook->flags &= ~G_HOOK_FLAG_IN_CALL;
-
- hook = g_hook_next_valid (hook_list, hook, may_recurse);
- }
-}
-
-GHook*
-g_hook_first_valid (GHookList *hook_list,
- gboolean may_be_in_call)
-{
- g_return_val_if_fail (hook_list != NULL, NULL);
-
- if (hook_list->is_setup)
- {
- GHook *hook;
-
- hook = hook_list->hooks;
- if (hook)
- {
- g_hook_ref (hook_list, hook);
- if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
- return hook;
- else
- return g_hook_next_valid (hook_list, hook, may_be_in_call);
- }
- }
-
- return NULL;
-}
-
-GHook*
-g_hook_next_valid (GHookList *hook_list,
- GHook *hook,
- gboolean may_be_in_call)
-{
- GHook *ohook = hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
-
- if (!hook)
- return NULL;
-
- hook = hook->next;
- while (hook)
- {
- if (G_HOOK_IS_VALID (hook) && (may_be_in_call || !G_HOOK_IN_CALL (hook)))
- {
- g_hook_ref (hook_list, hook);
- g_hook_unref (hook_list, ohook);
-
- return hook;
- }
- hook = hook->next;
- }
- g_hook_unref (hook_list, ohook);
-
- return NULL;
-}
-
-GHook*
-g_hook_get (GHookList *hook_list,
- gulong hook_id)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (hook_id > 0, NULL);
-
- hook = hook_list->hooks;
- while (hook)
- {
- if (hook->hook_id == hook_id)
- return hook;
- hook = hook->next;
- }
-
- return NULL;
-}
-
-GHook*
-g_hook_find (GHookList *hook_list,
- gboolean need_valids,
- GHookFindFunc func,
- gpointer data)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (func != NULL, NULL);
-
- hook = hook_list->hooks;
- while (hook)
- {
- GHook *tmp;
-
- /* test only non-destroyed hooks */
- if (!hook->hook_id)
- {
- hook = hook->next;
- continue;
- }
-
- g_hook_ref (hook_list, hook);
-
- if (func (hook, data) && hook->hook_id && (!need_valids || G_HOOK_ACTIVE (hook)))
- {
- g_hook_unref (hook_list, hook);
-
- return hook;
- }
-
- tmp = hook->next;
- g_hook_unref (hook_list, hook);
- hook = tmp;
- }
-
- return NULL;
-}
-
-GHook*
-g_hook_find_data (GHookList *hook_list,
- gboolean need_valids,
- gpointer data)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
-
- hook = hook_list->hooks;
- while (hook)
- {
- /* test only non-destroyed hooks */
- if (hook->data == data &&
- hook->hook_id &&
- (!need_valids || G_HOOK_ACTIVE (hook)))
- return hook;
-
- hook = hook->next;
- }
-
- return NULL;
-}
-
-GHook*
-g_hook_find_func (GHookList *hook_list,
- gboolean need_valids,
- gpointer func)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (func != NULL, NULL);
-
- hook = hook_list->hooks;
- while (hook)
- {
- /* test only non-destroyed hooks */
- if (hook->func == func &&
- hook->hook_id &&
- (!need_valids || G_HOOK_ACTIVE (hook)))
- return hook;
-
- hook = hook->next;
- }
-
- return NULL;
-}
-
-GHook*
-g_hook_find_func_data (GHookList *hook_list,
- gboolean need_valids,
- gpointer func,
- gpointer data)
-{
- GHook *hook;
-
- g_return_val_if_fail (hook_list != NULL, NULL);
- g_return_val_if_fail (func != NULL, NULL);
-
- hook = hook_list->hooks;
- while (hook)
- {
- /* test only non-destroyed hooks */
- if (hook->data == data &&
- hook->func == func &&
- hook->hook_id &&
- (!need_valids || G_HOOK_ACTIVE (hook)))
- return hook;
-
- hook = hook->next;
- }
-
- return NULL;
-}
-
-void
-g_hook_insert_sorted (GHookList *hook_list,
- GHook *hook,
- GHookCompareFunc func)
-{
- GHook *sibling;
-
- g_return_if_fail (hook_list != NULL);
- g_return_if_fail (hook_list->is_setup);
- g_return_if_fail (hook != NULL);
- g_return_if_fail (G_HOOK_IS_UNLINKED (hook));
- g_return_if_fail (hook->func != NULL);
- g_return_if_fail (func != NULL);
-
- /* first non-destroyed hook */
- sibling = hook_list->hooks;
- while (sibling && !sibling->hook_id)
- sibling = sibling->next;
-
- while (sibling)
- {
- GHook *tmp;
-
- g_hook_ref (hook_list, sibling);
- if (func (hook, sibling) <= 0 && sibling->hook_id)
- {
- g_hook_unref (hook_list, sibling);
- break;
- }
-
- /* next non-destroyed hook */
- tmp = sibling->next;
- while (tmp && !tmp->hook_id)
- tmp = tmp->next;
-
- g_hook_unref (hook_list, sibling);
- sibling = tmp;
- }
-
- g_hook_insert_before (hook_list, sibling, hook);
-}
-
-gint
-g_hook_compare_ids (GHook *new_hook,
- GHook *sibling)
-{
- if (new_hook->hook_id < sibling->hook_id)
- return -1;
- else if (new_hook->hook_id > sibling->hook_id)
- return 1;
-
- return 0;
-}
+++ /dev/null
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 2008 Red Hat, Inc.
- *
- * 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 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 "config.h"
-
-#include <string.h>
-
-#include "ghostutils.h"
-
-#include "garray.h"
-#include "gmem.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "glibintl.h"
-
-
-/**
- * SECTION:ghostutils
- * @short_description: Internet hostname utilities
- *
- * Functions for manipulating internet hostnames; in particular, for
- * converting between Unicode and ASCII-encoded forms of
- * Internationalized Domain Names (IDNs).
- *
- * The <ulink
- * url="http://www.ietf.org/rfc/rfc3490.txt">Internationalized Domain
- * Names for Applications (IDNA)</ulink> standards allow for the use
- * of Unicode domain names in applications, while providing
- * backward-compatibility with the old ASCII-only DNS, by defining an
- * ASCII-Compatible Encoding of any given Unicode name, which can be
- * used with non-IDN-aware applications and protocols. (For example,
- * "Παν語.org" maps to "xn--4wa8awb4637h.org".)
- **/
-
-#define IDNA_ACE_PREFIX "xn--"
-#define IDNA_ACE_PREFIX_LEN 4
-
-/* Punycode constants, from RFC 3492. */
-
-#define PUNYCODE_BASE 36
-#define PUNYCODE_TMIN 1
-#define PUNYCODE_TMAX 26
-#define PUNYCODE_SKEW 38
-#define PUNYCODE_DAMP 700
-#define PUNYCODE_INITIAL_BIAS 72
-#define PUNYCODE_INITIAL_N 0x80
-
-#define PUNYCODE_IS_BASIC(cp) ((guint)(cp) < 0x80)
-
-/* Encode/decode a single base-36 digit */
-static inline gchar
-encode_digit (guint dig)
-{
- if (dig < 26)
- return dig + 'a';
- else
- return dig - 26 + '0';
-}
-
-static inline guint
-decode_digit (gchar dig)
-{
- if (dig >= 'A' && dig <= 'Z')
- return dig - 'A';
- else if (dig >= 'a' && dig <= 'z')
- return dig - 'a';
- else if (dig >= '0' && dig <= '9')
- return dig - '0' + 26;
- else
- return G_MAXUINT;
-}
-
-/* Punycode bias adaptation algorithm, RFC 3492 section 6.1 */
-static guint
-adapt (guint delta,
- guint numpoints,
- gboolean firsttime)
-{
- guint k;
-
- delta = firsttime ? delta / PUNYCODE_DAMP : delta / 2;
- delta += delta / numpoints;
-
- k = 0;
- while (delta > ((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) / 2)
- {
- delta /= PUNYCODE_BASE - PUNYCODE_TMIN;
- k += PUNYCODE_BASE;
- }
-
- return k + ((PUNYCODE_BASE - PUNYCODE_TMIN + 1) * delta /
- (delta + PUNYCODE_SKEW));
-}
-
-/* Punycode encoder, RFC 3492 section 6.3. The algorithm is
- * sufficiently bizarre that it's not really worth trying to explain
- * here.
- */
-static gboolean
-punycode_encode (const gchar *input_utf8,
- gsize input_utf8_length,
- GString *output)
-{
- guint delta, handled_chars, num_basic_chars, bias, j, q, k, t, digit;
- gunichar n, m, *input;
- glong input_length;
- gboolean success = FALSE;
-
- /* Convert from UTF-8 to Unicode code points */
- input = g_utf8_to_ucs4 (input_utf8, input_utf8_length, NULL,
- &input_length, NULL);
- if (!input)
- return FALSE;
-
- /* Copy basic chars */
- for (j = num_basic_chars = 0; j < input_length; j++)
- {
- if (PUNYCODE_IS_BASIC (input[j]))
- {
- g_string_append_c (output, g_ascii_tolower (input[j]));
- num_basic_chars++;
- }
- }
- if (num_basic_chars)
- g_string_append_c (output, '-');
-
- handled_chars = num_basic_chars;
-
- /* Encode non-basic chars */
- delta = 0;
- bias = PUNYCODE_INITIAL_BIAS;
- n = PUNYCODE_INITIAL_N;
- while (handled_chars < input_length)
- {
- /* let m = the minimum {non-basic} code point >= n in the input */
- for (m = G_MAXUINT, j = 0; j < input_length; j++)
- {
- if (input[j] >= n && input[j] < m)
- m = input[j];
- }
-
- if (m - n > (G_MAXUINT - delta) / (handled_chars + 1))
- goto fail;
- delta += (m - n) * (handled_chars + 1);
- n = m;
-
- for (j = 0; j < input_length; j++)
- {
- if (input[j] < n)
- {
- if (++delta == 0)
- goto fail;
- }
- else if (input[j] == n)
- {
- q = delta;
- for (k = PUNYCODE_BASE; ; k += PUNYCODE_BASE)
- {
- if (k <= bias)
- t = PUNYCODE_TMIN;
- else if (k >= bias + PUNYCODE_TMAX)
- t = PUNYCODE_TMAX;
- else
- t = k - bias;
- if (q < t)
- break;
- digit = t + (q - t) % (PUNYCODE_BASE - t);
- g_string_append_c (output, encode_digit (digit));
- q = (q - t) / (PUNYCODE_BASE - t);
- }
-
- g_string_append_c (output, encode_digit (q));
- bias = adapt (delta, handled_chars + 1, handled_chars == num_basic_chars);
- delta = 0;
- handled_chars++;
- }
- }
-
- delta++;
- n++;
- }
-
- success = TRUE;
-
- fail:
- g_free (input);
- return success;
-}
-
-/* From RFC 3454, Table B.1 */
-#define idna_is_junk(ch) ((ch) == 0x00AD || (ch) == 0x1806 || (ch) == 0x200B || (ch) == 0x2060 || (ch) == 0xFEFF || (ch) == 0x034F || (ch) == 0x180B || (ch) == 0x180C || (ch) == 0x180D || (ch) == 0x200C || (ch) == 0x200D || ((ch) >= 0xFE00 && (ch) <= 0xFE0F))
-
-/* Scan @str for "junk" and return a cleaned-up string if any junk
- * is found. Else return %NULL.
- */
-static gchar *
-remove_junk (const gchar *str,
- gint len)
-{
- GString *cleaned = NULL;
- const gchar *p;
- gunichar ch;
-
- for (p = str; len == -1 ? *p : p < str + len; p = g_utf8_next_char (p))
- {
- ch = g_utf8_get_char (p);
- if (idna_is_junk (ch))
- {
- if (!cleaned)
- {
- cleaned = g_string_new (NULL);
- g_string_append_len (cleaned, str, p - str);
- }
- }
- else if (cleaned)
- g_string_append_unichar (cleaned, ch);
- }
-
- if (cleaned)
- return g_string_free (cleaned, FALSE);
- else
- return NULL;
-}
-
-static inline gboolean
-contains_uppercase_letters (const gchar *str,
- gint len)
-{
- const gchar *p;
-
- for (p = str; len == -1 ? *p : p < str + len; p = g_utf8_next_char (p))
- {
- if (g_unichar_isupper (g_utf8_get_char (p)))
- return TRUE;
- }
- return FALSE;
-}
-
-static inline gboolean
-contains_non_ascii (const gchar *str,
- gint len)
-{
- const gchar *p;
-
- for (p = str; len == -1 ? *p : p < str + len; p++)
- {
- if ((guchar)*p > 0x80)
- return TRUE;
- }
- return FALSE;
-}
-
-/* RFC 3454, Appendix C. ish. */
-static inline gboolean
-idna_is_prohibited (gunichar ch)
-{
- switch (g_unichar_type (ch))
- {
- case G_UNICODE_CONTROL:
- case G_UNICODE_FORMAT:
- case G_UNICODE_UNASSIGNED:
- case G_UNICODE_PRIVATE_USE:
- case G_UNICODE_SURROGATE:
- case G_UNICODE_LINE_SEPARATOR:
- case G_UNICODE_PARAGRAPH_SEPARATOR:
- case G_UNICODE_SPACE_SEPARATOR:
- return TRUE;
-
- case G_UNICODE_OTHER_SYMBOL:
- if (ch == 0xFFFC || ch == 0xFFFD ||
- (ch >= 0x2FF0 && ch <= 0x2FFB))
- return TRUE;
- return FALSE;
-
- case G_UNICODE_NON_SPACING_MARK:
- if (ch == 0x0340 || ch == 0x0341)
- return TRUE;
- return FALSE;
-
- default:
- return FALSE;
- }
-}
-
-/* RFC 3491 IDN cleanup algorithm. */
-static gchar *
-nameprep (const gchar *hostname,
- gint len)
-{
- gchar *name, *tmp = NULL, *p;
-
- /* It would be nice if we could do this without repeatedly
- * allocating strings and converting back and forth between
- * gunichars and UTF-8... The code does at least avoid doing most of
- * the sub-operations when they would just be equivalent to a
- * g_strdup().
- */
-
- /* Remove presentation-only characters */
- name = remove_junk (hostname, len);
- if (name)
- {
- tmp = name;
- len = -1;
- }
- else
- name = (gchar *)hostname;
-
- /* Convert to lowercase */
- if (contains_uppercase_letters (name, len))
- {
- name = g_utf8_strdown (name, len);
- g_free (tmp);
- tmp = name;
- len = -1;
- }
-
- /* If there are no UTF8 characters, we're done. */
- if (!contains_non_ascii (name, len))
- {
- if (name == (gchar *)hostname)
- return len == -1 ? g_strdup (hostname) : g_strndup (hostname, len);
- else
- return name;
- }
-
- /* Normalize */
- name = g_utf8_normalize (name, len, G_NORMALIZE_NFKC);
- g_free (tmp);
- tmp = name;
-
- if (!name)
- return NULL;
-
- /* KC normalization may have created more capital letters (eg,
- * angstrom -> capital A with ring). So we have to lowercasify a
- * second time. (This is more-or-less how the nameprep algorithm
- * does it. If tolower(nfkc(tolower(X))) is guaranteed to be the
- * same as tolower(nfkc(X)), then we could skip the first tolower,
- * but I'm not sure it is.)
- */
- if (contains_uppercase_letters (name, -1))
- {
- name = g_utf8_strdown (name, -1);
- g_free (tmp);
- tmp = name;
- }
-
- /* Check for prohibited characters */
- for (p = name; *p; p = g_utf8_next_char (p))
- {
- if (idna_is_prohibited (g_utf8_get_char (p)))
- {
- name = NULL;
- g_free (tmp);
- goto done;
- }
- }
-
- /* FIXME: We're supposed to verify certain constraints on bidi
- * characters, but glib does not appear to have that information.
- */
-
- done:
- return name;
-}
-
-/**
- * g_hostname_to_ascii:
- * @hostname: a valid UTF-8 or ASCII hostname
- *
- * Converts @hostname to its canonical ASCII form; an ASCII-only
- * string containing no uppercase letters and not ending with a
- * trailing dot.
- *
- * Return value: an ASCII hostname, which must be freed, or %NULL if
- * @hostname is in some way invalid.
- *
- * Since: 2.22
- **/
-gchar *
-g_hostname_to_ascii (const gchar *hostname)
-{
- gchar *name, *label, *p;
- GString *out;
- gssize llen, oldlen;
- gboolean unicode;
-
- label = name = nameprep (hostname, -1);
- if (!name)
- return NULL;
-
- out = g_string_new (NULL);
-
- do
- {
- unicode = FALSE;
- for (p = label; *p && *p != '.'; p++)
- {
- if ((guchar)*p > 0x80)
- unicode = TRUE;
- }
-
- oldlen = out->len;
- llen = p - label;
- if (unicode)
- {
- if (!strncmp (label, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
- goto fail;
-
- g_string_append (out, IDNA_ACE_PREFIX);
- if (!punycode_encode (label, llen, out))
- goto fail;
- }
- else
- g_string_append_len (out, label, llen);
-
- if (out->len - oldlen > 63)
- goto fail;
-
- label += llen;
- if (*label && *++label)
- g_string_append_c (out, '.');
- }
- while (*label);
-
- g_free (name);
- return g_string_free (out, FALSE);
-
- fail:
- g_free (name);
- g_string_free (out, TRUE);
- return NULL;
-}
-
-/**
- * g_hostname_is_non_ascii:
- * @hostname: a hostname
- *
- * Tests if @hostname contains Unicode characters. If this returns
- * %TRUE, you need to encode the hostname with g_hostname_to_ascii()
- * before using it in non-IDN-aware contexts.
- *
- * Note that a hostname might contain a mix of encoded and unencoded
- * segments, and so it is possible for g_hostname_is_non_ascii() and
- * g_hostname_is_ascii_encoded() to both return %TRUE for a name.
- *
- * Return value: %TRUE if @hostname contains any non-ASCII characters
- *
- * Since: 2.22
- **/
-gboolean
-g_hostname_is_non_ascii (const gchar *hostname)
-{
- return contains_non_ascii (hostname, -1);
-}
-
-/* Punycode decoder, RFC 3492 section 6.2. As with punycode_encode(),
- * read the RFC if you want to understand what this is actually doing.
- */
-static gboolean
-punycode_decode (const gchar *input,
- gsize input_length,
- GString *output)
-{
- GArray *output_chars;
- gunichar n;
- guint i, bias;
- guint oldi, w, k, digit, t;
- const gchar *split;
-
- n = PUNYCODE_INITIAL_N;
- i = 0;
- bias = PUNYCODE_INITIAL_BIAS;
-
- split = input + input_length - 1;
- while (split > input && *split != '-')
- split--;
- if (split > input)
- {
- output_chars = g_array_sized_new (FALSE, FALSE, sizeof (gunichar),
- split - input);
- input_length -= (split - input) + 1;
- while (input < split)
- {
- gunichar ch = (gunichar)*input++;
- if (!PUNYCODE_IS_BASIC (ch))
- goto fail;
- g_array_append_val (output_chars, ch);
- }
- input++;
- }
- else
- output_chars = g_array_new (FALSE, FALSE, sizeof (gunichar));
-
- while (input_length)
- {
- oldi = i;
- w = 1;
- for (k = PUNYCODE_BASE; ; k += PUNYCODE_BASE)
- {
- if (!input_length--)
- goto fail;
- digit = decode_digit (*input++);
- if (digit >= PUNYCODE_BASE)
- goto fail;
- if (digit > (G_MAXUINT - i) / w)
- goto fail;
- i += digit * w;
- if (k <= bias)
- t = PUNYCODE_TMIN;
- else if (k >= bias + PUNYCODE_TMAX)
- t = PUNYCODE_TMAX;
- else
- t = k - bias;
- if (digit < t)
- break;
- if (w > G_MAXUINT / (PUNYCODE_BASE - t))
- goto fail;
- w *= (PUNYCODE_BASE - t);
- }
-
- bias = adapt (i - oldi, output_chars->len + 1, oldi == 0);
-
- if (i / (output_chars->len + 1) > G_MAXUINT - n)
- goto fail;
- n += i / (output_chars->len + 1);
- i %= (output_chars->len + 1);
-
- g_array_insert_val (output_chars, i++, n);
- }
-
- for (i = 0; i < output_chars->len; i++)
- g_string_append_unichar (output, g_array_index (output_chars, gunichar, i));
- g_array_free (output_chars, TRUE);
- return TRUE;
-
- fail:
- g_array_free (output_chars, TRUE);
- return FALSE;
-}
-
-/**
- * g_hostname_to_unicode:
- * @hostname: a valid UTF-8 or ASCII hostname
- *
- * Converts @hostname to its canonical presentation form; a UTF-8
- * string in Unicode normalization form C, containing no uppercase
- * letters, no forbidden characters, and no ASCII-encoded segments,
- * and not ending with a trailing dot.
- *
- * Of course if @hostname is not an internationalized hostname, then
- * the canonical presentation form will be entirely ASCII.
- *
- * Return value: a UTF-8 hostname, which must be freed, or %NULL if
- * @hostname is in some way invalid.
- *
- * Since: 2.22
- **/
-gchar *
-g_hostname_to_unicode (const gchar *hostname)
-{
- GString *out;
- gssize llen;
-
- out = g_string_new (NULL);
-
- do
- {
- llen = strcspn (hostname, ".");
- if (!g_ascii_strncasecmp (hostname, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
- {
- hostname += IDNA_ACE_PREFIX_LEN;
- llen -= IDNA_ACE_PREFIX_LEN;
- if (!punycode_decode (hostname, llen, out))
- {
- g_string_free (out, TRUE);
- return NULL;
- }
- }
- else
- {
- gchar *canonicalized = nameprep (hostname, llen);
-
- if (!canonicalized)
- {
- g_string_free (out, TRUE);
- return NULL;
- }
- g_string_append (out, canonicalized);
- g_free (canonicalized);
- }
-
- hostname += llen;
- if (*hostname && *++hostname)
- g_string_append_c (out, '.');
- }
- while (*hostname);
-
- return g_string_free (out, FALSE);
-}
-
-/**
- * g_hostname_is_ascii_encoded:
- * @hostname: a hostname
- *
- * Tests if @hostname contains segments with an ASCII-compatible
- * encoding of an Internationalized Domain Name. If this returns
- * %TRUE, you should decode the hostname with g_hostname_to_unicode()
- * before displaying it to the user.
- *
- * Note that a hostname might contain a mix of encoded and unencoded
- * segments, and so it is possible for g_hostname_is_non_ascii() and
- * g_hostname_is_ascii_encoded() to both return %TRUE for a name.
- *
- * Return value: %TRUE if @hostname contains any ASCII-encoded
- * segments.
- *
- * Since: 2.22
- **/
-gboolean
-g_hostname_is_ascii_encoded (const gchar *hostname)
-{
- while (1)
- {
- if (!g_ascii_strncasecmp (hostname, IDNA_ACE_PREFIX, IDNA_ACE_PREFIX_LEN))
- return TRUE;
- hostname = strchr (hostname, '.');
- if (!hostname++)
- return FALSE;
- }
-}
-
-/**
- * g_hostname_is_ip_address:
- * @hostname: a hostname (or IP address in string form)
- *
- * Tests if @hostname is the string form of an IPv4 or IPv6 address.
- * (Eg, "192.168.0.1".)
- *
- * Return value: %TRUE if @hostname is an IP address
- *
- * Since: 2.22
- **/
-gboolean
-g_hostname_is_ip_address (const gchar *hostname)
-{
- gchar *p, *end;
- gint nsegments, octet;
-
- /* On Linux we could implement this using inet_pton, but the Windows
- * equivalent of that requires linking against winsock, so we just
- * figure this out ourselves. Tested by tests/hostutils.c.
- */
-
- p = (char *)hostname;
-
- if (strchr (p, ':'))
- {
- gboolean skipped;
-
- /* If it contains a ':', it's an IPv6 address (assuming it's an
- * IP address at all). This consists of eight ':'-separated
- * segments, each containing a 1-4 digit hex number, except that
- * optionally: (a) the last two segments can be replaced by an
- * IPv4 address, and (b) a single span of 1 to 8 "0000" segments
- * can be replaced with just "::".
- */
-
- nsegments = 0;
- skipped = FALSE;
- while (*p && nsegments < 8)
- {
- /* Each segment after the first must be preceded by a ':'.
- * (We also handle half of the "string starts with ::" case
- * here.)
- */
- if (p != (char *)hostname || (p[0] == ':' && p[1] == ':'))
- {
- if (*p != ':')
- return FALSE;
- p++;
- }
-
- /* If there's another ':', it means we're skipping some segments */
- if (*p == ':' && !skipped)
- {
- skipped = TRUE;
- nsegments++;
-
- /* Handle the "string ends with ::" case */
- if (!p[1])
- p++;
-
- continue;
- }
-
- /* Read the segment, make sure it's valid. */
- for (end = p; g_ascii_isxdigit (*end); end++)
- ;
- if (end == p || end > p + 4)
- return FALSE;
-
- if (*end == '.')
- {
- if ((nsegments == 6 && !skipped) || (nsegments <= 6 && skipped))
- goto parse_ipv4;
- else
- return FALSE;
- }
-
- nsegments++;
- p = end;
- }
-
- return !*p && (nsegments == 8 || skipped);
- }
-
- parse_ipv4:
-
- /* Parse IPv4: N.N.N.N, where each N <= 255 and doesn't have leading 0s. */
- for (nsegments = 0; nsegments < 4; nsegments++)
- {
- if (nsegments != 0)
- {
- if (*p != '.')
- return FALSE;
- p++;
- }
-
- /* Check the segment; a little tricker than the IPv6 case since
- * we can't allow extra leading 0s, and we can't assume that all
- * strings of valid length are within range.
- */
- octet = 0;
- if (*p == '0')
- end = p + 1;
- else
- {
- for (end = p; g_ascii_isdigit (*end); end++)
- octet = 10 * octet + (*end - '0');
- }
- if (end == p || end > p + 3 || octet > 255)
- return FALSE;
-
- p = end;
- }
-
- /* If there's nothing left to parse, then it's ok. */
- return !*p;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * giochannel.c: IO Channel abstraction
- * Copyright 1998 Owen Taylor
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <string.h>
-#include <errno.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#undef G_DISABLE_DEPRECATED
-
-#include "giochannel.h"
-
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "glibintl.h"
-
-
-/**
- * SECTION: iochannels
- * @title: IO Channels
- * @short_description: portable support for using files, pipes and
- * sockets
- * @see_also: <para> <variablelist> <varlistentry>
- * <term>g_io_add_watch(), g_io_add_watch_full(),
- * g_source_remove()</term> <listitem><para> Convenience
- * functions for creating #GIOChannel instances and adding
- * them to the <link linkend="glib-The-Main-Event-Loop">main
- * event loop</link>. </para></listitem> </varlistentry>
- * </variablelist> </para>
- *
- * The #GIOChannel data type aims to provide a portable method for
- * using file descriptors, pipes, and sockets, and integrating them
- * into the <link linkend="glib-The-Main-Event-Loop">main event
- * loop</link>. Currently full support is available on UNIX platforms,
- * support for Windows is only partially complete.
- *
- * To create a new #GIOChannel on UNIX systems use
- * g_io_channel_unix_new(). This works for plain file descriptors,
- * pipes and sockets. Alternatively, a channel can be created for a
- * file in a system independent manner using g_io_channel_new_file().
- *
- * Once a #GIOChannel has been created, it can be used in a generic
- * manner with the functions g_io_channel_read_chars(),
- * g_io_channel_write_chars(), g_io_channel_seek_position(), and
- * g_io_channel_shutdown().
- *
- * To add a #GIOChannel to the <link
- * linkend="glib-The-Main-Event-Loop">main event loop</link> use
- * g_io_add_watch() or g_io_add_watch_full(). Here you specify which
- * events you are interested in on the #GIOChannel, and provide a
- * function to be called whenever these events occur.
- *
- * #GIOChannel instances are created with an initial reference count of
- * 1. g_io_channel_ref() and g_io_channel_unref() can be used to
- * increment or decrement the reference count respectively. When the
- * reference count falls to 0, the #GIOChannel is freed. (Though it
- * isn't closed automatically, unless it was created using
- * g_io_channel_new_from_file().) Using g_io_add_watch() or
- * g_io_add_watch_full() increments a channel's reference count.
- *
- * The new functions g_io_channel_read_chars(),
- * g_io_channel_read_line(), g_io_channel_read_line_string(),
- * g_io_channel_read_to_end(), g_io_channel_write_chars(),
- * g_io_channel_seek_position(), and g_io_channel_flush() should not be
- * mixed with the deprecated functions g_io_channel_read(),
- * g_io_channel_write(), and g_io_channel_seek() on the same channel.
- **/
-
-/**
- * GIOChannel:
- *
- * A data structure representing an IO Channel. The fields should be
- * considered private and should only be accessed with the following
- * functions.
- **/
-
-/**
- * GIOFuncs:
- * @io_read: reads raw bytes from the channel. This is called from
- * various functions such as g_io_channel_read_chars() to
- * read raw bytes from the channel. Encoding and buffering
- * issues are dealt with at a higher level.
- * @io_write: writes raw bytes to the channel. This is called from
- * various functions such as g_io_channel_write_chars() to
- * write raw bytes to the channel. Encoding and buffering
- * issues are dealt with at a higher level.
- * @io_seek: (optional) seeks the channel. This is called from
- * g_io_channel_seek() on channels that support it.
- * @io_close: closes the channel. This is called from
- * g_io_channel_close() after flushing the buffers.
- * @io_create_watch: creates a watch on the channel. This call
- * corresponds directly to g_io_create_watch().
- * @io_free: called from g_io_channel_unref() when the channel needs to
- * be freed. This function must free the memory associated
- * with the channel, including freeing the #GIOChannel
- * structure itself. The channel buffers have been flushed
- * and possibly @io_close has been called by the time this
- * function is called.
- * @io_set_flags: sets the #GIOFlags on the channel. This is called
- * from g_io_channel_set_flags() with all flags except
- * for %G_IO_FLAG_APPEND and %G_IO_FLAG_NONBLOCK masked
- * out.
- * @io_get_flags: gets the #GIOFlags for the channel. This function
- * need only return the %G_IO_FLAG_APPEND and
- * %G_IO_FLAG_NONBLOCK flags; g_io_channel_get_flags()
- * automatically adds the others as appropriate.
- *
- * A table of functions used to handle different types of #GIOChannel
- * in a generic way.
- **/
-
-/**
- * GIOStatus:
- * @G_IO_STATUS_ERROR: An error occurred.
- * @G_IO_STATUS_NORMAL: Success.
- * @G_IO_STATUS_EOF: End of file.
- * @G_IO_STATUS_AGAIN: Resource temporarily unavailable.
- *
- * Stati returned by most of the #GIOFuncs functions.
- **/
-
-/**
- * GIOError:
- * @G_IO_ERROR_NONE: no error
- * @G_IO_ERROR_AGAIN: an EAGAIN error occurred
- * @G_IO_ERROR_INVAL: an EINVAL error occurred
- * @G_IO_ERROR_UNKNOWN: another error occurred
- *
- * #GIOError is only used by the deprecated functions
- * g_io_channel_read(), g_io_channel_write(), and g_io_channel_seek().
- **/
-
-#define G_IO_NICE_BUF_SIZE 1024
-
-/* This needs to be as wide as the largest character in any possible encoding */
-#define MAX_CHAR_SIZE 10
-
-/* Some simplifying macros, which reduce the need to worry whether the
- * buffers have been allocated. These also make USE_BUF () an lvalue,
- * which is used in g_io_channel_read_to_end ().
- */
-#define USE_BUF(channel) ((channel)->encoding ? (channel)->encoded_read_buf \
- : (channel)->read_buf)
-#define BUF_LEN(string) ((string) ? (string)->len : 0)
-
-static GIOError g_io_error_get_from_g_error (GIOStatus status,
- GError *err);
-static void g_io_channel_purge (GIOChannel *channel);
-static GIOStatus g_io_channel_fill_buffer (GIOChannel *channel,
- GError **err);
-static GIOStatus g_io_channel_read_line_backend (GIOChannel *channel,
- gsize *length,
- gsize *terminator_pos,
- GError **error);
-
-/**
- * g_io_channel_init:
- * @channel: a #GIOChannel
- *
- * Initializes a #GIOChannel struct.
- *
- * This is called by each of the above functions when creating a
- * #GIOChannel, and so is not often needed by the application
- * programmer (unless you are creating a new type of #GIOChannel).
- */
-void
-g_io_channel_init (GIOChannel *channel)
-{
- channel->ref_count = 1;
- channel->encoding = g_strdup ("UTF-8");
- channel->line_term = NULL;
- channel->line_term_len = 0;
- channel->buf_size = G_IO_NICE_BUF_SIZE;
- channel->read_cd = (GIConv) -1;
- channel->write_cd = (GIConv) -1;
- channel->read_buf = NULL; /* Lazy allocate buffers */
- channel->encoded_read_buf = NULL;
- channel->write_buf = NULL;
- channel->partial_write_buf[0] = '\0';
- channel->use_buffer = TRUE;
- channel->do_encode = FALSE;
- channel->close_on_unref = FALSE;
-}
-
-/**
- * g_io_channel_ref:
- * @channel: a #GIOChannel
- *
- * Increments the reference count of a #GIOChannel.
- *
- * Returns: the @channel that was passed in (since 2.6)
- */
-GIOChannel *
-g_io_channel_ref (GIOChannel *channel)
-{
- g_return_val_if_fail (channel != NULL, NULL);
-
- g_atomic_int_inc (&channel->ref_count);
-
- return channel;
-}
-
-/**
- * g_io_channel_unref:
- * @channel: a #GIOChannel
- *
- * Decrements the reference count of a #GIOChannel.
- */
-void
-g_io_channel_unref (GIOChannel *channel)
-{
- gboolean is_zero;
-
- g_return_if_fail (channel != NULL);
-
- is_zero = g_atomic_int_dec_and_test (&channel->ref_count);
-
- if (G_UNLIKELY (is_zero))
- {
- if (channel->close_on_unref)
- g_io_channel_shutdown (channel, TRUE, NULL);
- else
- g_io_channel_purge (channel);
- g_free (channel->encoding);
- if (channel->read_cd != (GIConv) -1)
- g_iconv_close (channel->read_cd);
- if (channel->write_cd != (GIConv) -1)
- g_iconv_close (channel->write_cd);
- g_free (channel->line_term);
- if (channel->read_buf)
- g_string_free (channel->read_buf, TRUE);
- if (channel->write_buf)
- g_string_free (channel->write_buf, TRUE);
- if (channel->encoded_read_buf)
- g_string_free (channel->encoded_read_buf, TRUE);
- channel->funcs->io_free (channel);
- }
-}
-
-static GIOError
-g_io_error_get_from_g_error (GIOStatus status,
- GError *err)
-{
- switch (status)
- {
- case G_IO_STATUS_NORMAL:
- case G_IO_STATUS_EOF:
- return G_IO_ERROR_NONE;
- case G_IO_STATUS_AGAIN:
- return G_IO_ERROR_AGAIN;
- case G_IO_STATUS_ERROR:
- g_return_val_if_fail (err != NULL, G_IO_ERROR_UNKNOWN);
-
- if (err->domain != G_IO_CHANNEL_ERROR)
- return G_IO_ERROR_UNKNOWN;
- switch (err->code)
- {
- case G_IO_CHANNEL_ERROR_INVAL:
- return G_IO_ERROR_INVAL;
- default:
- return G_IO_ERROR_UNKNOWN;
- }
- default:
- g_assert_not_reached ();
- }
-}
-
-/**
- * g_io_channel_read:
- * @channel: a #GIOChannel
- * @buf: a buffer to read the data into (which should be at least
- * count bytes long)
- * @count: the number of bytes to read from the #GIOChannel
- * @bytes_read: returns the number of bytes actually read
- *
- * Reads data from a #GIOChannel.
- *
- * Return value: %G_IO_ERROR_NONE if the operation was successful.
- *
- * Deprecated:2.2: Use g_io_channel_read_chars() instead.
- **/
-GIOError
-g_io_channel_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read)
-{
- GError *err = NULL;
- GIOError error;
- GIOStatus status;
-
- g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
- g_return_val_if_fail (bytes_read != NULL, G_IO_ERROR_UNKNOWN);
-
- if (count == 0)
- {
- if (bytes_read)
- *bytes_read = 0;
- return G_IO_ERROR_NONE;
- }
-
- g_return_val_if_fail (buf != NULL, G_IO_ERROR_UNKNOWN);
-
- status = channel->funcs->io_read (channel, buf, count, bytes_read, &err);
-
- error = g_io_error_get_from_g_error (status, err);
-
- if (err)
- g_error_free (err);
-
- return error;
-}
-
-/**
- * g_io_channel_write:
- * @channel: a #GIOChannel
- * @buf: the buffer containing the data to write
- * @count: the number of bytes to write
- * @bytes_written: the number of bytes actually written
- *
- * Writes data to a #GIOChannel.
- *
- * Return value: %G_IO_ERROR_NONE if the operation was successful.
- *
- * Deprecated:2.2: Use g_io_channel_write_chars() instead.
- **/
-GIOError
-g_io_channel_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written)
-{
- GError *err = NULL;
- GIOError error;
- GIOStatus status;
-
- g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
- g_return_val_if_fail (bytes_written != NULL, G_IO_ERROR_UNKNOWN);
-
- status = channel->funcs->io_write (channel, buf, count, bytes_written, &err);
-
- error = g_io_error_get_from_g_error (status, err);
-
- if (err)
- g_error_free (err);
-
- return error;
-}
-
-/**
- * g_io_channel_seek:
- * @channel: a #GIOChannel
- * @offset: an offset, in bytes, which is added to the position specified
- * by @type
- * @type: the position in the file, which can be %G_SEEK_CUR (the current
- * position), %G_SEEK_SET (the start of the file), or %G_SEEK_END
- * (the end of the file)
- *
- * Sets the current position in the #GIOChannel, similar to the standard
- * library function fseek().
- *
- * Return value: %G_IO_ERROR_NONE if the operation was successful.
- *
- * Deprecated:2.2: Use g_io_channel_seek_position() instead.
- **/
-GIOError
-g_io_channel_seek (GIOChannel *channel,
- gint64 offset,
- GSeekType type)
-{
- GError *err = NULL;
- GIOError error;
- GIOStatus status;
-
- g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
- g_return_val_if_fail (channel->is_seekable, G_IO_ERROR_UNKNOWN);
-
- switch (type)
- {
- case G_SEEK_CUR:
- case G_SEEK_SET:
- case G_SEEK_END:
- break;
- default:
- g_warning ("g_io_channel_seek: unknown seek type");
- return G_IO_ERROR_UNKNOWN;
- }
-
- status = channel->funcs->io_seek (channel, offset, type, &err);
-
- error = g_io_error_get_from_g_error (status, err);
-
- if (err)
- g_error_free (err);
-
- return error;
-}
-
-/* The function g_io_channel_new_file() is prototyped in both
- * giounix.c and giowin32.c, so we stick its documentation here.
- */
-
-/**
- * g_io_channel_new_file:
- * @filename: A string containing the name of a file
- * @mode: One of "r", "w", "a", "r+", "w+", "a+". These have
- * the same meaning as in fopen()
- * @error: A location to return an error of type %G_FILE_ERROR
- *
- * Open a file @filename as a #GIOChannel using mode @mode. This
- * channel will be closed when the last reference to it is dropped,
- * so there is no need to call g_io_channel_close() (though doing
- * so will not cause problems, as long as no attempt is made to
- * access the channel after it is closed).
- *
- * Return value: A #GIOChannel on success, %NULL on failure.
- **/
-
-/**
- * g_io_channel_close:
- * @channel: A #GIOChannel
- *
- * Close an IO channel. Any pending data to be written will be
- * flushed, ignoring errors. The channel will not be freed until the
- * last reference is dropped using g_io_channel_unref().
- *
- * Deprecated:2.2: Use g_io_channel_shutdown() instead.
- **/
-void
-g_io_channel_close (GIOChannel *channel)
-{
- GError *err = NULL;
-
- g_return_if_fail (channel != NULL);
-
- g_io_channel_purge (channel);
-
- channel->funcs->io_close (channel, &err);
-
- if (err)
- { /* No way to return the error */
- g_warning ("Error closing channel: %s", err->message);
- g_error_free (err);
- }
-
- channel->close_on_unref = FALSE; /* Because we already did */
- channel->is_readable = FALSE;
- channel->is_writeable = FALSE;
- channel->is_seekable = FALSE;
-}
-
-/**
- * g_io_channel_shutdown:
- * @channel: a #GIOChannel
- * @flush: if %TRUE, flush pending
- * @err: location to store a #GIOChannelError
- *
- * Close an IO channel. Any pending data to be written will be
- * flushed if @flush is %TRUE. The channel will not be freed until the
- * last reference is dropped using g_io_channel_unref().
- *
- * Return value: the status of the operation.
- **/
-GIOStatus
-g_io_channel_shutdown (GIOChannel *channel,
- gboolean flush,
- GError **err)
-{
- GIOStatus status, result;
- GError *tmperr = NULL;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (err == NULL || *err == NULL, G_IO_STATUS_ERROR);
-
- if (channel->write_buf && channel->write_buf->len > 0)
- {
- if (flush)
- {
- GIOFlags flags;
-
- /* Set the channel to blocking, to avoid a busy loop
- */
- flags = g_io_channel_get_flags (channel);
- /* Ignore any errors here, they're irrelevant */
- g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
-
- result = g_io_channel_flush (channel, &tmperr);
- }
- else
- result = G_IO_STATUS_NORMAL;
-
- g_string_truncate(channel->write_buf, 0);
- }
- else
- result = G_IO_STATUS_NORMAL;
-
- if (channel->partial_write_buf[0] != '\0')
- {
- if (flush)
- g_warning ("Partial character at end of write buffer not flushed.\n");
- channel->partial_write_buf[0] = '\0';
- }
-
- status = channel->funcs->io_close (channel, err);
-
- channel->close_on_unref = FALSE; /* Because we already did */
- channel->is_readable = FALSE;
- channel->is_writeable = FALSE;
- channel->is_seekable = FALSE;
-
- if (status != G_IO_STATUS_NORMAL)
- {
- g_clear_error (&tmperr);
- return status;
- }
- else if (result != G_IO_STATUS_NORMAL)
- {
- g_propagate_error (err, tmperr);
- return result;
- }
- else
- return G_IO_STATUS_NORMAL;
-}
-
-/* This function is used for the final flush on close or unref */
-static void
-g_io_channel_purge (GIOChannel *channel)
-{
- GError *err = NULL;
- GIOStatus status;
-
- g_return_if_fail (channel != NULL);
-
- if (channel->write_buf && channel->write_buf->len > 0)
- {
- GIOFlags flags;
-
- /* Set the channel to blocking, to avoid a busy loop
- */
- flags = g_io_channel_get_flags (channel);
- g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
-
- status = g_io_channel_flush (channel, &err);
-
- if (err)
- { /* No way to return the error */
- g_warning ("Error flushing string: %s", err->message);
- g_error_free (err);
- }
- }
-
- /* Flush these in case anyone tries to close without unrefing */
-
- if (channel->read_buf)
- g_string_truncate (channel->read_buf, 0);
- if (channel->write_buf)
- g_string_truncate (channel->write_buf, 0);
- if (channel->encoding)
- {
- if (channel->encoded_read_buf)
- g_string_truncate (channel->encoded_read_buf, 0);
-
- if (channel->partial_write_buf[0] != '\0')
- {
- g_warning ("Partial character at end of write buffer not flushed.\n");
- channel->partial_write_buf[0] = '\0';
- }
- }
-}
-
-/**
- * g_io_create_watch:
- * @channel: a #GIOChannel to watch
- * @condition: conditions to watch for
- *
- * Creates a #GSource that's dispatched when @condition is met for the
- * given @channel. For example, if condition is #G_IO_IN, the source will
- * be dispatched when there's data available for reading.
- *
- * g_io_add_watch() is a simpler interface to this same functionality, for
- * the case where you want to add the source to the default main loop context
- * at the default priority.
- *
- * On Windows, polling a #GSource created to watch a channel for a socket
- * puts the socket in non-blocking mode. This is a side-effect of the
- * implementation and unavoidable.
- *
- * Returns: a new #GSource
- */
-GSource *
-g_io_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- g_return_val_if_fail (channel != NULL, NULL);
-
- return channel->funcs->io_create_watch (channel, condition);
-}
-
-/**
- * g_io_add_watch_full:
- * @channel: a #GIOChannel
- * @priority: the priority of the #GIOChannel source
- * @condition: the condition to watch for
- * @func: the function to call when the condition is satisfied
- * @user_data: user data to pass to @func
- * @notify: the function to call when the source is removed
- *
- * Adds the #GIOChannel into the default main loop context
- * with the given priority.
- *
- * This internally creates a main loop source using g_io_create_watch()
- * and attaches it to the main loop context with g_source_attach().
- * You can do these steps manually if you need greater control.
- *
- * Returns: the event source id
- */
-guint
-g_io_add_watch_full (GIOChannel *channel,
- gint priority,
- GIOCondition condition,
- GIOFunc func,
- gpointer user_data,
- GDestroyNotify notify)
-{
- GSource *source;
- guint id;
-
- g_return_val_if_fail (channel != NULL, 0);
-
- source = g_io_create_watch (channel, condition);
-
- if (priority != G_PRIORITY_DEFAULT)
- g_source_set_priority (source, priority);
- g_source_set_callback (source, (GSourceFunc)func, user_data, notify);
-
- id = g_source_attach (source, NULL);
- g_source_unref (source);
-
- return id;
-}
-
-/**
- * g_io_add_watch:
- * @channel: a #GIOChannel
- * @condition: the condition to watch for
- * @func: the function to call when the condition is satisfied
- * @user_data: user data to pass to @func
- *
- * Adds the #GIOChannel into the default main loop context
- * with the default priority.
- *
- * Returns: the event source id
- */
-/**
- * GIOFunc:
- * @source: the #GIOChannel event source
- * @condition: the condition which has been satisfied
- * @data: user data set in g_io_add_watch() or g_io_add_watch_full()
- * @Returns: the function should return %FALSE if the event source
- * should be removed
- *
- * Specifies the type of function passed to g_io_add_watch() or
- * g_io_add_watch_full(), which is called when the requested condition
- * on a #GIOChannel is satisfied.
- **/
-/**
- * GIOCondition:
- * @G_IO_IN: There is data to read.
- * @G_IO_OUT: Data can be written (without blocking).
- * @G_IO_PRI: There is urgent data to read.
- * @G_IO_ERR: Error condition.
- * @G_IO_HUP: Hung up (the connection has been broken, usually for
- * pipes and sockets).
- * @G_IO_NVAL: Invalid request. The file descriptor is not open.
- *
- * A bitwise combination representing a condition to watch for on an
- * event source.
- **/
-guint
-g_io_add_watch (GIOChannel *channel,
- GIOCondition condition,
- GIOFunc func,
- gpointer user_data)
-{
- return g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, condition, func, user_data, NULL);
-}
-
-/**
- * g_io_channel_get_buffer_condition:
- * @channel: A #GIOChannel
- *
- * This function returns a #GIOCondition depending on whether there
- * is data to be read/space to write data in the internal buffers in
- * the #GIOChannel. Only the flags %G_IO_IN and %G_IO_OUT may be set.
- *
- * Return value: A #GIOCondition
- **/
-GIOCondition
-g_io_channel_get_buffer_condition (GIOChannel *channel)
-{
- GIOCondition condition = 0;
-
- if (channel->encoding)
- {
- if (channel->encoded_read_buf && (channel->encoded_read_buf->len > 0))
- condition |= G_IO_IN; /* Only return if we have full characters */
- }
- else
- {
- if (channel->read_buf && (channel->read_buf->len > 0))
- condition |= G_IO_IN;
- }
-
- if (channel->write_buf && (channel->write_buf->len < channel->buf_size))
- condition |= G_IO_OUT;
-
- return condition;
-}
-
-/**
- * g_io_channel_error_from_errno:
- * @en: an <literal>errno</literal> error number, e.g. %EINVAL
- *
- * Converts an <literal>errno</literal> error number to a #GIOChannelError.
- *
- * Return value: a #GIOChannelError error number, e.g.
- * %G_IO_CHANNEL_ERROR_INVAL.
- **/
-GIOChannelError
-g_io_channel_error_from_errno (gint en)
-{
-#ifdef EAGAIN
- g_return_val_if_fail (en != EAGAIN, G_IO_CHANNEL_ERROR_FAILED);
-#endif
-
- switch (en)
- {
-#ifdef EBADF
- case EBADF:
- g_warning("Invalid file descriptor.\n");
- return G_IO_CHANNEL_ERROR_FAILED;
-#endif
-
-#ifdef EFAULT
- case EFAULT:
- g_warning("Buffer outside valid address space.\n");
- return G_IO_CHANNEL_ERROR_FAILED;
-#endif
-
-#ifdef EFBIG
- case EFBIG:
- return G_IO_CHANNEL_ERROR_FBIG;
-#endif
-
-#ifdef EINTR
- /* In general, we should catch EINTR before we get here,
- * but close() is allowed to return EINTR by POSIX, so
- * we need to catch it here; EINTR from close() is
- * unrecoverable, because it's undefined whether
- * the fd was actually closed or not, so we just return
- * a generic error code.
- */
- case EINTR:
- return G_IO_CHANNEL_ERROR_FAILED;
-#endif
-
-#ifdef EINVAL
- case EINVAL:
- return G_IO_CHANNEL_ERROR_INVAL;
-#endif
-
-#ifdef EIO
- case EIO:
- return G_IO_CHANNEL_ERROR_IO;
-#endif
-
-#ifdef EISDIR
- case EISDIR:
- return G_IO_CHANNEL_ERROR_ISDIR;
-#endif
-
-#ifdef ENOSPC
- case ENOSPC:
- return G_IO_CHANNEL_ERROR_NOSPC;
-#endif
-
-#ifdef ENXIO
- case ENXIO:
- return G_IO_CHANNEL_ERROR_NXIO;
-#endif
-
-#ifdef EOVERFLOW
- case EOVERFLOW:
- return G_IO_CHANNEL_ERROR_OVERFLOW;
-#endif
-
-#ifdef EPIPE
- case EPIPE:
- return G_IO_CHANNEL_ERROR_PIPE;
-#endif
-
- default:
- return G_IO_CHANNEL_ERROR_FAILED;
- }
-}
-
-/**
- * g_io_channel_set_buffer_size:
- * @channel: a #GIOChannel
- * @size: the size of the buffer, or 0 to let GLib pick a good size
- *
- * Sets the buffer size.
- **/
-void
-g_io_channel_set_buffer_size (GIOChannel *channel,
- gsize size)
-{
- g_return_if_fail (channel != NULL);
-
- if (size == 0)
- size = G_IO_NICE_BUF_SIZE;
-
- if (size < MAX_CHAR_SIZE)
- size = MAX_CHAR_SIZE;
-
- channel->buf_size = size;
-}
-
-/**
- * g_io_channel_get_buffer_size:
- * @channel: a #GIOChannel
- *
- * Gets the buffer size.
- *
- * Return value: the size of the buffer.
- **/
-gsize
-g_io_channel_get_buffer_size (GIOChannel *channel)
-{
- g_return_val_if_fail (channel != NULL, 0);
-
- return channel->buf_size;
-}
-
-/**
- * g_io_channel_set_line_term:
- * @channel: a #GIOChannel
- * @line_term: The line termination string. Use %NULL for autodetect.
- * Autodetection breaks on "\n", "\r\n", "\r", "\0", and
- * the Unicode paragraph separator. Autodetection should
- * not be used for anything other than file-based channels.
- * @length: The length of the termination string. If -1 is passed, the
- * string is assumed to be nul-terminated. This option allows
- * termination strings with embedded nuls.
- *
- * This sets the string that #GIOChannel uses to determine
- * where in the file a line break occurs.
- **/
-void
-g_io_channel_set_line_term (GIOChannel *channel,
- const gchar *line_term,
- gint length)
-{
- g_return_if_fail (channel != NULL);
- g_return_if_fail (line_term == NULL || length != 0); /* Disallow "" */
-
- if (line_term == NULL)
- length = 0;
- else if (length < 0)
- length = strlen (line_term);
-
- g_free (channel->line_term);
- channel->line_term = line_term ? g_memdup (line_term, length) : NULL;
- channel->line_term_len = length;
-}
-
-/**
- * g_io_channel_get_line_term:
- * @channel: a #GIOChannel
- * @length: a location to return the length of the line terminator
- *
- * This returns the string that #GIOChannel uses to determine
- * where in the file a line break occurs. A value of %NULL
- * indicates autodetection.
- *
- * Return value: The line termination string. This value
- * is owned by GLib and must not be freed.
- **/
-G_CONST_RETURN gchar*
-g_io_channel_get_line_term (GIOChannel *channel,
- gint *length)
-{
- g_return_val_if_fail (channel != NULL, NULL);
-
- if (length)
- *length = channel->line_term_len;
-
- return channel->line_term;
-}
-
-/**
- * g_io_channel_set_flags:
- * @channel: a #GIOChannel
- * @flags: the flags to set on the IO channel
- * @error: A location to return an error of type #GIOChannelError
- *
- * Sets the (writeable) flags in @channel to (@flags & %G_IO_CHANNEL_SET_MASK).
- *
- * Return value: the status of the operation.
- **/
-/**
- * GIOFlags:
- * @G_IO_FLAG_APPEND: turns on append mode, corresponds to %O_APPEND
- * (see the documentation of the UNIX open()
- * syscall).
- * @G_IO_FLAG_NONBLOCK: turns on nonblocking mode, corresponds to
- * %O_NONBLOCK/%O_NDELAY (see the documentation of
- * the UNIX open() syscall).
- * @G_IO_FLAG_IS_READABLE: indicates that the io channel is readable.
- * This flag can not be changed.
- * @G_IO_FLAG_IS_WRITEABLE: indicates that the io channel is writable.
- * This flag can not be changed.
- * @G_IO_FLAG_IS_SEEKABLE: indicates that the io channel is seekable,
- * i.e. that g_io_channel_seek_position() can
- * be used on it. This flag can not be changed.
- * @G_IO_FLAG_MASK: the mask that specifies all the valid flags.
- * @G_IO_FLAG_GET_MASK: the mask of the flags that are returned from
- * g_io_channel_get_flags().
- * @G_IO_FLAG_SET_MASK: the mask of the flags that the user can modify
- * with g_io_channel_set_flags().
- *
- * Specifies properties of a #GIOChannel. Some of the flags can only be
- * read with g_io_channel_get_flags(), but not changed with
- * g_io_channel_set_flags().
- **/
-GIOStatus
-g_io_channel_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **error)
-{
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
-
- return (*channel->funcs->io_set_flags) (channel,
- flags & G_IO_FLAG_SET_MASK,
- error);
-}
-
-/**
- * g_io_channel_get_flags:
- * @channel: a #GIOChannel
- *
- * Gets the current flags for a #GIOChannel, including read-only
- * flags such as %G_IO_FLAG_IS_READABLE.
- *
- * The values of the flags %G_IO_FLAG_IS_READABLE and %G_IO_FLAG_IS_WRITEABLE
- * are cached for internal use by the channel when it is created.
- * If they should change at some later point (e.g. partial shutdown
- * of a socket with the UNIX shutdown() function), the user
- * should immediately call g_io_channel_get_flags() to update
- * the internal values of these flags.
- *
- * Return value: the flags which are set on the channel
- **/
-GIOFlags
-g_io_channel_get_flags (GIOChannel *channel)
-{
- GIOFlags flags;
-
- g_return_val_if_fail (channel != NULL, 0);
-
- flags = (* channel->funcs->io_get_flags) (channel);
-
- /* Cross implementation code */
-
- if (channel->is_seekable)
- flags |= G_IO_FLAG_IS_SEEKABLE;
- if (channel->is_readable)
- flags |= G_IO_FLAG_IS_READABLE;
- if (channel->is_writeable)
- flags |= G_IO_FLAG_IS_WRITEABLE;
-
- return flags;
-}
-
-/**
- * g_io_channel_set_close_on_unref:
- * @channel: a #GIOChannel
- * @do_close: Whether to close the channel on the final unref of
- * the GIOChannel data structure. The default value of
- * this is %TRUE for channels created by g_io_channel_new_file (),
- * and %FALSE for all other channels.
- *
- * Setting this flag to %TRUE for a channel you have already closed
- * can cause problems.
- **/
-void
-g_io_channel_set_close_on_unref (GIOChannel *channel,
- gboolean do_close)
-{
- g_return_if_fail (channel != NULL);
-
- channel->close_on_unref = do_close;
-}
-
-/**
- * g_io_channel_get_close_on_unref:
- * @channel: a #GIOChannel.
- *
- * Returns whether the file/socket/whatever associated with @channel
- * will be closed when @channel receives its final unref and is
- * destroyed. The default value of this is %TRUE for channels created
- * by g_io_channel_new_file (), and %FALSE for all other channels.
- *
- * Return value: Whether the channel will be closed on the final unref of
- * the GIOChannel data structure.
- **/
-gboolean
-g_io_channel_get_close_on_unref (GIOChannel *channel)
-{
- g_return_val_if_fail (channel != NULL, FALSE);
-
- return channel->close_on_unref;
-}
-
-/**
- * g_io_channel_seek_position:
- * @channel: a #GIOChannel
- * @offset: The offset in bytes from the position specified by @type
- * @type: a #GSeekType. The type %G_SEEK_CUR is only allowed in those
- * cases where a call to g_io_channel_set_encoding ()
- * is allowed. See the documentation for
- * g_io_channel_set_encoding () for details.
- * @error: A location to return an error of type #GIOChannelError
- *
- * Replacement for g_io_channel_seek() with the new API.
- *
- * Return value: the status of the operation.
- **/
-/**
- * GSeekType:
- * @G_SEEK_CUR: the current position in the file.
- * @G_SEEK_SET: the start of the file.
- * @G_SEEK_END: the end of the file.
- *
- * An enumeration specifying the base position for a
- * g_io_channel_seek_position() operation.
- **/
-GIOStatus
-g_io_channel_seek_position (GIOChannel *channel,
- gint64 offset,
- GSeekType type,
- GError **error)
-{
- GIOStatus status;
-
- /* For files, only one of the read and write buffers can contain data.
- * For sockets, both can contain data.
- */
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_seekable, G_IO_STATUS_ERROR);
-
- switch (type)
- {
- case G_SEEK_CUR: /* The user is seeking relative to the head of the buffer */
- if (channel->use_buffer)
- {
- if (channel->do_encode && channel->encoded_read_buf
- && channel->encoded_read_buf->len > 0)
- {
- g_warning ("Seek type G_SEEK_CUR not allowed for this"
- " channel's encoding.\n");
- return G_IO_STATUS_ERROR;
- }
- if (channel->read_buf)
- offset -= channel->read_buf->len;
- if (channel->encoded_read_buf)
- {
- g_assert (channel->encoded_read_buf->len == 0 || !channel->do_encode);
-
- /* If there's anything here, it's because the encoding is UTF-8,
- * so we can just subtract the buffer length, the same as for
- * the unencoded data.
- */
-
- offset -= channel->encoded_read_buf->len;
- }
- }
- break;
- case G_SEEK_SET:
- case G_SEEK_END:
- break;
- default:
- g_warning ("g_io_channel_seek_position: unknown seek type");
- return G_IO_STATUS_ERROR;
- }
-
- if (channel->use_buffer)
- {
- status = g_io_channel_flush (channel, error);
- if (status != G_IO_STATUS_NORMAL)
- return status;
- }
-
- status = channel->funcs->io_seek (channel, offset, type, error);
-
- if ((status == G_IO_STATUS_NORMAL) && (channel->use_buffer))
- {
- if (channel->read_buf)
- g_string_truncate (channel->read_buf, 0);
-
- /* Conversion state no longer matches position in file */
- if (channel->read_cd != (GIConv) -1)
- g_iconv (channel->read_cd, NULL, NULL, NULL, NULL);
- if (channel->write_cd != (GIConv) -1)
- g_iconv (channel->write_cd, NULL, NULL, NULL, NULL);
-
- if (channel->encoded_read_buf)
- {
- g_assert (channel->encoded_read_buf->len == 0 || !channel->do_encode);
- g_string_truncate (channel->encoded_read_buf, 0);
- }
-
- if (channel->partial_write_buf[0] != '\0')
- {
- g_warning ("Partial character at end of write buffer not flushed.\n");
- channel->partial_write_buf[0] = '\0';
- }
- }
-
- return status;
-}
-
-/**
- * g_io_channel_flush:
- * @channel: a #GIOChannel
- * @error: location to store an error of type #GIOChannelError
- *
- * Flushes the write buffer for the GIOChannel.
- *
- * Return value: the status of the operation: One of
- * #G_IO_STATUS_NORMAL, #G_IO_STATUS_AGAIN, or
- * #G_IO_STATUS_ERROR.
- **/
-GIOStatus
-g_io_channel_flush (GIOChannel *channel,
- GError **error)
-{
- GIOStatus status;
- gsize this_time = 1, bytes_written = 0;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
-
- if (channel->write_buf == NULL || channel->write_buf->len == 0)
- return G_IO_STATUS_NORMAL;
-
- do
- {
- g_assert (this_time > 0);
-
- status = channel->funcs->io_write (channel,
- channel->write_buf->str + bytes_written,
- channel->write_buf->len - bytes_written,
- &this_time, error);
- bytes_written += this_time;
- }
- while ((bytes_written < channel->write_buf->len)
- && (status == G_IO_STATUS_NORMAL));
-
- g_string_erase (channel->write_buf, 0, bytes_written);
-
- return status;
-}
-
-/**
- * g_io_channel_set_buffered:
- * @channel: a #GIOChannel
- * @buffered: whether to set the channel buffered or unbuffered
- *
- * The buffering state can only be set if the channel's encoding
- * is %NULL. For any other encoding, the channel must be buffered.
- *
- * A buffered channel can only be set unbuffered if the channel's
- * internal buffers have been flushed. Newly created channels or
- * channels which have returned %G_IO_STATUS_EOF
- * not require such a flush. For write-only channels, a call to
- * g_io_channel_flush () is sufficient. For all other channels,
- * the buffers may be flushed by a call to g_io_channel_seek_position ().
- * This includes the possibility of seeking with seek type %G_SEEK_CUR
- * and an offset of zero. Note that this means that socket-based
- * channels cannot be set unbuffered once they have had data
- * read from them.
- *
- * On unbuffered channels, it is safe to mix read and write
- * calls from the new and old APIs, if this is necessary for
- * maintaining old code.
- *
- * The default state of the channel is buffered.
- **/
-void
-g_io_channel_set_buffered (GIOChannel *channel,
- gboolean buffered)
-{
- g_return_if_fail (channel != NULL);
-
- if (channel->encoding != NULL)
- {
- g_warning ("Need to have NULL encoding to set the buffering state of the "
- "channel.\n");
- return;
- }
-
- g_return_if_fail (!channel->read_buf || channel->read_buf->len == 0);
- g_return_if_fail (!channel->write_buf || channel->write_buf->len == 0);
-
- channel->use_buffer = buffered;
-}
-
-/**
- * g_io_channel_get_buffered:
- * @channel: a #GIOChannel
- *
- * Returns whether @channel is buffered.
- *
- * Return Value: %TRUE if the @channel is buffered.
- **/
-gboolean
-g_io_channel_get_buffered (GIOChannel *channel)
-{
- g_return_val_if_fail (channel != NULL, FALSE);
-
- return channel->use_buffer;
-}
-
-/**
- * g_io_channel_set_encoding:
- * @channel: a #GIOChannel
- * @encoding: the encoding type
- * @error: location to store an error of type #GConvertError
- *
- * Sets the encoding for the input/output of the channel.
- * The internal encoding is always UTF-8. The default encoding
- * for the external file is UTF-8.
- *
- * The encoding %NULL is safe to use with binary data.
- *
- * The encoding can only be set if one of the following conditions
- * is true:
- * <itemizedlist>
- * <listitem><para>
- * The channel was just created, and has not been written to or read
- * from yet.
- * </para></listitem>
- * <listitem><para>
- * The channel is write-only.
- * </para></listitem>
- * <listitem><para>
- * The channel is a file, and the file pointer was just
- * repositioned by a call to g_io_channel_seek_position().
- * (This flushes all the internal buffers.)
- * </para></listitem>
- * <listitem><para>
- * The current encoding is %NULL or UTF-8.
- * </para></listitem>
- * <listitem><para>
- * One of the (new API) read functions has just returned %G_IO_STATUS_EOF
- * (or, in the case of g_io_channel_read_to_end(), %G_IO_STATUS_NORMAL).
- * </para></listitem>
- * <listitem><para>
- * One of the functions g_io_channel_read_chars() or
- * g_io_channel_read_unichar() has returned %G_IO_STATUS_AGAIN or
- * %G_IO_STATUS_ERROR. This may be useful in the case of
- * %G_CONVERT_ERROR_ILLEGAL_SEQUENCE.
- * Returning one of these statuses from g_io_channel_read_line(),
- * g_io_channel_read_line_string(), or g_io_channel_read_to_end()
- * does <emphasis>not</emphasis> guarantee that the encoding can
- * be changed.
- * </para></listitem>
- * </itemizedlist>
- * Channels which do not meet one of the above conditions cannot call
- * g_io_channel_seek_position() with an offset of %G_SEEK_CUR, and, if
- * they are "seekable", cannot call g_io_channel_write_chars() after
- * calling one of the API "read" functions.
- *
- * Return Value: %G_IO_STATUS_NORMAL if the encoding was successfully set.
- **/
-GIOStatus
-g_io_channel_set_encoding (GIOChannel *channel,
- const gchar *encoding,
- GError **error)
-{
- GIConv read_cd, write_cd;
- gboolean did_encode;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
-
- /* Make sure the encoded buffers are empty */
-
- g_return_val_if_fail (!channel->do_encode || !channel->encoded_read_buf ||
- channel->encoded_read_buf->len == 0, G_IO_STATUS_ERROR);
-
- if (!channel->use_buffer)
- {
- g_warning ("Need to set the channel buffered before setting the encoding.\n");
- g_warning ("Assuming this is what you meant and acting accordingly.\n");
-
- channel->use_buffer = TRUE;
- }
-
- if (channel->partial_write_buf[0] != '\0')
- {
- g_warning ("Partial character at end of write buffer not flushed.\n");
- channel->partial_write_buf[0] = '\0';
- }
-
- did_encode = channel->do_encode;
-
- if (!encoding || strcmp (encoding, "UTF8") == 0 || strcmp (encoding, "UTF-8") == 0)
- {
- channel->do_encode = FALSE;
- read_cd = write_cd = (GIConv) -1;
- }
- else
- {
- gint err = 0;
- const gchar *from_enc = NULL, *to_enc = NULL;
-
- if (channel->is_readable)
- {
- read_cd = g_iconv_open ("UTF-8", encoding);
-
- if (read_cd == (GIConv) -1)
- {
- err = errno;
- from_enc = encoding;
- to_enc = "UTF-8";
- }
- }
- else
- read_cd = (GIConv) -1;
-
- if (channel->is_writeable && err == 0)
- {
- write_cd = g_iconv_open (encoding, "UTF-8");
-
- if (write_cd == (GIConv) -1)
- {
- err = errno;
- from_enc = "UTF-8";
- to_enc = encoding;
- }
- }
- else
- write_cd = (GIConv) -1;
-
- if (err != 0)
- {
- g_assert (from_enc);
- g_assert (to_enc);
-
- if (err == EINVAL)
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
- _("Conversion from character set '%s' to '%s' is not supported"),
- from_enc, to_enc);
- else
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Could not open converter from '%s' to '%s': %s"),
- from_enc, to_enc, g_strerror (err));
-
- if (read_cd != (GIConv) -1)
- g_iconv_close (read_cd);
- if (write_cd != (GIConv) -1)
- g_iconv_close (write_cd);
-
- return G_IO_STATUS_ERROR;
- }
-
- channel->do_encode = TRUE;
- }
-
- /* The encoding is ok, so set the fields in channel */
-
- if (channel->read_cd != (GIConv) -1)
- g_iconv_close (channel->read_cd);
- if (channel->write_cd != (GIConv) -1)
- g_iconv_close (channel->write_cd);
-
- if (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)
- {
- g_assert (!did_encode); /* Encoding UTF-8, NULL doesn't use encoded_read_buf */
-
- /* This is just validated UTF-8, so we can copy it back into read_buf
- * so it can be encoded in whatever the new encoding is.
- */
-
- g_string_prepend_len (channel->read_buf, channel->encoded_read_buf->str,
- channel->encoded_read_buf->len);
- g_string_truncate (channel->encoded_read_buf, 0);
- }
-
- channel->read_cd = read_cd;
- channel->write_cd = write_cd;
-
- g_free (channel->encoding);
- channel->encoding = g_strdup (encoding);
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_get_encoding:
- * @channel: a #GIOChannel
- *
- * Gets the encoding for the input/output of the channel.
- * The internal encoding is always UTF-8. The encoding %NULL
- * makes the channel safe for binary data.
- *
- * Return value: A string containing the encoding, this string is
- * owned by GLib and must not be freed.
- **/
-G_CONST_RETURN gchar*
-g_io_channel_get_encoding (GIOChannel *channel)
-{
- g_return_val_if_fail (channel != NULL, NULL);
-
- return channel->encoding;
-}
-
-static GIOStatus
-g_io_channel_fill_buffer (GIOChannel *channel,
- GError **err)
-{
- gsize read_size, cur_len, oldlen;
- GIOStatus status;
-
- if (channel->is_seekable && channel->write_buf && channel->write_buf->len > 0)
- {
- status = g_io_channel_flush (channel, err);
- if (status != G_IO_STATUS_NORMAL)
- return status;
- }
- if (channel->is_seekable && channel->partial_write_buf[0] != '\0')
- {
- g_warning ("Partial character at end of write buffer not flushed.\n");
- channel->partial_write_buf[0] = '\0';
- }
-
- if (!channel->read_buf)
- channel->read_buf = g_string_sized_new (channel->buf_size);
-
- cur_len = channel->read_buf->len;
-
- g_string_set_size (channel->read_buf, channel->read_buf->len + channel->buf_size);
-
- status = channel->funcs->io_read (channel, channel->read_buf->str + cur_len,
- channel->buf_size, &read_size, err);
-
- g_assert ((status == G_IO_STATUS_NORMAL) || (read_size == 0));
-
- g_string_truncate (channel->read_buf, read_size + cur_len);
-
- if ((status != G_IO_STATUS_NORMAL) &&
- ((status != G_IO_STATUS_EOF) || (channel->read_buf->len == 0)))
- return status;
-
- g_assert (channel->read_buf->len > 0);
-
- if (channel->encoded_read_buf)
- oldlen = channel->encoded_read_buf->len;
- else
- {
- oldlen = 0;
- if (channel->encoding)
- channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
- }
-
- if (channel->do_encode)
- {
- gsize errnum, inbytes_left, outbytes_left;
- gchar *inbuf, *outbuf;
- int errval;
-
- g_assert (channel->encoded_read_buf);
-
-reencode:
-
- inbytes_left = channel->read_buf->len;
- outbytes_left = MAX (channel->read_buf->len,
- channel->encoded_read_buf->allocated_len
- - channel->encoded_read_buf->len - 1); /* 1 for NULL */
- outbytes_left = MAX (outbytes_left, 6);
-
- inbuf = channel->read_buf->str;
- g_string_set_size (channel->encoded_read_buf,
- channel->encoded_read_buf->len + outbytes_left);
- outbuf = channel->encoded_read_buf->str + channel->encoded_read_buf->len
- - outbytes_left;
-
- errnum = g_iconv (channel->read_cd, &inbuf, &inbytes_left,
- &outbuf, &outbytes_left);
- errval = errno;
-
- g_assert (inbuf + inbytes_left == channel->read_buf->str
- + channel->read_buf->len);
- g_assert (outbuf + outbytes_left == channel->encoded_read_buf->str
- + channel->encoded_read_buf->len);
-
- g_string_erase (channel->read_buf, 0,
- channel->read_buf->len - inbytes_left);
- g_string_truncate (channel->encoded_read_buf,
- channel->encoded_read_buf->len - outbytes_left);
-
- if (errnum == (gsize) -1)
- {
- switch (errval)
- {
- case EINVAL:
- if ((oldlen == channel->encoded_read_buf->len)
- && (status == G_IO_STATUS_EOF))
- status = G_IO_STATUS_EOF;
- else
- status = G_IO_STATUS_NORMAL;
- break;
- case E2BIG:
- /* Buffer size at least 6, wrote at least on character */
- g_assert (inbuf != channel->read_buf->str);
- goto reencode;
- case EILSEQ:
- if (oldlen < channel->encoded_read_buf->len)
- status = G_IO_STATUS_NORMAL;
- else
- {
- g_set_error_literal (err, G_CONVERT_ERROR,
- G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
- return G_IO_STATUS_ERROR;
- }
- break;
- default:
- g_assert (errval != EBADF); /* The converter should be open */
- g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Error during conversion: %s"), g_strerror (errval));
- return G_IO_STATUS_ERROR;
- }
- }
- g_assert ((status != G_IO_STATUS_NORMAL)
- || (channel->encoded_read_buf->len > 0));
- }
- else if (channel->encoding) /* UTF-8 */
- {
- gchar *nextchar, *lastchar;
-
- g_assert (channel->encoded_read_buf);
-
- nextchar = channel->read_buf->str;
- lastchar = channel->read_buf->str + channel->read_buf->len;
-
- while (nextchar < lastchar)
- {
- gunichar val_char;
-
- val_char = g_utf8_get_char_validated (nextchar, lastchar - nextchar);
-
- switch (val_char)
- {
- case -2:
- /* stop, leave partial character in buffer */
- lastchar = nextchar;
- break;
- case -1:
- if (oldlen < channel->encoded_read_buf->len)
- status = G_IO_STATUS_NORMAL;
- else
- {
- g_set_error_literal (err, G_CONVERT_ERROR,
- G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
- status = G_IO_STATUS_ERROR;
- }
- lastchar = nextchar;
- break;
- default:
- nextchar = g_utf8_next_char (nextchar);
- break;
- }
- }
-
- if (lastchar > channel->read_buf->str)
- {
- gint copy_len = lastchar - channel->read_buf->str;
-
- g_string_append_len (channel->encoded_read_buf, channel->read_buf->str,
- copy_len);
- g_string_erase (channel->read_buf, 0, copy_len);
- }
- }
-
- return status;
-}
-
-/**
- * g_io_channel_read_line:
- * @channel: a #GIOChannel
- * @str_return: The line read from the #GIOChannel, including the
- * line terminator. This data should be freed with g_free()
- * when no longer needed. This is a nul-terminated string.
- * If a @length of zero is returned, this will be %NULL instead.
- * @length: location to store length of the read data, or %NULL
- * @terminator_pos: location to store position of line terminator, or %NULL
- * @error: A location to return an error of type #GConvertError
- * or #GIOChannelError
- *
- * Reads a line, including the terminating character(s),
- * from a #GIOChannel into a newly-allocated string.
- * @str_return will contain allocated memory if the return
- * is %G_IO_STATUS_NORMAL.
- *
- * Return value: the status of the operation.
- **/
-GIOStatus
-g_io_channel_read_line (GIOChannel *channel,
- gchar **str_return,
- gsize *length,
- gsize *terminator_pos,
- GError **error)
-{
- GIOStatus status;
- gsize got_length;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (str_return != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
-
- status = g_io_channel_read_line_backend (channel, &got_length, terminator_pos, error);
-
- if (length)
- *length = got_length;
-
- if (status == G_IO_STATUS_NORMAL)
- {
- g_assert (USE_BUF (channel));
- *str_return = g_strndup (USE_BUF (channel)->str, got_length);
- g_string_erase (USE_BUF (channel), 0, got_length);
- }
- else
- *str_return = NULL;
-
- return status;
-}
-
-/**
- * g_io_channel_read_line_string:
- * @channel: a #GIOChannel
- * @buffer: a #GString into which the line will be written.
- * If @buffer already contains data, the old data will
- * be overwritten.
- * @terminator_pos: location to store position of line terminator, or %NULL
- * @error: a location to store an error of type #GConvertError
- * or #GIOChannelError
- *
- * Reads a line from a #GIOChannel, using a #GString as a buffer.
- *
- * Return value: the status of the operation.
- **/
-GIOStatus
-g_io_channel_read_line_string (GIOChannel *channel,
- GString *buffer,
- gsize *terminator_pos,
- GError **error)
-{
- gsize length;
- GIOStatus status;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (buffer != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
-
- if (buffer->len > 0)
- g_string_truncate (buffer, 0); /* clear out the buffer */
-
- status = g_io_channel_read_line_backend (channel, &length, terminator_pos, error);
-
- if (status == G_IO_STATUS_NORMAL)
- {
- g_assert (USE_BUF (channel));
- g_string_append_len (buffer, USE_BUF (channel)->str, length);
- g_string_erase (USE_BUF (channel), 0, length);
- }
-
- return status;
-}
-
-
-static GIOStatus
-g_io_channel_read_line_backend (GIOChannel *channel,
- gsize *length,
- gsize *terminator_pos,
- GError **error)
-{
- GIOStatus status;
- gsize checked_to, line_term_len, line_length, got_term_len;
- gboolean first_time = TRUE;
-
- if (!channel->use_buffer)
- {
- /* Can't do a raw read in read_line */
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Can't do a raw read in g_io_channel_read_line_string"));
- return G_IO_STATUS_ERROR;
- }
-
- status = G_IO_STATUS_NORMAL;
-
- if (channel->line_term)
- line_term_len = channel->line_term_len;
- else
- line_term_len = 3;
- /* This value used for setting checked_to, it's the longest of the four
- * we autodetect for.
- */
-
- checked_to = 0;
-
- while (TRUE)
- {
- gchar *nextchar, *lastchar;
- GString *use_buf;
-
- if (!first_time || (BUF_LEN (USE_BUF (channel)) == 0))
- {
-read_again:
- status = g_io_channel_fill_buffer (channel, error);
- switch (status)
- {
- case G_IO_STATUS_NORMAL:
- if (BUF_LEN (USE_BUF (channel)) == 0)
- /* Can happen when using conversion and only read
- * part of a character
- */
- {
- first_time = FALSE;
- continue;
- }
- break;
- case G_IO_STATUS_EOF:
- if (BUF_LEN (USE_BUF (channel)) == 0)
- {
- if (length)
- *length = 0;
-
- if (channel->encoding && channel->read_buf->len != 0)
- {
- g_set_error_literal (error, G_CONVERT_ERROR,
- G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Leftover unconverted data in "
- "read buffer"));
- return G_IO_STATUS_ERROR;
- }
- else
- return G_IO_STATUS_EOF;
- }
- break;
- default:
- if (length)
- *length = 0;
- return status;
- }
- }
-
- g_assert (BUF_LEN (USE_BUF (channel)) != 0);
-
- use_buf = USE_BUF (channel); /* The buffer has been created by this point */
-
- first_time = FALSE;
-
- lastchar = use_buf->str + use_buf->len;
-
- for (nextchar = use_buf->str + checked_to; nextchar < lastchar;
- channel->encoding ? nextchar = g_utf8_next_char (nextchar) : nextchar++)
- {
- if (channel->line_term)
- {
- if (memcmp (channel->line_term, nextchar, line_term_len) == 0)
- {
- line_length = nextchar - use_buf->str;
- got_term_len = line_term_len;
- goto done;
- }
- }
- else /* auto detect */
- {
- switch (*nextchar)
- {
- case '\n': /* unix */
- line_length = nextchar - use_buf->str;
- got_term_len = 1;
- goto done;
- case '\r': /* Warning: do not use with sockets */
- line_length = nextchar - use_buf->str;
- if ((nextchar == lastchar - 1) && (status != G_IO_STATUS_EOF)
- && (lastchar == use_buf->str + use_buf->len))
- goto read_again; /* Try to read more data */
- if ((nextchar < lastchar - 1) && (*(nextchar + 1) == '\n')) /* dos */
- got_term_len = 2;
- else /* mac */
- got_term_len = 1;
- goto done;
- case '\xe2': /* Unicode paragraph separator */
- if (strncmp ("\xe2\x80\xa9", nextchar, 3) == 0)
- {
- line_length = nextchar - use_buf->str;
- got_term_len = 3;
- goto done;
- }
- break;
- case '\0': /* Embeded null in input */
- line_length = nextchar - use_buf->str;
- got_term_len = 1;
- goto done;
- default: /* no match */
- break;
- }
- }
- }
-
- /* If encoding != NULL, valid UTF-8, didn't overshoot */
- g_assert (nextchar == lastchar);
-
- /* Check for EOF */
-
- if (status == G_IO_STATUS_EOF)
- {
- if (channel->encoding && channel->read_buf->len > 0)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Channel terminates in a partial character"));
- return G_IO_STATUS_ERROR;
- }
- line_length = use_buf->len;
- got_term_len = 0;
- break;
- }
-
- if (use_buf->len > line_term_len - 1)
- checked_to = use_buf->len - (line_term_len - 1);
- else
- checked_to = 0;
- }
-
-done:
-
- if (terminator_pos)
- *terminator_pos = line_length;
-
- if (length)
- *length = line_length + got_term_len;
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_read_to_end:
- * @channel: a #GIOChannel
- * @str_return: Location to store a pointer to a string holding
- * the remaining data in the #GIOChannel. This data should
- * be freed with g_free() when no longer needed. This
- * data is terminated by an extra nul character, but there
- * may be other nuls in the intervening data.
- * @length: location to store length of the data
- * @error: location to return an error of type #GConvertError
- * or #GIOChannelError
- *
- * Reads all the remaining data from the file.
- *
- * Return value: %G_IO_STATUS_NORMAL on success.
- * This function never returns %G_IO_STATUS_EOF.
- **/
-GIOStatus
-g_io_channel_read_to_end (GIOChannel *channel,
- gchar **str_return,
- gsize *length,
- GError **error)
-{
- GIOStatus status;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
-
- if (str_return)
- *str_return = NULL;
- if (length)
- *length = 0;
-
- if (!channel->use_buffer)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Can't do a raw read in g_io_channel_read_to_end"));
- return G_IO_STATUS_ERROR;
- }
-
- do
- status = g_io_channel_fill_buffer (channel, error);
- while (status == G_IO_STATUS_NORMAL);
-
- if (status != G_IO_STATUS_EOF)
- return status;
-
- if (channel->encoding && channel->read_buf->len > 0)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Channel terminates in a partial character"));
- return G_IO_STATUS_ERROR;
- }
-
- if (USE_BUF (channel) == NULL)
- {
- /* length is already set to zero */
- if (str_return)
- *str_return = g_strdup ("");
- }
- else
- {
- if (length)
- *length = USE_BUF (channel)->len;
-
- if (str_return)
- *str_return = g_string_free (USE_BUF (channel), FALSE);
- else
- g_string_free (USE_BUF (channel), TRUE);
-
- if (channel->encoding)
- channel->encoded_read_buf = NULL;
- else
- channel->read_buf = NULL;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_read_chars:
- * @channel: a #GIOChannel
- * @buf: a buffer to read data into
- * @count: the size of the buffer. Note that the buffer may
- * not be complelely filled even if there is data
- * in the buffer if the remaining data is not a
- * complete character.
- * @bytes_read: The number of bytes read. This may be zero even on
- * success if count < 6 and the channel's encoding is non-%NULL.
- * This indicates that the next UTF-8 character is too wide for
- * the buffer.
- * @error: a location to return an error of type #GConvertError
- * or #GIOChannelError.
- *
- * Replacement for g_io_channel_read() with the new API.
- *
- * Return value: the status of the operation.
- **/
-GIOStatus
-g_io_channel_read_chars (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **error)
-{
- GIOStatus status;
- gsize got_bytes;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
-
- if (count == 0)
- {
- *bytes_read = 0;
- return G_IO_STATUS_NORMAL;
- }
- g_return_val_if_fail (buf != NULL, G_IO_STATUS_ERROR);
-
- if (!channel->use_buffer)
- {
- gsize tmp_bytes;
-
- g_assert (!channel->read_buf || channel->read_buf->len == 0);
-
- status = channel->funcs->io_read (channel, buf, count, &tmp_bytes, error);
-
- if (bytes_read)
- *bytes_read = tmp_bytes;
-
- return status;
- }
-
- status = G_IO_STATUS_NORMAL;
-
- while (BUF_LEN (USE_BUF (channel)) < count && status == G_IO_STATUS_NORMAL)
- status = g_io_channel_fill_buffer (channel, error);
-
- /* Only return an error if we have no data */
-
- if (BUF_LEN (USE_BUF (channel)) == 0)
- {
- g_assert (status != G_IO_STATUS_NORMAL);
-
- if (status == G_IO_STATUS_EOF && channel->encoding
- && BUF_LEN (channel->read_buf) > 0)
- {
- g_set_error_literal (error, G_CONVERT_ERROR,
- G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Leftover unconverted data in read buffer"));
- status = G_IO_STATUS_ERROR;
- }
-
- if (bytes_read)
- *bytes_read = 0;
-
- return status;
- }
-
- if (status == G_IO_STATUS_ERROR)
- g_clear_error (error);
-
- got_bytes = MIN (count, BUF_LEN (USE_BUF (channel)));
-
- g_assert (got_bytes > 0);
-
- if (channel->encoding)
- /* Don't validate for NULL encoding, binary safe */
- {
- gchar *nextchar, *prevchar;
-
- g_assert (USE_BUF (channel) == channel->encoded_read_buf);
-
- nextchar = channel->encoded_read_buf->str;
-
- do
- {
- prevchar = nextchar;
- nextchar = g_utf8_next_char (nextchar);
- g_assert (nextchar != prevchar); /* Possible for *prevchar of -1 or -2 */
- }
- while (nextchar < channel->encoded_read_buf->str + got_bytes);
-
- if (nextchar > channel->encoded_read_buf->str + got_bytes)
- got_bytes = prevchar - channel->encoded_read_buf->str;
-
- g_assert (got_bytes > 0 || count < 6);
- }
-
- memcpy (buf, USE_BUF (channel)->str, got_bytes);
- g_string_erase (USE_BUF (channel), 0, got_bytes);
-
- if (bytes_read)
- *bytes_read = got_bytes;
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_read_unichar:
- * @channel: a #GIOChannel
- * @thechar: a location to return a character
- * @error: a location to return an error of type #GConvertError
- * or #GIOChannelError
- *
- * Reads a Unicode character from @channel.
- * This function cannot be called on a channel with %NULL encoding.
- *
- * Return value: a #GIOStatus
- **/
-GIOStatus
-g_io_channel_read_unichar (GIOChannel *channel,
- gunichar *thechar,
- GError **error)
-{
- GIOStatus status = G_IO_STATUS_NORMAL;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->encoding != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
-
- while (BUF_LEN (channel->encoded_read_buf) == 0 && status == G_IO_STATUS_NORMAL)
- status = g_io_channel_fill_buffer (channel, error);
-
- /* Only return an error if we have no data */
-
- if (BUF_LEN (USE_BUF (channel)) == 0)
- {
- g_assert (status != G_IO_STATUS_NORMAL);
-
- if (status == G_IO_STATUS_EOF && BUF_LEN (channel->read_buf) > 0)
- {
- g_set_error_literal (error, G_CONVERT_ERROR,
- G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Leftover unconverted data in read buffer"));
- status = G_IO_STATUS_ERROR;
- }
-
- if (thechar)
- *thechar = (gunichar) -1;
-
- return status;
- }
-
- if (status == G_IO_STATUS_ERROR)
- g_clear_error (error);
-
- if (thechar)
- *thechar = g_utf8_get_char (channel->encoded_read_buf->str);
-
- g_string_erase (channel->encoded_read_buf, 0,
- g_utf8_next_char (channel->encoded_read_buf->str)
- - channel->encoded_read_buf->str);
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_write_chars:
- * @channel: a #GIOChannel
- * @buf: a buffer to write data from
- * @count: the size of the buffer. If -1, the buffer
- * is taken to be a nul-terminated string.
- * @bytes_written: The number of bytes written. This can be nonzero
- * even if the return value is not %G_IO_STATUS_NORMAL.
- * If the return value is %G_IO_STATUS_NORMAL and the
- * channel is blocking, this will always be equal
- * to @count if @count >= 0.
- * @error: a location to return an error of type #GConvertError
- * or #GIOChannelError
- *
- * Replacement for g_io_channel_write() with the new API.
- *
- * On seekable channels with encodings other than %NULL or UTF-8, generic
- * mixing of reading and writing is not allowed. A call to g_io_channel_write_chars ()
- * may only be made on a channel from which data has been read in the
- * cases described in the documentation for g_io_channel_set_encoding ().
- *
- * Return value: the status of the operation.
- **/
-GIOStatus
-g_io_channel_write_chars (GIOChannel *channel,
- const gchar *buf,
- gssize count,
- gsize *bytes_written,
- GError **error)
-{
- GIOStatus status;
- gssize wrote_bytes = 0;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_writeable, G_IO_STATUS_ERROR);
-
- if ((count < 0) && buf)
- count = strlen (buf);
-
- if (count == 0)
- {
- if (bytes_written)
- *bytes_written = 0;
- return G_IO_STATUS_NORMAL;
- }
-
- g_return_val_if_fail (buf != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (count > 0, G_IO_STATUS_ERROR);
-
- /* Raw write case */
-
- if (!channel->use_buffer)
- {
- gsize tmp_bytes;
-
- g_assert (!channel->write_buf || channel->write_buf->len == 0);
- g_assert (channel->partial_write_buf[0] == '\0');
-
- status = channel->funcs->io_write (channel, buf, count, &tmp_bytes, error);
-
- if (bytes_written)
- *bytes_written = tmp_bytes;
-
- return status;
- }
-
- /* General case */
-
- if (channel->is_seekable && (( BUF_LEN (channel->read_buf) > 0)
- || (BUF_LEN (channel->encoded_read_buf) > 0)))
- {
- if (channel->do_encode && BUF_LEN (channel->encoded_read_buf) > 0)
- {
- g_warning("Mixed reading and writing not allowed on encoded files");
- return G_IO_STATUS_ERROR;
- }
- status = g_io_channel_seek_position (channel, 0, G_SEEK_CUR, error);
- if (status != G_IO_STATUS_NORMAL)
- {
- if (bytes_written)
- *bytes_written = 0;
- return status;
- }
- }
-
- if (!channel->write_buf)
- channel->write_buf = g_string_sized_new (channel->buf_size);
-
- while (wrote_bytes < count)
- {
- gsize space_in_buf;
-
- /* If the buffer is full, try a write immediately. In
- * the nonblocking case, this prevents the user from
- * writing just a little bit to the buffer every time
- * and never receiving an EAGAIN.
- */
-
- if (channel->write_buf->len >= channel->buf_size - MAX_CHAR_SIZE)
- {
- gsize did_write = 0, this_time;
-
- do
- {
- status = channel->funcs->io_write (channel, channel->write_buf->str
- + did_write, channel->write_buf->len
- - did_write, &this_time, error);
- did_write += this_time;
- }
- while (status == G_IO_STATUS_NORMAL &&
- did_write < MIN (channel->write_buf->len, MAX_CHAR_SIZE));
-
- g_string_erase (channel->write_buf, 0, did_write);
-
- if (status != G_IO_STATUS_NORMAL)
- {
- if (status == G_IO_STATUS_AGAIN && wrote_bytes > 0)
- status = G_IO_STATUS_NORMAL;
- if (bytes_written)
- *bytes_written = wrote_bytes;
- return status;
- }
- }
-
- space_in_buf = MAX (channel->buf_size, channel->write_buf->allocated_len - 1)
- - channel->write_buf->len; /* 1 for NULL */
-
- /* This is only true because g_io_channel_set_buffer_size ()
- * ensures that channel->buf_size >= MAX_CHAR_SIZE.
- */
- g_assert (space_in_buf >= MAX_CHAR_SIZE);
-
- if (!channel->encoding)
- {
- gssize write_this = MIN (space_in_buf, count - wrote_bytes);
-
- g_string_append_len (channel->write_buf, buf, write_this);
- buf += write_this;
- wrote_bytes += write_this;
- }
- else
- {
- const gchar *from_buf;
- gsize from_buf_len, from_buf_old_len, left_len;
- gsize err;
- gint errnum;
-
- if (channel->partial_write_buf[0] != '\0')
- {
- g_assert (wrote_bytes == 0);
-
- from_buf = channel->partial_write_buf;
- from_buf_old_len = strlen (channel->partial_write_buf);
- g_assert (from_buf_old_len > 0);
- from_buf_len = MIN (6, from_buf_old_len + count);
-
- memcpy (channel->partial_write_buf + from_buf_old_len, buf,
- from_buf_len - from_buf_old_len);
- }
- else
- {
- from_buf = buf;
- from_buf_len = count - wrote_bytes;
- from_buf_old_len = 0;
- }
-
-reconvert:
-
- if (!channel->do_encode) /* UTF-8 encoding */
- {
- const gchar *badchar;
- gsize try_len = MIN (from_buf_len, space_in_buf);
-
- /* UTF-8, just validate, emulate g_iconv */
-
- if (!g_utf8_validate (from_buf, try_len, &badchar))
- {
- gunichar try_char;
- gsize incomplete_len = from_buf + try_len - badchar;
-
- left_len = from_buf + from_buf_len - badchar;
-
- try_char = g_utf8_get_char_validated (badchar, incomplete_len);
-
- switch (try_char)
- {
- case -2:
- g_assert (incomplete_len < 6);
- if (try_len == from_buf_len)
- {
- errnum = EINVAL;
- err = (gsize) -1;
- }
- else
- {
- errnum = 0;
- err = (gsize) 0;
- }
- break;
- case -1:
- g_warning ("Invalid UTF-8 passed to g_io_channel_write_chars().");
- /* FIXME bail here? */
- errnum = EILSEQ;
- err = (gsize) -1;
- break;
- default:
- g_assert_not_reached ();
- err = (gsize) -1;
- errnum = 0; /* Don't confunse the compiler */
- }
- }
- else
- {
- err = (gsize) 0;
- errnum = 0;
- left_len = from_buf_len - try_len;
- }
-
- g_string_append_len (channel->write_buf, from_buf,
- from_buf_len - left_len);
- from_buf += from_buf_len - left_len;
- }
- else
- {
- gchar *outbuf;
-
- left_len = from_buf_len;
- g_string_set_size (channel->write_buf, channel->write_buf->len
- + space_in_buf);
- outbuf = channel->write_buf->str + channel->write_buf->len
- - space_in_buf;
- err = g_iconv (channel->write_cd, (gchar **) &from_buf, &left_len,
- &outbuf, &space_in_buf);
- errnum = errno;
- g_string_truncate (channel->write_buf, channel->write_buf->len
- - space_in_buf);
- }
-
- if (err == (gsize) -1)
- {
- switch (errnum)
- {
- case EINVAL:
- g_assert (left_len < 6);
-
- if (from_buf_old_len == 0)
- {
- /* Not from partial_write_buf */
-
- memcpy (channel->partial_write_buf, from_buf, left_len);
- channel->partial_write_buf[left_len] = '\0';
- if (bytes_written)
- *bytes_written = count;
- return G_IO_STATUS_NORMAL;
- }
-
- /* Working in partial_write_buf */
-
- if (left_len == from_buf_len)
- {
- /* Didn't convert anything, must still have
- * less than a full character
- */
-
- g_assert (count == from_buf_len - from_buf_old_len);
-
- channel->partial_write_buf[from_buf_len] = '\0';
-
- if (bytes_written)
- *bytes_written = count;
-
- return G_IO_STATUS_NORMAL;
- }
-
- g_assert (from_buf_len - left_len >= from_buf_old_len);
-
- /* We converted all the old data. This is fine */
-
- break;
- case E2BIG:
- if (from_buf_len == left_len)
- {
- /* Nothing was written, add enough space for
- * at least one character.
- */
- space_in_buf += MAX_CHAR_SIZE;
- goto reconvert;
- }
- break;
- case EILSEQ:
- g_set_error_literal (error, G_CONVERT_ERROR,
- G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
- if (from_buf_old_len > 0 && from_buf_len == left_len)
- g_warning ("Illegal sequence due to partial character "
- "at the end of a previous write.\n");
- else
- wrote_bytes += from_buf_len - left_len - from_buf_old_len;
- if (bytes_written)
- *bytes_written = wrote_bytes;
- channel->partial_write_buf[0] = '\0';
- return G_IO_STATUS_ERROR;
- default:
- g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
- _("Error during conversion: %s"), g_strerror (errnum));
- if (from_buf_len >= left_len + from_buf_old_len)
- wrote_bytes += from_buf_len - left_len - from_buf_old_len;
- if (bytes_written)
- *bytes_written = wrote_bytes;
- channel->partial_write_buf[0] = '\0';
- return G_IO_STATUS_ERROR;
- }
- }
-
- g_assert (from_buf_len - left_len >= from_buf_old_len);
-
- wrote_bytes += from_buf_len - left_len - from_buf_old_len;
-
- if (from_buf_old_len > 0)
- {
- /* We were working in partial_write_buf */
-
- buf += from_buf_len - left_len - from_buf_old_len;
- channel->partial_write_buf[0] = '\0';
- }
- else
- buf = from_buf;
- }
- }
-
- if (bytes_written)
- *bytes_written = count;
-
- return G_IO_STATUS_NORMAL;
-}
-
-/**
- * g_io_channel_write_unichar:
- * @channel: a #GIOChannel
- * @thechar: a character
- * @error: location to return an error of type #GConvertError
- * or #GIOChannelError
- *
- * Writes a Unicode character to @channel.
- * This function cannot be called on a channel with %NULL encoding.
- *
- * Return value: a #GIOStatus
- **/
-GIOStatus
-g_io_channel_write_unichar (GIOChannel *channel,
- gunichar thechar,
- GError **error)
-{
- GIOStatus status;
- gchar static_buf[6];
- gsize char_len, wrote_len;
-
- g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->encoding != NULL, G_IO_STATUS_ERROR);
- g_return_val_if_fail ((error == NULL) || (*error == NULL),
- G_IO_STATUS_ERROR);
- g_return_val_if_fail (channel->is_writeable, G_IO_STATUS_ERROR);
-
- char_len = g_unichar_to_utf8 (thechar, static_buf);
-
- if (channel->partial_write_buf[0] != '\0')
- {
- g_warning ("Partial charater written before writing unichar.\n");
- channel->partial_write_buf[0] = '\0';
- }
-
- status = g_io_channel_write_chars (channel, static_buf,
- char_len, &wrote_len, error);
-
- /* We validate UTF-8, so we can't get a partial write */
-
- g_assert (wrote_len == char_len || status != G_IO_STATUS_NORMAL);
-
- return status;
-}
-
-/**
- * g_io_channel_error_quark:
- *
- * Return value: the quark used as %G_IO_CHANNEL_ERROR
- **/
-/**
- * G_IO_CHANNEL_ERROR:
- *
- * Error domain for #GIOChannel operations. Errors in this domain will
- * be from the #GIOChannelError enumeration. See #GError for
- * information on error domains.
- **/
-/**
- * GIOChannelError:
- * @G_IO_CHANNEL_ERROR_FBIG: File too large.
- * @G_IO_CHANNEL_ERROR_INVAL: Invalid argument.
- * @G_IO_CHANNEL_ERROR_IO: IO error.
- * @G_IO_CHANNEL_ERROR_ISDIR: File is a directory.
- * @G_IO_CHANNEL_ERROR_NOSPC: No space left on device.
- * @G_IO_CHANNEL_ERROR_NXIO: No such device or address.
- * @G_IO_CHANNEL_ERROR_OVERFLOW: Value too large for defined datatype.
- * @G_IO_CHANNEL_ERROR_PIPE: Broken pipe.
- * @G_IO_CHANNEL_ERROR_FAILED: Some other error.
- *
- * Error codes returned by #GIOChannel operations.
- **/
-GQuark
-g_io_channel_error_quark (void)
-{
- return g_quark_from_static_string ("g-io-channel-error-quark");
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * giounix.c: IO Channels using unix file descriptors
- * Copyright 1998 Owen Taylor
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#define _POSIX_SOURCE /* for SSIZE_MAX */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-
-#include "giochannel.h"
-
-#include "gerror.h"
-#include "gfileutils.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-
-/*
- * Unix IO Channels
- */
-
-typedef struct _GIOUnixChannel GIOUnixChannel;
-typedef struct _GIOUnixWatch GIOUnixWatch;
-
-struct _GIOUnixChannel
-{
- GIOChannel channel;
- gint fd;
-};
-
-struct _GIOUnixWatch
-{
- GSource source;
- GPollFD pollfd;
- GIOChannel *channel;
- GIOCondition condition;
-};
-
-
-static GIOStatus g_io_unix_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err);
-static GIOStatus g_io_unix_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err);
-static GIOStatus g_io_unix_seek (GIOChannel *channel,
- gint64 offset,
- GSeekType type,
- GError **err);
-static GIOStatus g_io_unix_close (GIOChannel *channel,
- GError **err);
-static void g_io_unix_free (GIOChannel *channel);
-static GSource* g_io_unix_create_watch (GIOChannel *channel,
- GIOCondition condition);
-static GIOStatus g_io_unix_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **err);
-static GIOFlags g_io_unix_get_flags (GIOChannel *channel);
-
-static gboolean g_io_unix_prepare (GSource *source,
- gint *timeout);
-static gboolean g_io_unix_check (GSource *source);
-static gboolean g_io_unix_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data);
-static void g_io_unix_finalize (GSource *source);
-
-GSourceFuncs g_io_watch_funcs = {
- g_io_unix_prepare,
- g_io_unix_check,
- g_io_unix_dispatch,
- g_io_unix_finalize
-};
-
-static GIOFuncs unix_channel_funcs = {
- g_io_unix_read,
- g_io_unix_write,
- g_io_unix_seek,
- g_io_unix_close,
- g_io_unix_create_watch,
- g_io_unix_free,
- g_io_unix_set_flags,
- g_io_unix_get_flags,
-};
-
-static gboolean
-g_io_unix_prepare (GSource *source,
- gint *timeout)
-{
- GIOUnixWatch *watch = (GIOUnixWatch *)source;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
-
- *timeout = -1;
-
- /* Only return TRUE here if _all_ bits in watch->condition will be set
- */
- return ((watch->condition & buffer_condition) == watch->condition);
-}
-
-static gboolean
-g_io_unix_check (GSource *source)
-{
- GIOUnixWatch *watch = (GIOUnixWatch *)source;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
- GIOCondition poll_condition = watch->pollfd.revents;
-
- return ((poll_condition | buffer_condition) & watch->condition);
-}
-
-static gboolean
-g_io_unix_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-
-{
- GIOFunc func = (GIOFunc)callback;
- GIOUnixWatch *watch = (GIOUnixWatch *)source;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
-
- if (!func)
- {
- g_warning ("IO watch dispatched without callback\n"
- "You must call g_source_connect().");
- return FALSE;
- }
-
- return (*func) (watch->channel,
- (watch->pollfd.revents | buffer_condition) & watch->condition,
- user_data);
-}
-
-static void
-g_io_unix_finalize (GSource *source)
-{
- GIOUnixWatch *watch = (GIOUnixWatch *)source;
-
- g_io_channel_unref (watch->channel);
-}
-
-static GIOStatus
-g_io_unix_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
- gssize result;
-
- if (count > SSIZE_MAX) /* At least according to the Debian manpage for read */
- count = SSIZE_MAX;
-
- retry:
- result = read (unix_channel->fd, buf, count);
-
- if (result < 0)
- {
- int errsv = errno;
- *bytes_read = 0;
-
- switch (errsv)
- {
-#ifdef EINTR
- case EINTR:
- goto retry;
-#endif
-#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
-#endif
- default:
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errsv),
- g_strerror (errsv));
- return G_IO_STATUS_ERROR;
- }
- }
-
- *bytes_read = result;
-
- return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
-}
-
-static GIOStatus
-g_io_unix_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
- gssize result;
-
- retry:
- result = write (unix_channel->fd, buf, count);
-
- if (result < 0)
- {
- int errsv = errno;
- *bytes_written = 0;
-
- switch (errsv)
- {
-#ifdef EINTR
- case EINTR:
- goto retry;
-#endif
-#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
-#endif
- default:
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errsv),
- g_strerror (errsv));
- return G_IO_STATUS_ERROR;
- }
- }
-
- *bytes_written = result;
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOStatus
-g_io_unix_seek (GIOChannel *channel,
- gint64 offset,
- GSeekType type,
- GError **err)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
- int whence;
- off_t tmp_offset;
- off_t result;
-
- switch (type)
- {
- case G_SEEK_SET:
- whence = SEEK_SET;
- break;
- case G_SEEK_CUR:
- whence = SEEK_CUR;
- break;
- case G_SEEK_END:
- whence = SEEK_END;
- break;
- default:
- whence = -1; /* Shut the compiler up */
- g_assert_not_reached ();
- }
-
- tmp_offset = offset;
- if (tmp_offset != offset)
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (EINVAL),
- g_strerror (EINVAL));
- return G_IO_STATUS_ERROR;
- }
-
- result = lseek (unix_channel->fd, tmp_offset, whence);
-
- if (result < 0)
- {
- int errsv = errno;
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errsv),
- g_strerror (errsv));
- return G_IO_STATUS_ERROR;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-
-static GIOStatus
-g_io_unix_close (GIOChannel *channel,
- GError **err)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
-
- if (close (unix_channel->fd) < 0)
- {
- int errsv = errno;
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errsv),
- g_strerror (errsv));
- return G_IO_STATUS_ERROR;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-static void
-g_io_unix_free (GIOChannel *channel)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
-
- g_free (unix_channel);
-}
-
-static GSource *
-g_io_unix_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
- GSource *source;
- GIOUnixWatch *watch;
-
-
- source = g_source_new (&g_io_watch_funcs, sizeof (GIOUnixWatch));
- g_source_set_name (source, "GIOChannel (Unix)");
- watch = (GIOUnixWatch *)source;
-
- watch->channel = channel;
- g_io_channel_ref (channel);
-
- watch->condition = condition;
-
- watch->pollfd.fd = unix_channel->fd;
- watch->pollfd.events = condition;
-
- g_source_add_poll (source, &watch->pollfd);
-
- return source;
-}
-
-static GIOStatus
-g_io_unix_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **err)
-{
- glong fcntl_flags;
- GIOUnixChannel *unix_channel = (GIOUnixChannel *) channel;
-
- fcntl_flags = 0;
-
- if (flags & G_IO_FLAG_APPEND)
- fcntl_flags |= O_APPEND;
- if (flags & G_IO_FLAG_NONBLOCK)
-#ifdef O_NONBLOCK
- fcntl_flags |= O_NONBLOCK;
-#else
- fcntl_flags |= O_NDELAY;
-#endif
-
- if (fcntl (unix_channel->fd, F_SETFL, fcntl_flags) == -1)
- {
- int errsv = errno;
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errsv),
- g_strerror (errsv));
- return G_IO_STATUS_ERROR;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOFlags
-g_io_unix_get_flags (GIOChannel *channel)
-{
- GIOFlags flags = 0;
- glong fcntl_flags;
- GIOUnixChannel *unix_channel = (GIOUnixChannel *) channel;
-
- fcntl_flags = fcntl (unix_channel->fd, F_GETFL);
-
- if (fcntl_flags == -1)
- {
- int err = errno;
- g_warning (G_STRLOC "Error while getting flags for FD: %s (%d)\n",
- g_strerror (err), err);
- return 0;
- }
-
- if (fcntl_flags & O_APPEND)
- flags |= G_IO_FLAG_APPEND;
-#ifdef O_NONBLOCK
- if (fcntl_flags & O_NONBLOCK)
-#else
- if (fcntl_flags & O_NDELAY)
-#endif
- flags |= G_IO_FLAG_NONBLOCK;
-
- switch (fcntl_flags & (O_RDONLY | O_WRONLY | O_RDWR))
- {
- case O_RDONLY:
- channel->is_readable = TRUE;
- channel->is_writeable = FALSE;
- break;
- case O_WRONLY:
- channel->is_readable = FALSE;
- channel->is_writeable = TRUE;
- break;
- case O_RDWR:
- channel->is_readable = TRUE;
- channel->is_writeable = TRUE;
- break;
- default:
- g_assert_not_reached ();
- }
-
- return flags;
-}
-
-GIOChannel *
-g_io_channel_new_file (const gchar *filename,
- const gchar *mode,
- GError **error)
-{
- int fid, flags;
- mode_t create_mode;
- GIOChannel *channel;
- enum { /* Cheesy hack */
- MODE_R = 1 << 0,
- MODE_W = 1 << 1,
- MODE_A = 1 << 2,
- MODE_PLUS = 1 << 3
- } mode_num;
- struct stat buffer;
-
- g_return_val_if_fail (filename != NULL, NULL);
- g_return_val_if_fail (mode != NULL, NULL);
- g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);
-
- switch (mode[0])
- {
- case 'r':
- mode_num = MODE_R;
- break;
- case 'w':
- mode_num = MODE_W;
- break;
- case 'a':
- mode_num = MODE_A;
- break;
- default:
- g_warning ("Invalid GIOFileMode %s.\n", mode);
- return NULL;
- }
-
- switch (mode[1])
- {
- case '\0':
- break;
- case '+':
- if (mode[2] == '\0')
- {
- mode_num |= MODE_PLUS;
- break;
- }
- /* Fall through */
- default:
- g_warning ("Invalid GIOFileMode %s.\n", mode);
- return NULL;
- }
-
- switch (mode_num)
- {
- case MODE_R:
- flags = O_RDONLY;
- break;
- case MODE_W:
- flags = O_WRONLY | O_TRUNC | O_CREAT;
- break;
- case MODE_A:
- flags = O_WRONLY | O_APPEND | O_CREAT;
- break;
- case MODE_R | MODE_PLUS:
- flags = O_RDWR;
- break;
- case MODE_W | MODE_PLUS:
- flags = O_RDWR | O_TRUNC | O_CREAT;
- break;
- case MODE_A | MODE_PLUS:
- flags = O_RDWR | O_APPEND | O_CREAT;
- break;
- default:
- g_assert_not_reached ();
- flags = 0;
- }
-
- create_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
- fid = open (filename, flags, create_mode);
- if (fid == -1)
- {
- int err = errno;
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (err),
- g_strerror (err));
- return (GIOChannel *)NULL;
- }
-
- if (fstat (fid, &buffer) == -1) /* In case someone opens a FIFO */
- {
- int err = errno;
- close (fid);
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (err),
- g_strerror (err));
- return (GIOChannel *)NULL;
- }
-
- channel = (GIOChannel *) g_new (GIOUnixChannel, 1);
-
- channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
- || S_ISBLK (buffer.st_mode);
-
- switch (mode_num)
- {
- case MODE_R:
- channel->is_readable = TRUE;
- channel->is_writeable = FALSE;
- break;
- case MODE_W:
- case MODE_A:
- channel->is_readable = FALSE;
- channel->is_writeable = TRUE;
- break;
- case MODE_R | MODE_PLUS:
- case MODE_W | MODE_PLUS:
- case MODE_A | MODE_PLUS:
- channel->is_readable = TRUE;
- channel->is_writeable = TRUE;
- break;
- default:
- g_assert_not_reached ();
- }
-
- g_io_channel_init (channel);
- channel->close_on_unref = TRUE; /* must be after g_io_channel_init () */
- channel->funcs = &unix_channel_funcs;
-
- ((GIOUnixChannel *) channel)->fd = fid;
- return channel;
-}
-
-/**
- * g_io_channel_unix_new:
- * @fd: a file descriptor.
- * @Returns: a new #GIOChannel.
- *
- * Creates a new #GIOChannel given a file descriptor. On UNIX systems
- * this works for plain files, pipes, and sockets.
- *
- * The returned #GIOChannel has a reference count of 1.
- *
- * The default encoding for #GIOChannel is UTF-8. If your application
- * is reading output from a command using via pipe, you may need to set
- * the encoding to the encoding of the current locale (see
- * g_get_charset()) with the g_io_channel_set_encoding() function.
- *
- * If you want to read raw binary data without interpretation, then
- * call the g_io_channel_set_encoding() function with %NULL for the
- * encoding argument.
- *
- * This function is available in GLib on Windows, too, but you should
- * avoid using it on Windows. The domain of file descriptors and
- * sockets overlap. There is no way for GLib to know which one you mean
- * in case the argument you pass to this function happens to be both a
- * valid file descriptor and socket. If that happens a warning is
- * issued, and GLib assumes that it is the file descriptor you mean.
- **/
-GIOChannel *
-g_io_channel_unix_new (gint fd)
-{
- struct stat buffer;
- GIOUnixChannel *unix_channel = g_new (GIOUnixChannel, 1);
- GIOChannel *channel = (GIOChannel *)unix_channel;
-
- g_io_channel_init (channel);
- channel->funcs = &unix_channel_funcs;
-
- unix_channel->fd = fd;
-
- /* I'm not sure if fstat on a non-file (e.g., socket) works
- * it should be safe to say if it fails, the fd isn't seekable.
- */
- /* Newer UNIX versions support S_ISSOCK(), fstat() will probably
- * succeed in most cases.
- */
- if (fstat (unix_channel->fd, &buffer) == 0)
- channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
- || S_ISBLK (buffer.st_mode);
- else /* Assume not seekable */
- channel->is_seekable = FALSE;
-
- g_io_unix_get_flags (channel); /* Sets is_readable, is_writeable */
-
- return channel;
-}
-
-/**
- * g_io_channel_unix_get_fd:
- * @channel: a #GIOChannel, created with g_io_channel_unix_new().
- * @Returns: the file descriptor of the #GIOChannel.
- *
- * Returns the file descriptor of the #GIOChannel.
- *
- * On Windows this function returns the file descriptor or socket of
- * the #GIOChannel.
- **/
-gint
-g_io_channel_unix_get_fd (GIOChannel *channel)
-{
- GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
- return unix_channel->fd;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * giowin32.c: IO Channels for Win32.
- * Copyright 1998 Owen Taylor and Tor Lillqvist
- * Copyright 1999-2000 Tor Lillqvist and Craig Setera
- * Copyright 2001-2003 Andrew Lanoix
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * Bugs that are related to the code in this file:
- *
- * Bug 137968 - Sometimes a GIOFunc on Win32 is called with zero condition
- * http://bugzilla.gnome.org/show_bug.cgi?id=137968
- *
- * Bug 324234 - Using g_io_add_watch_full() to wait for connect() to return on a non-blocking socket returns prematurely
- * http://bugzilla.gnome.org/show_bug.cgi?id=324234
- *
- * Bug 331214 - g_io_channel async socket io stalls
- * http://bugzilla.gnome.org/show_bug.cgi?id=331214
- *
- * Bug 338943 - Multiple watches on the same socket
- * http://bugzilla.gnome.org/show_bug.cgi?id=338943
- *
- * Bug 357674 - 2 serious bugs in giowin32.c making glib iochannels useless
- * http://bugzilla.gnome.org/show_bug.cgi?id=357674
- *
- * Bug 425156 - GIOChannel deadlocks on a win32 socket
- * http://bugzilla.gnome.org/show_bug.cgi?id=425156
- *
- * Bug 468910 - giofunc condition=0
- * http://bugzilla.gnome.org/show_bug.cgi?id=468910
- *
- * Bug 500246 - Bug fixes for giowin32
- * http://bugzilla.gnome.org/show_bug.cgi?id=500246
- *
- * Bug 548278 - Async GETs connections are always terminated unexpectedly on windows
- * http://bugzilla.gnome.org/show_bug.cgi?id=548278
- *
- * Bug 548536 - giowin32 problem when adding and removing watches
- * http://bugzilla.gnome.org/show_bug.cgi?id=548536
- *
- * When fixing bugs related to the code in this file, either the above
- * bugs or others, make sure that the test programs attached to the
- * above bugs continue to work.
- */
-
-#include "config.h"
-
-#include "glib.h"
-
-#include <stdlib.h>
-#include <winsock2.h>
-#include <windows.h>
-#include <conio.h>
-#include <fcntl.h>
-#include <io.h>
-#include <process.h>
-#include <errno.h>
-#include <sys/stat.h>
-
-#include "gstdio.h"
-#include "glibintl.h"
-
-
-typedef struct _GIOWin32Channel GIOWin32Channel;
-typedef struct _GIOWin32Watch GIOWin32Watch;
-
-#define BUFFER_SIZE 4096
-
-typedef enum {
- G_IO_WIN32_WINDOWS_MESSAGES, /* Windows messages */
-
- G_IO_WIN32_FILE_DESC, /* Unix-like file descriptors from
- * _open() or _pipe(), except for
- * console IO. Separate thread to read
- * or write.
- */
-
- G_IO_WIN32_CONSOLE, /* Console IO (usually stdin, stdout, stderr) */
-
- G_IO_WIN32_SOCKET /* Sockets. No separate thread. */
-} GIOWin32ChannelType;
-
-struct _GIOWin32Channel {
- GIOChannel channel;
- gint fd; /* Either a Unix-like file handle as provided
- * by the Microsoft C runtime, or a SOCKET
- * as provided by WinSock.
- */
- GIOWin32ChannelType type;
-
- gboolean debug;
-
- /* Field used by G_IO_WIN32_WINDOWS_MESSAGES channels */
- HWND hwnd; /* Handle of window, or NULL */
-
- /* Fields used by G_IO_WIN32_FILE_DESC channels. */
- CRITICAL_SECTION mutex;
-
- int direction; /* 0 means we read from it,
- * 1 means we write to it.
- */
-
- gboolean running; /* Is reader or writer thread
- * running. FALSE if EOF has been
- * reached by the reader thread.
- */
-
- gboolean needs_close; /* If the channel has been closed while
- * the reader thread was still running.
- */
-
- guint thread_id; /* If non-NULL the channel has or has
- * had a reader or writer thread.
- */
- HANDLE data_avail_event;
-
- gushort revents;
-
- /* Data is kept in a circular buffer. To be able to distinguish between
- * empty and full buffers, we cannot fill it completely, but have to
- * leave a one character gap.
- *
- * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE).
- *
- * Empty: wrp == rdp
- * Full: (wrp + 1) % BUFFER_SIZE == rdp
- * Partial: otherwise
- */
- guchar *buffer; /* (Circular) buffer */
- gint wrp, rdp; /* Buffer indices for writing and reading */
- HANDLE space_avail_event;
-
- /* Fields used by G_IO_WIN32_SOCKET channels */
- int event_mask;
- int last_events;
- HANDLE event;
- gboolean write_would_have_blocked;
- gboolean ever_writable;
-};
-
-struct _GIOWin32Watch {
- GSource source;
- GPollFD pollfd;
- GIOChannel *channel;
- GIOCondition condition;
-};
-
-static void
-g_win32_print_access_mode (int flags)
-{
- g_print ("%s%s%s%s%s%s%s%s%s%s",
- ((flags & 0x3) == _O_RDWR ? "O_RDWR" :
- ((flags & 0x3) == _O_RDONLY ? "O_RDONLY" :
- ((flags & 0x3) == _O_WRONLY ? "O_WRONLY" : "0"))),
- (flags & _O_APPEND ? "|O_APPEND" : ""),
- (flags & _O_RANDOM ? "|O_RANDOM" : ""),
- (flags & _O_SEQUENTIAL ? "|O_SEQUENTIAL" : ""),
- (flags & _O_TEMPORARY ? "|O_TEMPORARY" : ""),
- (flags & _O_CREAT ? "|O_CREAT" : ""),
- (flags & _O_TRUNC ? "|O_TRUNC" : ""),
- (flags & _O_EXCL ? "|O_EXCL" : ""),
- (flags & _O_TEXT ? "|O_TEXT" : ""),
- (flags & _O_BINARY ? "|O_BINARY" : ""));
-}
-
-static void
-g_win32_print_gioflags (GIOFlags flags)
-{
- char *bar = "";
-
- if (flags & G_IO_FLAG_APPEND)
- bar = "|", g_print ("APPEND");
- if (flags & G_IO_FLAG_NONBLOCK)
- g_print ("%sNONBLOCK", bar), bar = "|";
- if (flags & G_IO_FLAG_IS_READABLE)
- g_print ("%sREADABLE", bar), bar = "|";
- if (flags & G_IO_FLAG_IS_WRITEABLE)
- g_print ("%sWRITEABLE", bar), bar = "|";
- if (flags & G_IO_FLAG_IS_SEEKABLE)
- g_print ("%sSEEKABLE", bar), bar = "|";
-}
-
-static const char *
-event_mask_to_string (int mask)
-{
- char buf[100];
- int checked_bits = 0;
- char *bufp = buf;
-
- if (mask == 0)
- return "";
-
-#define BIT(n) checked_bits |= FD_##n; if (mask & FD_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))
-
- BIT (READ);
- BIT (WRITE);
- BIT (OOB);
- BIT (ACCEPT);
- BIT (CONNECT);
- BIT (CLOSE);
- BIT (QOS);
- BIT (GROUP_QOS);
- BIT (ROUTING_INTERFACE_CHANGE);
- BIT (ADDRESS_LIST_CHANGE);
-
-#undef BIT
-
- if ((mask & ~checked_bits) != 0)
- bufp += sprintf (bufp, "|%#x", mask & ~checked_bits);
-
- return g_quark_to_string (g_quark_from_string (buf));
-}
-
-static const char *
-condition_to_string (GIOCondition condition)
-{
- char buf[100];
- int checked_bits = 0;
- char *bufp = buf;
-
- if (condition == 0)
- return "";
-
-#define BIT(n) checked_bits |= G_IO_##n; if (condition & G_IO_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))
-
- BIT (IN);
- BIT (OUT);
- BIT (PRI);
- BIT (ERR);
- BIT (HUP);
- BIT (NVAL);
-
-#undef BIT
-
- if ((condition & ~checked_bits) != 0)
- bufp += sprintf (bufp, "|%#x", condition & ~checked_bits);
-
- return g_quark_to_string (g_quark_from_string (buf));
-}
-
-static gboolean
-g_io_win32_get_debug_flag (void)
-{
- return (getenv ("G_IO_WIN32_DEBUG") != NULL);
-}
-
-static void
-g_io_channel_win32_init (GIOWin32Channel *channel)
-{
- channel->debug = g_io_win32_get_debug_flag ();
-
- InitializeCriticalSection (&channel->mutex);
- channel->running = FALSE;
- channel->needs_close = FALSE;
- channel->thread_id = 0;
- channel->data_avail_event = NULL;
- channel->revents = 0;
- channel->buffer = NULL;
- channel->space_avail_event = NULL;
-
- channel->event_mask = 0;
- channel->last_events = 0;
- channel->event = NULL;
- channel->write_would_have_blocked = FALSE;
- channel->ever_writable = FALSE;
-}
-
-static void
-create_events (GIOWin32Channel *channel)
-{
- SECURITY_ATTRIBUTES sec_attrs;
-
- sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
- sec_attrs.lpSecurityDescriptor = NULL;
- sec_attrs.bInheritHandle = FALSE;
-
- /* The data available event is manual reset, the space available event
- * is automatic reset.
- */
- if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
- || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL)))
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
-
- g_error ("Error creating event: %s", emsg);
- g_free (emsg);
- }
-}
-
-static unsigned __stdcall
-read_thread (void *parameter)
-{
- GIOWin32Channel *channel = parameter;
- guchar *buffer;
- gint nbytes;
-
- g_io_channel_ref ((GIOChannel *)channel);
-
- if (channel->debug)
- g_print ("read_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n",
- channel->thread_id,
- channel->fd,
- channel->data_avail_event,
- channel->space_avail_event);
-
- channel->direction = 0;
- channel->buffer = g_malloc (BUFFER_SIZE);
- channel->rdp = channel->wrp = 0;
- channel->running = TRUE;
-
- SetEvent (channel->space_avail_event);
-
- EnterCriticalSection (&channel->mutex);
- while (channel->running)
- {
- if (channel->debug)
- g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
- {
- /* Buffer is full */
- if (channel->debug)
- g_print ("read_thread %#x: resetting space_avail\n",
- channel->thread_id);
- ResetEvent (channel->space_avail_event);
- if (channel->debug)
- g_print ("read_thread %#x: waiting for space\n",
- channel->thread_id);
- LeaveCriticalSection (&channel->mutex);
- WaitForSingleObject (channel->space_avail_event, INFINITE);
- EnterCriticalSection (&channel->mutex);
- if (channel->debug)
- g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- }
-
- buffer = channel->buffer + channel->wrp;
-
- /* Always leave at least one byte unused gap to be able to
- * distinguish between the full and empty condition...
- */
- nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
- BUFFER_SIZE - channel->wrp);
-
- if (channel->debug)
- g_print ("read_thread %#x: calling read() for %d bytes\n",
- channel->thread_id, nbytes);
-
- LeaveCriticalSection (&channel->mutex);
-
- nbytes = read (channel->fd, buffer, nbytes);
-
- EnterCriticalSection (&channel->mutex);
-
- channel->revents = G_IO_IN;
- if (nbytes == 0)
- channel->revents |= G_IO_HUP;
- else if (nbytes < 0)
- channel->revents |= G_IO_ERR;
-
- if (channel->debug)
- g_print ("read_thread %#x: read() returned %d, rdp=%d, wrp=%d\n",
- channel->thread_id, nbytes, channel->rdp, channel->wrp);
-
- if (nbytes <= 0)
- break;
-
- channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
- if (channel->debug)
- g_print ("read_thread %#x: rdp=%d, wrp=%d, setting data_avail\n",
- channel->thread_id, channel->rdp, channel->wrp);
- SetEvent (channel->data_avail_event);
- }
-
- channel->running = FALSE;
- if (channel->needs_close)
- {
- if (channel->debug)
- g_print ("read_thread %#x: channel fd %d needs closing\n",
- channel->thread_id, channel->fd);
- close (channel->fd);
- channel->fd = -1;
- }
-
- if (channel->debug)
- g_print ("read_thread %#x: EOF, rdp=%d, wrp=%d, setting data_avail\n",
- channel->thread_id, channel->rdp, channel->wrp);
- SetEvent (channel->data_avail_event);
- LeaveCriticalSection (&channel->mutex);
-
- g_io_channel_unref ((GIOChannel *)channel);
-
- /* No need to call _endthreadex(), the actual thread starter routine
- * in MSVCRT (see crt/src/threadex.c:_threadstartex) calls
- * _endthreadex() for us.
- */
-
- return 0;
-}
-
-static unsigned __stdcall
-write_thread (void *parameter)
-{
- GIOWin32Channel *channel = parameter;
- guchar *buffer;
- gint nbytes;
-
- g_io_channel_ref ((GIOChannel *)channel);
-
- if (channel->debug)
- g_print ("write_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n",
- channel->thread_id,
- channel->fd,
- channel->data_avail_event,
- channel->space_avail_event);
-
- channel->direction = 1;
- channel->buffer = g_malloc (BUFFER_SIZE);
- channel->rdp = channel->wrp = 0;
- channel->running = TRUE;
-
- SetEvent (channel->space_avail_event);
-
- /* We use the same event objects as for a reader thread, but with
- * reversed meaning. So, space_avail is used if data is available
- * for writing, and data_avail is used if space is available in the
- * write buffer.
- */
-
- EnterCriticalSection (&channel->mutex);
- while (channel->running || channel->rdp != channel->wrp)
- {
- if (channel->debug)
- g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- if (channel->wrp == channel->rdp)
- {
- /* Buffer is empty. */
- if (channel->debug)
- g_print ("write_thread %#x: resetting space_avail\n",
- channel->thread_id);
- ResetEvent (channel->space_avail_event);
- if (channel->debug)
- g_print ("write_thread %#x: waiting for data\n",
- channel->thread_id);
- channel->revents = G_IO_OUT;
- SetEvent (channel->data_avail_event);
- LeaveCriticalSection (&channel->mutex);
- WaitForSingleObject (channel->space_avail_event, INFINITE);
-
- EnterCriticalSection (&channel->mutex);
- if (channel->rdp == channel->wrp)
- break;
-
- if (channel->debug)
- g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- }
-
- buffer = channel->buffer + channel->rdp;
- if (channel->rdp < channel->wrp)
- nbytes = channel->wrp - channel->rdp;
- else
- nbytes = BUFFER_SIZE - channel->rdp;
-
- if (channel->debug)
- g_print ("write_thread %#x: calling write() for %d bytes\n",
- channel->thread_id, nbytes);
-
- LeaveCriticalSection (&channel->mutex);
- nbytes = write (channel->fd, buffer, nbytes);
- EnterCriticalSection (&channel->mutex);
-
- if (channel->debug)
- g_print ("write_thread %#x: write(%i) returned %d, rdp=%d, wrp=%d\n",
- channel->thread_id, channel->fd, nbytes, channel->rdp, channel->wrp);
-
- channel->revents = 0;
- if (nbytes > 0)
- channel->revents |= G_IO_OUT;
- else if (nbytes <= 0)
- channel->revents |= G_IO_ERR;
-
- channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
-
- if (nbytes <= 0)
- break;
-
- if (channel->debug)
- g_print ("write_thread: setting data_avail for thread %#x\n",
- channel->thread_id);
- SetEvent (channel->data_avail_event);
- }
-
- channel->running = FALSE;
- if (channel->needs_close)
- {
- if (channel->debug)
- g_print ("write_thread %#x: channel fd %d needs closing\n",
- channel->thread_id, channel->fd);
- close (channel->fd);
- channel->fd = -1;
- }
-
- LeaveCriticalSection (&channel->mutex);
-
- g_io_channel_unref ((GIOChannel *)channel);
-
- return 0;
-}
-
-static void
-create_thread (GIOWin32Channel *channel,
- GIOCondition condition,
- unsigned (__stdcall *thread) (void *parameter))
-{
- HANDLE thread_handle;
-
- thread_handle = (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0,
- &channel->thread_id);
- if (thread_handle == 0)
- g_warning ("Error creating thread: %s.",
- g_strerror (errno));
- else if (!CloseHandle (thread_handle))
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
-
- g_warning ("Error closing thread handle: %s.", emsg);
- g_free (emsg);
- }
-
- WaitForSingleObject (channel->space_avail_event, INFINITE);
-}
-
-static GIOStatus
-buffer_read (GIOWin32Channel *channel,
- gchar *dest,
- gsize count,
- gsize *bytes_read,
- GError **err)
-{
- guint nbytes;
- guint left = count;
-
- EnterCriticalSection (&channel->mutex);
- if (channel->debug)
- g_print ("reading from thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n",
- channel->thread_id, count, channel->rdp, channel->wrp);
-
- if (channel->wrp == channel->rdp)
- {
- LeaveCriticalSection (&channel->mutex);
- if (channel->debug)
- g_print ("waiting for data from thread %#x\n", channel->thread_id);
- WaitForSingleObject (channel->data_avail_event, INFINITE);
- if (channel->debug)
- g_print ("done waiting for data from thread %#x\n", channel->thread_id);
- EnterCriticalSection (&channel->mutex);
- if (channel->wrp == channel->rdp && !channel->running)
- {
- if (channel->debug)
- g_print ("wrp==rdp, !running\n");
- LeaveCriticalSection (&channel->mutex);
- *bytes_read = 0;
- return G_IO_STATUS_EOF;
- }
- }
-
- if (channel->rdp < channel->wrp)
- nbytes = channel->wrp - channel->rdp;
- else
- nbytes = BUFFER_SIZE - channel->rdp;
- LeaveCriticalSection (&channel->mutex);
- nbytes = MIN (left, nbytes);
- if (channel->debug)
- g_print ("moving %d bytes from thread %#x\n",
- nbytes, channel->thread_id);
- memcpy (dest, channel->buffer + channel->rdp, nbytes);
- dest += nbytes;
- left -= nbytes;
- EnterCriticalSection (&channel->mutex);
- channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
- if (channel->debug)
- g_print ("setting space_avail for thread %#x\n", channel->thread_id);
- SetEvent (channel->space_avail_event);
- if (channel->debug)
- g_print ("for thread %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- if (channel->running && channel->wrp == channel->rdp)
- {
- if (channel->debug)
- g_print ("resetting data_avail of thread %#x\n",
- channel->thread_id);
- ResetEvent (channel->data_avail_event);
- };
- LeaveCriticalSection (&channel->mutex);
-
- /* We have no way to indicate any errors form the actual
- * read() or recv() call in the reader thread. Should we have?
- */
- *bytes_read = count - left;
- return (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
-}
-
-
-static GIOStatus
-buffer_write (GIOWin32Channel *channel,
- const gchar *dest,
- gsize count,
- gsize *bytes_written,
- GError **err)
-{
- guint nbytes;
- guint left = count;
-
- EnterCriticalSection (&channel->mutex);
- if (channel->debug)
- g_print ("buffer_write: writing to thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n",
- channel->thread_id, count, channel->rdp, channel->wrp);
-
- if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
- {
- /* Buffer is full */
- if (channel->debug)
- g_print ("buffer_write: tid %#x: resetting data_avail\n",
- channel->thread_id);
- ResetEvent (channel->data_avail_event);
- if (channel->debug)
- g_print ("buffer_write: tid %#x: waiting for space\n",
- channel->thread_id);
- LeaveCriticalSection (&channel->mutex);
- WaitForSingleObject (channel->data_avail_event, INFINITE);
- EnterCriticalSection (&channel->mutex);
- if (channel->debug)
- g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d\n",
- channel->thread_id, channel->rdp, channel->wrp);
- }
-
- nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
- BUFFER_SIZE - channel->wrp);
-
- LeaveCriticalSection (&channel->mutex);
- nbytes = MIN (left, nbytes);
- if (channel->debug)
- g_print ("buffer_write: tid %#x: writing %d bytes\n",
- channel->thread_id, nbytes);
- memcpy (channel->buffer + channel->wrp, dest, nbytes);
- dest += nbytes;
- left -= nbytes;
- EnterCriticalSection (&channel->mutex);
-
- channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
- if (channel->debug)
- g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d, setting space_avail\n",
- channel->thread_id, channel->rdp, channel->wrp);
- SetEvent (channel->space_avail_event);
-
- if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
- {
- /* Buffer is full */
- if (channel->debug)
- g_print ("buffer_write: tid %#x: resetting data_avail\n",
- channel->thread_id);
- ResetEvent (channel->data_avail_event);
- }
-
- LeaveCriticalSection (&channel->mutex);
-
- /* We have no way to indicate any errors form the actual
- * write() call in the writer thread. Should we have?
- */
- *bytes_written = count - left;
- return (*bytes_written > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
-}
-
-
-static gboolean
-g_io_win32_prepare (GSource *source,
- gint *timeout)
-{
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
- GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
- int event_mask;
-
- *timeout = -1;
-
- if (channel->debug)
- g_print ("g_io_win32_prepare: source=%p channel=%p", source, channel);
-
- switch (channel->type)
- {
- case G_IO_WIN32_WINDOWS_MESSAGES:
- if (channel->debug)
- g_print (" MSG");
- break;
-
- case G_IO_WIN32_CONSOLE:
- if (channel->debug)
- g_print (" CON");
- break;
-
- case G_IO_WIN32_FILE_DESC:
- if (channel->debug)
- g_print (" FD thread=%#x buffer_condition:{%s}"
- "\n watch->pollfd.events:{%s} watch->pollfd.revents:{%s} channel->revents:{%s}",
- channel->thread_id, condition_to_string (buffer_condition),
- condition_to_string (watch->pollfd.events),
- condition_to_string (watch->pollfd.revents),
- condition_to_string (channel->revents));
-
- EnterCriticalSection (&channel->mutex);
- if (channel->running)
- {
- if (channel->direction == 0 && channel->wrp == channel->rdp)
- {
- if (channel->debug)
- g_print ("\n setting revents=0");
- channel->revents = 0;
- }
- }
- else
- {
- if (channel->direction == 1
- && (channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
- {
- if (channel->debug)
- g_print ("\n setting revents=0");
- channel->revents = 0;
- }
- }
- LeaveCriticalSection (&channel->mutex);
- break;
-
- case G_IO_WIN32_SOCKET:
- if (channel->debug)
- g_print (" SOCK");
- event_mask = 0;
- if (watch->condition & G_IO_IN)
- event_mask |= (FD_READ | FD_ACCEPT);
- if (watch->condition & G_IO_OUT)
- event_mask |= (FD_WRITE | FD_CONNECT);
- event_mask |= FD_CLOSE;
-
- if (channel->event_mask != event_mask)
- {
- if (channel->debug)
- g_print ("\n WSAEventSelect(%d,%p,{%s})",
- channel->fd, (HANDLE) watch->pollfd.fd,
- event_mask_to_string (event_mask));
- if (WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd,
- event_mask) == SOCKET_ERROR)
- if (channel->debug)
- {
- gchar *emsg = g_win32_error_message (WSAGetLastError ());
-
- g_print (" failed: %s", emsg);
- g_free (emsg);
- }
- channel->event_mask = event_mask;
-
- if (channel->debug)
- g_print ("\n setting last_events=0");
- channel->last_events = 0;
-
- if ((event_mask & FD_WRITE) &&
- channel->ever_writable &&
- !channel->write_would_have_blocked)
- {
- if (channel->debug)
- g_print (" WSASetEvent(%p)", (WSAEVENT) watch->pollfd.fd);
- WSASetEvent ((WSAEVENT) watch->pollfd.fd);
- }
- }
- break;
-
- default:
- g_assert_not_reached ();
- abort ();
- }
- if (channel->debug)
- g_print ("\n");
-
- return ((watch->condition & buffer_condition) == watch->condition);
-}
-
-static gboolean
-g_io_win32_check (GSource *source)
-{
- MSG msg;
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
- GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
- WSANETWORKEVENTS events;
-
- if (channel->debug)
- g_print ("g_io_win32_check: source=%p channel=%p", source, channel);
-
- switch (channel->type)
- {
- case G_IO_WIN32_WINDOWS_MESSAGES:
- if (channel->debug)
- g_print (" MSG\n");
- return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE));
-
- case G_IO_WIN32_FILE_DESC:
- if (channel->debug)
- g_print (" FD thread=%#x buffer_condition=%s\n"
- " watch->pollfd.events={%s} watch->pollfd.revents={%s} channel->revents={%s}\n",
- channel->thread_id, condition_to_string (buffer_condition),
- condition_to_string (watch->pollfd.events),
- condition_to_string (watch->pollfd.revents),
- condition_to_string (channel->revents));
-
- watch->pollfd.revents = (watch->pollfd.events & channel->revents);
-
- return ((watch->pollfd.revents | buffer_condition) & watch->condition);
-
- case G_IO_WIN32_CONSOLE:
- if (channel->debug)
- g_print (" CON\n");
- if (watch->channel->is_writeable)
- return TRUE;
- else if (watch->channel->is_readable)
- {
- INPUT_RECORD buffer;
- DWORD n;
- if (PeekConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n) &&
- n == 1)
- {
- /* _kbhit() does quite complex processing to find out
- * whether at least one of the key events pending corresponds
- * to a "real" character that can be read.
- */
- if (_kbhit ())
- return TRUE;
-
- /* Discard all other kinds of events */
- ReadConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n);
- }
- }
- return FALSE;
-
- case G_IO_WIN32_SOCKET:
- if (channel->debug)
- g_print (" SOCK");
- if (channel->last_events & FD_WRITE)
- {
- if (channel->debug)
- g_print (" sock=%d event=%p last_events has FD_WRITE",
- channel->fd, (HANDLE) watch->pollfd.fd);
- }
- else
- {
- WSAEnumNetworkEvents (channel->fd, 0, &events);
-
- if (channel->debug)
- g_print ("\n revents={%s} condition={%s}"
- "\n WSAEnumNetworkEvents(%d,0) sets events={%s}",
- condition_to_string (watch->pollfd.revents),
- condition_to_string (watch->condition),
- channel->fd,
- event_mask_to_string (events.lNetworkEvents));
-
- if (watch->pollfd.revents != 0 &&
- events.lNetworkEvents == 0 &&
- !(channel->event_mask & FD_WRITE))
- {
- channel->event_mask = 0;
- if (channel->debug)
- g_print ("\n WSAEventSelect(%d,%p,{})",
- channel->fd, (HANDLE) watch->pollfd.fd);
- WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, 0);
- if (channel->debug)
- g_print (" ResetEvent(%p)",
- (HANDLE) watch->pollfd.fd);
- ResetEvent ((HANDLE) watch->pollfd.fd);
- }
- else if (events.lNetworkEvents & FD_WRITE)
- channel->ever_writable = TRUE;
- channel->last_events = events.lNetworkEvents;
- }
-
- watch->pollfd.revents = 0;
- if (channel->last_events & (FD_READ | FD_ACCEPT))
- watch->pollfd.revents |= G_IO_IN;
-
- if (channel->last_events & FD_WRITE)
- watch->pollfd.revents |= G_IO_OUT;
- else
- {
- /* We have called WSAEnumNetworkEvents() above but it didn't
- * set FD_WRITE.
- */
- if (events.lNetworkEvents & FD_CONNECT)
- {
- if (events.iErrorCode[FD_CONNECT_BIT] == 0)
- watch->pollfd.revents |= G_IO_OUT;
- else
- watch->pollfd.revents |= (G_IO_HUP | G_IO_ERR);
- }
- if (watch->pollfd.revents == 0 && (channel->last_events & (FD_CLOSE)))
- watch->pollfd.revents |= G_IO_HUP;
- }
-
- /* Regardless of WSAEnumNetworkEvents() result, if watching for
- * writability, and if we have ever got a FD_WRITE event, and
- * unless last write would have blocked, set G_IO_OUT. But never
- * set both G_IO_OUT and G_IO_HUP.
- */
- if (!(watch->pollfd.revents & G_IO_HUP) &&
- channel->ever_writable &&
- !channel->write_would_have_blocked &&
- (channel->event_mask & FD_WRITE))
- watch->pollfd.revents |= G_IO_OUT;
-
- if (channel->debug)
- g_print ("\n revents={%s} retval={%s}\n",
- condition_to_string (watch->pollfd.revents),
- condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition));
-
- return ((watch->pollfd.revents | buffer_condition) & watch->condition);
-
- default:
- g_assert_not_reached ();
- abort ();
- }
-}
-
-static gboolean
-g_io_win32_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- GIOFunc func = (GIOFunc)callback;
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
- GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
- GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
-
- if (!func)
- {
- g_warning ("IO Watch dispatched without callback\n"
- "You must call g_source_connect().");
- return FALSE;
- }
-
- if (channel->debug)
- g_print ("g_io_win32_dispatch: pollfd.revents=%s condition=%s result=%s\n",
- condition_to_string (watch->pollfd.revents),
- condition_to_string (watch->condition),
- condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition));
-
- return (*func) (watch->channel,
- (watch->pollfd.revents | buffer_condition) & watch->condition,
- user_data);
-}
-
-static void
-g_io_win32_finalize (GSource *source)
-{
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
- GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
-
- if (channel->debug)
- g_print ("g_io_win32_finalize: source=%p channel=%p", source, channel);
-
- switch (channel->type)
- {
- case G_IO_WIN32_WINDOWS_MESSAGES:
- if (channel->debug)
- g_print (" MSG");
- break;
-
- case G_IO_WIN32_CONSOLE:
- if (channel->debug)
- g_print (" CON");
- break;
-
- case G_IO_WIN32_FILE_DESC:
- if (channel->debug)
- g_print (" FD thread=%#x", channel->thread_id);
- break;
-
- case G_IO_WIN32_SOCKET:
- if (channel->debug)
- g_print (" SOCK sock=%d", channel->fd);
- break;
-
- default:
- g_assert_not_reached ();
- abort ();
- }
- if (channel->debug)
- g_print ("\n");
- g_io_channel_unref (watch->channel);
-}
-
-GSourceFuncs g_io_watch_funcs = {
- g_io_win32_prepare,
- g_io_win32_check,
- g_io_win32_dispatch,
- g_io_win32_finalize
-};
-
-static GIOStatus
-g_io_win32_msg_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- MSG msg; /* In case of alignment problems */
-
- if (count < sizeof (MSG))
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
- "Incorrect message size"); /* Informative enough error message? */
- return G_IO_STATUS_ERROR;
- }
-
- if (win32_channel->debug)
- g_print ("g_io_win32_msg_read: channel=%p hwnd=%p\n",
- channel, win32_channel->hwnd);
- if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE))
- return G_IO_STATUS_AGAIN;
-
- memmove (buf, &msg, sizeof (MSG));
- *bytes_read = sizeof (MSG);
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOStatus
-g_io_win32_msg_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- MSG msg;
-
- if (count != sizeof (MSG))
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
- "Incorrect message size"); /* Informative enough error message? */
- return G_IO_STATUS_ERROR;
- }
-
- /* In case of alignment problems */
- memmove (&msg, buf, sizeof (MSG));
- if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
-
- g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, emsg);
- g_free (emsg);
-
- return G_IO_STATUS_ERROR;
- }
-
- *bytes_written = sizeof (MSG);
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOStatus
-g_io_win32_msg_close (GIOChannel *channel,
- GError **err)
-{
- /* Nothing to be done. Or should we set hwnd to some invalid value? */
-
- return G_IO_STATUS_NORMAL;
-}
-
-static void
-g_io_win32_free (GIOChannel *channel)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_free channel=%p fd=%d\n", channel, win32_channel->fd);
-
- DeleteCriticalSection (&win32_channel->mutex);
-
- if (win32_channel->data_avail_event)
- if (!CloseHandle (win32_channel->data_avail_event))
- if (win32_channel->debug)
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
-
- g_print (" CloseHandle(%p) failed: %s\n",
- win32_channel->data_avail_event, emsg);
- g_free (emsg);
- }
-
- g_free (win32_channel->buffer);
-
- if (win32_channel->space_avail_event)
- if (!CloseHandle (win32_channel->space_avail_event))
- if (win32_channel->debug)
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
-
- g_print (" CloseHandle(%p) failed: %s\n",
- win32_channel->space_avail_event, emsg);
- g_free (emsg);
- }
-
- if (win32_channel->type == G_IO_WIN32_SOCKET &&
- win32_channel->fd != -1)
- if (WSAEventSelect (win32_channel->fd, NULL, 0) == SOCKET_ERROR)
- if (win32_channel->debug)
- {
- gchar *emsg = g_win32_error_message (WSAGetLastError ());
-
- g_print (" WSAEventSelect(%d,NULL,{}) failed: %s\n",
- win32_channel->fd, emsg);
- g_free (emsg);
- }
-
- if (win32_channel->event)
- if (!WSACloseEvent (win32_channel->event))
- if (win32_channel->debug)
- {
- gchar *emsg = g_win32_error_message (WSAGetLastError ());
-
- g_print (" WSACloseEvent(%p) failed: %s\n",
- win32_channel->event, emsg);
- g_free (emsg);
- }
-
- g_free (win32_channel);
-}
-
-static GSource *
-g_io_win32_msg_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- GIOWin32Watch *watch;
- GSource *source;
-
- source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
- g_source_set_name (source, "GIOChannel (Win32)");
- watch = (GIOWin32Watch *)source;
-
- watch->channel = channel;
- g_io_channel_ref (channel);
-
- watch->condition = condition;
-
- watch->pollfd.fd = (gintptr) G_WIN32_MSG_HANDLE;
- watch->pollfd.events = condition;
-
- g_source_add_poll (source, &watch->pollfd);
-
- return source;
-}
-
-static GIOStatus
-g_io_win32_fd_and_console_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- gint result;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_fd_read: fd=%d count=%" G_GSIZE_FORMAT "\n",
- win32_channel->fd, count);
-
- if (win32_channel->thread_id)
- {
- return buffer_read (win32_channel, buf, count, bytes_read, err);
- }
-
- result = read (win32_channel->fd, buf, count);
-
- if (win32_channel->debug)
- g_print ("g_io_win32_fd_read: read() => %d\n", result);
-
- if (result < 0)
- {
- *bytes_read = 0;
-
- switch (errno)
- {
-#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
-#endif
- default:
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- g_strerror (errno));
- return G_IO_STATUS_ERROR;
- }
- }
-
- *bytes_read = result;
-
- return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
-}
-
-static GIOStatus
-g_io_win32_fd_and_console_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- gint result;
-
- if (win32_channel->thread_id)
- {
- return buffer_write (win32_channel, buf, count, bytes_written, err);
- }
-
- result = write (win32_channel->fd, buf, count);
- if (win32_channel->debug)
- g_print ("g_io_win32_fd_write: fd=%d count=%" G_GSIZE_FORMAT " => %d\n",
- win32_channel->fd, count, result);
-
- if (result < 0)
- {
- *bytes_written = 0;
-
- switch (errno)
- {
-#ifdef EAGAIN
- case EAGAIN:
- return G_IO_STATUS_AGAIN;
-#endif
- default:
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- g_strerror (errno));
- return G_IO_STATUS_ERROR;
- }
- }
-
- *bytes_written = result;
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOStatus
-g_io_win32_fd_seek (GIOChannel *channel,
- gint64 offset,
- GSeekType type,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- int whence;
- off_t tmp_offset;
- off_t result;
-
- switch (type)
- {
- case G_SEEK_SET:
- whence = SEEK_SET;
- break;
- case G_SEEK_CUR:
- whence = SEEK_CUR;
- break;
- case G_SEEK_END:
- whence = SEEK_END;
- break;
- default:
- whence = -1; /* Keep the compiler quiet */
- g_assert_not_reached ();
- abort ();
- }
-
- tmp_offset = offset;
- if (tmp_offset != offset)
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (EINVAL),
- g_strerror (EINVAL));
- return G_IO_STATUS_ERROR;
- }
-
- result = lseek (win32_channel->fd, tmp_offset, whence);
-
- if (result < 0)
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- g_strerror (errno));
- return G_IO_STATUS_ERROR;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOStatus
-g_io_win32_fd_close (GIOChannel *channel,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_fd_close: thread=%#x: fd=%d\n",
- win32_channel->thread_id,
- win32_channel->fd);
- EnterCriticalSection (&win32_channel->mutex);
- if (win32_channel->running)
- {
- if (win32_channel->debug)
- g_print ("thread %#x: running, marking fd %d for later close\n",
- win32_channel->thread_id, win32_channel->fd);
- win32_channel->running = FALSE;
- win32_channel->needs_close = TRUE;
- if (win32_channel->direction == 0)
- SetEvent (win32_channel->data_avail_event);
- else
- SetEvent (win32_channel->space_avail_event);
- }
- else
- {
- if (win32_channel->debug)
- g_print ("closing fd %d\n", win32_channel->fd);
- close (win32_channel->fd);
- if (win32_channel->debug)
- g_print ("closed fd %d, setting to -1\n",
- win32_channel->fd);
- win32_channel->fd = -1;
- }
- LeaveCriticalSection (&win32_channel->mutex);
-
- /* FIXME error detection? */
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GSource *
-g_io_win32_fd_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
-
- watch->channel = channel;
- g_io_channel_ref (channel);
-
- watch->condition = condition;
-
- if (win32_channel->data_avail_event == NULL)
- create_events (win32_channel);
-
- watch->pollfd.fd = (gintptr) win32_channel->data_avail_event;
- watch->pollfd.events = condition;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_fd_create_watch: channel=%p fd=%d condition={%s} event=%p\n",
- channel, win32_channel->fd,
- condition_to_string (condition), (HANDLE) watch->pollfd.fd);
-
- EnterCriticalSection (&win32_channel->mutex);
- if (win32_channel->thread_id == 0)
- {
- if (condition & G_IO_IN)
- create_thread (win32_channel, condition, read_thread);
- else if (condition & G_IO_OUT)
- create_thread (win32_channel, condition, write_thread);
- }
-
- g_source_add_poll (source, &watch->pollfd);
- LeaveCriticalSection (&win32_channel->mutex);
-
- return source;
-}
-
-static GIOStatus
-g_io_win32_console_close (GIOChannel *channel,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- if (close (win32_channel->fd) < 0)
- {
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- g_io_channel_error_from_errno (errno),
- g_strerror (errno));
- return G_IO_STATUS_ERROR;
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GSource *
-g_io_win32_console_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
-
- watch->channel = channel;
- g_io_channel_ref (channel);
-
- watch->condition = condition;
-
- watch->pollfd.fd = _get_osfhandle (win32_channel->fd);
- watch->pollfd.events = condition;
-
- g_source_add_poll (source, &watch->pollfd);
-
- return source;
-}
-
-static GIOStatus
-g_io_win32_sock_read (GIOChannel *channel,
- gchar *buf,
- gsize count,
- gsize *bytes_read,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- gint result;
- GIOChannelError error;
- int winsock_error;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_sock_read: channel=%p sock=%d count=%" G_GSIZE_FORMAT,
- channel, win32_channel->fd, count);
-
- result = recv (win32_channel->fd, buf, count, 0);
- if (result == SOCKET_ERROR)
- winsock_error = WSAGetLastError ();
-
- if (win32_channel->debug)
- g_print (" recv=%d", result);
-
- if (result == SOCKET_ERROR)
- {
- gchar *emsg = g_win32_error_message (winsock_error);
-
- if (win32_channel->debug)
- g_print (" %s\n", emsg);
-
- *bytes_read = 0;
-
- switch (winsock_error)
- {
- case WSAEINVAL:
- error = G_IO_CHANNEL_ERROR_INVAL;
- break;
- case WSAEWOULDBLOCK:
- g_free (emsg);
- return G_IO_STATUS_AGAIN;
- default:
- error = G_IO_CHANNEL_ERROR_FAILED;
- break;
- }
- g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg);
- g_free (emsg);
-
- return G_IO_STATUS_ERROR;
- }
- else
- {
- if (win32_channel->debug)
- g_print ("\n");
- *bytes_read = result;
- if (result == 0)
- return G_IO_STATUS_EOF;
- else
- return G_IO_STATUS_NORMAL;
- }
-}
-
-static GIOStatus
-g_io_win32_sock_write (GIOChannel *channel,
- const gchar *buf,
- gsize count,
- gsize *bytes_written,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- gint result;
- GIOChannelError error;
- int winsock_error;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_sock_write: channel=%p sock=%d count=%" G_GSIZE_FORMAT,
- channel, win32_channel->fd, count);
-
- result = send (win32_channel->fd, buf, count, 0);
- if (result == SOCKET_ERROR)
- winsock_error = WSAGetLastError ();
-
- if (win32_channel->debug)
- g_print (" send=%d", result);
-
- if (result == SOCKET_ERROR)
- {
- gchar *emsg = g_win32_error_message (winsock_error);
-
- if (win32_channel->debug)
- g_print (" %s\n", emsg);
-
- *bytes_written = 0;
-
- switch (winsock_error)
- {
- case WSAEINVAL:
- error = G_IO_CHANNEL_ERROR_INVAL;
- break;
- case WSAEWOULDBLOCK:
- win32_channel->write_would_have_blocked = TRUE;
- win32_channel->last_events = 0;
- g_free (emsg);
- return G_IO_STATUS_AGAIN;
- default:
- error = G_IO_CHANNEL_ERROR_FAILED;
- break;
- }
- g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg);
- g_free (emsg);
-
- return G_IO_STATUS_ERROR;
- }
- else
- {
- if (win32_channel->debug)
- g_print ("\n");
- *bytes_written = result;
- win32_channel->write_would_have_blocked = FALSE;
-
- return G_IO_STATUS_NORMAL;
- }
-}
-
-static GIOStatus
-g_io_win32_sock_close (GIOChannel *channel,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- if (win32_channel->fd != -1)
- {
- if (win32_channel->debug)
- g_print ("g_io_win32_sock_close: channel=%p sock=%d\n",
- channel, win32_channel->fd);
-
- closesocket (win32_channel->fd);
- win32_channel->fd = -1;
- }
-
- /* FIXME error detection? */
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GSource *
-g_io_win32_sock_create_watch (GIOChannel *channel,
- GIOCondition condition)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
- GIOWin32Watch *watch = (GIOWin32Watch *)source;
-
- watch->channel = channel;
- g_io_channel_ref (channel);
-
- watch->condition = condition;
-
- if (win32_channel->event == 0)
- win32_channel->event = WSACreateEvent ();
-
- watch->pollfd.fd = (gintptr) win32_channel->event;
- watch->pollfd.events = condition;
-
- if (win32_channel->debug)
- g_print ("g_io_win32_sock_create_watch: channel=%p sock=%d event=%p condition={%s}\n",
- channel, win32_channel->fd, (HANDLE) watch->pollfd.fd,
- condition_to_string (watch->condition));
-
- g_source_add_poll (source, &watch->pollfd);
-
- return source;
-}
-
-GIOChannel *
-g_io_channel_new_file (const gchar *filename,
- const gchar *mode,
- GError **error)
-{
- int fid, flags, pmode;
- GIOChannel *channel;
-
- enum { /* Cheesy hack */
- MODE_R = 1 << 0,
- MODE_W = 1 << 1,
- MODE_A = 1 << 2,
- MODE_PLUS = 1 << 3,
- } mode_num;
-
- g_return_val_if_fail (filename != NULL, NULL);
- g_return_val_if_fail (mode != NULL, NULL);
- g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);
-
- switch (mode[0])
- {
- case 'r':
- mode_num = MODE_R;
- break;
- case 'w':
- mode_num = MODE_W;
- break;
- case 'a':
- mode_num = MODE_A;
- break;
- default:
- g_warning ("Invalid GIOFileMode %s.", mode);
- return NULL;
- }
-
- switch (mode[1])
- {
- case '\0':
- break;
- case '+':
- if (mode[2] == '\0')
- {
- mode_num |= MODE_PLUS;
- break;
- }
- /* Fall through */
- default:
- g_warning ("Invalid GIOFileMode %s.", mode);
- return NULL;
- }
-
- switch (mode_num)
- {
- case MODE_R:
- flags = O_RDONLY;
- pmode = _S_IREAD;
- break;
- case MODE_W:
- flags = O_WRONLY | O_TRUNC | O_CREAT;
- pmode = _S_IWRITE;
- break;
- case MODE_A:
- flags = O_WRONLY | O_APPEND | O_CREAT;
- pmode = _S_IWRITE;
- break;
- case MODE_R | MODE_PLUS:
- flags = O_RDWR;
- pmode = _S_IREAD | _S_IWRITE;
- break;
- case MODE_W | MODE_PLUS:
- flags = O_RDWR | O_TRUNC | O_CREAT;
- pmode = _S_IREAD | _S_IWRITE;
- break;
- case MODE_A | MODE_PLUS:
- flags = O_RDWR | O_APPEND | O_CREAT;
- pmode = _S_IREAD | _S_IWRITE;
- break;
- default:
- g_assert_not_reached ();
- abort ();
- }
-
- /* always open 'untranslated' */
- fid = g_open (filename, flags | _O_BINARY, pmode);
-
- if (g_io_win32_get_debug_flag ())
- {
- g_print ("g_io_channel_win32_new_file: open(\"%s\",", filename);
- g_win32_print_access_mode (flags|_O_BINARY);
- g_print (",%#o)=%d\n", pmode, fid);
- }
-
- if (fid < 0)
- {
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (errno),
- g_strerror (errno));
- return (GIOChannel *)NULL;
- }
-
- channel = g_io_channel_win32_new_fd (fid);
-
- /* XXX: move this to g_io_channel_win32_new_fd () */
- channel->close_on_unref = TRUE;
- channel->is_seekable = TRUE;
-
- /* g_io_channel_win32_new_fd sets is_readable and is_writeable to
- * correspond to actual readability/writeability. Set to FALSE those
- * that mode doesn't allow
- */
- switch (mode_num)
- {
- case MODE_R:
- channel->is_writeable = FALSE;
- break;
- case MODE_W:
- case MODE_A:
- channel->is_readable = FALSE;
- break;
- case MODE_R | MODE_PLUS:
- case MODE_W | MODE_PLUS:
- case MODE_A | MODE_PLUS:
- break;
- default:
- g_assert_not_reached ();
- abort ();
- }
-
- return channel;
-}
-
-#if !defined (_WIN64)
-
-#undef g_io_channel_new_file
-
-/* Binary compatibility version. Not for newly compiled code. */
-
-GIOChannel *
-g_io_channel_new_file (const gchar *filename,
- const gchar *mode,
- GError **error)
-{
- gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
- GIOChannel *retval;
-
- if (utf8_filename == NULL)
- return NULL;
-
- retval = g_io_channel_new_file_utf8 (utf8_filename, mode, error);
-
- g_free (utf8_filename);
-
- return retval;
-}
-
-#endif
-
-static GIOStatus
-g_io_win32_unimpl_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- if (win32_channel->debug)
- {
- g_print ("g_io_win32_unimpl_set_flags: ");
- g_win32_print_gioflags (flags);
- g_print ("\n");
- }
-
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- G_IO_CHANNEL_ERROR_FAILED,
- "Not implemented on Win32");
-
- return G_IO_STATUS_ERROR;
-}
-
-static GIOFlags
-g_io_win32_fd_get_flags_internal (GIOChannel *channel,
- struct _stati64 *st)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
- gchar c;
- DWORD count;
-
- if (st->st_mode & _S_IFIFO)
- {
- channel->is_readable =
- (PeekNamedPipe ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL, NULL) != 0) || GetLastError () == ERROR_BROKEN_PIPE;
- channel->is_writeable =
- (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
- channel->is_seekable = FALSE;
- }
- else
- {
- channel->is_readable =
- (ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
- channel->is_writeable =
- (WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
- channel->is_seekable = TRUE;
- }
-
- /* XXX: G_IO_FLAG_APPEND */
- /* XXX: G_IO_FLAG_NONBLOCK */
-
- return 0;
-}
-
-static GIOFlags
-g_io_win32_fd_get_flags (GIOChannel *channel)
-{
- struct _stati64 st;
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- g_return_val_if_fail (win32_channel != NULL, 0);
- g_return_val_if_fail (win32_channel->type == G_IO_WIN32_FILE_DESC, 0);
-
- if (0 == _fstati64 (win32_channel->fd, &st))
- return g_io_win32_fd_get_flags_internal (channel, &st);
- else
- return 0;
-}
-
-static GIOFlags
-g_io_win32_console_get_flags_internal (GIOChannel *channel)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
- HANDLE handle = (HANDLE) _get_osfhandle (win32_channel->fd);
- gchar c;
- DWORD count;
- INPUT_RECORD record;
-
- channel->is_readable = PeekConsoleInput (handle, &record, 1, &count);
- channel->is_writeable = WriteFile (handle, &c, 0, &count, NULL);
- channel->is_seekable = FALSE;
-
- return 0;
-}
-
-static GIOFlags
-g_io_win32_console_get_flags (GIOChannel *channel)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- g_return_val_if_fail (win32_channel != NULL, 0);
- g_return_val_if_fail (win32_channel->type == G_IO_WIN32_CONSOLE, 0);
-
- return g_io_win32_console_get_flags_internal (channel);
-}
-
-static GIOFlags
-g_io_win32_msg_get_flags (GIOChannel *channel)
-{
- return 0;
-}
-
-static GIOStatus
-g_io_win32_sock_set_flags (GIOChannel *channel,
- GIOFlags flags,
- GError **err)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
- u_long arg;
-
- if (win32_channel->debug)
- {
- g_print ("g_io_win32_sock_set_flags: ");
- g_win32_print_gioflags (flags);
- g_print ("\n");
- }
-
- if (flags & G_IO_FLAG_NONBLOCK)
- {
- arg = 1;
- if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
- {
- gchar *emsg = g_win32_error_message (WSAGetLastError ());
-
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- G_IO_CHANNEL_ERROR_FAILED,
- emsg);
- g_free (emsg);
-
- return G_IO_STATUS_ERROR;
- }
- }
- else
- {
- arg = 0;
- if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
- {
- gchar *emsg = g_win32_error_message (WSAGetLastError ());
-
- g_set_error_literal (err, G_IO_CHANNEL_ERROR,
- G_IO_CHANNEL_ERROR_FAILED,
- emsg);
- g_free (emsg);
-
- return G_IO_STATUS_ERROR;
- }
- }
-
- return G_IO_STATUS_NORMAL;
-}
-
-static GIOFlags
-g_io_win32_sock_get_flags (GIOChannel *channel)
-{
- /* Could we do something here? */
- return 0;
-}
-
-static GIOFuncs win32_channel_msg_funcs = {
- g_io_win32_msg_read,
- g_io_win32_msg_write,
- NULL,
- g_io_win32_msg_close,
- g_io_win32_msg_create_watch,
- g_io_win32_free,
- g_io_win32_unimpl_set_flags,
- g_io_win32_msg_get_flags,
-};
-
-static GIOFuncs win32_channel_fd_funcs = {
- g_io_win32_fd_and_console_read,
- g_io_win32_fd_and_console_write,
- g_io_win32_fd_seek,
- g_io_win32_fd_close,
- g_io_win32_fd_create_watch,
- g_io_win32_free,
- g_io_win32_unimpl_set_flags,
- g_io_win32_fd_get_flags,
-};
-
-static GIOFuncs win32_channel_console_funcs = {
- g_io_win32_fd_and_console_read,
- g_io_win32_fd_and_console_write,
- NULL,
- g_io_win32_console_close,
- g_io_win32_console_create_watch,
- g_io_win32_free,
- g_io_win32_unimpl_set_flags,
- g_io_win32_console_get_flags,
-};
-
-static GIOFuncs win32_channel_sock_funcs = {
- g_io_win32_sock_read,
- g_io_win32_sock_write,
- NULL,
- g_io_win32_sock_close,
- g_io_win32_sock_create_watch,
- g_io_win32_free,
- g_io_win32_sock_set_flags,
- g_io_win32_sock_get_flags,
-};
-
-/**
- * g_io_channel_win32_new_messages:
- * @hwnd: a window handle.
- * @Returns: a new #GIOChannel.
- *
- * Creates a new #GIOChannel given a window handle on Windows.
- *
- * This function creates a #GIOChannel that can be used to poll for
- * Windows messages for the window in question.
- **/
-GIOChannel *
-#if GLIB_SIZEOF_VOID_P == 8
-g_io_channel_win32_new_messages (gsize hwnd)
-#else
-g_io_channel_win32_new_messages (guint hwnd)
-#endif
-{
- GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
- GIOChannel *channel = (GIOChannel *)win32_channel;
-
- g_io_channel_init (channel);
- g_io_channel_win32_init (win32_channel);
- if (win32_channel->debug)
- g_print ("g_io_channel_win32_new_messages: channel=%p hwnd=%p\n",
- channel, (HWND) hwnd);
- channel->funcs = &win32_channel_msg_funcs;
- win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES;
- win32_channel->hwnd = (HWND) hwnd;
-
- /* XXX: check this. */
- channel->is_readable = IsWindow (win32_channel->hwnd);
- channel->is_writeable = IsWindow (win32_channel->hwnd);
-
- channel->is_seekable = FALSE;
-
- return channel;
-}
-
-static GIOChannel *
-g_io_channel_win32_new_fd_internal (gint fd,
- struct _stati64 *st)
-{
- GIOWin32Channel *win32_channel;
- GIOChannel *channel;
-
- win32_channel = g_new (GIOWin32Channel, 1);
- channel = (GIOChannel *)win32_channel;
-
- g_io_channel_init (channel);
- g_io_channel_win32_init (win32_channel);
-
- win32_channel->fd = fd;
-
- if (win32_channel->debug)
- g_print ("g_io_channel_win32_new_fd: channel=%p fd=%u\n",
- channel, fd);
-
- if (st->st_mode & _S_IFCHR) /* console */
- {
- channel->funcs = &win32_channel_console_funcs;
- win32_channel->type = G_IO_WIN32_CONSOLE;
- g_io_win32_console_get_flags_internal (channel);
- }
- else
- {
- channel->funcs = &win32_channel_fd_funcs;
- win32_channel->type = G_IO_WIN32_FILE_DESC;
- g_io_win32_fd_get_flags_internal (channel, st);
- }
-
- return channel;
-}
-
-/**
- * g_io_channel_win32_new_fd:
- * @fd: a C library file descriptor.
- * @Returns: a new #GIOChannel.
- *
- * Creates a new #GIOChannel given a file descriptor on Windows. This
- * works for file descriptors from the C runtime.
- *
- * This function works for file descriptors as returned by the open(),
- * creat(), pipe() and fileno() calls in the Microsoft C runtime. In
- * order to meaningfully use this function your code should use the
- * same C runtime as GLib uses, which is msvcrt.dll. Note that in
- * current Microsoft compilers it is near impossible to convince it to
- * build code that would use msvcrt.dll. The last Microsoft compiler
- * version that supported using msvcrt.dll as the C runtime was version
- * 6. The GNU compiler and toolchain for Windows, also known as Mingw,
- * fully supports msvcrt.dll.
- *
- * If you have created a #GIOChannel for a file descriptor and started
- * watching (polling) it, you shouldn't call read() on the file
- * descriptor. This is because adding polling for a file descriptor is
- * implemented in GLib on Windows by starting a thread that sits
- * blocked in a read() from the file descriptor most of the time. All
- * reads from the file descriptor should be done by this internal GLib
- * thread. Your code should call only g_io_channel_read().
- *
- * This function is available only in GLib on Windows.
- **/
-GIOChannel *
-g_io_channel_win32_new_fd (gint fd)
-{
- struct _stati64 st;
-
- if (_fstati64 (fd, &st) == -1)
- {
- g_warning ("g_io_channel_win32_new_fd: %d isn't an open file descriptor in the C library GLib uses.", fd);
- return NULL;
- }
-
- return g_io_channel_win32_new_fd_internal (fd, &st);
-}
-
-gint
-g_io_channel_win32_get_fd (GIOChannel *channel)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- return win32_channel->fd;
-}
-
-/**
- * g_io_channel_win32_new_socket:
- * @socket: a Winsock socket
- * @Returns: a new #GIOChannel
- *
- * Creates a new #GIOChannel given a socket on Windows.
- *
- * This function works for sockets created by Winsock. It's available
- * only in GLib on Windows.
- *
- * Polling a #GSource created to watch a channel for a socket puts the
- * socket in non-blocking mode. This is a side-effect of the
- * implementation and unavoidable.
- **/
-GIOChannel *
-g_io_channel_win32_new_socket (int socket)
-{
- GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
- GIOChannel *channel = (GIOChannel *)win32_channel;
-
- g_io_channel_init (channel);
- g_io_channel_win32_init (win32_channel);
- if (win32_channel->debug)
- g_print ("g_io_channel_win32_new_socket: channel=%p sock=%d\n",
- channel, socket);
- channel->funcs = &win32_channel_sock_funcs;
- win32_channel->type = G_IO_WIN32_SOCKET;
- win32_channel->fd = socket;
-
- channel->is_readable = TRUE;
- channel->is_writeable = TRUE;
- channel->is_seekable = FALSE;
-
- return channel;
-}
-
-GIOChannel *
-g_io_channel_unix_new (gint fd)
-{
- gboolean is_fd, is_socket;
- struct _stati64 st;
- int optval, optlen;
-
- is_fd = (_fstati64 (fd, &st) == 0);
-
- optlen = sizeof (optval);
- is_socket = (getsockopt (fd, SOL_SOCKET, SO_TYPE, (char *) &optval, &optlen) != SOCKET_ERROR);
-
- if (is_fd && is_socket)
- g_warning ("g_io_channel_unix_new: %d is both a file descriptor and a socket. File descriptor interpretation assumed. To avoid ambiguity, call either g_io_channel_win32_new_fd() or g_io_channel_win32_new_socket() instead.", fd);
-
- if (is_fd)
- return g_io_channel_win32_new_fd_internal (fd, &st);
-
- if (is_socket)
- return g_io_channel_win32_new_socket(fd);
-
- g_warning ("g_io_channel_unix_new: %d is neither a file descriptor or a socket.", fd);
-
- return NULL;
-}
-
-gint
-g_io_channel_unix_get_fd (GIOChannel *channel)
-{
- return g_io_channel_win32_get_fd (channel);
-}
-
-void
-g_io_channel_win32_set_debug (GIOChannel *channel,
- gboolean flag)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- win32_channel->debug = flag;
-}
-
-gint
-g_io_channel_win32_poll (GPollFD *fds,
- gint n_fds,
- gint timeout)
-{
- g_return_val_if_fail (n_fds >= 0, 0);
-
- return g_poll (fds, n_fds, timeout);
-}
-
-void
-g_io_channel_win32_make_pollfd (GIOChannel *channel,
- GIOCondition condition,
- GPollFD *fd)
-{
- GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
-
- switch (win32_channel->type)
- {
- case G_IO_WIN32_FILE_DESC:
- if (win32_channel->data_avail_event == NULL)
- create_events (win32_channel);
-
- fd->fd = (gintptr) win32_channel->data_avail_event;
-
- if (win32_channel->thread_id == 0)
- {
- /* Is it meaningful for a file descriptor to be polled for
- * both IN and OUT? For what kind of file descriptor would
- * that be? Doesn't seem to make sense, in practise the file
- * descriptors handled here are always read or write ends of
- * pipes surely, and thus unidirectional.
- */
- if (condition & G_IO_IN)
- create_thread (win32_channel, condition, read_thread);
- else if (condition & G_IO_OUT)
- create_thread (win32_channel, condition, write_thread);
- }
- break;
-
- case G_IO_WIN32_CONSOLE:
- fd->fd = _get_osfhandle (win32_channel->fd);
- break;
-
- case G_IO_WIN32_SOCKET:
- fd->fd = (gintptr) WSACreateEvent ();
- break;
-
- case G_IO_WIN32_WINDOWS_MESSAGES:
- fd->fd = G_WIN32_MSG_HANDLE;
- break;
-
- default:
- g_assert_not_reached ();
- abort ();
- }
-
- fd->events = condition;
-}
-
-#ifndef _WIN64
-
-/* Binary compatibility */
-GIOChannel *
-g_io_channel_win32_new_stream_socket (int socket)
-{
- return g_io_channel_win32_new_socket (socket);
-}
-
-#endif
+++ /dev/null
-/* gkeyfile.c - key file parser
- *
- * Copyright 2004 Red Hat, Inc.
- * Copyright 2009-2010 Collabora Ltd.
- * Copyright 2009 Nokia Corporation
- *
- * Written by Ray Strode <rstrode@redhat.com>
- * Matthias Clasen <mclasen@redhat.com>
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "gkeyfile.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <locale.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-
-#define fstat(a,b) _fstati64(a,b)
-#define stat _stati64
-
-#ifndef S_ISREG
-#define S_ISREG(mode) ((mode)&_S_IFREG)
-#endif
-
-#endif /* G_OS_WIN23 */
-
-#include "gconvert.h"
-#include "gdataset.h"
-#include "gerror.h"
-#include "gfileutils.h"
-#include "ghash.h"
-#include "glibintl.h"
-#include "glist.h"
-#include "gslist.h"
-#include "gmem.h"
-#include "gmessages.h"
-#include "gstdio.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "gutils.h"
-
-
-typedef struct _GKeyFileGroup GKeyFileGroup;
-
-struct _GKeyFile
-{
- GList *groups;
- GHashTable *group_hash;
-
- GKeyFileGroup *start_group;
- GKeyFileGroup *current_group;
-
- GString *parse_buffer; /* Holds up to one line of not-yet-parsed data */
-
- /* Used for sizing the output buffer during serialization
- */
- gsize approximate_size;
-
- gchar list_separator;
-
- GKeyFileFlags flags;
-
- gchar **locales;
-};
-
-typedef struct _GKeyFileKeyValuePair GKeyFileKeyValuePair;
-
-struct _GKeyFileGroup
-{
- const gchar *name; /* NULL for above first group (which will be comments) */
-
- GKeyFileKeyValuePair *comment; /* Special comment that is stuck to the top of a group */
- gboolean has_trailing_blank_line;
-
- GList *key_value_pairs;
-
- /* Used in parallel with key_value_pairs for
- * increased lookup performance
- */
- GHashTable *lookup_map;
-};
-
-struct _GKeyFileKeyValuePair
-{
- gchar *key; /* NULL for comments */
- gchar *value;
-};
-
-static gint find_file_in_data_dirs (const gchar *file,
- const gchar **data_dirs,
- gchar **output_file,
- GError **error);
-static gboolean g_key_file_load_from_fd (GKeyFile *key_file,
- gint fd,
- GKeyFileFlags flags,
- GError **error);
-static GList *g_key_file_lookup_group_node (GKeyFile *key_file,
- const gchar *group_name);
-static GKeyFileGroup *g_key_file_lookup_group (GKeyFile *key_file,
- const gchar *group_name);
-
-static GList *g_key_file_lookup_key_value_pair_node (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key);
-static GKeyFileKeyValuePair *g_key_file_lookup_key_value_pair (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key);
-
-static void g_key_file_remove_group_node (GKeyFile *key_file,
- GList *group_node);
-static void g_key_file_remove_key_value_pair_node (GKeyFile *key_file,
- GKeyFileGroup *group,
- GList *pair_node);
-
-static void g_key_file_add_key (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key,
- const gchar *value);
-static void g_key_file_add_group (GKeyFile *key_file,
- const gchar *group_name);
-static gboolean g_key_file_is_group_name (const gchar *name);
-static gboolean g_key_file_is_key_name (const gchar *name);
-static void g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair);
-static gboolean g_key_file_line_is_comment (const gchar *line);
-static gboolean g_key_file_line_is_group (const gchar *line);
-static gboolean g_key_file_line_is_key_value_pair (const gchar *line);
-static gchar *g_key_file_parse_value_as_string (GKeyFile *key_file,
- const gchar *value,
- GSList **separators,
- GError **error);
-static gchar *g_key_file_parse_string_as_value (GKeyFile *key_file,
- const gchar *string,
- gboolean escape_separator);
-static gint g_key_file_parse_value_as_integer (GKeyFile *key_file,
- const gchar *value,
- GError **error);
-static gchar *g_key_file_parse_integer_as_value (GKeyFile *key_file,
- gint value);
-static gdouble g_key_file_parse_value_as_double (GKeyFile *key_file,
- const gchar *value,
- GError **error);
-static gboolean g_key_file_parse_value_as_boolean (GKeyFile *key_file,
- const gchar *value,
- GError **error);
-static gchar *g_key_file_parse_boolean_as_value (GKeyFile *key_file,
- gboolean value);
-static gchar *g_key_file_parse_value_as_comment (GKeyFile *key_file,
- const gchar *value);
-static gchar *g_key_file_parse_comment_as_value (GKeyFile *key_file,
- const gchar *comment);
-static void g_key_file_parse_key_value_pair (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error);
-static void g_key_file_parse_comment (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error);
-static void g_key_file_parse_group (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error);
-static gchar *key_get_locale (const gchar *key);
-static void g_key_file_parse_data (GKeyFile *key_file,
- const gchar *data,
- gsize length,
- GError **error);
-static void g_key_file_flush_parse_buffer (GKeyFile *key_file,
- GError **error);
-
-
-GQuark
-g_key_file_error_quark (void)
-{
- return g_quark_from_static_string ("g-key-file-error-quark");
-}
-
-static void
-g_key_file_init (GKeyFile *key_file)
-{
- key_file->current_group = g_slice_new0 (GKeyFileGroup);
- key_file->groups = g_list_prepend (NULL, key_file->current_group);
- key_file->group_hash = g_hash_table_new (g_str_hash, g_str_equal);
- key_file->start_group = NULL;
- key_file->parse_buffer = g_string_sized_new (128);
- key_file->approximate_size = 0;
- key_file->list_separator = ';';
- key_file->flags = 0;
- key_file->locales = g_strdupv ((gchar **)g_get_language_names ());
-}
-
-static void
-g_key_file_clear (GKeyFile *key_file)
-{
- GList *tmp, *group_node;
-
- if (key_file->locales)
- {
- g_strfreev (key_file->locales);
- key_file->locales = NULL;
- }
-
- if (key_file->parse_buffer)
- {
- g_string_free (key_file->parse_buffer, TRUE);
- key_file->parse_buffer = NULL;
- }
-
- tmp = key_file->groups;
- while (tmp != NULL)
- {
- group_node = tmp;
- tmp = tmp->next;
- g_key_file_remove_group_node (key_file, group_node);
- }
-
- g_hash_table_destroy (key_file->group_hash);
- key_file->group_hash = NULL;
-
- g_warn_if_fail (key_file->groups == NULL);
-}
-
-
-/**
- * g_key_file_new:
- *
- * Creates a new empty #GKeyFile object. Use
- * g_key_file_load_from_file(), g_key_file_load_from_data(),
- * g_key_file_load_from_dirs() or g_key_file_load_from_data_dirs() to
- * read an existing key file.
- *
- * Return value: an empty #GKeyFile.
- *
- * Since: 2.6
- **/
-GKeyFile *
-g_key_file_new (void)
-{
- GKeyFile *key_file;
-
- key_file = g_slice_new0 (GKeyFile);
- g_key_file_init (key_file);
-
- return key_file;
-}
-
-/**
- * g_key_file_set_list_separator:
- * @key_file: a #GKeyFile
- * @separator: the separator
- *
- * Sets the character which is used to separate
- * values in lists. Typically ';' or ',' are used
- * as separators. The default list separator is ';'.
- *
- * Since: 2.6
- */
-void
-g_key_file_set_list_separator (GKeyFile *key_file,
- gchar separator)
-{
- g_return_if_fail (key_file != NULL);
-
- key_file->list_separator = separator;
-}
-
-
-/* Iterates through all the directories in *dirs trying to
- * open file. When it successfully locates and opens a file it
- * returns the file descriptor to the open file. It also
- * outputs the absolute path of the file in output_file.
- */
-static gint
-find_file_in_data_dirs (const gchar *file,
- const gchar **dirs,
- gchar **output_file,
- GError **error)
-{
- const gchar **data_dirs, *data_dir;
- gchar *path;
- gint fd;
-
- path = NULL;
- fd = -1;
-
- if (dirs == NULL)
- return fd;
-
- data_dirs = dirs;
-
- while (data_dirs && (data_dir = *data_dirs) && fd < 0)
- {
- gchar *candidate_file, *sub_dir;
-
- candidate_file = (gchar *) file;
- sub_dir = g_strdup ("");
- while (candidate_file != NULL && fd < 0)
- {
- gchar *p;
-
- path = g_build_filename (data_dir, sub_dir,
- candidate_file, NULL);
-
- fd = g_open (path, O_RDONLY, 0);
-
- if (fd < 0)
- {
- g_free (path);
- path = NULL;
- }
-
- candidate_file = strchr (candidate_file, '-');
-
- if (candidate_file == NULL)
- break;
-
- candidate_file++;
-
- g_free (sub_dir);
- sub_dir = g_strndup (file, candidate_file - file - 1);
-
- for (p = sub_dir; *p != '\0'; p++)
- {
- if (*p == '-')
- *p = G_DIR_SEPARATOR;
- }
- }
- g_free (sub_dir);
- data_dirs++;
- }
-
- if (fd < 0)
- {
- g_set_error_literal (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_NOT_FOUND,
- _("Valid key file could not be "
- "found in search dirs"));
- }
-
- if (output_file != NULL && fd > 0)
- *output_file = g_strdup (path);
-
- g_free (path);
-
- return fd;
-}
-
-static gboolean
-g_key_file_load_from_fd (GKeyFile *key_file,
- gint fd,
- GKeyFileFlags flags,
- GError **error)
-{
- GError *key_file_error = NULL;
- gssize bytes_read;
- struct stat stat_buf;
- gchar read_buf[4096];
-
- if (fstat (fd, &stat_buf) < 0)
- {
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (errno),
- g_strerror (errno));
- return FALSE;
- }
-
- if (!S_ISREG (stat_buf.st_mode))
- {
- g_set_error_literal (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_PARSE,
- _("Not a regular file"));
- return FALSE;
- }
-
- if (stat_buf.st_size == 0)
- {
- g_set_error_literal (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_PARSE,
- _("File is empty"));
- return FALSE;
- }
-
- if (key_file->approximate_size > 0)
- {
- g_key_file_clear (key_file);
- g_key_file_init (key_file);
- }
- key_file->flags = flags;
-
- do
- {
- bytes_read = read (fd, read_buf, 4096);
-
- if (bytes_read == 0) /* End of File */
- break;
-
- if (bytes_read < 0)
- {
- if (errno == EINTR || errno == EAGAIN)
- continue;
-
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (errno),
- g_strerror (errno));
- return FALSE;
- }
-
- g_key_file_parse_data (key_file,
- read_buf, bytes_read,
- &key_file_error);
- }
- while (!key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- g_key_file_flush_parse_buffer (key_file, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * g_key_file_load_from_file:
- * @key_file: an empty #GKeyFile struct
- * @file: the path of a filename to load, in the GLib filename encoding
- * @flags: flags from #GKeyFileFlags
- * @error: return location for a #GError, or %NULL
- *
- * Loads a key file into an empty #GKeyFile structure.
- * If the file could not be loaded then %error is set to
- * either a #GFileError or #GKeyFileError.
- *
- * Return value: %TRUE if a key file could be loaded, %FALSE otherwise
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_load_from_file (GKeyFile *key_file,
- const gchar *file,
- GKeyFileFlags flags,
- GError **error)
-{
- GError *key_file_error = NULL;
- gint fd;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (file != NULL, FALSE);
-
- fd = g_open (file, O_RDONLY, 0);
-
- if (fd < 0)
- {
- g_set_error_literal (error, G_FILE_ERROR,
- g_file_error_from_errno (errno),
- g_strerror (errno));
- return FALSE;
- }
-
- g_key_file_load_from_fd (key_file, fd, flags, &key_file_error);
- close (fd);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * g_key_file_load_from_data:
- * @key_file: an empty #GKeyFile struct
- * @data: key file loaded in memory
- * @length: the length of @data in bytes
- * @flags: flags from #GKeyFileFlags
- * @error: return location for a #GError, or %NULL
- *
- * Loads a key file from memory into an empty #GKeyFile structure.
- * If the object cannot be created then %error is set to a #GKeyFileError.
- *
- * Return value: %TRUE if a key file could be loaded, %FALSE otherwise
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_load_from_data (GKeyFile *key_file,
- const gchar *data,
- gsize length,
- GKeyFileFlags flags,
- GError **error)
-{
- GError *key_file_error = NULL;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (data != NULL, FALSE);
- g_return_val_if_fail (length != 0, FALSE);
-
- if (length == (gsize)-1)
- length = strlen (data);
-
- if (key_file->approximate_size > 0)
- {
- g_key_file_clear (key_file);
- g_key_file_init (key_file);
- }
- key_file->flags = flags;
-
- g_key_file_parse_data (key_file, data, length, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- g_key_file_flush_parse_buffer (key_file, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * g_key_file_load_from_dirs:
- * @key_file: an empty #GKeyFile struct
- * @file: a relative path to a filename to open and parse
- * @search_dirs: %NULL-terminated array of directories to search
- * @full_path: return location for a string containing the full path
- * of the file, or %NULL
- * @flags: flags from #GKeyFileFlags
- * @error: return location for a #GError, or %NULL
- *
- * This function looks for a key file named @file in the paths
- * specified in @search_dirs, loads the file into @key_file and
- * returns the file's full path in @full_path. If the file could not
- * be loaded then an %error is set to either a #GFileError or
- * #GKeyFileError.
- *
- * Return value: %TRUE if a key file could be loaded, %FALSE otherwise
- *
- * Since: 2.14
- **/
-gboolean
-g_key_file_load_from_dirs (GKeyFile *key_file,
- const gchar *file,
- const gchar **search_dirs,
- gchar **full_path,
- GKeyFileFlags flags,
- GError **error)
-{
- GError *key_file_error = NULL;
- const gchar **data_dirs;
- gchar *output_path;
- gint fd;
- gboolean found_file;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
- g_return_val_if_fail (search_dirs != NULL, FALSE);
-
- found_file = FALSE;
- data_dirs = search_dirs;
- output_path = NULL;
- while (*data_dirs != NULL && !found_file)
- {
- g_free (output_path);
-
- fd = find_file_in_data_dirs (file, data_dirs, &output_path,
- &key_file_error);
-
- if (fd < 0)
- {
- if (key_file_error)
- g_propagate_error (error, key_file_error);
- break;
- }
-
- found_file = g_key_file_load_from_fd (key_file, fd, flags,
- &key_file_error);
- close (fd);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- break;
- }
- }
-
- if (found_file && full_path)
- *full_path = output_path;
- else
- g_free (output_path);
-
- return found_file;
-}
-
-/**
- * g_key_file_load_from_data_dirs:
- * @key_file: an empty #GKeyFile struct
- * @file: a relative path to a filename to open and parse
- * @full_path: return location for a string containing the full path
- * of the file, or %NULL
- * @flags: flags from #GKeyFileFlags
- * @error: return location for a #GError, or %NULL
- *
- * This function looks for a key file named @file in the paths
- * returned from g_get_user_data_dir() and g_get_system_data_dirs(),
- * loads the file into @key_file and returns the file's full path in
- * @full_path. If the file could not be loaded then an %error is
- * set to either a #GFileError or #GKeyFileError.
- *
- * Return value: %TRUE if a key file could be loaded, %FALSE othewise
- * Since: 2.6
- **/
-gboolean
-g_key_file_load_from_data_dirs (GKeyFile *key_file,
- const gchar *file,
- gchar **full_path,
- GKeyFileFlags flags,
- GError **error)
-{
- gchar **all_data_dirs;
- const gchar * user_data_dir;
- const gchar * const * system_data_dirs;
- gsize i, j;
- gboolean found_file;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
-
- user_data_dir = g_get_user_data_dir ();
- system_data_dirs = g_get_system_data_dirs ();
- all_data_dirs = g_new (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
-
- i = 0;
- all_data_dirs[i++] = g_strdup (user_data_dir);
-
- j = 0;
- while (system_data_dirs[j] != NULL)
- all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
- all_data_dirs[i] = NULL;
-
- found_file = g_key_file_load_from_dirs (key_file,
- file,
- (const gchar **)all_data_dirs,
- full_path,
- flags,
- error);
-
- g_strfreev (all_data_dirs);
-
- return found_file;
-}
-
-/**
- * g_key_file_free:
- * @key_file: a #GKeyFile
- *
- * Frees a #GKeyFile.
- *
- * Since: 2.6
- **/
-void
-g_key_file_free (GKeyFile *key_file)
-{
- g_return_if_fail (key_file != NULL);
-
- g_key_file_clear (key_file);
- g_slice_free (GKeyFile, key_file);
-}
-
-/* If G_KEY_FILE_KEEP_TRANSLATIONS is not set, only returns
- * true for locales that match those in g_get_language_names().
- */
-static gboolean
-g_key_file_locale_is_interesting (GKeyFile *key_file,
- const gchar *locale)
-{
- gsize i;
-
- if (key_file->flags & G_KEY_FILE_KEEP_TRANSLATIONS)
- return TRUE;
-
- for (i = 0; key_file->locales[i] != NULL; i++)
- {
- if (g_ascii_strcasecmp (key_file->locales[i], locale) == 0)
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-g_key_file_parse_line (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error)
-{
- GError *parse_error = NULL;
- gchar *line_start;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (line != NULL);
-
- line_start = (gchar *) line;
- while (g_ascii_isspace (*line_start))
- line_start++;
-
- if (g_key_file_line_is_comment (line_start))
- g_key_file_parse_comment (key_file, line, length, &parse_error);
- else if (g_key_file_line_is_group (line_start))
- g_key_file_parse_group (key_file, line_start,
- length - (line_start - line),
- &parse_error);
- else if (g_key_file_line_is_key_value_pair (line_start))
- g_key_file_parse_key_value_pair (key_file, line_start,
- length - (line_start - line),
- &parse_error);
- else
- {
- gchar *line_utf8 = _g_utf8_make_valid (line);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_PARSE,
- _("Key file contains line '%s' which is not "
- "a key-value pair, group, or comment"),
- line_utf8);
- g_free (line_utf8);
-
- return;
- }
-
- if (parse_error)
- g_propagate_error (error, parse_error);
-}
-
-static void
-g_key_file_parse_comment (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error)
-{
- GKeyFileKeyValuePair *pair;
-
- if (!(key_file->flags & G_KEY_FILE_KEEP_COMMENTS))
- return;
-
- g_warn_if_fail (key_file->current_group != NULL);
-
- pair = g_slice_new (GKeyFileKeyValuePair);
- pair->key = NULL;
- pair->value = g_strndup (line, length);
-
- key_file->current_group->key_value_pairs =
- g_list_prepend (key_file->current_group->key_value_pairs, pair);
-
- if (length == 0 || line[0] != '#')
- key_file->current_group->has_trailing_blank_line = TRUE;
-}
-
-static void
-g_key_file_parse_group (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error)
-{
- gchar *group_name;
- const gchar *group_name_start, *group_name_end;
-
- /* advance past opening '['
- */
- group_name_start = line + 1;
- group_name_end = line + length - 1;
-
- while (*group_name_end != ']')
- group_name_end--;
-
- group_name = g_strndup (group_name_start,
- group_name_end - group_name_start);
-
- if (!g_key_file_is_group_name (group_name))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_PARSE,
- _("Invalid group name: %s"), group_name);
- g_free (group_name);
- return;
- }
-
- g_key_file_add_group (key_file, group_name);
- g_free (group_name);
-}
-
-static void
-g_key_file_parse_key_value_pair (GKeyFile *key_file,
- const gchar *line,
- gsize length,
- GError **error)
-{
- gchar *key, *value, *key_end, *value_start, *locale;
- gsize key_len, value_len;
-
- if (key_file->current_group == NULL || key_file->current_group->name == NULL)
- {
- g_set_error_literal (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not start with a group"));
- return;
- }
-
- key_end = value_start = strchr (line, '=');
-
- g_warn_if_fail (key_end != NULL);
-
- key_end--;
- value_start++;
-
- /* Pull the key name from the line (chomping trailing whitespace)
- */
- while (g_ascii_isspace (*key_end))
- key_end--;
-
- key_len = key_end - line + 2;
-
- g_warn_if_fail (key_len <= length);
-
- key = g_strndup (line, key_len - 1);
-
- if (!g_key_file_is_key_name (key))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_PARSE,
- _("Invalid key name: %s"), key);
- g_free (key);
- return;
- }
-
- /* Pull the value from the line (chugging leading whitespace)
- */
- while (g_ascii_isspace (*value_start))
- value_start++;
-
- value_len = line + length - value_start + 1;
-
- value = g_strndup (value_start, value_len);
-
- g_warn_if_fail (key_file->start_group != NULL);
-
- if (key_file->current_group
- && key_file->current_group->name
- && strcmp (key_file->start_group->name,
- key_file->current_group->name) == 0
- && strcmp (key, "Encoding") == 0)
- {
- if (g_ascii_strcasecmp (value, "UTF-8") != 0)
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
- _("Key file contains unsupported "
- "encoding '%s'"), value_utf8);
- g_free (value_utf8);
-
- g_free (key);
- g_free (value);
- return;
- }
- }
-
- /* Is this key a translation? If so, is it one that we care about?
- */
- locale = key_get_locale (key);
-
- if (locale == NULL || g_key_file_locale_is_interesting (key_file, locale))
- g_key_file_add_key (key_file, key_file->current_group, key, value);
-
- g_free (locale);
- g_free (key);
- g_free (value);
-}
-
-static gchar *
-key_get_locale (const gchar *key)
-{
- gchar *locale;
-
- locale = g_strrstr (key, "[");
-
- if (locale && strlen (locale) <= 2)
- locale = NULL;
-
- if (locale)
- locale = g_strndup (locale + 1, strlen (locale) - 2);
-
- return locale;
-}
-
-static void
-g_key_file_parse_data (GKeyFile *key_file,
- const gchar *data,
- gsize length,
- GError **error)
-{
- GError *parse_error;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (data != NULL);
-
- parse_error = NULL;
-
- for (i = 0; i < length; i++)
- {
- if (data[i] == '\n')
- {
- if (i > 0 && data[i - 1] == '\r')
- g_string_erase (key_file->parse_buffer,
- key_file->parse_buffer->len - 1,
- 1);
-
- /* When a newline is encountered flush the parse buffer so that the
- * line can be parsed. Note that completely blank lines won't show
- * up in the parse buffer, so they get parsed directly.
- */
- if (key_file->parse_buffer->len > 0)
- g_key_file_flush_parse_buffer (key_file, &parse_error);
- else
- g_key_file_parse_comment (key_file, "", 1, &parse_error);
-
- if (parse_error)
- {
- g_propagate_error (error, parse_error);
- return;
- }
- }
- else
- g_string_append_c (key_file->parse_buffer, data[i]);
- }
-
- key_file->approximate_size += length;
-}
-
-static void
-g_key_file_flush_parse_buffer (GKeyFile *key_file,
- GError **error)
-{
- GError *file_error = NULL;
-
- g_return_if_fail (key_file != NULL);
-
- file_error = NULL;
-
- if (key_file->parse_buffer->len > 0)
- {
- g_key_file_parse_line (key_file, key_file->parse_buffer->str,
- key_file->parse_buffer->len,
- &file_error);
- g_string_erase (key_file->parse_buffer, 0, -1);
-
- if (file_error)
- {
- g_propagate_error (error, file_error);
- return;
- }
- }
-}
-
-/**
- * g_key_file_to_data:
- * @key_file: a #GKeyFile
- * @length: return location for the length of the
- * returned string, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * This function outputs @key_file as a string.
- *
- * Note that this function never reports an error,
- * so it is safe to pass %NULL as @error.
- *
- * Return value: a newly allocated string holding
- * the contents of the #GKeyFile
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_to_data (GKeyFile *key_file,
- gsize *length,
- GError **error)
-{
- GString *data_string;
- GList *group_node, *key_file_node;
- gboolean has_blank_line = TRUE;
-
- g_return_val_if_fail (key_file != NULL, NULL);
-
- data_string = g_string_sized_new (2 * key_file->approximate_size);
-
- for (group_node = g_list_last (key_file->groups);
- group_node != NULL;
- group_node = group_node->prev)
- {
- GKeyFileGroup *group;
-
- group = (GKeyFileGroup *) group_node->data;
-
- /* separate groups by at least an empty line */
- if (!has_blank_line)
- g_string_append_c (data_string, '\n');
- has_blank_line = group->has_trailing_blank_line;
-
- if (group->comment != NULL)
- g_string_append_printf (data_string, "%s\n", group->comment->value);
-
- if (group->name != NULL)
- g_string_append_printf (data_string, "[%s]\n", group->name);
-
- for (key_file_node = g_list_last (group->key_value_pairs);
- key_file_node != NULL;
- key_file_node = key_file_node->prev)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) key_file_node->data;
-
- if (pair->key != NULL)
- g_string_append_printf (data_string, "%s=%s\n", pair->key, pair->value);
- else
- g_string_append_printf (data_string, "%s\n", pair->value);
- }
- }
-
- if (length)
- *length = data_string->len;
-
- return g_string_free (data_string, FALSE);
-}
-
-/**
- * g_key_file_get_keys:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @length: return location for the number of keys returned, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Returns all keys for the group name @group_name. The array of
- * returned keys will be %NULL-terminated, so @length may
- * optionally be %NULL. In the event that the @group_name cannot
- * be found, %NULL is returned and @error is set to
- * #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
- *
- * Return value: a newly-allocated %NULL-terminated array of strings.
- * Use g_strfreev() to free it.
- *
- * Since: 2.6
- **/
-gchar **
-g_key_file_get_keys (GKeyFile *key_file,
- const gchar *group_name,
- gsize *length,
- GError **error)
-{
- GKeyFileGroup *group;
- GList *tmp;
- gchar **keys;
- gsize i, num_keys;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
-
- group = g_key_file_lookup_group (key_file, group_name);
-
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
- return NULL;
- }
-
- num_keys = 0;
- for (tmp = group->key_value_pairs; tmp; tmp = tmp->next)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (pair->key)
- num_keys++;
- }
-
- keys = g_new (gchar *, num_keys + 1);
-
- i = num_keys - 1;
- for (tmp = group->key_value_pairs; tmp; tmp = tmp->next)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (pair->key)
- {
- keys[i] = g_strdup (pair->key);
- i--;
- }
- }
-
- keys[num_keys] = NULL;
-
- if (length)
- *length = num_keys;
-
- return keys;
-}
-
-/**
- * g_key_file_get_start_group:
- * @key_file: a #GKeyFile
- *
- * Returns the name of the start group of the file.
- *
- * Return value: The start group of the key file.
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_get_start_group (GKeyFile *key_file)
-{
- g_return_val_if_fail (key_file != NULL, NULL);
-
- if (key_file->start_group)
- return g_strdup (key_file->start_group->name);
-
- return NULL;
-}
-
-/**
- * g_key_file_get_groups:
- * @key_file: a #GKeyFile
- * @length: return location for the number of returned groups, or %NULL
- *
- * Returns all groups in the key file loaded with @key_file.
- * The array of returned groups will be %NULL-terminated, so
- * @length may optionally be %NULL.
- *
- * Return value: a newly-allocated %NULL-terminated array of strings.
- * Use g_strfreev() to free it.
- * Since: 2.6
- **/
-gchar **
-g_key_file_get_groups (GKeyFile *key_file,
- gsize *length)
-{
- GList *group_node;
- gchar **groups;
- gsize i, num_groups;
-
- g_return_val_if_fail (key_file != NULL, NULL);
-
- num_groups = g_list_length (key_file->groups);
-
- g_return_val_if_fail (num_groups > 0, NULL);
-
- group_node = g_list_last (key_file->groups);
-
- g_return_val_if_fail (((GKeyFileGroup *) group_node->data)->name == NULL, NULL);
-
- /* Only need num_groups instead of num_groups + 1
- * because the first group of the file (last in the
- * list) is always the comment group at the top,
- * which we skip
- */
- groups = g_new (gchar *, num_groups);
-
-
- i = 0;
- for (group_node = group_node->prev;
- group_node != NULL;
- group_node = group_node->prev)
- {
- GKeyFileGroup *group;
-
- group = (GKeyFileGroup *) group_node->data;
-
- g_warn_if_fail (group->name != NULL);
-
- groups[i++] = g_strdup (group->name);
- }
- groups[i] = NULL;
-
- if (length)
- *length = i;
-
- return groups;
-}
-
-/**
- * g_key_file_get_value:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @error: return location for a #GError, or %NULL
- *
- * Returns the raw value associated with @key under @group_name.
- * Use g_key_file_get_string() to retrieve an unescaped UTF-8 string.
- *
- * In the event the key cannot be found, %NULL is returned and
- * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. In the
- * event that the @group_name cannot be found, %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
- *
- *
- * Return value: a newly allocated string or %NULL if the specified
- * key cannot be found.
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_get_value (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
- gchar *value = NULL;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- group = g_key_file_lookup_group (key_file, group_name);
-
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
- return NULL;
- }
-
- pair = g_key_file_lookup_key_value_pair (key_file, group, key);
-
- if (pair)
- value = g_strdup (pair->value);
- else
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_KEY_NOT_FOUND,
- _("Key file does not have key '%s'"), key);
-
- return value;
-}
-
-/**
- * g_key_file_set_value:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: a string
- *
- * Associates a new value with @key under @group_name.
- *
- * If @key cannot be found then it is created. If @group_name cannot
- * be found then it is created. To set an UTF-8 string which may contain
- * characters that need escaping (such as newlines or spaces), use
- * g_key_file_set_string().
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_value (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *value)
-{
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (g_key_file_is_group_name (group_name));
- g_return_if_fail (g_key_file_is_key_name (key));
- g_return_if_fail (value != NULL);
-
- group = g_key_file_lookup_group (key_file, group_name);
-
- if (!group)
- {
- g_key_file_add_group (key_file, group_name);
- group = (GKeyFileGroup *) key_file->groups->data;
-
- g_key_file_add_key (key_file, group, key, value);
- }
- else
- {
- pair = g_key_file_lookup_key_value_pair (key_file, group, key);
-
- if (!pair)
- g_key_file_add_key (key_file, group, key, value);
- else
- {
- g_free (pair->value);
- pair->value = g_strdup (value);
- }
- }
-}
-
-/**
- * g_key_file_get_string:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @error: return location for a #GError, or %NULL
- *
- * Returns the string value associated with @key under @group_name.
- * Unlike g_key_file_get_value(), this function handles escape sequences
- * like \s.
- *
- * In the event the key cannot be found, %NULL is returned and
- * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. In the
- * event that the @group_name cannot be found, %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
- *
- * Return value: a newly allocated string or %NULL if the specified
- * key cannot be found.
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_get_string (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- gchar *value, *string_value;
- GError *key_file_error;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- key_file_error = NULL;
-
- value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return NULL;
- }
-
- if (!g_utf8_validate (value, -1, NULL))
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
- _("Key file contains key '%s' with value '%s' "
- "which is not UTF-8"), key, value_utf8);
- g_free (value_utf8);
- g_free (value);
-
- return NULL;
- }
-
- string_value = g_key_file_parse_value_as_string (key_file, value, NULL,
- &key_file_error);
- g_free (value);
-
- if (key_file_error)
- {
- if (g_error_matches (key_file_error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains key '%s' "
- "which has value that cannot be interpreted."),
- key);
- g_error_free (key_file_error);
- }
- else
- g_propagate_error (error, key_file_error);
- }
-
- return string_value;
-}
-
-/**
- * g_key_file_set_string:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @string: a string
- *
- * Associates a new string value with @key under @group_name.
- * If @key cannot be found then it is created.
- * If @group_name cannot be found then it is created.
- * Unlike g_key_file_set_value(), this function handles characters
- * that need escaping, such as newlines.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_string (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *string)
-{
- gchar *value;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (string != NULL);
-
- value = g_key_file_parse_string_as_value (key_file, string, FALSE);
- g_key_file_set_value (key_file, group_name, key, value);
- g_free (value);
-}
-
-/**
- * g_key_file_get_string_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @length: return location for the number of returned strings, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Returns the values associated with @key under @group_name.
- *
- * In the event the key cannot be found, %NULL is returned and
- * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. In the
- * event that the @group_name cannot be found, %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
- *
- * Return value: a %NULL-terminated string array or %NULL if the specified
- * key cannot be found. The array should be freed with g_strfreev().
- *
- * Since: 2.6
- **/
-gchar **
-g_key_file_get_string_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gsize *length,
- GError **error)
-{
- GError *key_file_error = NULL;
- gchar *value, *string_value, **values;
- gint i, len;
- GSList *p, *pieces = NULL;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- if (length)
- *length = 0;
-
- value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return NULL;
- }
-
- if (!g_utf8_validate (value, -1, NULL))
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
- _("Key file contains key '%s' with value '%s' "
- "which is not UTF-8"), key, value_utf8);
- g_free (value_utf8);
- g_free (value);
-
- return NULL;
- }
-
- string_value = g_key_file_parse_value_as_string (key_file, value, &pieces, &key_file_error);
- g_free (value);
- g_free (string_value);
-
- if (key_file_error)
- {
- if (g_error_matches (key_file_error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains key '%s' "
- "which has a value that cannot be interpreted."),
- key);
- g_error_free (key_file_error);
- }
- else
- g_propagate_error (error, key_file_error);
-
- return NULL;
- }
-
- len = g_slist_length (pieces);
- values = g_new (gchar *, len + 1);
- for (p = pieces, i = 0; p; p = p->next)
- values[i++] = p->data;
- values[len] = NULL;
-
- g_slist_free (pieces);
-
- if (length)
- *length = len;
-
- return values;
-}
-
-/**
- * g_key_file_set_string_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @list: an array of string values
- * @length: number of string values in @list
- *
- * Associates a list of string values for @key under @group_name.
- * If @key cannot be found then it is created.
- * If @group_name cannot be found then it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_string_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar * const list[],
- gsize length)
-{
- GString *value_list;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (list != NULL || length == 0);
-
- value_list = g_string_sized_new (length * 128);
- for (i = 0; i < length && list[i] != NULL; i++)
- {
- gchar *value;
-
- value = g_key_file_parse_string_as_value (key_file, list[i], TRUE);
- g_string_append (value_list, value);
- g_string_append_c (value_list, key_file->list_separator);
-
- g_free (value);
- }
-
- g_key_file_set_value (key_file, group_name, key, value_list->str);
- g_string_free (value_list, TRUE);
-}
-
-/**
- * g_key_file_set_locale_string:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @locale: a locale identifier
- * @string: a string
- *
- * Associates a string value for @key and @locale under @group_name.
- * If the translation for @key cannot be found then it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_locale_string (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *locale,
- const gchar *string)
-{
- gchar *full_key, *value;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (key != NULL);
- g_return_if_fail (locale != NULL);
- g_return_if_fail (string != NULL);
-
- value = g_key_file_parse_string_as_value (key_file, string, FALSE);
- full_key = g_strdup_printf ("%s[%s]", key, locale);
- g_key_file_set_value (key_file, group_name, full_key, value);
- g_free (full_key);
- g_free (value);
-}
-
-extern GSList *_g_compute_locale_variants (const gchar *locale);
-
-/**
- * g_key_file_get_locale_string:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @locale: a locale identifier or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Returns the value associated with @key under @group_name
- * translated in the given @locale if available. If @locale is
- * %NULL then the current locale is assumed.
- *
- * If @key cannot be found then %NULL is returned and @error is set
- * to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. If the value associated
- * with @key cannot be interpreted or no suitable translation can
- * be found then the untranslated value is returned.
- *
- * Return value: a newly allocated string or %NULL if the specified
- * key cannot be found.
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_get_locale_string (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *locale,
- GError **error)
-{
- gchar *candidate_key, *translated_value;
- GError *key_file_error;
- gchar **languages;
- gboolean free_languages = FALSE;
- gint i;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- candidate_key = NULL;
- translated_value = NULL;
- key_file_error = NULL;
-
- if (locale)
- {
- GSList *l, *list;
-
- list = _g_compute_locale_variants (locale);
-
- languages = g_new (gchar *, g_slist_length (list) + 1);
- for (l = list, i = 0; l; l = l->next, i++)
- languages[i] = l->data;
- languages[i] = NULL;
-
- g_slist_free (list);
- free_languages = TRUE;
- }
- else
- {
- languages = (gchar **) g_get_language_names ();
- free_languages = FALSE;
- }
-
- for (i = 0; languages[i]; i++)
- {
- candidate_key = g_strdup_printf ("%s[%s]", key, languages[i]);
-
- translated_value = g_key_file_get_string (key_file,
- group_name,
- candidate_key, NULL);
- g_free (candidate_key);
-
- if (translated_value)
- break;
-
- g_free (translated_value);
- translated_value = NULL;
- }
-
- /* Fallback to untranslated key
- */
- if (!translated_value)
- {
- translated_value = g_key_file_get_string (key_file, group_name, key,
- &key_file_error);
-
- if (!translated_value)
- g_propagate_error (error, key_file_error);
- }
-
- if (free_languages)
- g_strfreev (languages);
-
- return translated_value;
-}
-
-/**
- * g_key_file_get_locale_string_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @locale: a locale identifier or %NULL
- * @length: return location for the number of returned strings or %NULL
- * @error: return location for a #GError or %NULL
- *
- * Returns the values associated with @key under @group_name
- * translated in the given @locale if available. If @locale is
- * %NULL then the current locale is assumed.
-
- * If @key cannot be found then %NULL is returned and @error is set
- * to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. If the values associated
- * with @key cannot be interpreted or no suitable translations
- * can be found then the untranslated values are returned. The
- * returned array is %NULL-terminated, so @length may optionally
- * be %NULL.
- *
- * Return value: a newly allocated %NULL-terminated string array
- * or %NULL if the key isn't found. The string array should be freed
- * with g_strfreev().
- *
- * Since: 2.6
- **/
-gchar **
-g_key_file_get_locale_string_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *locale,
- gsize *length,
- GError **error)
-{
- GError *key_file_error;
- gchar **values, *value;
- char list_separator[2];
- gsize len;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- key_file_error = NULL;
-
- value = g_key_file_get_locale_string (key_file, group_name,
- key, locale,
- &key_file_error);
-
- if (key_file_error)
- g_propagate_error (error, key_file_error);
-
- if (!value)
- {
- if (length)
- *length = 0;
- return NULL;
- }
-
- len = strlen (value);
- if (value[len - 1] == key_file->list_separator)
- value[len - 1] = '\0';
-
- list_separator[0] = key_file->list_separator;
- list_separator[1] = '\0';
- values = g_strsplit (value, list_separator, 0);
-
- g_free (value);
-
- if (length)
- *length = g_strv_length (values);
-
- return values;
-}
-
-/**
- * g_key_file_set_locale_string_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @locale: a locale identifier
- * @list: a %NULL-terminated array of locale string values
- * @length: the length of @list
- *
- * Associates a list of string values for @key and @locale under
- * @group_name. If the translation for @key cannot be found then
- * it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_locale_string_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *locale,
- const gchar * const list[],
- gsize length)
-{
- GString *value_list;
- gchar *full_key;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (key != NULL);
- g_return_if_fail (locale != NULL);
- g_return_if_fail (length != 0);
-
- value_list = g_string_sized_new (length * 128);
- for (i = 0; i < length && list[i] != NULL; i++)
- {
- gchar *value;
-
- value = g_key_file_parse_string_as_value (key_file, list[i], TRUE);
- g_string_append (value_list, value);
- g_string_append_c (value_list, key_file->list_separator);
-
- g_free (value);
- }
-
- full_key = g_strdup_printf ("%s[%s]", key, locale);
- g_key_file_set_value (key_file, group_name, full_key, value_list->str);
- g_free (full_key);
- g_string_free (value_list, TRUE);
-}
-
-/**
- * g_key_file_get_boolean:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @error: return location for a #GError
- *
- * Returns the value associated with @key under @group_name as a
- * boolean.
- *
- * If @key cannot be found then %FALSE is returned and @error is set
- * to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the value
- * associated with @key cannot be interpreted as a boolean then %FALSE
- * is returned and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the value associated with the key as a boolean,
- * or %FALSE if the key was not found or could not be parsed.
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_get_boolean (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GError *key_file_error = NULL;
- gchar *value;
- gboolean bool_value;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (group_name != NULL, FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
-
- value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
-
- if (!value)
- {
- g_propagate_error (error, key_file_error);
- return FALSE;
- }
-
- bool_value = g_key_file_parse_value_as_boolean (key_file, value,
- &key_file_error);
- g_free (value);
-
- if (key_file_error)
- {
- if (g_error_matches (key_file_error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains key '%s' "
- "which has value that cannot be interpreted."),
- key);
- g_error_free (key_file_error);
- }
- else
- g_propagate_error (error, key_file_error);
- }
-
- return bool_value;
-}
-
-/**
- * g_key_file_set_boolean:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: %TRUE or %FALSE
- *
- * Associates a new boolean value with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_boolean (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gboolean value)
-{
- gchar *result;
-
- g_return_if_fail (key_file != NULL);
-
- result = g_key_file_parse_boolean_as_value (key_file, value);
- g_key_file_set_value (key_file, group_name, key, result);
- g_free (result);
-}
-
-/**
- * g_key_file_get_boolean_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @length: the number of booleans returned
- * @error: return location for a #GError
- *
- * Returns the values associated with @key under @group_name as
- * booleans.
- *
- * If @key cannot be found then %NULL is returned and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the values associated
- * with @key cannot be interpreted as booleans then %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the values associated with the key as a list of
- * booleans, or %NULL if the key was not found or could not be parsed.
- *
- * Since: 2.6
- **/
-gboolean *
-g_key_file_get_boolean_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gsize *length,
- GError **error)
-{
- GError *key_file_error;
- gchar **values;
- gboolean *bool_values;
- gsize i, num_bools;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- if (length)
- *length = 0;
-
- key_file_error = NULL;
-
- values = g_key_file_get_string_list (key_file, group_name, key,
- &num_bools, &key_file_error);
-
- if (key_file_error)
- g_propagate_error (error, key_file_error);
-
- if (!values)
- return NULL;
-
- bool_values = g_new (gboolean, num_bools);
-
- for (i = 0; i < num_bools; i++)
- {
- bool_values[i] = g_key_file_parse_value_as_boolean (key_file,
- values[i],
- &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- g_strfreev (values);
- g_free (bool_values);
-
- return NULL;
- }
- }
- g_strfreev (values);
-
- if (length)
- *length = num_bools;
-
- return bool_values;
-}
-
-/**
- * g_key_file_set_boolean_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @list: an array of boolean values
- * @length: length of @list
- *
- * Associates a list of boolean values with @key under @group_name.
- * If @key cannot be found then it is created.
- * If @group_name is %NULL, the start_group is used.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_boolean_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gboolean list[],
- gsize length)
-{
- GString *value_list;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (list != NULL);
-
- value_list = g_string_sized_new (length * 8);
- for (i = 0; i < length; i++)
- {
- gchar *value;
-
- value = g_key_file_parse_boolean_as_value (key_file, list[i]);
-
- g_string_append (value_list, value);
- g_string_append_c (value_list, key_file->list_separator);
-
- g_free (value);
- }
-
- g_key_file_set_value (key_file, group_name, key, value_list->str);
- g_string_free (value_list, TRUE);
-}
-
-/**
- * g_key_file_get_integer:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @error: return location for a #GError
- *
- * Returns the value associated with @key under @group_name as an
- * integer.
- *
- * If @key cannot be found then 0 is returned and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the value associated
- * with @key cannot be interpreted as an integer then 0 is returned
- * and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the value associated with the key as an integer, or
- * 0 if the key was not found or could not be parsed.
- *
- * Since: 2.6
- **/
-gint
-g_key_file_get_integer (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GError *key_file_error;
- gchar *value;
- gint int_value;
-
- g_return_val_if_fail (key_file != NULL, -1);
- g_return_val_if_fail (group_name != NULL, -1);
- g_return_val_if_fail (key != NULL, -1);
-
- key_file_error = NULL;
-
- value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return 0;
- }
-
- int_value = g_key_file_parse_value_as_integer (key_file, value,
- &key_file_error);
- g_free (value);
-
- if (key_file_error)
- {
- if (g_error_matches (key_file_error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains key '%s' in group '%s' "
- "which has value that cannot be interpreted."), key,
- group_name);
- g_error_free (key_file_error);
- }
- else
- g_propagate_error (error, key_file_error);
- }
-
- return int_value;
-}
-
-/**
- * g_key_file_set_integer:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: an integer value
- *
- * Associates a new integer value with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_integer (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gint value)
-{
- gchar *result;
-
- g_return_if_fail (key_file != NULL);
-
- result = g_key_file_parse_integer_as_value (key_file, value);
- g_key_file_set_value (key_file, group_name, key, result);
- g_free (result);
-}
-
-/**
- * g_key_file_get_int64:
- * @key_file: a non-%NULL #GKeyFile
- * @group_name: a non-%NULL group name
- * @key: a non-%NULL key
- * @error: return location for a #GError
- *
- * Returns the value associated with @key under @group_name as a signed
- * 64-bit integer. This is similar to g_key_file_get_integer() but can return
- * 64-bit results without truncation.
- *
- * Returns: the value associated with the key as a signed 64-bit integer, or
- * 0 if the key was not found or could not be parsed.
- *
- * Since: 2.26
- */
-gint64
-g_key_file_get_int64 (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- gchar *s, *end;
- gint64 v;
-
- g_return_val_if_fail (key_file != NULL, -1);
- g_return_val_if_fail (group_name != NULL, -1);
- g_return_val_if_fail (key != NULL, -1);
-
- s = g_key_file_get_value (key_file, group_name, key, error);
-
- if (s == NULL)
- return 0;
-
- v = g_ascii_strtoll (s, &end, 10);
-
- if (*s == '\0' || *end != '\0')
- {
- g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
- "Key '%s' in group '%s' has value '%s' where int64 was expected",
- key, group_name, s);
- return 0;
- }
-
- g_free (s);
- return v;
-}
-
-/**
- * g_key_file_set_int64:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: an integer value
- *
- * Associates a new integer value with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.26
- **/
-void
-g_key_file_set_int64 (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gint64 value)
-{
- gchar *result;
-
- g_return_if_fail (key_file != NULL);
-
- result = g_strdup_printf ("%" G_GINT64_FORMAT, value);
- g_key_file_set_value (key_file, group_name, key, result);
- g_free (result);
-}
-
-/**
- * g_key_file_get_uint64:
- * @key_file: a non-%NULL #GKeyFile
- * @group_name: a non-%NULL group name
- * @key: a non-%NULL key
- * @error: return location for a #GError
- *
- * Returns the value associated with @key under @group_name as an unsigned
- * 64-bit integer. This is similar to g_key_file_get_integer() but can return
- * large positive results without truncation.
- *
- * Returns: the value associated with the key as an unsigned 64-bit integer,
- * or 0 if the key was not found or could not be parsed.
- *
- * Since: 2.26
- */
-guint64
-g_key_file_get_uint64 (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- gchar *s, *end;
- guint64 v;
-
- g_return_val_if_fail (key_file != NULL, -1);
- g_return_val_if_fail (group_name != NULL, -1);
- g_return_val_if_fail (key != NULL, -1);
-
- s = g_key_file_get_value (key_file, group_name, key, error);
-
- if (s == NULL)
- return 0;
-
- v = g_ascii_strtoull (s, &end, 10);
-
- if (*s == '\0' || *end != '\0')
- {
- g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
- "Key '%s' in group '%s' has value '%s' where uint64 was expected",
- key, group_name, s);
- return 0;
- }
-
- g_free (s);
- return v;
-}
-
-/**
- * g_key_file_set_uint64:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: an integer value
- *
- * Associates a new integer value with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.26
- **/
-void
-g_key_file_set_uint64 (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- guint64 value)
-{
- gchar *result;
-
- g_return_if_fail (key_file != NULL);
-
- result = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
- g_key_file_set_value (key_file, group_name, key, result);
- g_free (result);
-}
-
-/**
- * g_key_file_get_integer_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @length: the number of integers returned
- * @error: return location for a #GError
- *
- * Returns the values associated with @key under @group_name as
- * integers.
- *
- * If @key cannot be found then %NULL is returned and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the values associated
- * with @key cannot be interpreted as integers then %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the values associated with the key as a list of
- * integers, or %NULL if the key was not found or could not be parsed.
- *
- * Since: 2.6
- **/
-gint *
-g_key_file_get_integer_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gsize *length,
- GError **error)
-{
- GError *key_file_error = NULL;
- gchar **values;
- gint *int_values;
- gsize i, num_ints;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- if (length)
- *length = 0;
-
- values = g_key_file_get_string_list (key_file, group_name, key,
- &num_ints, &key_file_error);
-
- if (key_file_error)
- g_propagate_error (error, key_file_error);
-
- if (!values)
- return NULL;
-
- int_values = g_new (gint, num_ints);
-
- for (i = 0; i < num_ints; i++)
- {
- int_values[i] = g_key_file_parse_value_as_integer (key_file,
- values[i],
- &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- g_strfreev (values);
- g_free (int_values);
-
- return NULL;
- }
- }
- g_strfreev (values);
-
- if (length)
- *length = num_ints;
-
- return int_values;
-}
-
-/**
- * g_key_file_set_integer_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @list: an array of integer values
- * @length: number of integer values in @list
- *
- * Associates a list of integer values with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.6
- **/
-void
-g_key_file_set_integer_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gint list[],
- gsize length)
-{
- GString *values;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (list != NULL);
-
- values = g_string_sized_new (length * 16);
- for (i = 0; i < length; i++)
- {
- gchar *value;
-
- value = g_key_file_parse_integer_as_value (key_file, list[i]);
-
- g_string_append (values, value);
- g_string_append_c (values, key_file->list_separator);
-
- g_free (value);
- }
-
- g_key_file_set_value (key_file, group_name, key, values->str);
- g_string_free (values, TRUE);
-}
-
-/**
- * g_key_file_get_double:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @error: return location for a #GError
- *
- * Returns the value associated with @key under @group_name as a
- * double. If @group_name is %NULL, the start_group is used.
- *
- * If @key cannot be found then 0.0 is returned and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the value associated
- * with @key cannot be interpreted as a double then 0.0 is returned
- * and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the value associated with the key as a double, or
- * 0.0 if the key was not found or could not be parsed.
- *
- * Since: 2.12
- **/
-gdouble
-g_key_file_get_double (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GError *key_file_error;
- gchar *value;
- gdouble double_value;
-
- g_return_val_if_fail (key_file != NULL, -1);
- g_return_val_if_fail (group_name != NULL, -1);
- g_return_val_if_fail (key != NULL, -1);
-
- key_file_error = NULL;
-
- value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- return 0;
- }
-
- double_value = g_key_file_parse_value_as_double (key_file, value,
- &key_file_error);
- g_free (value);
-
- if (key_file_error)
- {
- if (g_error_matches (key_file_error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE))
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains key '%s' in group '%s' "
- "which has value that cannot be interpreted."), key,
- group_name);
- g_error_free (key_file_error);
- }
- else
- g_propagate_error (error, key_file_error);
- }
-
- return double_value;
-}
-
-/**
- * g_key_file_set_double:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @value: an double value
- *
- * Associates a new double value with @key under @group_name.
- * If @key cannot be found then it is created.
- *
- * Since: 2.12
- **/
-void
-g_key_file_set_double (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gdouble value)
-{
- gchar result[G_ASCII_DTOSTR_BUF_SIZE];
-
- g_return_if_fail (key_file != NULL);
-
- g_ascii_dtostr (result, sizeof (result), value);
- g_key_file_set_value (key_file, group_name, key, result);
-}
-
-/**
- * g_key_file_get_double_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @length: the number of doubles returned
- * @error: return location for a #GError
- *
- * Returns the values associated with @key under @group_name as
- * doubles.
- *
- * If @key cannot be found then %NULL is returned and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the values associated
- * with @key cannot be interpreted as doubles then %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE.
- *
- * Return value: the values associated with the key as a list of
- * doubles, or %NULL if the key was not found or could not be parsed.
- *
- * Since: 2.12
- **/
-gdouble *
-g_key_file_get_double_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gsize *length,
- GError **error)
-{
- GError *key_file_error = NULL;
- gchar **values;
- gdouble *double_values;
- gsize i, num_doubles;
-
- g_return_val_if_fail (key_file != NULL, NULL);
- g_return_val_if_fail (group_name != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- if (length)
- *length = 0;
-
- values = g_key_file_get_string_list (key_file, group_name, key,
- &num_doubles, &key_file_error);
-
- if (key_file_error)
- g_propagate_error (error, key_file_error);
-
- if (!values)
- return NULL;
-
- double_values = g_new (gdouble, num_doubles);
-
- for (i = 0; i < num_doubles; i++)
- {
- double_values[i] = g_key_file_parse_value_as_double (key_file,
- values[i],
- &key_file_error);
-
- if (key_file_error)
- {
- g_propagate_error (error, key_file_error);
- g_strfreev (values);
- g_free (double_values);
-
- return NULL;
- }
- }
- g_strfreev (values);
-
- if (length)
- *length = num_doubles;
-
- return double_values;
-}
-
-/**
- * g_key_file_set_double_list:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key
- * @list: an array of double values
- * @length: number of double values in @list
- *
- * Associates a list of double values with @key under
- * @group_name. If @key cannot be found then it is created.
- *
- * Since: 2.12
- **/
-void
-g_key_file_set_double_list (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- gdouble list[],
- gsize length)
-{
- GString *values;
- gsize i;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (list != NULL);
-
- values = g_string_sized_new (length * 16);
- for (i = 0; i < length; i++)
- {
- gchar result[G_ASCII_DTOSTR_BUF_SIZE];
-
- g_ascii_dtostr( result, sizeof (result), list[i] );
-
- g_string_append (values, result);
- g_string_append_c (values, key_file->list_separator);
- }
-
- g_key_file_set_value (key_file, group_name, key, values->str);
- g_string_free (values, TRUE);
-}
-
-static gboolean
-g_key_file_set_key_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *comment,
- GError **error)
-{
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
- GList *key_node, *comment_node, *tmp;
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
-
- return FALSE;
- }
-
- /* First find the key the comments are supposed to be
- * associated with
- */
- key_node = g_key_file_lookup_key_value_pair_node (key_file, group, key);
-
- if (key_node == NULL)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_KEY_NOT_FOUND,
- _("Key file does not have key '%s' in group '%s'"),
- key, group->name);
- return FALSE;
- }
-
- /* Then find all the comments already associated with the
- * key and free them
- */
- tmp = key_node->next;
- while (tmp != NULL)
- {
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (pair->key != NULL)
- break;
-
- comment_node = tmp;
- tmp = tmp->next;
- g_key_file_remove_key_value_pair_node (key_file, group,
- comment_node);
- }
-
- if (comment == NULL)
- return TRUE;
-
- /* Now we can add our new comment
- */
- pair = g_slice_new (GKeyFileKeyValuePair);
- pair->key = NULL;
- pair->value = g_key_file_parse_comment_as_value (key_file, comment);
-
- key_node = g_list_insert (key_node, pair, 1);
-
- return TRUE;
-}
-
-static gboolean
-g_key_file_set_group_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *comment,
- GError **error)
-{
- GKeyFileGroup *group;
-
- g_return_val_if_fail (g_key_file_is_group_name (group_name), FALSE);
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
-
- return FALSE;
- }
-
- /* First remove any existing comment
- */
- if (group->comment)
- {
- g_key_file_key_value_pair_free (group->comment);
- group->comment = NULL;
- }
-
- if (comment == NULL)
- return TRUE;
-
- /* Now we can add our new comment
- */
- group->comment = g_slice_new (GKeyFileKeyValuePair);
- group->comment->key = NULL;
- group->comment->value = g_key_file_parse_comment_as_value (key_file, comment);
-
- return TRUE;
-}
-
-static gboolean
-g_key_file_set_top_comment (GKeyFile *key_file,
- const gchar *comment,
- GError **error)
-{
- GList *group_node;
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
-
- /* The last group in the list should be the top (comments only)
- * group in the file
- */
- g_warn_if_fail (key_file->groups != NULL);
- group_node = g_list_last (key_file->groups);
- group = (GKeyFileGroup *) group_node->data;
- g_warn_if_fail (group->name == NULL);
-
- /* Note all keys must be comments at the top of
- * the file, so we can just free it all.
- */
- if (group->key_value_pairs != NULL)
- {
- g_list_foreach (group->key_value_pairs,
- (GFunc) g_key_file_key_value_pair_free,
- NULL);
- g_list_free (group->key_value_pairs);
- group->key_value_pairs = NULL;
- }
-
- if (comment == NULL)
- return TRUE;
-
- pair = g_slice_new (GKeyFileKeyValuePair);
- pair->key = NULL;
- pair->value = g_key_file_parse_comment_as_value (key_file, comment);
-
- group->key_value_pairs =
- g_list_prepend (group->key_value_pairs, pair);
-
- return TRUE;
-}
-
-/**
- * g_key_file_set_comment:
- * @key_file: a #GKeyFile
- * @group_name: a group name, or %NULL
- * @key: a key
- * @comment: a comment
- * @error: return location for a #GError
- *
- * Places a comment above @key from @group_name.
- * If @key is %NULL then @comment will be written above @group_name.
- * If both @key and @group_name are %NULL, then @comment will be
- * written above the first group in the file.
- *
- * Returns: %TRUE if the comment was written, %FALSE otherwise
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_set_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- const gchar *comment,
- GError **error)
-{
- g_return_val_if_fail (key_file != NULL, FALSE);
-
- if (group_name != NULL && key != NULL)
- {
- if (!g_key_file_set_key_comment (key_file, group_name, key, comment, error))
- return FALSE;
- }
- else if (group_name != NULL)
- {
- if (!g_key_file_set_group_comment (key_file, group_name, comment, error))
- return FALSE;
- }
- else
- {
- if (!g_key_file_set_top_comment (key_file, comment, error))
- return FALSE;
- }
-
- if (comment != NULL)
- key_file->approximate_size += strlen (comment);
-
- return TRUE;
-}
-
-static gchar *
-g_key_file_get_key_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
- GList *key_node, *tmp;
- GString *string;
- gchar *comment;
-
- g_return_val_if_fail (g_key_file_is_group_name (group_name), NULL);
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
-
- return NULL;
- }
-
- /* First find the key the comments are supposed to be
- * associated with
- */
- key_node = g_key_file_lookup_key_value_pair_node (key_file, group, key);
-
- if (key_node == NULL)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_KEY_NOT_FOUND,
- _("Key file does not have key '%s' in group '%s'"),
- key, group->name);
- return NULL;
- }
-
- string = NULL;
-
- /* Then find all the comments already associated with the
- * key and concatentate them.
- */
- tmp = key_node->next;
- if (!key_node->next)
- return NULL;
-
- pair = (GKeyFileKeyValuePair *) tmp->data;
- if (pair->key != NULL)
- return NULL;
-
- while (tmp->next)
- {
- pair = (GKeyFileKeyValuePair *) tmp->next->data;
-
- if (pair->key != NULL)
- break;
-
- tmp = tmp->next;
- }
-
- while (tmp != key_node)
- {
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (string == NULL)
- string = g_string_sized_new (512);
-
- comment = g_key_file_parse_value_as_comment (key_file, pair->value);
- g_string_append (string, comment);
- g_free (comment);
-
- tmp = tmp->prev;
- }
-
- if (string != NULL)
- {
- comment = string->str;
- g_string_free (string, FALSE);
- }
- else
- comment = NULL;
-
- return comment;
-}
-
-static gchar *
-get_group_comment (GKeyFile *key_file,
- GKeyFileGroup *group,
- GError **error)
-{
- GString *string;
- GList *tmp;
- gchar *comment;
-
- string = NULL;
-
- tmp = group->key_value_pairs;
- while (tmp)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (pair->key != NULL)
- {
- tmp = tmp->prev;
- break;
- }
-
- if (tmp->next == NULL)
- break;
-
- tmp = tmp->next;
- }
-
- while (tmp != NULL)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) tmp->data;
-
- if (string == NULL)
- string = g_string_sized_new (512);
-
- comment = g_key_file_parse_value_as_comment (key_file, pair->value);
- g_string_append (string, comment);
- g_free (comment);
-
- tmp = tmp->prev;
- }
-
- if (string != NULL)
- return g_string_free (string, FALSE);
-
- return NULL;
-}
-
-static gchar *
-g_key_file_get_group_comment (GKeyFile *key_file,
- const gchar *group_name,
- GError **error)
-{
- GList *group_node;
- GKeyFileGroup *group;
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
-
- return NULL;
- }
-
- if (group->comment)
- return g_strdup (group->comment->value);
-
- group_node = g_key_file_lookup_group_node (key_file, group_name);
- group_node = group_node->next;
- group = (GKeyFileGroup *)group_node->data;
- return get_group_comment (key_file, group, error);
-}
-
-static gchar *
-g_key_file_get_top_comment (GKeyFile *key_file,
- GError **error)
-{
- GList *group_node;
- GKeyFileGroup *group;
-
- /* The last group in the list should be the top (comments only)
- * group in the file
- */
- g_warn_if_fail (key_file->groups != NULL);
- group_node = g_list_last (key_file->groups);
- group = (GKeyFileGroup *) group_node->data;
- g_warn_if_fail (group->name == NULL);
-
- return get_group_comment (key_file, group, error);
-}
-
-/**
- * g_key_file_get_comment:
- * @key_file: a #GKeyFile
- * @group_name: a group name, or %NULL
- * @key: a key
- * @error: return location for a #GError
- *
- * Retrieves a comment above @key from @group_name.
- * If @key is %NULL then @comment will be read from above
- * @group_name. If both @key and @group_name are %NULL, then
- * @comment will be read from above the first group in the file.
- *
- * Returns: a comment that should be freed with g_free()
- *
- * Since: 2.6
- **/
-gchar *
-g_key_file_get_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- g_return_val_if_fail (key_file != NULL, NULL);
-
- if (group_name != NULL && key != NULL)
- return g_key_file_get_key_comment (key_file, group_name, key, error);
- else if (group_name != NULL)
- return g_key_file_get_group_comment (key_file, group_name, error);
- else
- return g_key_file_get_top_comment (key_file, error);
-}
-
-/**
- * g_key_file_remove_comment:
- * @key_file: a #GKeyFile
- * @group_name: a group name, or %NULL
- * @key: a key
- * @error: return location for a #GError
- *
- * Removes a comment above @key from @group_name.
- * If @key is %NULL then @comment will be removed above @group_name.
- * If both @key and @group_name are %NULL, then @comment will
- * be removed above the first group in the file.
- *
- * Returns: %TRUE if the comment was removed, %FALSE otherwise
- *
- * Since: 2.6
- **/
-
-gboolean
-g_key_file_remove_comment (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- g_return_val_if_fail (key_file != NULL, FALSE);
-
- if (group_name != NULL && key != NULL)
- return g_key_file_set_key_comment (key_file, group_name, key, NULL, error);
- else if (group_name != NULL)
- return g_key_file_set_group_comment (key_file, group_name, NULL, error);
- else
- return g_key_file_set_top_comment (key_file, NULL, error);
-}
-
-/**
- * g_key_file_has_group:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- *
- * Looks whether the key file has the group @group_name.
- *
- * Return value: %TRUE if @group_name is a part of @key_file, %FALSE
- * otherwise.
- * Since: 2.6
- **/
-gboolean
-g_key_file_has_group (GKeyFile *key_file,
- const gchar *group_name)
-{
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (group_name != NULL, FALSE);
-
- return g_key_file_lookup_group (key_file, group_name) != NULL;
-}
-
-/**
- * g_key_file_has_key:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key name
- * @error: return location for a #GError
- *
- * Looks whether the key file has the key @key in the group
- * @group_name.
- *
- * Return value: %TRUE if @key is a part of @group_name, %FALSE
- * otherwise.
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_has_key (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GKeyFileKeyValuePair *pair;
- GKeyFileGroup *group;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (group_name != NULL, FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
-
- group = g_key_file_lookup_group (key_file, group_name);
-
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
-
- return FALSE;
- }
-
- pair = g_key_file_lookup_key_value_pair (key_file, group, key);
-
- return pair != NULL;
-}
-
-static void
-g_key_file_add_group (GKeyFile *key_file,
- const gchar *group_name)
-{
- GKeyFileGroup *group;
-
- g_return_if_fail (key_file != NULL);
- g_return_if_fail (g_key_file_is_group_name (group_name));
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (group != NULL)
- {
- key_file->current_group = group;
- return;
- }
-
- group = g_slice_new0 (GKeyFileGroup);
- group->name = g_strdup (group_name);
- group->lookup_map = g_hash_table_new (g_str_hash, g_str_equal);
- key_file->groups = g_list_prepend (key_file->groups, group);
- key_file->approximate_size += strlen (group_name) + 3;
- key_file->current_group = group;
-
- if (key_file->start_group == NULL)
- key_file->start_group = group;
-
- g_hash_table_insert (key_file->group_hash, (gpointer)group->name, group);
-}
-
-static void
-g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair)
-{
- if (pair != NULL)
- {
- g_free (pair->key);
- g_free (pair->value);
- g_slice_free (GKeyFileKeyValuePair, pair);
- }
-}
-
-/* Be careful not to call this function on a node with data in the
- * lookup map without removing it from the lookup map, first.
- *
- * Some current cases where this warning is not a concern are
- * when:
- * - the node being removed is a comment node
- * - the entire lookup map is getting destroyed soon after
- * anyway.
- */
-static void
-g_key_file_remove_key_value_pair_node (GKeyFile *key_file,
- GKeyFileGroup *group,
- GList *pair_node)
-{
-
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) pair_node->data;
-
- group->key_value_pairs = g_list_remove_link (group->key_value_pairs, pair_node);
-
- if (pair->key != NULL)
- key_file->approximate_size -= strlen (pair->key) + 1;
-
- g_warn_if_fail (pair->value != NULL);
- key_file->approximate_size -= strlen (pair->value);
-
- g_key_file_key_value_pair_free (pair);
-
- g_list_free_1 (pair_node);
-}
-
-static void
-g_key_file_remove_group_node (GKeyFile *key_file,
- GList *group_node)
-{
- GKeyFileGroup *group;
- GList *tmp;
-
- group = (GKeyFileGroup *) group_node->data;
-
- if (group->name)
- g_hash_table_remove (key_file->group_hash, group->name);
-
- /* If the current group gets deleted make the current group the last
- * added group.
- */
- if (key_file->current_group == group)
- {
- /* groups should always contain at least the top comment group,
- * unless g_key_file_clear has been called
- */
- if (key_file->groups)
- key_file->current_group = (GKeyFileGroup *) key_file->groups->data;
- else
- key_file->current_group = NULL;
- }
-
- /* If the start group gets deleted make the start group the first
- * added group.
- */
- if (key_file->start_group == group)
- {
- tmp = g_list_last (key_file->groups);
- while (tmp != NULL)
- {
- if (tmp != group_node &&
- ((GKeyFileGroup *) tmp->data)->name != NULL)
- break;
-
- tmp = tmp->prev;
- }
-
- if (tmp)
- key_file->start_group = (GKeyFileGroup *) tmp->data;
- else
- key_file->start_group = NULL;
- }
-
- key_file->groups = g_list_remove_link (key_file->groups, group_node);
-
- if (group->name != NULL)
- key_file->approximate_size -= strlen (group->name) + 3;
-
- tmp = group->key_value_pairs;
- while (tmp != NULL)
- {
- GList *pair_node;
-
- pair_node = tmp;
- tmp = tmp->next;
- g_key_file_remove_key_value_pair_node (key_file, group, pair_node);
- }
-
- g_warn_if_fail (group->key_value_pairs == NULL);
-
- if (group->lookup_map)
- {
- g_hash_table_destroy (group->lookup_map);
- group->lookup_map = NULL;
- }
-
- g_free ((gchar *) group->name);
- g_slice_free (GKeyFileGroup, group);
- g_list_free_1 (group_node);
-}
-
-/**
- * g_key_file_remove_group:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @error: return location for a #GError or %NULL
- *
- * Removes the specified group, @group_name,
- * from the key file.
- *
- * Returns: %TRUE if the group was removed, %FALSE otherwise
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_remove_group (GKeyFile *key_file,
- const gchar *group_name,
- GError **error)
-{
- GList *group_node;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (group_name != NULL, FALSE);
-
- group_node = g_key_file_lookup_group_node (key_file, group_name);
-
- if (!group_node)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name);
- return FALSE;
- }
-
- g_key_file_remove_group_node (key_file, group_node);
-
- return TRUE;
-}
-
-static void
-g_key_file_add_key (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key,
- const gchar *value)
-{
- GKeyFileKeyValuePair *pair;
-
- pair = g_slice_new (GKeyFileKeyValuePair);
- pair->key = g_strdup (key);
- pair->value = g_strdup (value);
-
- g_hash_table_replace (group->lookup_map, pair->key, pair);
- group->key_value_pairs = g_list_prepend (group->key_value_pairs, pair);
- group->has_trailing_blank_line = FALSE;
- key_file->approximate_size += strlen (key) + strlen (value) + 2;
-}
-
-/**
- * g_key_file_remove_key:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- * @key: a key name to remove
- * @error: return location for a #GError or %NULL
- *
- * Removes @key in @group_name from the key file.
- *
- * Returns: %TRUE if the key was removed, %FALSE otherwise
- *
- * Since: 2.6
- **/
-gboolean
-g_key_file_remove_key (GKeyFile *key_file,
- const gchar *group_name,
- const gchar *key,
- GError **error)
-{
- GKeyFileGroup *group;
- GKeyFileKeyValuePair *pair;
-
- g_return_val_if_fail (key_file != NULL, FALSE);
- g_return_val_if_fail (group_name != NULL, FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
-
- pair = NULL;
-
- group = g_key_file_lookup_group (key_file, group_name);
- if (!group)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
- _("Key file does not have group '%s'"),
- group_name ? group_name : "(null)");
- return FALSE;
- }
-
- pair = g_key_file_lookup_key_value_pair (key_file, group, key);
-
- if (!pair)
- {
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_KEY_NOT_FOUND,
- _("Key file does not have key '%s' in group '%s'"),
- key, group->name);
- return FALSE;
- }
-
- key_file->approximate_size -= strlen (pair->key) + strlen (pair->value) + 2;
-
- group->key_value_pairs = g_list_remove (group->key_value_pairs, pair);
- g_hash_table_remove (group->lookup_map, pair->key);
- g_key_file_key_value_pair_free (pair);
-
- return TRUE;
-}
-
-static GList *
-g_key_file_lookup_group_node (GKeyFile *key_file,
- const gchar *group_name)
-{
- GKeyFileGroup *group;
- GList *tmp;
-
- for (tmp = key_file->groups; tmp != NULL; tmp = tmp->next)
- {
- group = (GKeyFileGroup *) tmp->data;
-
- if (group && group->name && strcmp (group->name, group_name) == 0)
- break;
- }
-
- return tmp;
-}
-
-static GKeyFileGroup *
-g_key_file_lookup_group (GKeyFile *key_file,
- const gchar *group_name)
-{
- return (GKeyFileGroup *)g_hash_table_lookup (key_file->group_hash, group_name);
-}
-
-static GList *
-g_key_file_lookup_key_value_pair_node (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key)
-{
- GList *key_node;
-
- for (key_node = group->key_value_pairs;
- key_node != NULL;
- key_node = key_node->next)
- {
- GKeyFileKeyValuePair *pair;
-
- pair = (GKeyFileKeyValuePair *) key_node->data;
-
- if (pair->key && strcmp (pair->key, key) == 0)
- break;
- }
-
- return key_node;
-}
-
-static GKeyFileKeyValuePair *
-g_key_file_lookup_key_value_pair (GKeyFile *key_file,
- GKeyFileGroup *group,
- const gchar *key)
-{
- return (GKeyFileKeyValuePair *) g_hash_table_lookup (group->lookup_map, key);
-}
-
-/* Lines starting with # or consisting entirely of whitespace are merely
- * recorded, not parsed. This function assumes all leading whitespace
- * has been stripped.
- */
-static gboolean
-g_key_file_line_is_comment (const gchar *line)
-{
- return (*line == '#' || *line == '\0' || *line == '\n');
-}
-
-static gboolean
-g_key_file_is_group_name (const gchar *name)
-{
- gchar *p, *q;
-
- if (name == NULL)
- return FALSE;
-
- p = q = (gchar *) name;
- while (*q && *q != ']' && *q != '[' && !g_ascii_iscntrl (*q))
- q = g_utf8_find_next_char (q, NULL);
-
- if (*q != '\0' || q == p)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-g_key_file_is_key_name (const gchar *name)
-{
- gchar *p, *q;
-
- if (name == NULL)
- return FALSE;
-
- p = q = (gchar *) name;
- /* We accept a little more than the desktop entry spec says,
- * since gnome-vfs uses mime-types as keys in its cache.
- */
- while (*q && *q != '=' && *q != '[' && *q != ']')
- q = g_utf8_find_next_char (q, NULL);
-
- /* No empty keys, please */
- if (q == p)
- return FALSE;
-
- /* We accept spaces in the middle of keys to not break
- * existing apps, but we don't tolerate initial or final
- * spaces, which would lead to silent corruption when
- * rereading the file.
- */
- if (*p == ' ' || q[-1] == ' ')
- return FALSE;
-
- if (*q == '[')
- {
- q++;
- while (*q && (g_unichar_isalnum (g_utf8_get_char_validated (q, -1)) || *q == '-' || *q == '_' || *q == '.' || *q == '@'))
- q = g_utf8_find_next_char (q, NULL);
-
- if (*q != ']')
- return FALSE;
-
- q++;
- }
-
- if (*q != '\0')
- return FALSE;
-
- return TRUE;
-}
-
-/* A group in a key file is made up of a starting '[' followed by one
- * or more letters making up the group name followed by ']'.
- */
-static gboolean
-g_key_file_line_is_group (const gchar *line)
-{
- gchar *p;
-
- p = (gchar *) line;
- if (*p != '[')
- return FALSE;
-
- p++;
-
- while (*p && *p != ']')
- p = g_utf8_find_next_char (p, NULL);
-
- if (*p != ']')
- return FALSE;
-
- /* silently accept whitespace after the ] */
- p = g_utf8_find_next_char (p, NULL);
- while (*p == ' ' || *p == '\t')
- p = g_utf8_find_next_char (p, NULL);
-
- if (*p)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean
-g_key_file_line_is_key_value_pair (const gchar *line)
-{
- gchar *p;
-
- p = (gchar *) g_utf8_strchr (line, -1, '=');
-
- if (!p)
- return FALSE;
-
- /* Key must be non-empty
- */
- if (*p == line[0])
- return FALSE;
-
- return TRUE;
-}
-
-static gchar *
-g_key_file_parse_value_as_string (GKeyFile *key_file,
- const gchar *value,
- GSList **pieces,
- GError **error)
-{
- gchar *string_value, *p, *q0, *q;
-
- string_value = g_new (gchar, strlen (value) + 1);
-
- p = (gchar *) value;
- q0 = q = string_value;
- while (*p)
- {
- if (*p == '\\')
- {
- p++;
-
- switch (*p)
- {
- case 's':
- *q = ' ';
- break;
-
- case 'n':
- *q = '\n';
- break;
-
- case 't':
- *q = '\t';
- break;
-
- case 'r':
- *q = '\r';
- break;
-
- case '\\':
- *q = '\\';
- break;
-
- case '\0':
- g_set_error_literal (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains escape character "
- "at end of line"));
- break;
-
- default:
- if (pieces && *p == key_file->list_separator)
- *q = key_file->list_separator;
- else
- {
- *q++ = '\\';
- *q = *p;
-
- if (*error == NULL)
- {
- gchar sequence[3];
-
- sequence[0] = '\\';
- sequence[1] = *p;
- sequence[2] = '\0';
-
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Key file contains invalid escape "
- "sequence '%s'"), sequence);
- }
- }
- break;
- }
- }
- else
- {
- *q = *p;
- if (pieces && (*p == key_file->list_separator))
- {
- *pieces = g_slist_prepend (*pieces, g_strndup (q0, q - q0));
- q0 = q + 1;
- }
- }
-
- if (*p == '\0')
- break;
-
- q++;
- p++;
- }
-
- *q = '\0';
- if (pieces)
- {
- if (q0 < q)
- *pieces = g_slist_prepend (*pieces, g_strndup (q0, q - q0));
- *pieces = g_slist_reverse (*pieces);
- }
-
- return string_value;
-}
-
-static gchar *
-g_key_file_parse_string_as_value (GKeyFile *key_file,
- const gchar *string,
- gboolean escape_separator)
-{
- gchar *value, *p, *q;
- gsize length;
- gboolean parsing_leading_space;
-
- length = strlen (string) + 1;
-
- /* Worst case would be that every character needs to be escaped.
- * In other words every character turns to two characters
- */
- value = g_new (gchar, 2 * length);
-
- p = (gchar *) string;
- q = value;
- parsing_leading_space = TRUE;
- while (p < (string + length - 1))
- {
- gchar escaped_character[3] = { '\\', 0, 0 };
-
- switch (*p)
- {
- case ' ':
- if (parsing_leading_space)
- {
- escaped_character[1] = 's';
- strcpy (q, escaped_character);
- q += 2;
- }
- else
- {
- *q = *p;
- q++;
- }
- break;
- case '\t':
- if (parsing_leading_space)
- {
- escaped_character[1] = 't';
- strcpy (q, escaped_character);
- q += 2;
- }
- else
- {
- *q = *p;
- q++;
- }
- break;
- case '\n':
- escaped_character[1] = 'n';
- strcpy (q, escaped_character);
- q += 2;
- break;
- case '\r':
- escaped_character[1] = 'r';
- strcpy (q, escaped_character);
- q += 2;
- break;
- case '\\':
- escaped_character[1] = '\\';
- strcpy (q, escaped_character);
- q += 2;
- parsing_leading_space = FALSE;
- break;
- default:
- if (escape_separator && *p == key_file->list_separator)
- {
- escaped_character[1] = key_file->list_separator;
- strcpy (q, escaped_character);
- q += 2;
- parsing_leading_space = TRUE;
- }
- else
- {
- *q = *p;
- q++;
- parsing_leading_space = FALSE;
- }
- break;
- }
- p++;
- }
- *q = '\0';
-
- return value;
-}
-
-static gint
-g_key_file_parse_value_as_integer (GKeyFile *key_file,
- const gchar *value,
- GError **error)
-{
- gchar *end_of_valid_int;
- glong long_value;
- gint int_value;
-
- errno = 0;
- long_value = strtol (value, &end_of_valid_int, 10);
-
- if (*value == '\0' || *end_of_valid_int != '\0')
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Value '%s' cannot be interpreted "
- "as a number."), value_utf8);
- g_free (value_utf8);
-
- return 0;
- }
-
- int_value = long_value;
- if (int_value != long_value || errno == ERANGE)
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error,
- G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Integer value '%s' out of range"),
- value_utf8);
- g_free (value_utf8);
-
- return 0;
- }
-
- return int_value;
-}
-
-static gchar *
-g_key_file_parse_integer_as_value (GKeyFile *key_file,
- gint value)
-
-{
- return g_strdup_printf ("%d", value);
-}
-
-static gdouble
-g_key_file_parse_value_as_double (GKeyFile *key_file,
- const gchar *value,
- GError **error)
-{
- gchar *end_of_valid_d;
- gdouble double_value = 0;
-
- double_value = g_ascii_strtod (value, &end_of_valid_d);
-
- if (*end_of_valid_d != '\0' || end_of_valid_d == value)
- {
- gchar *value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Value '%s' cannot be interpreted "
- "as a float number."),
- value_utf8);
- g_free (value_utf8);
- }
-
- return double_value;
-}
-
-static gboolean
-g_key_file_parse_value_as_boolean (GKeyFile *key_file,
- const gchar *value,
- GError **error)
-{
- gchar *value_utf8;
-
- if (strcmp (value, "true") == 0 || strcmp (value, "1") == 0)
- return TRUE;
- else if (strcmp (value, "false") == 0 || strcmp (value, "0") == 0)
- return FALSE;
-
- value_utf8 = _g_utf8_make_valid (value);
- g_set_error (error, G_KEY_FILE_ERROR,
- G_KEY_FILE_ERROR_INVALID_VALUE,
- _("Value '%s' cannot be interpreted "
- "as a boolean."), value_utf8);
- g_free (value_utf8);
-
- return FALSE;
-}
-
-static gchar *
-g_key_file_parse_boolean_as_value (GKeyFile *key_file,
- gboolean value)
-{
- if (value)
- return g_strdup ("true");
- else
- return g_strdup ("false");
-}
-
-static gchar *
-g_key_file_parse_value_as_comment (GKeyFile *key_file,
- const gchar *value)
-{
- GString *string;
- gchar **lines;
- gsize i;
-
- string = g_string_sized_new (512);
-
- lines = g_strsplit (value, "\n", 0);
-
- for (i = 0; lines[i] != NULL; i++)
- {
- if (lines[i][0] != '#')
- g_string_append_printf (string, "%s\n", lines[i]);
- else
- g_string_append_printf (string, "%s\n", lines[i] + 1);
- }
- g_strfreev (lines);
-
- return g_string_free (string, FALSE);
-}
-
-static gchar *
-g_key_file_parse_comment_as_value (GKeyFile *key_file,
- const gchar *comment)
-{
- GString *string;
- gchar **lines;
- gsize i;
-
- string = g_string_sized_new (512);
-
- lines = g_strsplit (comment, "\n", 0);
-
- for (i = 0; lines[i] != NULL; i++)
- g_string_append_printf (string, "#%s%s", lines[i],
- lines[i + 1] == NULL? "" : "\n");
- g_strfreev (lines);
-
- return g_string_free (string, FALSE);
-}
+++ /dev/null
-
-CFLAGS = `pkg-config --cflags glib-2.0`
-LIBS = `pkg-config --libs glib-2.0`
-
-
-all: gen-mirroring-tab
-
-gen-mirroring-tab: gen-mirroring-tab.o packtab.o
-
-clean:
- $(RM) gen-mirroring-tab *.o
+++ /dev/null
-/* gen-mirroring-tab.c - generate gmirroringtable.h for glib
- * copied from FriBidi.
- *
- * $Id$
- * $Author$
- * $Date$
- * $Revision$
- * $Source$
- *
- * Author:
- * Behdad Esfahbod, 2001, 2002, 2004
- *
- * Copyright (C) 2004 Sharif FarsiWeb, Inc
- * Copyright (C) 2001,2002,2004 Behdad Esfahbod
- *
- * 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, in a file named COPYING; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA
- *
- * For licensing issues, contact <license@farsiweb.info>.
- */
-
-#include <glib.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "packtab.h"
-
-#define appname "gen-mirroring-tab"
-#define outputname "gmirroringtable.h"
-
-static void
-die (
- const char *msg
-)
-{
- fprintf (stderr, appname ": %s\n", msg);
- exit (1);
-}
-
-static void
-die2 (
- const char *fmt,
- const char *p
-)
-{
- fprintf (stderr, appname ": ");
- fprintf (stderr, fmt, p);
- fprintf (stderr, "\n");
- exit (1);
-}
-
-static void
-die4 (
- const char *fmt,
- unsigned long l,
- unsigned long p,
- unsigned long q
-)
-{
- fprintf (stderr, appname ": ");
- fprintf (stderr, fmt, l, p, q);
- fprintf (stderr, "\n");
- exit (1);
-}
-
-#define table_name "Mir"
-#define macro_name "GLIB_GET_MIRRORING"
-
-#define UNICODE_CHARS 0x110000
-
-static signed int table[UNICODE_CHARS];
-static char buf[4000];
-static signed long max_dist;
-
-static void
-init (
- void
-)
-{
- max_dist = 0;
-}
-
-static void
-clear_tab (
- void
-)
-{
- register gunichar c;
-
- for (c = 0; c < UNICODE_CHARS; c++)
- table[c] = 0;
-}
-
-static void
-init_tab_mirroring_txt (
- void
-)
-{
- clear_tab ();
-}
-
-static void
-read_bidi_mirroring_txt (
- FILE *f
-)
-{
- unsigned long l;
-
- init_tab_mirroring_txt ();
-
- l = 0;
- while (fgets (buf, sizeof buf, f))
- {
- unsigned long i, j;
- signed long dist;
- int k;
- const char *s = buf;
-
- l++;
-
- while (*s == ' ')
- s++;
-
- if (s[0] == '#' || s[0] == '\0' || s[0] == '\n')
- continue;
-
- k = sscanf (s, "%lx; %lx", &i, &j);
- if (k != 2 || i >= UNICODE_CHARS || j >= UNICODE_CHARS)
- die4 ("invalid pair in input at line %ld: %04lX, %04lX", l, i, j);
- dist = ((signed long) j - (signed long) i);
- table[i] = dist;
- if (dist > max_dist)
- max_dist = dist;
- else if (-dist > max_dist)
- max_dist = -dist;
- }
-}
-
-static void
-read_data (
- const char *data_file_type,
- const char *data_file_name
-)
-{
- FILE *f;
-
- fprintf (stderr, "Reading `%s'\n", data_file_name);
- if (!(f = fopen (data_file_name, "rt")))
- die2 ("error: cannot open `%s' for reading", data_file_name);
-
- if (!strcmp (data_file_type, "BidiMirroring.txt"))
- read_bidi_mirroring_txt (f);
- else
- die2 ("error: unknown data-file-type %s", data_file_type);
-
- fclose (f);
-}
-
-static void
-gen_mirroring_tab (
- int max_depth,
- const char *data_file_type
-)
-{
- int key_bytes;
- const char *key_type;
-
- fprintf (stderr,
- "Generating `" outputname "', it may take up to a few minutes\n");
- printf ("/* " outputname "\n * generated by " appname " "
- "\n" " * from the file %s of */\n\n", data_file_type);
-
- printf ("#define PACKTAB_UINT8 guint8\n"
- "#define PACKTAB_UINT16 guint16\n"
- "#define PACKTAB_UINT32 guint32\n\n");
-
- key_bytes = max_dist <= 0x7f ? 1 : max_dist < 0x7fff ? 2 : 4;
- key_type = key_bytes == 1 ? "gint8" : key_bytes == 2 ?
- "gint16" : "gint32";
-
- if (!pack_table
- (table, UNICODE_CHARS, key_bytes, 0, max_depth, 1, NULL,
- key_type, table_name, macro_name "_DELTA", stdout))
- die ("error: insufficient memory, decrease max_depth");
-
- printf ("#undef PACKTAB_UINT8\n"
- "#undef PACKTAB_UINT16\n" "#undef PACKTAB_UINT32\n\n");
-
- printf ("#define " macro_name "(x) ((x) + " macro_name "_DELTA(x))\n\n");
-
- printf ("/* End of generated " outputname " */\n");
-}
-
-int
-main (
- int argc,
- const char **argv
-)
-{
- const char *data_file_type = "BidiMirroring.txt";
-
- if (argc < 3)
- die2 ("usage:\n " appname " max-lookups /path/to/%s [junk...]",
- data_file_type);
-
- {
- int max_depth = atoi (argv[1]);
- const char *data_file_name = argv[2];
-
- if (max_depth < 2)
- die ("invalid depth");
-
- init ();
- read_data (data_file_type, data_file_name);
- gen_mirroring_tab (max_depth, data_file_type);
- }
-
- return 0;
-}
+++ /dev/null
-/* PackTab - Pack a static table
- * Copyright (C) 2001 Behdad Esfahbod.
- *
- * 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, in a file named COPYING; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA
- *
- * For licensing issues, contact <fwpg@sharif.edu>.
- */
-
-/*
- 8 <= N <= 2^21
- int key
- 1 <= max_depth <= 21
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "packtab.h"
-
-typedef signed int uni_table[1024 * 1024 * 2];
-static int n, a, max_depth, digits, tab_width, per_row;
-static long N;
-signed int def_key;
-static uni_table temp, x, perm, *tab;
-static long pow[22], cluster, cmpcluster;
-static const char *const *name, *key_type_name, *table_name, *macro_name;
-static FILE *f;
-
-static long
-most_binary (
- long min,
- long max
-)
-{
- /* min should be less than max */
- register int i, ii;
-
- if (min == max)
- return max;
-
- for (i = 21; max < pow[i]; i--)
- ;
- ii = i;
- while (i && !((min ^ max) & pow[i]))
- i--;
-
- if (ii == i)
- {
- /* min is less than half of max */
- for (i = 21 - 1; min < pow[i]; i--)
- ;
- i++;
- return pow[i];
- }
-
- return max & (pow[i] - 1);
-}
-
-static void
-init (
- const signed int *table
-)
-{
- register int i;
-
- /* initialize powers of two */
- pow[0] = 1;
- for (i = 1; i <= 21; i++)
- pow[i] = pow[i - 1] << 1;
-
- /* reduce number of elements to get a more binary number */
- {
- long essen;
-
- /* find number of essential items */
- essen = N - 1;
- while (essen && table[essen] == def_key)
- essen--;
- essen++;
-
- N = most_binary (essen, N);
- }
-
- for (n = 21; N % pow[n]; n--)
- ;
- digits = (n + 3) / 4;
- for (i = 6; i; i--)
- if (pow[i] * (tab_width + 1) < 80)
- break;
- per_row = pow[i];
-}
-
-static int
-compare (
- const void *r,
- const void *s
-)
-{
- int i;
- for (i = 0; i < cmpcluster; i++)
- if (((int *) r)[i] != ((int *) s)[i])
- return ((int *) r)[i] - ((int *) s)[i];
- return 0;
-}
-
-static int lev, best_lev, p[22], best_p[22], nn;
-static long c[22], best_c[22], s, best_s;
-static long t[22], best_t[22], clusters[22], best_cluster[22];
-
-static void
-found (
- void
-)
-{
- int i;
-
- if (s < best_s)
- {
- best_s = s;
- best_lev = lev;
- for (i = 0; i <= lev; i++)
- {
- best_p[i] = p[i];
- best_c[i] = c[i];
- best_t[i] = t[i];
- best_cluster[i] = clusters[i];
- }
- }
-}
-
-static void
-bt (
- int node_size
-)
-{
- long i, j, k, y, sbak;
- long key_bytes;
-
- if (t[lev] == 1)
- {
- found ();
- return;
- }
- if (lev == max_depth)
- return;
-
- for (i = 1 - t[lev] % 2; i <= nn + (t[lev] >> nn) % 2; i++)
- {
- nn -= (p[lev] = i);
- clusters[lev] = cluster = (i && nn >= 0) ? pow[i] : t[lev];
- cmpcluster = cluster + 1;
-
- t[lev + 1] = (t[lev] - 1) / cluster + 1;
- for (j = 0; j < t[lev + 1]; j++)
- {
- memmove (temp + j * cmpcluster, tab[lev] + j * cluster,
- cluster * sizeof (tab[lev][0]));
- temp[j * cmpcluster + cluster] = j;
- }
- qsort (temp, t[lev + 1], cmpcluster * sizeof (temp[0]), compare);
- for (j = 0; j < t[lev + 1]; j++)
- {
- perm[j] = temp[j * cmpcluster + cluster];
- temp[j * cmpcluster + cluster] = 0;
- }
- k = 1;
- y = 0;
- tab[lev + 1][perm[0]] = perm[0];
- for (j = 1; j < t[lev + 1]; j++)
- {
- if (compare (temp + y, temp + y + cmpcluster))
- {
- k++;
- tab[lev + 1][perm[j]] = perm[j];
- }
- else
- tab[lev + 1][perm[j]] = tab[lev + 1][perm[j - 1]];
- y += cmpcluster;
- }
- sbak = s;
- s += k * node_size * cluster;
- c[lev] = k;
-
- if (s >= best_s)
- {
- s = sbak;
- nn += i;
- return;
- }
-
- key_bytes = k * cluster;
- key_bytes = key_bytes < 0xff ? 1 : key_bytes < 0xffff ? 2 : 4;
- lev++;
- bt (key_bytes);
- lev--;
-
- s = sbak;
- nn += i;
- }
-}
-
-static void
-solve (
- void
-)
-{
- best_lev = max_depth + 2;
- best_s = N * a * 2;
- lev = 0;
- s = 0;
- nn = n;
- t[0] = N;
- bt (a);
-}
-
-static void
-write_array (
- long max_key
-)
-{
- int i, j, k, y, ii, ofs;
- const char *key_type;
-
- if (best_t[lev] == 1)
- return;
-
- nn -= (i = best_p[lev]);
- cluster = best_cluster[lev];
- cmpcluster = cluster + 1;
-
- t[lev + 1] = best_t[lev + 1];
- for (j = 0; j < t[lev + 1]; j++)
- {
- memmove (temp + j * cmpcluster, tab[lev] + j * cluster,
- cluster * sizeof (tab[lev][0]));
- temp[j * cmpcluster + cluster] = j;
- }
- qsort (temp, t[lev + 1], cmpcluster * sizeof (temp[0]), compare);
- for (j = 0; j < t[lev + 1]; j++)
- {
- perm[j] = temp[j * cmpcluster + cluster];
- temp[j * cmpcluster + cluster] = 0;
- }
- k = 1;
- y = 0;
- tab[lev + 1][perm[0]] = x[0] = perm[0];
- for (j = 1; j < t[lev + 1]; j++)
- {
- if (compare (temp + y, temp + y + cmpcluster))
- {
- x[k] = perm[j];
- tab[lev + 1][perm[j]] = x[k];
- k++;
- }
- else
- tab[lev + 1][perm[j]] = tab[lev + 1][perm[j - 1]];
- y += cmpcluster;
- }
-
- i = 0;
- for (ii = 1; ii < k; ii++)
- if (x[ii] < x[i])
- i = ii;
-
- key_type = !lev ? key_type_name :
- max_key <= 0xff ? "PACKTAB_UINT8" :
- max_key <= 0xffff ? "PACKTAB_UINT16" : "PACKTAB_UINT32";
- fprintf (f, "static const %s %sLev%d[%ld*%d] = {", key_type, table_name,
- best_lev - lev - 1, cluster, k);
- ofs = 0;
- for (ii = 0; ii < k; ii++)
- {
- int kk, jj;
- fprintf (f, "\n#define %sLev%d_%0*lX 0x%0X", table_name,
- best_lev - lev - 1, digits, x[i] * pow[n - nn], ofs);
- kk = x[i] * cluster;
- if (!lev)
- if (name)
- for (j = 0; j < cluster; j++)
- {
- if (!(j % per_row) && j != cluster - 1)
- fprintf (f, "\n ");
- fprintf (f, "%*s,", tab_width, name[tab[lev][kk++]]);
- }
- else
- for (j = 0; j < cluster; j++)
- {
- if (!(j % per_row) && j != cluster - 1)
- fprintf (f, "\n ");
- fprintf (f, "%*d,", tab_width, tab[lev][kk++]);
- }
- else
- for (j = 0; j < cluster; j++, kk++)
- fprintf (f, "\n %sLev%d_%0*lX, /* %0*lX..%0*lX */", table_name,
- best_lev - lev, digits,
- tab[lev][kk] * pow[n - nn - best_p[lev]], digits,
- x[i] * pow[n - nn] + j * pow[n - nn - best_p[lev]], digits,
- x[i] * pow[n - nn] + (j + 1) * pow[n - nn - best_p[lev]] -
- 1);
- ofs += cluster;
- jj = i;
- for (j = 0; j < k; j++)
- if (x[j] > x[i] && (x[j] < x[jj] || jj == i))
- jj = j;
- i = jj;
- }
- fprintf (f, "\n};\n\n");
- lev++;
- write_array (cluster * k);
- lev--;
-}
-
-static void
-write_source (
- void
-)
-{
- int i, j;
-
- lev = 0;
- s = 0;
- nn = n;
- t[0] = N;
- fprintf (f, "\n" "/* *IND" "ENT-OFF* */\n\n");
- write_array (0);
- fprintf (f, "/* *IND" "ENT-ON* */\n\n");
-
- fprintf (f, "#define %s(x) \\\n", macro_name);
- fprintf (f, "\t((x) >= 0x%lx ? ", N);
- if (name)
- fprintf (f, "%s", name[def_key]);
- else
- fprintf (f, "%d", def_key);
- fprintf (f, " : ");
- j = 0;
- for (i = best_lev - 1; i >= 0; i--)
- {
- fprintf (f, " \\\n\t%sLev%d[((x)", table_name, i);
- if (j != 0)
- fprintf (f, " >> %d", j);
- if (i)
- fprintf (f, " & 0x%02lx) +", pow[best_p[best_lev - 1 - i]] - 1);
- j += best_p[best_lev - 1 - i];
- }
- fprintf (f, ")");
- for (i = 0; i < best_lev; i++)
- fprintf (f, "]");
- fprintf (f, ")\n\n");
-}
-
-static void
-write_out (
- void
-)
-{
- int i;
- fprintf (f, "/*\n"
- " generated by packtab.c version %d\n\n"
- " use %s(key) to access your table\n\n"
- " assumed sizeof(%s): %d\n"
- " required memory: %ld\n"
- " lookups: %d\n"
- " partition shape: %s",
- packtab_version, macro_name, key_type_name, a, best_s, best_lev,
- table_name);
- for (i = best_lev - 1; i >= 0; i--)
- fprintf (f, "[%ld]", best_cluster[i]);
- fprintf (f, "\n" " different table entries:");
- for (i = best_lev - 1; i >= 0; i--)
- fprintf (f, " %ld", best_c[i]);
- fprintf (f, "\n*/\n");
- write_source ();
-}
-
-int
-pack_table (
- const signed int *base,
- long key_num,
- int key_size,
- signed int default_key,
- int p_max_depth,
- int p_tab_width,
- const char *const *p_name,
- const char *p_key_type_name,
- const char *p_table_name,
- const char *p_macro_name,
- FILE *out
-)
-{
- N = key_num;
- a = key_size;
- def_key = default_key;
- max_depth = p_max_depth;
- tab_width = p_tab_width;
- name = p_name;
- key_type_name = p_key_type_name;
- table_name = p_table_name;
- macro_name = p_macro_name;
- f = out;
- init (base);
- if (!(tab = malloc ((n + 1) * sizeof (tab[0]))))
- return 0;
- memmove (tab[0], base, N * sizeof (base[0]));
- solve ();
- write_out ();
- free (tab);
- return 1;
-}
-
-/* End of packtab.c */
+++ /dev/null
-/* PackTab - Pack a static table
- * Copyright (C) 2001 Behdad Esfahbod.
- *
- * 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, in a file named COPYING; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA
- *
- * For licensing issues, contact <fwpg@sharif.edu>.
- */
-
-#ifndef PACKTAB_H
-#define PACKTAB_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#define packtab_version 3
-
- int pack_table (
- const signed int *base,
- long key_num,
- int key_size,
- signed int default_key,
- int max_depth,
- int tab_width,
- const char *const *name,
- const char *key_type_name,
- const char *table_name,
- const char *macro_name,
- FILE *out
- );
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* PACKTAB_H */
+++ /dev/null
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
- PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
- FILEFLAGSMASK 0
- FILEFLAGS 0
- FILEOS VOS__WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
- BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904B0"
- BEGIN
- VALUE "CompanyName", "The GLib developer community"
- VALUE "FileDescription", "GLib"
- VALUE "FileVersion", "@GLIB_VERSION@.0"
- VALUE "InternalName", "libglib-2.0-@LT_CURRENT_MINUS_AGE@"
- VALUE "LegalCopyright", "Copyright © 1995-2010 Peter Mattis, Spencer Kimball, Josh MacDonald and others."
- VALUE "OriginalFilename", "libglib-2.0-@LT_CURRENT_MINUS_AGE@.dll"
- VALUE "ProductName", "GLib"
- VALUE "ProductVersion", "@GLIB_VERSION@"
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x409, 1200
- END
- END
+++ /dev/null
-global gquarks
-
-/* This is needed to keep track of gquark for use in other probes.*/
-probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("quark__new")
-{
- gquarks[pid(), $arg2] = user_string($arg1)
-}
-
-/**
- * probe glib.quark_new - Called when a #GQuark is initially created
- * @quark: integer value for the quark
- * @str: string form of the quark
- */
-probe glib.quark_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("quark__new")
-{
- str = user_string ($arg1);
- quark = $arg2;
- probestr = sprintf("glib.quark_new(%s) -> %d", str, quark);
-}
-
-/**
- * probe glib.mem_alloc - Called when a malloc block is initially requested
- * @mem: Raw memory pointer returned
- * @n_bytes: number of bytes
- * @zeroed: Boolean value, %TRUE if this block was filled with NUL bytes
- * @failable: Boolean value, %TRUE if program execution can continue on allocation failure
- */
-probe glib.mem_alloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("mem__alloc")
-{
- mem = $arg1;
- n_bytes = $arg2;
- zeroed = $arg3;
- failable = $arg4;
- probestr = sprintf("glib.mem_alloc(n_bytes=%d) -> %p", n_bytes, mem);
-}
-
-/**
- * probe glib.mem_free - Called when a malloc block freed
- */
-probe glib.mem_free = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("mem__free")
-{
- mem = $arg1; /* ARG: @mem: Raw memory pointer */
- probestr = sprintf("glib.mem_free(mem=%p)", mem);
-}
-
-/**
- * probe glib.mem_realloc - Called when a malloc block is resized
- * @mem: Raw memory pointer returned
- * @old_mem: Original memory pointer
- * @n_bytes: number of bytes
- * @failable: Boolean value, %TRUE if program execution can continue on allocation failure
- */
-probe glib.mem_realloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("mem__realloc")
-{
- mem = $arg1;
- old_mem = $arg2;
- n_bytes = $arg3;
- failable = $arg4;
- probestr = sprintf("glib.mem_realloc(old_mem=%p, n_bytes=%d) -> %p", old_mem, n_bytes, mem);
-}
-
-/**
- * probe glib.slice_alloc - Called when g_slice_alloc() is used
- * @mem: Raw memory pointer returned
- * @n_bytes: number of bytes
- */
-probe glib.slice_alloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("slice__alloc")
-{
- mem = $arg1;
- n_bytes = $arg2;
- probestr = sprintf("glib.slice_alloc(n_bytes=%d) -> %p", n_bytes, mem);
-}
-
-/**
- * probe glib.slice_free - Called when memory slice is freed
- * @mem: Raw memory pointer returned
- * @n_bytes: Number of bytes
- */
-probe glib.slice_free = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2.0.so.0.@LT_CURRENT@.@LT_REVISION@").mark("slice__free")
-{
- mem = $arg1;
- n_bytes = $arg2;
- probestr = sprintf("glib.slice_free(n_bytes=%d) -> %p", n_bytes, mem);
-}
+++ /dev/null
-/* glibconfig.h.win32.in. Originally merged from two versions of
- * glibconfig.h, generated by the GLib configure script, for gcc and
- * MSVC.
- */
-
-/* glibconfig.h
- *
- * This is a generated file. Please modify 'glibconfig.h.win32.in'
- */
-
-#ifndef __G_LIBCONFIG_H__
-#define __G_LIBCONFIG_H__
-
-#include <glib/gmacros.h>
-
-#include <limits.h>
-#include <float.h>
-
-G_BEGIN_DECLS
-
-#define G_MINFLOAT FLT_MIN
-#define G_MAXFLOAT FLT_MAX
-#define G_MINDOUBLE DBL_MIN
-#define G_MAXDOUBLE DBL_MAX
-#define G_MINSHORT SHRT_MIN
-#define G_MAXSHORT SHRT_MAX
-#define G_MAXUSHORT USHRT_MAX
-#define G_MININT INT_MIN
-#define G_MAXINT INT_MAX
-#define G_MAXUINT UINT_MAX
-#define G_MINLONG LONG_MIN
-#define G_MAXLONG LONG_MAX
-#define G_MAXULONG ULONG_MAX
-
-typedef signed char gint8;
-typedef unsigned char guint8;
-typedef signed short gint16;
-typedef unsigned short guint16;
-#define G_GINT16_MODIFIER "h"
-#define G_GINT16_FORMAT "hi"
-#define G_GUINT16_FORMAT "hu"
-typedef signed int gint32;
-typedef unsigned int guint32;
-#define G_GINT32_MODIFIER ""
-#define G_GINT32_FORMAT "i"
-#define G_GUINT32_FORMAT "u"
-#define G_HAVE_GINT64 1 /* deprecated, always true */
-
-#ifndef _MSC_VER
-G_GNUC_EXTENSION typedef signed long long gint64;
-G_GNUC_EXTENSION typedef unsigned long long guint64;
-#else /* _MSC_VER */
-typedef signed __int64 gint64;
-typedef unsigned __int64 guint64;
-#endif /* _MSC_VER */
-
-#ifndef _MSC_VER
-#define G_GINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##LL))
-#else /* _MSC_VER */
-#define G_GINT64_CONSTANT(val) (val##i64)
-#endif /* _MSC_VER */
-#ifndef _MSC_VER
-#define G_GUINT64_CONSTANT(val) (G_GNUC_EXTENSION (val##ULL))
-#else /* _MSC_VER */
-#define G_GUINT64_CONSTANT(val) (val##Ui64)
-#endif /* _MSC_VER */
-#define G_GINT64_MODIFIER "I64"
-#define G_GINT64_FORMAT "I64i"
-#define G_GUINT64_FORMAT "I64u"
-
-#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
-
-#define GLIB_SIZEOF_VOID_P 8
-#define GLIB_SIZEOF_LONG 4
-#define GLIB_SIZEOF_SIZE_T 8
-
-typedef signed long long gssize;
-typedef unsigned long long gsize;
-#define G_GSIZE_MODIFIER "I64"
-#define G_GSSIZE_FORMAT "I64d"
-#define G_GSIZE_FORMAT "I64u"
-
-#define G_MAXSIZE G_MAXUINT64
-#define G_MINSSIZE G_MININT64
-#define G_MAXSSIZE G_MAXINT64
-
-#else
-
-#define GLIB_SIZEOF_VOID_P 4
-#define GLIB_SIZEOF_LONG 4
-#define GLIB_SIZEOF_SIZE_T 4
-
-typedef signed int gssize;
-typedef unsigned int gsize;
-#define G_GSIZE_MODIFIER ""
-#define G_GSSIZE_FORMAT "i"
-#define G_GSIZE_FORMAT "u"
-
-#define G_MAXSIZE G_MAXUINT
-#define G_MINSSIZE G_MININT
-#define G_MAXSSIZE G_MAXINT
-
-#endif
-
-typedef gint64 goffset;
-#define G_MINOFFSET G_MININT64
-#define G_MAXOFFSET G_MAXINT64
-
-#define G_GOFFSET_MODIFIER G_GINT64_MODIFIER
-#define G_GOFFSET_FORMAT G_GINT64_FORMAT
-#define G_GOFFSET_CONSTANT(val) G_GINT64_CONSTANT(val)
-
-
-#ifndef _WIN64
-
-#define GPOINTER_TO_INT(p) ((gint) (p))
-#define GPOINTER_TO_UINT(p) ((guint) (p))
-
-#define GINT_TO_POINTER(i) ((gpointer) (i))
-#define GUINT_TO_POINTER(u) ((gpointer) (u))
-
-typedef signed int gintptr;
-typedef unsigned int guintptr;
-
-#define G_GINTPTR_MODIFIER ""
-#define G_GINTPTR_FORMAT "i"
-#define G_GUINTPTR_FORMAT "u"
-
-#else
-
-#define GPOINTER_TO_INT(p) ((gint) (gint64) (p))
-#define GPOINTER_TO_UINT(p) ((guint) (guint64) (p))
-
-#define GINT_TO_POINTER(i) ((gpointer) (gint64) (i))
-#define GUINT_TO_POINTER(u) ((gpointer) (guint64) (u))
-
-#ifndef _MSC_VER
-typedef signed long long gintptr;
-typedef unsigned long long guintptr;
-#else
-typedef signed __int64 gintptr;
-typedef unsigned __int64 guintptr;
-#endif
-
-#define G_GINTPTR_MODIFIER "I64"
-#define G_GINTPTR_FORMAT "I64i"
-#define G_GUINTPTR_FORMAT "I64u"
-
-#endif
-
-#ifdef NeXT /* @#%@! NeXTStep */
-# define g_ATEXIT(proc) (!atexit (proc))
-#else
-# define g_ATEXIT(proc) (atexit (proc))
-#endif
-
-#define g_memmove(dest,src,len) G_STMT_START { memmove ((dest), (src), (len)); } G_STMT_END
-
-#define GLIB_MAJOR_VERSION @GLIB_MAJOR_VERSION@
-#define GLIB_MINOR_VERSION @GLIB_MINOR_VERSION@
-#define GLIB_MICRO_VERSION @GLIB_MICRO_VERSION@
-
-#define G_OS_WIN32
-#define G_PLATFORM_WIN32
-@GLIB_WIN32_STATIC_COMPILATION_DEFINE@
-
-#ifndef _MSC_VER
-#define G_VA_COPY va_copy
-#endif /* not _MSC_VER */
-
-#ifdef __cplusplus
-#define G_HAVE_INLINE 1
-#else /* !__cplusplus */
-#ifndef _MSC_VER
-#define G_HAVE_INLINE 1
-#endif /* _MSC_VER */
-#define G_HAVE___INLINE 1
-#if !defined(_MSC_VER) && !defined(__DMC__)
-#define G_HAVE___INLINE__ 1
-#endif /* !_MSC_VER and !__DMC__ */
-#endif /* !__cplusplus */
-
-#define G_CAN_INLINE 1
-
-#ifndef _MSC_VER
-#define G_HAVE_ISO_VARARGS 1
-
-/* gcc-2.95.x supports both gnu style and ISO varargs, but if -ansi
- * is passed ISO vararg support is turned off, and there is no work
- * around to turn it on, so we unconditionally turn it off.
- */
-#if __GNUC__ == 2 && __GNUC_MINOR__ == 95
-# undef G_HAVE_ISO_VARARGS
-#endif
-
-#define G_HAVE_GNUC_VARARGS 1
-#else /* _MSC_VER */
-/* varargs macros available since msvc8 (vs2005) */
-# if _MSC_VER >= 1400
-# define G_HAVE_ISO_VARARGS 1
-# endif
-#endif /* not _MSC_VER */
-#define G_HAVE_GROWING_STACK 0
-
-#define G_GNUC_INTERNAL
-
-#define G_THREADS_ENABLED
-#define G_THREADS_IMPL_WIN32
-typedef struct _GMutex* GStaticMutex;
-#define G_STATIC_MUTEX_INIT NULL
-#define g_static_mutex_get_mutex(mutex) \
- (g_static_mutex_get_mutex_impl_shortcut (mutex))
-/* This represents a system thread as used by the implementation. An
- * alien implementaion, as loaded by g_thread_init can only count on
- * "sizeof (gpointer)" bytes to store their info. We however need more
- * for some of our native implementations. */
-typedef union _GSystemThread GSystemThread;
-union _GSystemThread
-{
-#ifndef _WIN64
- char data[4];
-#else
- char data[8];
-#endif
- double dummy_double;
- void *dummy_pointer;
- long dummy_long;
-};
-
-#define GINT16_TO_LE(val) ((gint16) (val))
-#define GUINT16_TO_LE(val) ((guint16) (val))
-#define GINT16_TO_BE(val) ((gint16) GUINT16_SWAP_LE_BE (val))
-#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
-#define GINT32_TO_LE(val) ((gint32) (val))
-#define GUINT32_TO_LE(val) ((guint32) (val))
-#define GINT32_TO_BE(val) ((gint32) GUINT32_SWAP_LE_BE (val))
-#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
-#define GINT64_TO_LE(val) ((gint64) (val))
-#define GUINT64_TO_LE(val) ((guint64) (val))
-#define GINT64_TO_BE(val) ((gint64) GUINT64_SWAP_LE_BE (val))
-#define GUINT64_TO_BE(val) (GUINT64_SWAP_LE_BE (val))
-#define GLONG_TO_LE(val) ((glong) GINT32_TO_LE (val))
-#define GULONG_TO_LE(val) ((gulong) GUINT32_TO_LE (val))
-#define GLONG_TO_BE(val) ((glong) GINT32_TO_BE (val))
-#define GULONG_TO_BE(val) ((gulong) GUINT32_TO_BE (val))
-#define GINT_TO_LE(val) ((gint) GINT32_TO_LE (val))
-#define GUINT_TO_LE(val) ((guint) GUINT32_TO_LE (val))
-#define GINT_TO_BE(val) ((gint) GINT32_TO_BE (val))
-#define GUINT_TO_BE(val) ((guint) GUINT32_TO_BE (val))
-#define GSIZE_TO_LE(val) ((gsize) GUINT32_TO_LE (val))
-#define GSSIZE_TO_LE(val) ((gssize) GINT32_TO_LE (val))
-#define GSIZE_TO_BE(val) ((gsize) GUINT32_TO_BE (val))
-#define GSSIZE_TO_BE(val) ((gssize) GINT32_TO_BE (val))
-#define G_BYTE_ORDER G_LITTLE_ENDIAN
-
-#define GLIB_SYSDEF_POLLIN =1
-#define GLIB_SYSDEF_POLLOUT =4
-#define GLIB_SYSDEF_POLLPRI =2
-#define GLIB_SYSDEF_POLLHUP =16
-#define GLIB_SYSDEF_POLLERR =8
-#define GLIB_SYSDEF_POLLNVAL =32
-
-#define G_MODULE_SUFFIX "dll"
-
-/* A GPid is an abstraction for a process "handle". It is *not* an
- * abstraction for a process identifier in general. GPid is used in
- * GLib only for descendant processes spawned with the g_spawn*
- * functions. On POSIX there is no "process handle" concept as such,
- * but on Windows a GPid is a handle to a process, a kind of pointer,
- * not a process identifier.
- */
-typedef void * GPid;
-
-#define GLIB_SYSDEF_AF_UNIX 1
-#define GLIB_SYSDEF_AF_INET 2
-#define GLIB_SYSDEF_AF_INET6 23
-
-#define GLIB_SYSDEF_MSG_OOB 1
-#define GLIB_SYSDEF_MSG_PEEK 2
-#define GLIB_SYSDEF_MSG_DONTROUTE 4
-
-G_END_DECLS
-
-#endif /* GLIBCONFIG_H */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "glist.h"
-
-#include "gtestutils.h"
-
-/**
- * SECTION: linked_lists_double
- * @title: Doubly-Linked Lists
- * @short_description: linked lists containing integer values or
- * pointers to data, with the ability to iterate
- * over the list in both directions
- *
- * The #GList structure and its associated functions provide a standard
- * doubly-linked list data structure.
- *
- * Each element in the list contains a piece of data, together with
- * pointers which link to the previous and next elements in the list.
- * Using these pointers it is possible to move through the list in both
- * directions (unlike the <link
- * linkend="glib-Singly-Linked-lists">Singly-Linked Lists</link> which
- * only allows movement through the list in the forward direction).
- *
- * The data contained in each element can be either integer values, by
- * using one of the <link linkend="glib-Type-Conversion-Macros">Type
- * Conversion Macros</link>, or simply pointers to any type of data.
- *
- * List elements are allocated from the <link
- * linkend="glib-Memory-Slices">slice allocator</link>, which is more
- * efficient than allocating elements individually.
- *
- * Note that most of the #GList functions expect to be passed a pointer
- * to the first element in the list. The functions which insert
- * elements return the new start of the list, which may have changed.
- *
- * There is no function to create a #GList. %NULL is considered to be
- * the empty list so you simply set a #GList* to %NULL.
- *
- * To add elements, use g_list_append(), g_list_prepend(),
- * g_list_insert() and g_list_insert_sorted().
- *
- * To remove elements, use g_list_remove().
- *
- * To find elements in the list use g_list_first(), g_list_last(),
- * g_list_next(), g_list_previous(), g_list_nth(), g_list_nth_data(),
- * g_list_find() and g_list_find_custom().
- *
- * To find the index of an element use g_list_position() and
- * g_list_index().
- *
- * To call a function for each element in the list use g_list_foreach().
- *
- * To free the entire list, use g_list_free().
- **/
-
-/**
- * GList:
- * @data: holds the element's data, which can be a pointer to any kind
- * of data, or any integer value using the <link
- * linkend="glib-Type-Conversion-Macros">Type Conversion
- * Macros</link>.
- * @next: contains the link to the next element in the list.
- * @prev: contains the link to the previous element in the list.
- *
- * The #GList struct is used for each element in a doubly-linked list.
- **/
-
-/**
- * g_list_previous:
- * @list: an element in a #GList.
- * @Returns: the previous element, or %NULL if there are no previous
- * elements.
- *
- * A convenience macro to get the previous element in a #GList.
- **/
-
-/**
- * g_list_next:
- * @list: an element in a #GList.
- * @Returns: the next element, or %NULL if there are no more elements.
- *
- * A convenience macro to get the next element in a #GList.
- **/
-
-
-
-/**
- * g_list_push_allocator:
- * @allocator: the #GAllocator to use when allocating #GList elements.
- *
- * Sets the allocator to use to allocate #GList elements. Use
- * g_list_pop_allocator() to restore the previous allocator.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated:2.10: It does nothing, since #GList has been converted
- * to the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_list_push_allocator (gpointer dummy) { /* present for binary compat only */ }
-
-/**
- * g_list_pop_allocator:
- *
- * Restores the previous #GAllocator, used when allocating #GList
- * elements.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated:2.10: It does nothing, since #GList has been converted
- * to the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_list_pop_allocator (void) { /* present for binary compat only */ }
-
-#define _g_list_alloc() g_slice_new (GList)
-#define _g_list_alloc0() g_slice_new0 (GList)
-#define _g_list_free1(list) g_slice_free (GList, list)
-
-/**
- * g_list_alloc:
- * @Returns: a pointer to the newly-allocated #GList element.
- *
- * Allocates space for one #GList element. It is called by
- * g_list_append(), g_list_prepend(), g_list_insert() and
- * g_list_insert_sorted() and so is rarely used on its own.
- **/
-GList*
-g_list_alloc (void)
-{
- return _g_list_alloc0 ();
-}
-
-/**
- * g_list_free:
- * @list: a #GList
- *
- * Frees all of the memory used by a #GList.
- * The freed elements are returned to the slice allocator.
- *
- * <note><para>
- * If list elements contain dynamically-allocated memory,
- * they should be freed first.
- * </para></note>
- */
-void
-g_list_free (GList *list)
-{
- g_slice_free_chain (GList, list, next);
-}
-
-/**
- * g_list_free_1:
- * @list: a #GList element
- *
- * Frees one #GList element.
- * It is usually used after g_list_remove_link().
- */
-/**
- * g_list_free1:
- *
- * Another name for g_list_free_1().
- **/
-void
-g_list_free_1 (GList *list)
-{
- _g_list_free1 (list);
-}
-
-/**
- * g_list_append:
- * @list: a pointer to a #GList
- * @data: the data for the new element
- *
- * Adds a new element on to the end of the list.
- *
- * <note><para>
- * The return value is the new start of the list, which
- * may have changed, so make sure you store the new value.
- * </para></note>
- *
- * <note><para>
- * Note that g_list_append() has to traverse the entire list
- * to find the end, which is inefficient when adding multiple
- * elements. A common idiom to avoid the inefficiency is to prepend
- * the elements and reverse the list when all elements have been added.
- * </para></note>
- *
- * |[
- * /* Notice that these are initialized to the empty list. */
- * GList *list = NULL, *number_list = NULL;
- *
- * /* This is a list of strings. */
- * list = g_list_append (list, "first");
- * list = g_list_append (list, "second");
- *
- * /* This is a list of integers. */
- * number_list = g_list_append (number_list, GINT_TO_POINTER (27));
- * number_list = g_list_append (number_list, GINT_TO_POINTER (14));
- * ]|
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_append (GList *list,
- gpointer data)
-{
- GList *new_list;
- GList *last;
-
- new_list = _g_list_alloc ();
- new_list->data = data;
- new_list->next = NULL;
-
- if (list)
- {
- last = g_list_last (list);
- /* g_assert (last != NULL); */
- last->next = new_list;
- new_list->prev = last;
-
- return list;
- }
- else
- {
- new_list->prev = NULL;
- return new_list;
- }
-}
-
-/**
- * g_list_prepend:
- * @list: a pointer to a #GList
- * @data: the data for the new element
- *
- * Adds a new element on to the start of the list.
- *
- * <note><para>
- * The return value is the new start of the list, which
- * may have changed, so make sure you store the new value.
- * </para></note>
- *
- * |[
- * /* Notice that it is initialized to the empty list. */
- * GList *list = NULL;
- * list = g_list_prepend (list, "last");
- * list = g_list_prepend (list, "first");
- * ]|
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_prepend (GList *list,
- gpointer data)
-{
- GList *new_list;
-
- new_list = _g_list_alloc ();
- new_list->data = data;
- new_list->next = list;
-
- if (list)
- {
- new_list->prev = list->prev;
- if (list->prev)
- list->prev->next = new_list;
- list->prev = new_list;
- }
- else
- new_list->prev = NULL;
-
- return new_list;
-}
-
-/**
- * g_list_insert:
- * @list: a pointer to a #GList
- * @data: the data for the new element
- * @position: the position to insert the element. If this is
- * negative, or is larger than the number of elements in the
- * list, the new element is added on to the end of the list.
- *
- * Inserts a new element into the list at the given position.
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_insert (GList *list,
- gpointer data,
- gint position)
-{
- GList *new_list;
- GList *tmp_list;
-
- if (position < 0)
- return g_list_append (list, data);
- else if (position == 0)
- return g_list_prepend (list, data);
-
- tmp_list = g_list_nth (list, position);
- if (!tmp_list)
- return g_list_append (list, data);
-
- new_list = _g_list_alloc ();
- new_list->data = data;
- new_list->prev = tmp_list->prev;
- if (tmp_list->prev)
- tmp_list->prev->next = new_list;
- new_list->next = tmp_list;
- tmp_list->prev = new_list;
-
- if (tmp_list == list)
- return new_list;
- else
- return list;
-}
-
-/**
- * g_list_insert_before:
- * @list: a pointer to a #GList
- * @sibling: the list element before which the new element
- * is inserted or %NULL to insert at the end of the list
- * @data: the data for the new element
- *
- * Inserts a new element into the list before the given position.
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_insert_before (GList *list,
- GList *sibling,
- gpointer data)
-{
- if (!list)
- {
- list = g_list_alloc ();
- list->data = data;
- g_return_val_if_fail (sibling == NULL, list);
- return list;
- }
- else if (sibling)
- {
- GList *node;
-
- node = _g_list_alloc ();
- node->data = data;
- node->prev = sibling->prev;
- node->next = sibling;
- sibling->prev = node;
- if (node->prev)
- {
- node->prev->next = node;
- return list;
- }
- else
- {
- g_return_val_if_fail (sibling == list, node);
- return node;
- }
- }
- else
- {
- GList *last;
-
- last = list;
- while (last->next)
- last = last->next;
-
- last->next = _g_list_alloc ();
- last->next->data = data;
- last->next->prev = last;
- last->next->next = NULL;
-
- return list;
- }
-}
-
-/**
- * g_list_concat:
- * @list1: a #GList
- * @list2: the #GList to add to the end of the first #GList
- *
- * Adds the second #GList onto the end of the first #GList.
- * Note that the elements of the second #GList are not copied.
- * They are used directly.
- *
- * Returns: the start of the new #GList
- */
-GList *
-g_list_concat (GList *list1, GList *list2)
-{
- GList *tmp_list;
-
- if (list2)
- {
- tmp_list = g_list_last (list1);
- if (tmp_list)
- tmp_list->next = list2;
- else
- list1 = list2;
- list2->prev = tmp_list;
- }
-
- return list1;
-}
-
-/**
- * g_list_remove:
- * @list: a #GList
- * @data: the data of the element to remove
- *
- * Removes an element from a #GList.
- * If two elements contain the same data, only the first is removed.
- * If none of the elements contain the data, the #GList is unchanged.
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_remove (GList *list,
- gconstpointer data)
-{
- GList *tmp;
-
- tmp = list;
- while (tmp)
- {
- if (tmp->data != data)
- tmp = tmp->next;
- else
- {
- if (tmp->prev)
- tmp->prev->next = tmp->next;
- if (tmp->next)
- tmp->next->prev = tmp->prev;
-
- if (list == tmp)
- list = list->next;
-
- _g_list_free1 (tmp);
-
- break;
- }
- }
- return list;
-}
-
-/**
- * g_list_remove_all:
- * @list: a #GList
- * @data: data to remove
- *
- * Removes all list nodes with data equal to @data.
- * Returns the new head of the list. Contrast with
- * g_list_remove() which removes only the first node
- * matching the given data.
- *
- * Returns: new head of @list
- */
-GList*
-g_list_remove_all (GList *list,
- gconstpointer data)
-{
- GList *tmp = list;
-
- while (tmp)
- {
- if (tmp->data != data)
- tmp = tmp->next;
- else
- {
- GList *next = tmp->next;
-
- if (tmp->prev)
- tmp->prev->next = next;
- else
- list = next;
- if (next)
- next->prev = tmp->prev;
-
- _g_list_free1 (tmp);
- tmp = next;
- }
- }
- return list;
-}
-
-static inline GList*
-_g_list_remove_link (GList *list,
- GList *link)
-{
- if (link)
- {
- if (link->prev)
- link->prev->next = link->next;
- if (link->next)
- link->next->prev = link->prev;
-
- if (link == list)
- list = list->next;
-
- link->next = NULL;
- link->prev = NULL;
- }
-
- return list;
-}
-
-/**
- * g_list_remove_link:
- * @list: a #GList
- * @llink: an element in the #GList
- *
- * Removes an element from a #GList, without freeing the element.
- * The removed element's prev and next links are set to %NULL, so
- * that it becomes a self-contained list with one element.
- *
- * Returns: the new start of the #GList, without the element
- */
-GList*
-g_list_remove_link (GList *list,
- GList *llink)
-{
- return _g_list_remove_link (list, llink);
-}
-
-/**
- * g_list_delete_link:
- * @list: a #GList
- * @link_: node to delete from @list
- *
- * Removes the node link_ from the list and frees it.
- * Compare this to g_list_remove_link() which removes the node
- * without freeing it.
- *
- * Returns: the new head of @list
- */
-GList*
-g_list_delete_link (GList *list,
- GList *link_)
-{
- list = _g_list_remove_link (list, link_);
- _g_list_free1 (link_);
-
- return list;
-}
-
-/**
- * g_list_copy:
- * @list: a #GList
- *
- * Copies a #GList.
- *
- * <note><para>
- * Note that this is a "shallow" copy. If the list elements
- * consist of pointers to data, the pointers are copied but
- * the actual data is not.
- * </para></note>
- *
- * Returns: a copy of @list
- */
-GList*
-g_list_copy (GList *list)
-{
- GList *new_list = NULL;
-
- if (list)
- {
- GList *last;
-
- new_list = _g_list_alloc ();
- new_list->data = list->data;
- new_list->prev = NULL;
- last = new_list;
- list = list->next;
- while (list)
- {
- last->next = _g_list_alloc ();
- last->next->prev = last;
- last = last->next;
- last->data = list->data;
- list = list->next;
- }
- last->next = NULL;
- }
-
- return new_list;
-}
-
-/**
- * g_list_reverse:
- * @list: a #GList
- *
- * Reverses a #GList.
- * It simply switches the next and prev pointers of each element.
- *
- * Returns: the start of the reversed #GList
- */
-GList*
-g_list_reverse (GList *list)
-{
- GList *last;
-
- last = NULL;
- while (list)
- {
- last = list;
- list = last->next;
- last->next = last->prev;
- last->prev = list;
- }
-
- return last;
-}
-
-/**
- * g_list_nth:
- * @list: a #GList
- * @n: the position of the element, counting from 0
- *
- * Gets the element at the given position in a #GList.
- *
- * Returns: the element, or %NULL if the position is off
- * the end of the #GList
- */
-GList*
-g_list_nth (GList *list,
- guint n)
-{
- while ((n-- > 0) && list)
- list = list->next;
-
- return list;
-}
-
-/**
- * g_list_nth_prev:
- * @list: a #GList
- * @n: the position of the element, counting from 0
- *
- * Gets the element @n places before @list.
- *
- * Returns: the element, or %NULL if the position is
- * off the end of the #GList
- */
-GList*
-g_list_nth_prev (GList *list,
- guint n)
-{
- while ((n-- > 0) && list)
- list = list->prev;
-
- return list;
-}
-
-/**
- * g_list_nth_data:
- * @list: a #GList
- * @n: the position of the element
- *
- * Gets the data of the element at the given position.
- *
- * Returns: the element's data, or %NULL if the position
- * is off the end of the #GList
- */
-gpointer
-g_list_nth_data (GList *list,
- guint n)
-{
- while ((n-- > 0) && list)
- list = list->next;
-
- return list ? list->data : NULL;
-}
-
-/**
- * g_list_find:
- * @list: a #GList
- * @data: the element data to find
- *
- * Finds the element in a #GList which
- * contains the given data.
- *
- * Returns: the found #GList element,
- * or %NULL if it is not found
- */
-GList*
-g_list_find (GList *list,
- gconstpointer data)
-{
- while (list)
- {
- if (list->data == data)
- break;
- list = list->next;
- }
-
- return list;
-}
-
-/**
- * g_list_find_custom:
- * @list: a #GList
- * @data: user data passed to the function
- * @func: the function to call for each element.
- * It should return 0 when the desired element is found
- *
- * Finds an element in a #GList, using a supplied function to
- * find the desired element. It iterates over the list, calling
- * the given function which should return 0 when the desired
- * element is found. The function takes two #gconstpointer arguments,
- * the #GList element's data as the first argument and the
- * given user data.
- *
- * Returns: the found #GList element, or %NULL if it is not found
- */
-GList*
-g_list_find_custom (GList *list,
- gconstpointer data,
- GCompareFunc func)
-{
- g_return_val_if_fail (func != NULL, list);
-
- while (list)
- {
- if (! func (list->data, data))
- return list;
- list = list->next;
- }
-
- return NULL;
-}
-
-
-/**
- * g_list_position:
- * @list: a #GList
- * @llink: an element in the #GList
- *
- * Gets the position of the given element
- * in the #GList (starting from 0).
- *
- * Returns: the position of the element in the #GList,
- * or -1 if the element is not found
- */
-gint
-g_list_position (GList *list,
- GList *llink)
-{
- gint i;
-
- i = 0;
- while (list)
- {
- if (list == llink)
- return i;
- i++;
- list = list->next;
- }
-
- return -1;
-}
-
-/**
- * g_list_index:
- * @list: a #GList
- * @data: the data to find
- *
- * Gets the position of the element containing
- * the given data (starting from 0).
- *
- * Returns: the index of the element containing the data,
- * or -1 if the data is not found
- */
-gint
-g_list_index (GList *list,
- gconstpointer data)
-{
- gint i;
-
- i = 0;
- while (list)
- {
- if (list->data == data)
- return i;
- i++;
- list = list->next;
- }
-
- return -1;
-}
-
-/**
- * g_list_last:
- * @list: a #GList
- *
- * Gets the last element in a #GList.
- *
- * Returns: the last element in the #GList,
- * or %NULL if the #GList has no elements
- */
-GList*
-g_list_last (GList *list)
-{
- if (list)
- {
- while (list->next)
- list = list->next;
- }
-
- return list;
-}
-
-/**
- * g_list_first:
- * @list: a #GList
- *
- * Gets the first element in a #GList.
- *
- * Returns: the first element in the #GList,
- * or %NULL if the #GList has no elements
- */
-GList*
-g_list_first (GList *list)
-{
- if (list)
- {
- while (list->prev)
- list = list->prev;
- }
-
- return list;
-}
-
-/**
- * g_list_length:
- * @list: a #GList
- *
- * Gets the number of elements in a #GList.
- *
- * <note><para>
- * This function iterates over the whole list to
- * count its elements.
- * </para></note>
- *
- * Returns: the number of elements in the #GList
- */
-guint
-g_list_length (GList *list)
-{
- guint length;
-
- length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
-
- return length;
-}
-
-/**
- * g_list_foreach:
- * @list: a #GList
- * @func: the function to call with each element's data
- * @user_data: user data to pass to the function
- *
- * Calls a function for each element of a #GList.
- */
-/**
- * GFunc:
- * @data: the element's data.
- * @user_data: user data passed to g_list_foreach() or
- * g_slist_foreach().
- *
- * Specifies the type of functions passed to g_list_foreach() and
- * g_slist_foreach().
- **/
-void
-g_list_foreach (GList *list,
- GFunc func,
- gpointer user_data)
-{
- while (list)
- {
- GList *next = list->next;
- (*func) (list->data, user_data);
- list = next;
- }
-}
-
-static GList*
-g_list_insert_sorted_real (GList *list,
- gpointer data,
- GFunc func,
- gpointer user_data)
-{
- GList *tmp_list = list;
- GList *new_list;
- gint cmp;
-
- g_return_val_if_fail (func != NULL, list);
-
- if (!list)
- {
- new_list = _g_list_alloc0 ();
- new_list->data = data;
- return new_list;
- }
-
- cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data);
-
- while ((tmp_list->next) && (cmp > 0))
- {
- tmp_list = tmp_list->next;
-
- cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data);
- }
-
- new_list = _g_list_alloc0 ();
- new_list->data = data;
-
- if ((!tmp_list->next) && (cmp > 0))
- {
- tmp_list->next = new_list;
- new_list->prev = tmp_list;
- return list;
- }
-
- if (tmp_list->prev)
- {
- tmp_list->prev->next = new_list;
- new_list->prev = tmp_list->prev;
- }
- new_list->next = tmp_list;
- tmp_list->prev = new_list;
-
- if (tmp_list == list)
- return new_list;
- else
- return list;
-}
-
-/**
- * g_list_insert_sorted:
- * @list: a pointer to a #GList
- * @data: the data for the new element
- * @func: the function to compare elements in the list. It should
- * return a number > 0 if the first parameter comes after the
- * second parameter in the sort order.
- *
- * Inserts a new element into the list, using the given comparison
- * function to determine its position.
- *
- * Returns: the new start of the #GList
- */
-GList*
-g_list_insert_sorted (GList *list,
- gpointer data,
- GCompareFunc func)
-{
- return g_list_insert_sorted_real (list, data, (GFunc) func, NULL);
-}
-
-/**
- * g_list_insert_sorted_with_data:
- * @list: a pointer to a #GList
- * @data: the data for the new element
- * @func: the function to compare elements in the list.
- * It should return a number > 0 if the first parameter
- * comes after the second parameter in the sort order.
- * @user_data: user data to pass to comparison function.
- *
- * Inserts a new element into the list, using the given comparison
- * function to determine its position.
- *
- * Returns: the new start of the #GList
- *
- * Since: 2.10
- */
-GList*
-g_list_insert_sorted_with_data (GList *list,
- gpointer data,
- GCompareDataFunc func,
- gpointer user_data)
-{
- return g_list_insert_sorted_real (list, data, (GFunc) func, user_data);
-}
-
-static GList *
-g_list_sort_merge (GList *l1,
- GList *l2,
- GFunc compare_func,
- gpointer user_data)
-{
- GList list, *l, *lprev;
- gint cmp;
-
- l = &list;
- lprev = NULL;
-
- while (l1 && l2)
- {
- cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
-
- if (cmp <= 0)
- {
- l->next = l1;
- l1 = l1->next;
- }
- else
- {
- l->next = l2;
- l2 = l2->next;
- }
- l = l->next;
- l->prev = lprev;
- lprev = l;
- }
- l->next = l1 ? l1 : l2;
- l->next->prev = l;
-
- return list.next;
-}
-
-static GList*
-g_list_sort_real (GList *list,
- GFunc compare_func,
- gpointer user_data)
-{
- GList *l1, *l2;
-
- if (!list)
- return NULL;
- if (!list->next)
- return list;
-
- l1 = list;
- l2 = list->next;
-
- while ((l2 = l2->next) != NULL)
- {
- if ((l2 = l2->next) == NULL)
- break;
- l1 = l1->next;
- }
- l2 = l1->next;
- l1->next = NULL;
-
- return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data),
- g_list_sort_real (l2, compare_func, user_data),
- compare_func,
- user_data);
-}
-
-/**
- * g_list_sort:
- * @list: a #GList
- * @compare_func: the comparison function used to sort the #GList.
- * This function is passed the data from 2 elements of the #GList
- * and should return 0 if they are equal, a negative value if the
- * first element comes before the second, or a positive value if
- * the first element comes after the second.
- *
- * Sorts a #GList using the given comparison function.
- *
- * Returns: the start of the sorted #GList
- */
-/**
- * GCompareFunc:
- * @a: a value.
- * @b: a value to compare with.
- * @Returns: negative value if @a < @b; zero if @a = @b; positive
- * value if @a > @b.
- *
- * Specifies the type of a comparison function used to compare two
- * values. The function should return a negative integer if the first
- * value comes before the second, 0 if they are equal, or a positive
- * integer if the first value comes after the second.
- **/
-GList *
-g_list_sort (GList *list,
- GCompareFunc compare_func)
-{
- return g_list_sort_real (list, (GFunc) compare_func, NULL);
-
-}
-
-/**
- * g_list_sort_with_data:
- * @list: a #GList
- * @compare_func: comparison function
- * @user_data: user data to pass to comparison function
- *
- * Like g_list_sort(), but the comparison function accepts
- * a user data argument.
- *
- * Returns: the new head of @list
- */
-/**
- * GCompareDataFunc:
- * @a: a value.
- * @b: a value to compare with.
- * @user_data: user data to pass to comparison function.
- * @Returns: negative value if @a < @b; zero if @a = @b; positive
- * value if @a > @b.
- *
- * Specifies the type of a comparison function used to compare two
- * values. The function should return a negative integer if the first
- * value comes before the second, 0 if they are equal, or a positive
- * integer if the first value comes after the second.
- **/
-GList *
-g_list_sort_with_data (GList *list,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- return g_list_sort_real (list, (GFunc) compare_func, user_data);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gmain.c: Main loop abstraction, timeouts, and idle functions
- * Copyright 1998 Owen Taylor
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#ifndef _WIN32
-/* for pipe2; need to define it first to avoid
- * other headers pulling in unistd.h
- */
-/* The meaning of_GNU_SOURCE that is intended here is present only on
- * Linux; avoid the possibility that some misguided header in MinGW
- * looks at it. Ideally we should define _GNU_SOURCE only on platforms
- * where we know what it means and that is what we want here
- * (i.e. Linux with glibc). After all, there might be some other POSIX
- * platform even where _GNU_SOURCE is used for some unrelated change
- * in semantics that isn't wanted. Sigh.
- */
-#define _GNU_SOURCE
-#endif
-
-#include "config.h"
-#include "glibconfig.h"
-
-/* Uncomment the next line (and the corresponding line in gpoll.c) to
- * enable debugging printouts if the environment variable
- * G_MAIN_POLL_DEBUG is set to some value.
- */
-/* #define G_MAIN_POLL_DEBUG */
-
-#ifdef _WIN32
-/* Always enable debugging printout on Windows, as it is more often
- * needed there...
- */
-#define G_MAIN_POLL_DEBUG
-#endif
-
-#include <signal.h>
-#include <sys/types.h>
-#include <time.h>
-#include <stdlib.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif /* HAVE_SYS_TIME_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#include <errno.h>
-
-#ifdef G_OS_WIN32
-#define STRICT
-#include <windows.h>
-#endif /* G_OS_WIN32 */
-
-#ifdef G_OS_BEOS
-#include <sys/socket.h>
-#include <sys/wait.h>
-#endif /* G_OS_BEOS */
-
-#ifdef G_OS_UNIX
-#include <fcntl.h>
-#include <sys/wait.h>
-#endif
-
-#include "gmain.h"
-
-#include "garray.h"
-#include "giochannel.h"
-#include "ghash.h"
-#include "ghook.h"
-#include "gqueue.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthreadprivate.h"
-
-#ifdef G_OS_WIN32
-#include "gwin32.h"
-#endif
-
-#ifdef G_MAIN_POLL_DEBUG
-#include "gtimer.h"
-#endif
-
-/**
- * SECTION:main
- * @title: The Main Event Loop
- * @short_description: manages all available sources of events
- *
- * The main event loop manages all the available sources of events for
- * GLib and GTK+ applications. These events can come from any number of
- * different types of sources such as file descriptors (plain files,
- * pipes or sockets) and timeouts. New types of event sources can also
- * be added using g_source_attach().
- *
- * To allow multiple independent sets of sources to be handled in
- * different threads, each source is associated with a #GMainContext.
- * A GMainContext can only be running in a single thread, but
- * sources can be added to it and removed from it from other threads.
- *
- * Each event source is assigned a priority. The default priority,
- * #G_PRIORITY_DEFAULT, is 0. Values less than 0 denote higher priorities.
- * Values greater than 0 denote lower priorities. Events from high priority
- * sources are always processed before events from lower priority sources.
- *
- * Idle functions can also be added, and assigned a priority. These will
- * be run whenever no events with a higher priority are ready to be processed.
- *
- * The #GMainLoop data type represents a main event loop. A GMainLoop is
- * created with g_main_loop_new(). After adding the initial event sources,
- * g_main_loop_run() is called. This continuously checks for new events from
- * each of the event sources and dispatches them. Finally, the processing of
- * an event from one of the sources leads to a call to g_main_loop_quit() to
- * exit the main loop, and g_main_loop_run() returns.
- *
- * It is possible to create new instances of #GMainLoop recursively.
- * This is often used in GTK+ applications when showing modal dialog
- * boxes. Note that event sources are associated with a particular
- * #GMainContext, and will be checked and dispatched for all main
- * loops associated with that GMainContext.
- *
- * GTK+ contains wrappers of some of these functions, e.g. gtk_main(),
- * gtk_main_quit() and gtk_events_pending().
- *
- * <refsect2><title>Creating new source types</title>
- * <para>One of the unusual features of the #GMainLoop functionality
- * is that new types of event source can be created and used in
- * addition to the builtin type of event source. A new event source
- * type is used for handling GDK events. A new source type is created
- * by <firstterm>deriving</firstterm> from the #GSource structure.
- * The derived type of source is represented by a structure that has
- * the #GSource structure as a first element, and other elements specific
- * to the new source type. To create an instance of the new source type,
- * call g_source_new() passing in the size of the derived structure and
- * a table of functions. These #GSourceFuncs determine the behavior of
- * the new source type.</para>
- * <para>New source types basically interact with the main context
- * in two ways. Their prepare function in #GSourceFuncs can set a timeout
- * to determine the maximum amount of time that the main loop will sleep
- * before checking the source again. In addition, or as well, the source
- * can add file descriptors to the set that the main context checks using
- * g_source_add_poll().</para>
- * </refsect2>
- * <refsect2><title>Customizing the main loop iteration</title>
- * <para>Single iterations of a #GMainContext can be run with
- * g_main_context_iteration(). In some cases, more detailed control
- * of exactly how the details of the main loop work is desired, for
- * instance, when integrating the #GMainLoop with an external main loop.
- * In such cases, you can call the component functions of
- * g_main_context_iteration() directly. These functions are
- * g_main_context_prepare(), g_main_context_query(),
- * g_main_context_check() and g_main_context_dispatch().</para>
- * <para>The operation of these functions can best be seen in terms
- * of a state diagram, as shown in <xref linkend="mainloop-states"/>.</para>
- * <figure id="mainloop-states"><title>States of a Main Context</title>
- * <graphic fileref="mainloop-states.gif" format="GIF"></graphic>
- * </figure>
- * </refsect2>
- */
-
-/* Types */
-
-typedef struct _GTimeoutSource GTimeoutSource;
-typedef struct _GChildWatchSource GChildWatchSource;
-typedef struct _GPollRec GPollRec;
-typedef struct _GSourceCallback GSourceCallback;
-
-typedef enum
-{
- G_SOURCE_READY = 1 << G_HOOK_FLAG_USER_SHIFT,
- G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)
-} GSourceFlags;
-
-#ifdef G_THREADS_ENABLED
-typedef struct _GMainWaiter GMainWaiter;
-
-struct _GMainWaiter
-{
- GCond *cond;
- GMutex *mutex;
-};
-#endif
-
-typedef struct _GMainDispatch GMainDispatch;
-
-struct _GMainDispatch
-{
- gint depth;
- GSList *dispatching_sources; /* stack of current sources */
-};
-
-#ifdef G_MAIN_POLL_DEBUG
-gboolean _g_main_poll_debug = FALSE;
-#endif
-
-struct _GMainContext
-{
-#ifdef G_THREADS_ENABLED
- /* The following lock is used for both the list of sources
- * and the list of poll records
- */
- GStaticMutex mutex;
- GCond *cond;
- GThread *owner;
- guint owner_count;
- GSList *waiters;
-#endif
-
- gint ref_count;
-
- GPtrArray *pending_dispatches;
- gint timeout; /* Timeout for current iteration */
-
- guint next_id;
- GSource *source_list;
- gint in_check_or_prepare;
-
- GPollRec *poll_records;
- guint n_poll_records;
- GPollFD *cached_poll_array;
- guint cached_poll_array_size;
-
-#ifdef G_THREADS_ENABLED
-#ifndef G_OS_WIN32
-/* this pipe is used to wake up the main loop when a source is added.
- */
- gint wake_up_pipe[2];
-#else /* G_OS_WIN32 */
- HANDLE wake_up_semaphore;
-#endif /* G_OS_WIN32 */
-
- GPollFD wake_up_rec;
- gboolean poll_waiting;
-
-/* Flag indicating whether the set of fd's changed during a poll */
- gboolean poll_changed;
-#endif /* G_THREADS_ENABLED */
-
- GPollFunc poll_func;
-
- GTimeVal current_time;
- gboolean time_is_current;
-};
-
-struct _GSourceCallback
-{
- guint ref_count;
- GSourceFunc func;
- gpointer data;
- GDestroyNotify notify;
-};
-
-struct _GMainLoop
-{
- GMainContext *context;
- gboolean is_running;
- gint ref_count;
-};
-
-struct _GTimeoutSource
-{
- GSource source;
- GTimeVal expiration;
- guint interval;
- guint granularity;
-};
-
-struct _GChildWatchSource
-{
- GSource source;
- GPid pid;
- gint child_status;
-#ifdef G_OS_WIN32
- GPollFD poll;
-#else /* G_OS_WIN32 */
- gint count;
- gboolean child_exited;
-#endif /* G_OS_WIN32 */
-};
-
-struct _GPollRec
-{
- GPollFD *fd;
- GPollRec *next;
- gint priority;
-};
-
-#ifdef G_THREADS_ENABLED
-#define LOCK_CONTEXT(context) g_static_mutex_lock (&context->mutex)
-#define UNLOCK_CONTEXT(context) g_static_mutex_unlock (&context->mutex)
-#define G_THREAD_SELF g_thread_self ()
-#else
-#define LOCK_CONTEXT(context) (void)0
-#define UNLOCK_CONTEXT(context) (void)0
-#define G_THREAD_SELF NULL
-#endif
-
-#define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
-#define SOURCE_BLOCKED(source) (((source)->flags & G_HOOK_FLAG_IN_CALL) != 0 && \
- ((source)->flags & G_SOURCE_CAN_RECURSE) == 0)
-
-#define SOURCE_UNREF(source, context) \
- G_STMT_START { \
- if ((source)->ref_count > 1) \
- (source)->ref_count--; \
- else \
- g_source_unref_internal ((source), (context), TRUE); \
- } G_STMT_END
-
-
-/* Forward declarations */
-
-static void g_source_unref_internal (GSource *source,
- GMainContext *context,
- gboolean have_lock);
-static void g_source_destroy_internal (GSource *source,
- GMainContext *context,
- gboolean have_lock);
-static void g_main_context_poll (GMainContext *context,
- gint timeout,
- gint priority,
- GPollFD *fds,
- gint n_fds);
-static void g_main_context_add_poll_unlocked (GMainContext *context,
- gint priority,
- GPollFD *fd);
-static void g_main_context_remove_poll_unlocked (GMainContext *context,
- GPollFD *fd);
-static void g_main_context_wakeup_unlocked (GMainContext *context);
-
-static gboolean g_timeout_prepare (GSource *source,
- gint *timeout);
-static gboolean g_timeout_check (GSource *source);
-static gboolean g_timeout_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data);
-static gboolean g_child_watch_prepare (GSource *source,
- gint *timeout);
-static gboolean g_child_watch_check (GSource *source);
-static gboolean g_child_watch_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data);
-static gboolean g_idle_prepare (GSource *source,
- gint *timeout);
-static gboolean g_idle_check (GSource *source);
-static gboolean g_idle_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data);
-
-G_LOCK_DEFINE_STATIC (main_loop);
-static GMainContext *default_main_context;
-static GSList *main_contexts_without_pipe = NULL;
-
-#ifndef G_OS_WIN32
-/* Child status monitoring code */
-enum {
- CHILD_WATCH_UNINITIALIZED,
- CHILD_WATCH_INITIALIZED_SINGLE,
- CHILD_WATCH_INITIALIZED_THREADED
-};
-static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED;
-static gint child_watch_count = 1;
-static gint child_watch_wake_up_pipe[2] = {0, 0};
-#endif /* !G_OS_WIN32 */
-G_LOCK_DEFINE_STATIC (main_context_list);
-static GSList *main_context_list = NULL;
-
-static gint timer_perturb = -1;
-
-GSourceFuncs g_timeout_funcs =
-{
- g_timeout_prepare,
- g_timeout_check,
- g_timeout_dispatch,
- NULL
-};
-
-GSourceFuncs g_child_watch_funcs =
-{
- g_child_watch_prepare,
- g_child_watch_check,
- g_child_watch_dispatch,
- NULL
-};
-
-GSourceFuncs g_idle_funcs =
-{
- g_idle_prepare,
- g_idle_check,
- g_idle_dispatch,
- NULL
-};
-
-/**
- * g_main_context_ref:
- * @context: a #GMainContext
- *
- * Increases the reference count on a #GMainContext object by one.
- *
- * Returns: the @context that was passed in (since 2.6)
- **/
-GMainContext *
-g_main_context_ref (GMainContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
- g_return_val_if_fail (g_atomic_int_get (&context->ref_count) > 0, NULL);
-
- g_atomic_int_inc (&context->ref_count);
-
- return context;
-}
-
-static inline void
-poll_rec_list_free (GMainContext *context,
- GPollRec *list)
-{
- g_slice_free_chain (GPollRec, list, next);
-}
-
-/**
- * g_main_context_unref:
- * @context: a #GMainContext
- *
- * Decreases the reference count on a #GMainContext object by one. If
- * the result is zero, free the context and free all associated memory.
- **/
-void
-g_main_context_unref (GMainContext *context)
-{
- GSource *source;
- g_return_if_fail (context != NULL);
- g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
-
- if (!g_atomic_int_dec_and_test (&context->ref_count))
- return;
-
- G_LOCK (main_context_list);
- main_context_list = g_slist_remove (main_context_list, context);
- G_UNLOCK (main_context_list);
-
- source = context->source_list;
- while (source)
- {
- GSource *next = source->next;
- g_source_destroy_internal (source, context, FALSE);
- source = next;
- }
-
-#ifdef G_THREADS_ENABLED
- g_static_mutex_free (&context->mutex);
-#endif
-
- g_ptr_array_free (context->pending_dispatches, TRUE);
- g_free (context->cached_poll_array);
-
- poll_rec_list_free (context, context->poll_records);
-
-#ifdef G_THREADS_ENABLED
- if (g_thread_supported())
- {
-#ifndef G_OS_WIN32
- close (context->wake_up_pipe[0]);
- close (context->wake_up_pipe[1]);
-#else
- CloseHandle (context->wake_up_semaphore);
-#endif
- }
- else
- main_contexts_without_pipe = g_slist_remove (main_contexts_without_pipe,
- context);
-
- if (context->cond != NULL)
- g_cond_free (context->cond);
-#endif
-
- g_free (context);
-}
-
-#ifdef G_THREADS_ENABLED
-static void
-g_main_context_init_pipe (GMainContext *context)
-{
-# ifndef G_OS_WIN32
- if (context->wake_up_pipe[0] != -1)
- return;
-
-#ifdef HAVE_PIPE2
- /* if this fails, we fall through and try pipe */
- pipe2 (context->wake_up_pipe, O_CLOEXEC);
-#endif
- if (context->wake_up_pipe[0] == -1)
- {
- if (pipe (context->wake_up_pipe) < 0)
- g_error ("Cannot create pipe main loop wake-up: %s\n",
- g_strerror (errno));
-
- fcntl (context->wake_up_pipe[0], F_SETFD, FD_CLOEXEC);
- fcntl (context->wake_up_pipe[1], F_SETFD, FD_CLOEXEC);
- }
-
- context->wake_up_rec.fd = context->wake_up_pipe[0];
- context->wake_up_rec.events = G_IO_IN;
-# else
- if (context->wake_up_semaphore != NULL)
- return;
- context->wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL);
- if (context->wake_up_semaphore == NULL)
- g_error ("Cannot create wake-up semaphore: %s",
- g_win32_error_message (GetLastError ()));
- context->wake_up_rec.fd = (gintptr) context->wake_up_semaphore;
- context->wake_up_rec.events = G_IO_IN;
-
- if (_g_main_poll_debug)
- g_print ("wake-up semaphore: %p\n", context->wake_up_semaphore);
-# endif
- g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec);
-}
-
-void
-_g_main_thread_init (void)
-{
- GSList *curr = main_contexts_without_pipe;
- while (curr)
- {
- g_main_context_init_pipe ((GMainContext *)curr->data);
- curr = curr->next;
- }
- g_slist_free (main_contexts_without_pipe);
- main_contexts_without_pipe = NULL;
-}
-#endif /* G_THREADS_ENABLED */
-
-/**
- * g_main_context_new:
- *
- * Creates a new #GMainContext structure.
- *
- * Return value: the new #GMainContext
- **/
-GMainContext *
-g_main_context_new (void)
-{
- GMainContext *context = g_new0 (GMainContext, 1);
-
-#ifdef G_MAIN_POLL_DEBUG
- {
- static gboolean beenhere = FALSE;
-
- if (!beenhere)
- {
- if (getenv ("G_MAIN_POLL_DEBUG") != NULL)
- _g_main_poll_debug = TRUE;
- beenhere = TRUE;
- }
- }
-#endif
-
-#ifdef G_THREADS_ENABLED
- g_static_mutex_init (&context->mutex);
-
- context->owner = NULL;
- context->waiters = NULL;
-
-# ifndef G_OS_WIN32
- context->wake_up_pipe[0] = -1;
- context->wake_up_pipe[1] = -1;
-# else
- context->wake_up_semaphore = NULL;
-# endif
-#endif
-
- context->ref_count = 1;
-
- context->next_id = 1;
-
- context->source_list = NULL;
-
- context->poll_func = g_poll;
-
- context->cached_poll_array = NULL;
- context->cached_poll_array_size = 0;
-
- context->pending_dispatches = g_ptr_array_new ();
-
- context->time_is_current = FALSE;
-
-#ifdef G_THREADS_ENABLED
- if (g_thread_supported ())
- g_main_context_init_pipe (context);
- else
- main_contexts_without_pipe = g_slist_prepend (main_contexts_without_pipe,
- context);
-#endif
-
- G_LOCK (main_context_list);
- main_context_list = g_slist_append (main_context_list, context);
-
-#ifdef G_MAIN_POLL_DEBUG
- if (_g_main_poll_debug)
- g_print ("created context=%p\n", context);
-#endif
-
- G_UNLOCK (main_context_list);
-
- return context;
-}
-
-/**
- * g_main_context_default:
- *
- * Returns the global default main context. This is the main context
- * used for main loop functions when a main loop is not explicitly
- * specified, and corresponds to the "main" main loop. See also
- * g_main_context_get_thread_default().
- *
- * Return value: the global default main context.
- **/
-GMainContext *
-g_main_context_default (void)
-{
- /* Slow, but safe */
-
- G_LOCK (main_loop);
-
- if (!default_main_context)
- {
- default_main_context = g_main_context_new ();
-#ifdef G_MAIN_POLL_DEBUG
- if (_g_main_poll_debug)
- g_print ("default context=%p\n", default_main_context);
-#endif
- }
-
- G_UNLOCK (main_loop);
-
- return default_main_context;
-}
-
-static GStaticPrivate thread_context_stack = G_STATIC_PRIVATE_INIT;
-
-static void
-free_context_stack (gpointer data)
-{
- GQueue *stack = data;
- GMainContext *context;
-
- while (!g_queue_is_empty (stack))
- {
- context = g_queue_pop_head (stack);
- g_main_context_release (context);
- if (context)
- g_main_context_unref (context);
- }
- g_queue_free (stack);
-}
-
-/**
- * g_main_context_push_thread_default:
- * @context: a #GMainContext, or %NULL for the global default context
- *
- * Acquires @context and sets it as the thread-default context for the
- * current thread. This will cause certain asynchronous operations
- * (such as most <link linkend="gio">gio</link>-based I/O) which are
- * started in this thread to run under @context and deliver their
- * results to its main loop, rather than running under the global
- * default context in the main thread. Note that calling this function
- * changes the context returned by
- * g_main_context_get_thread_default(), <emphasis>not</emphasis> the
- * one returned by g_main_context_default(), so it does not affect the
- * context used by functions like g_idle_add().
- *
- * Normally you would call this function shortly after creating a new
- * thread, passing it a #GMainContext which will be run by a
- * #GMainLoop in that thread, to set a new default context for all
- * async operations in that thread. (In this case, you don't need to
- * ever call g_main_context_pop_thread_default().) In some cases
- * however, you may want to schedule a single operation in a
- * non-default context, or temporarily use a non-default context in
- * the main thread. In that case, you can wrap the call to the
- * asynchronous operation inside a
- * g_main_context_push_thread_default() /
- * g_main_context_pop_thread_default() pair, but it is up to you to
- * ensure that no other asynchronous operations accidentally get
- * started while the non-default context is active.
- *
- * Beware that libraries that predate this function may not correctly
- * handle being used from a thread with a thread-default context. Eg,
- * see g_file_supports_thread_contexts().
- *
- * Since: 2.22
- **/
-void
-g_main_context_push_thread_default (GMainContext *context)
-{
- GQueue *stack;
- gboolean acquired_context;
-
- acquired_context = g_main_context_acquire (context);
- g_return_if_fail (acquired_context);
-
- if (context == g_main_context_default ())
- context = NULL;
- else if (context)
- g_main_context_ref (context);
-
- stack = g_static_private_get (&thread_context_stack);
- if (!stack)
- {
- stack = g_queue_new ();
- g_static_private_set (&thread_context_stack, stack,
- free_context_stack);
- }
-
- g_queue_push_head (stack, context);
-}
-
-/**
- * g_main_context_pop_thread_default:
- * @context: a #GMainContext object, or %NULL
- *
- * Pops @context off the thread-default context stack (verifying that
- * it was on the top of the stack).
- *
- * Since: 2.22
- **/
-void
-g_main_context_pop_thread_default (GMainContext *context)
-{
- GQueue *stack;
-
- if (context == g_main_context_default ())
- context = NULL;
-
- stack = g_static_private_get (&thread_context_stack);
-
- g_return_if_fail (stack != NULL);
- g_return_if_fail (g_queue_peek_head (stack) == context);
-
- g_queue_pop_head (stack);
-
- g_main_context_release (context);
- if (context)
- g_main_context_unref (context);
-}
-
-/**
- * g_main_context_get_thread_default:
- *
- * Gets the thread-default #GMainContext for this thread. Asynchronous
- * operations that want to be able to be run in contexts other than
- * the default one should call this method to get a #GMainContext to
- * add their #GSource<!-- -->s to. (Note that even in single-threaded
- * programs applications may sometimes want to temporarily push a
- * non-default context, so it is not safe to assume that this will
- * always return %NULL if threads are not initialized.)
- *
- * Returns: the thread-default #GMainContext, or %NULL if the
- * thread-default context is the global default context.
- *
- * Since: 2.22
- **/
-GMainContext *
-g_main_context_get_thread_default (void)
-{
- GQueue *stack;
-
- stack = g_static_private_get (&thread_context_stack);
- if (stack)
- return g_queue_peek_head (stack);
- else
- return NULL;
-}
-
-/* Hooks for adding to the main loop */
-
-/**
- * g_source_new:
- * @source_funcs: structure containing functions that implement
- * the sources behavior.
- * @struct_size: size of the #GSource structure to create.
- *
- * Creates a new #GSource structure. The size is specified to
- * allow creating structures derived from #GSource that contain
- * additional data. The size passed in must be at least
- * <literal>sizeof (GSource)</literal>.
- *
- * The source will not initially be associated with any #GMainContext
- * and must be added to one with g_source_attach() before it will be
- * executed.
- *
- * Return value: the newly-created #GSource.
- **/
-GSource *
-g_source_new (GSourceFuncs *source_funcs,
- guint struct_size)
-{
- GSource *source;
-
- g_return_val_if_fail (source_funcs != NULL, NULL);
- g_return_val_if_fail (struct_size >= sizeof (GSource), NULL);
-
- source = (GSource*) g_malloc0 (struct_size);
-
- source->source_funcs = source_funcs;
- source->ref_count = 1;
-
- source->priority = G_PRIORITY_DEFAULT;
-
- source->flags = G_HOOK_FLAG_ACTIVE;
-
- /* NULL/0 initialization for all other fields */
-
- return source;
-}
-
-/* Holds context's lock
- */
-static void
-g_source_list_add (GSource *source,
- GMainContext *context)
-{
- GSource *tmp_source, *last_source;
-
- last_source = NULL;
- tmp_source = context->source_list;
- while (tmp_source && tmp_source->priority <= source->priority)
- {
- last_source = tmp_source;
- tmp_source = tmp_source->next;
- }
-
- source->next = tmp_source;
- if (tmp_source)
- tmp_source->prev = source;
-
- source->prev = last_source;
- if (last_source)
- last_source->next = source;
- else
- context->source_list = source;
-}
-
-/* Holds context's lock
- */
-static void
-g_source_list_remove (GSource *source,
- GMainContext *context)
-{
- if (source->prev)
- source->prev->next = source->next;
- else
- context->source_list = source->next;
-
- if (source->next)
- source->next->prev = source->prev;
-
- source->prev = NULL;
- source->next = NULL;
-}
-
-/**
- * g_source_attach:
- * @source: a #GSource
- * @context: a #GMainContext (if %NULL, the default context will be used)
- *
- * Adds a #GSource to a @context so that it will be executed within
- * that context. Remove it by calling g_source_destroy().
- *
- * Return value: the ID (greater than 0) for the source within the
- * #GMainContext.
- **/
-guint
-g_source_attach (GSource *source,
- GMainContext *context)
-{
- guint result = 0;
- GSList *tmp_list;
-
- g_return_val_if_fail (source->context == NULL, 0);
- g_return_val_if_fail (!SOURCE_DESTROYED (source), 0);
-
- if (!context)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- source->context = context;
- result = source->source_id = context->next_id++;
-
- source->ref_count++;
- g_source_list_add (source, context);
-
- tmp_list = source->poll_fds;
- while (tmp_list)
- {
- g_main_context_add_poll_unlocked (context, source->priority, tmp_list->data);
- tmp_list = tmp_list->next;
- }
-
-#ifdef G_THREADS_ENABLED
- /* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
-#endif
-
- UNLOCK_CONTEXT (context);
-
- return result;
-}
-
-static void
-g_source_destroy_internal (GSource *source,
- GMainContext *context,
- gboolean have_lock)
-{
- if (!have_lock)
- LOCK_CONTEXT (context);
-
- if (!SOURCE_DESTROYED (source))
- {
- GSList *tmp_list;
- gpointer old_cb_data;
- GSourceCallbackFuncs *old_cb_funcs;
-
- source->flags &= ~G_HOOK_FLAG_ACTIVE;
-
- old_cb_data = source->callback_data;
- old_cb_funcs = source->callback_funcs;
-
- source->callback_data = NULL;
- source->callback_funcs = NULL;
-
- if (old_cb_funcs)
- {
- UNLOCK_CONTEXT (context);
- old_cb_funcs->unref (old_cb_data);
- LOCK_CONTEXT (context);
- }
-
- if (!SOURCE_BLOCKED (source))
- {
- tmp_list = source->poll_fds;
- while (tmp_list)
- {
- g_main_context_remove_poll_unlocked (context, tmp_list->data);
- tmp_list = tmp_list->next;
- }
- }
-
- g_source_unref_internal (source, context, TRUE);
- }
-
- if (!have_lock)
- UNLOCK_CONTEXT (context);
-}
-
-/**
- * g_source_destroy:
- * @source: a #GSource
- *
- * Removes a source from its #GMainContext, if any, and mark it as
- * destroyed. The source cannot be subsequently added to another
- * context.
- **/
-void
-g_source_destroy (GSource *source)
-{
- GMainContext *context;
-
- g_return_if_fail (source != NULL);
-
- context = source->context;
-
- if (context)
- g_source_destroy_internal (source, context, FALSE);
- else
- source->flags &= ~G_HOOK_FLAG_ACTIVE;
-}
-
-/**
- * g_source_get_id:
- * @source: a #GSource
- *
- * Returns the numeric ID for a particular source. The ID of a source
- * is a positive integer which is unique within a particular main loop
- * context. The reverse
- * mapping from ID to source is done by g_main_context_find_source_by_id().
- *
- * Return value: the ID (greater than 0) for the source
- **/
-guint
-g_source_get_id (GSource *source)
-{
- guint result;
-
- g_return_val_if_fail (source != NULL, 0);
- g_return_val_if_fail (source->context != NULL, 0);
-
- LOCK_CONTEXT (source->context);
- result = source->source_id;
- UNLOCK_CONTEXT (source->context);
-
- return result;
-}
-
-/**
- * g_source_get_context:
- * @source: a #GSource
- *
- * Gets the #GMainContext with which the source is associated.
- * Calling this function on a destroyed source is an error.
- *
- * Return value: the #GMainContext with which the source is associated,
- * or %NULL if the context has not yet been added
- * to a source.
- **/
-GMainContext *
-g_source_get_context (GSource *source)
-{
- g_return_val_if_fail (!SOURCE_DESTROYED (source), NULL);
-
- return source->context;
-}
-
-/**
- * g_source_add_poll:
- * @source:a #GSource
- * @fd: a #GPollFD structure holding information about a file
- * descriptor to watch.
- *
- * Adds a file descriptor to the set of file descriptors polled for
- * this source. This is usually combined with g_source_new() to add an
- * event source. The event source's check function will typically test
- * the @revents field in the #GPollFD struct and return %TRUE if events need
- * to be processed.
- **/
-void
-g_source_add_poll (GSource *source,
- GPollFD *fd)
-{
- GMainContext *context;
-
- g_return_if_fail (source != NULL);
- g_return_if_fail (fd != NULL);
- g_return_if_fail (!SOURCE_DESTROYED (source));
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- source->poll_fds = g_slist_prepend (source->poll_fds, fd);
-
- if (context)
- {
- if (!SOURCE_BLOCKED (source))
- g_main_context_add_poll_unlocked (context, source->priority, fd);
- UNLOCK_CONTEXT (context);
- }
-}
-
-/**
- * g_source_remove_poll:
- * @source:a #GSource
- * @fd: a #GPollFD structure previously passed to g_source_add_poll().
- *
- * Removes a file descriptor from the set of file descriptors polled for
- * this source.
- **/
-void
-g_source_remove_poll (GSource *source,
- GPollFD *fd)
-{
- GMainContext *context;
-
- g_return_if_fail (source != NULL);
- g_return_if_fail (fd != NULL);
- g_return_if_fail (!SOURCE_DESTROYED (source));
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- source->poll_fds = g_slist_remove (source->poll_fds, fd);
-
- if (context)
- {
- if (!SOURCE_BLOCKED (source))
- g_main_context_remove_poll_unlocked (context, fd);
- UNLOCK_CONTEXT (context);
- }
-}
-
-/**
- * g_source_set_callback_indirect:
- * @source: the source
- * @callback_data: pointer to callback data "object"
- * @callback_funcs: functions for reference counting @callback_data
- * and getting the callback and data
- *
- * Sets the callback function storing the data as a refcounted callback
- * "object". This is used internally. Note that calling
- * g_source_set_callback_indirect() assumes
- * an initial reference count on @callback_data, and thus
- * @callback_funcs->unref will eventually be called once more
- * than @callback_funcs->ref.
- **/
-void
-g_source_set_callback_indirect (GSource *source,
- gpointer callback_data,
- GSourceCallbackFuncs *callback_funcs)
-{
- GMainContext *context;
- gpointer old_cb_data;
- GSourceCallbackFuncs *old_cb_funcs;
-
- g_return_if_fail (source != NULL);
- g_return_if_fail (callback_funcs != NULL || callback_data == NULL);
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- old_cb_data = source->callback_data;
- old_cb_funcs = source->callback_funcs;
-
- source->callback_data = callback_data;
- source->callback_funcs = callback_funcs;
-
- if (context)
- UNLOCK_CONTEXT (context);
-
- if (old_cb_funcs)
- old_cb_funcs->unref (old_cb_data);
-}
-
-static void
-g_source_callback_ref (gpointer cb_data)
-{
- GSourceCallback *callback = cb_data;
-
- callback->ref_count++;
-}
-
-
-static void
-g_source_callback_unref (gpointer cb_data)
-{
- GSourceCallback *callback = cb_data;
-
- callback->ref_count--;
- if (callback->ref_count == 0)
- {
- if (callback->notify)
- callback->notify (callback->data);
- g_free (callback);
- }
-}
-
-static void
-g_source_callback_get (gpointer cb_data,
- GSource *source,
- GSourceFunc *func,
- gpointer *data)
-{
- GSourceCallback *callback = cb_data;
-
- *func = callback->func;
- *data = callback->data;
-}
-
-static GSourceCallbackFuncs g_source_callback_funcs = {
- g_source_callback_ref,
- g_source_callback_unref,
- g_source_callback_get,
-};
-
-/**
- * g_source_set_callback:
- * @source: the source
- * @func: a callback function
- * @data: the data to pass to callback function
- * @notify: a function to call when @data is no longer in use, or %NULL.
- *
- * Sets the callback function for a source. The callback for a source is
- * called from the source's dispatch function.
- *
- * The exact type of @func depends on the type of source; ie. you
- * should not count on @func being called with @data as its first
- * parameter.
- *
- * Typically, you won't use this function. Instead use functions specific
- * to the type of source you are using.
- **/
-void
-g_source_set_callback (GSource *source,
- GSourceFunc func,
- gpointer data,
- GDestroyNotify notify)
-{
- GSourceCallback *new_callback;
-
- g_return_if_fail (source != NULL);
-
- new_callback = g_new (GSourceCallback, 1);
-
- new_callback->ref_count = 1;
- new_callback->func = func;
- new_callback->data = data;
- new_callback->notify = notify;
-
- g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs);
-}
-
-
-/**
- * g_source_set_funcs:
- * @source: a #GSource
- * @funcs: the new #GSourceFuncs
- *
- * Sets the source functions (can be used to override
- * default implementations) of an unattached source.
- *
- * Since: 2.12
- */
-void
-g_source_set_funcs (GSource *source,
- GSourceFuncs *funcs)
-{
- g_return_if_fail (source != NULL);
- g_return_if_fail (source->context == NULL);
- g_return_if_fail (source->ref_count > 0);
- g_return_if_fail (funcs != NULL);
-
- source->source_funcs = funcs;
-}
-
-/**
- * g_source_set_priority:
- * @source: a #GSource
- * @priority: the new priority.
- *
- * Sets the priority of a source. While the main loop is being
- * run, a source will be dispatched if it is ready to be dispatched and no sources
- * at a higher (numerically smaller) priority are ready to be dispatched.
- **/
-void
-g_source_set_priority (GSource *source,
- gint priority)
-{
- GSList *tmp_list;
- GMainContext *context;
-
- g_return_if_fail (source != NULL);
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- source->priority = priority;
-
- if (context)
- {
- /* Remove the source from the context's source and then
- * add it back so it is sorted in the correct plcae
- */
- g_source_list_remove (source, source->context);
- g_source_list_add (source, source->context);
-
- if (!SOURCE_BLOCKED (source))
- {
- tmp_list = source->poll_fds;
- while (tmp_list)
- {
- g_main_context_remove_poll_unlocked (context, tmp_list->data);
- g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
-
- tmp_list = tmp_list->next;
- }
- }
-
- UNLOCK_CONTEXT (source->context);
- }
-}
-
-/**
- * g_source_get_priority:
- * @source: a #GSource
- *
- * Gets the priority of a source.
- *
- * Return value: the priority of the source
- **/
-gint
-g_source_get_priority (GSource *source)
-{
- g_return_val_if_fail (source != NULL, 0);
-
- return source->priority;
-}
-
-/**
- * g_source_set_can_recurse:
- * @source: a #GSource
- * @can_recurse: whether recursion is allowed for this source
- *
- * Sets whether a source can be called recursively. If @can_recurse is
- * %TRUE, then while the source is being dispatched then this source
- * will be processed normally. Otherwise, all processing of this
- * source is blocked until the dispatch function returns.
- **/
-void
-g_source_set_can_recurse (GSource *source,
- gboolean can_recurse)
-{
- GMainContext *context;
-
- g_return_if_fail (source != NULL);
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- if (can_recurse)
- source->flags |= G_SOURCE_CAN_RECURSE;
- else
- source->flags &= ~G_SOURCE_CAN_RECURSE;
-
- if (context)
- UNLOCK_CONTEXT (context);
-}
-
-/**
- * g_source_get_can_recurse:
- * @source: a #GSource
- *
- * Checks whether a source is allowed to be called recursively.
- * see g_source_set_can_recurse().
- *
- * Return value: whether recursion is allowed.
- **/
-gboolean
-g_source_get_can_recurse (GSource *source)
-{
- g_return_val_if_fail (source != NULL, FALSE);
-
- return (source->flags & G_SOURCE_CAN_RECURSE) != 0;
-}
-
-
-/**
- * g_source_set_name:
- * @source: a #GSource
- * @name: debug name for the source
- *
- * Sets a name for the source, used in debugging and profiling.
- * The name defaults to #NULL.
- *
- * The source name should describe in a human-readable way
- * what the source does. For example, "X11 event queue"
- * or "GTK+ repaint idle handler" or whatever it is.
- *
- * It is permitted to call this function multiple times, but is not
- * recommended due to the potential performance impact. For example,
- * one could change the name in the "check" function of a #GSourceFuncs
- * to include details like the event type in the source name.
- *
- * Since: 2.26
- **/
-void
-g_source_set_name (GSource *source,
- const char *name)
-{
- g_return_if_fail (source != NULL);
-
- /* setting back to NULL is allowed, just because it's
- * weird if get_name can return NULL but you can't
- * set that.
- */
-
- g_free (source->name);
- source->name = g_strdup (name);
-}
-
-/**
- * g_source_get_name:
- * @source: a #GSource
- *
- * Gets a name for the source, used in debugging and profiling.
- * The name may be #NULL if it has never been set with
- * g_source_set_name().
- *
- * Return value: the name of the source
- * Since: 2.26
- **/
-G_CONST_RETURN char*
-g_source_get_name (GSource *source)
-{
- g_return_val_if_fail (source != NULL, NULL);
-
- return source->name;
-}
-
-/**
- * g_source_set_name_by_id:
- * @tag: a #GSource ID
- * @name: debug name for the source
- *
- * Sets the name of a source using its ID.
- *
- * This is a convenience utility to set source names from the return
- * value of g_idle_add(), g_timeout_add(), etc.
- *
- * Since: 2.26
- **/
-void
-g_source_set_name_by_id (guint tag,
- const char *name)
-{
- GSource *source;
-
- g_return_if_fail (tag > 0);
-
- source = g_main_context_find_source_by_id (NULL, tag);
- if (source == NULL)
- return;
-
- g_source_set_name (source, name);
-}
-
-
-/**
- * g_source_ref:
- * @source: a #GSource
- *
- * Increases the reference count on a source by one.
- *
- * Return value: @source
- **/
-GSource *
-g_source_ref (GSource *source)
-{
- GMainContext *context;
-
- g_return_val_if_fail (source != NULL, NULL);
-
- context = source->context;
-
- if (context)
- LOCK_CONTEXT (context);
-
- source->ref_count++;
-
- if (context)
- UNLOCK_CONTEXT (context);
-
- return source;
-}
-
-/* g_source_unref() but possible to call within context lock
- */
-static void
-g_source_unref_internal (GSource *source,
- GMainContext *context,
- gboolean have_lock)
-{
- gpointer old_cb_data = NULL;
- GSourceCallbackFuncs *old_cb_funcs = NULL;
-
- g_return_if_fail (source != NULL);
-
- if (!have_lock && context)
- LOCK_CONTEXT (context);
-
- source->ref_count--;
- if (source->ref_count == 0)
- {
- old_cb_data = source->callback_data;
- old_cb_funcs = source->callback_funcs;
-
- source->callback_data = NULL;
- source->callback_funcs = NULL;
-
- if (context && !SOURCE_DESTROYED (source))
- {
- g_warning (G_STRLOC ": ref_count == 0, but source is still attached to a context!");
- source->ref_count++;
- }
- else if (context)
- g_source_list_remove (source, context);
-
- if (source->source_funcs->finalize)
- source->source_funcs->finalize (source);
-
- g_free (source->name);
- source->name = NULL;
-
- g_slist_free (source->poll_fds);
- source->poll_fds = NULL;
- g_free (source);
- }
-
- if (!have_lock && context)
- UNLOCK_CONTEXT (context);
-
- if (old_cb_funcs)
- {
- if (have_lock)
- UNLOCK_CONTEXT (context);
-
- old_cb_funcs->unref (old_cb_data);
-
- if (have_lock)
- LOCK_CONTEXT (context);
- }
-}
-
-/**
- * g_source_unref:
- * @source: a #GSource
- *
- * Decreases the reference count of a source by one. If the
- * resulting reference count is zero the source and associated
- * memory will be destroyed.
- **/
-void
-g_source_unref (GSource *source)
-{
- g_return_if_fail (source != NULL);
-
- g_source_unref_internal (source, source->context, FALSE);
-}
-
-/**
- * g_main_context_find_source_by_id:
- * @context: a #GMainContext (if %NULL, the default context will be used)
- * @source_id: the source ID, as returned by g_source_get_id().
- *
- * Finds a #GSource given a pair of context and ID.
- *
- * Return value: the #GSource if found, otherwise, %NULL
- **/
-GSource *
-g_main_context_find_source_by_id (GMainContext *context,
- guint source_id)
-{
- GSource *source;
-
- g_return_val_if_fail (source_id > 0, NULL);
-
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- source = context->source_list;
- while (source)
- {
- if (!SOURCE_DESTROYED (source) &&
- source->source_id == source_id)
- break;
- source = source->next;
- }
-
- UNLOCK_CONTEXT (context);
-
- return source;
-}
-
-/**
- * g_main_context_find_source_by_funcs_user_data:
- * @context: a #GMainContext (if %NULL, the default context will be used).
- * @funcs: the @source_funcs passed to g_source_new().
- * @user_data: the user data from the callback.
- *
- * Finds a source with the given source functions and user data. If
- * multiple sources exist with the same source function and user data,
- * the first one found will be returned.
- *
- * Return value: the source, if one was found, otherwise %NULL
- **/
-GSource *
-g_main_context_find_source_by_funcs_user_data (GMainContext *context,
- GSourceFuncs *funcs,
- gpointer user_data)
-{
- GSource *source;
-
- g_return_val_if_fail (funcs != NULL, NULL);
-
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- source = context->source_list;
- while (source)
- {
- if (!SOURCE_DESTROYED (source) &&
- source->source_funcs == funcs &&
- source->callback_funcs)
- {
- GSourceFunc callback;
- gpointer callback_data;
-
- source->callback_funcs->get (source->callback_data, source, &callback, &callback_data);
-
- if (callback_data == user_data)
- break;
- }
- source = source->next;
- }
-
- UNLOCK_CONTEXT (context);
-
- return source;
-}
-
-/**
- * g_main_context_find_source_by_user_data:
- * @context: a #GMainContext
- * @user_data: the user_data for the callback.
- *
- * Finds a source with the given user data for the callback. If
- * multiple sources exist with the same user data, the first
- * one found will be returned.
- *
- * Return value: the source, if one was found, otherwise %NULL
- **/
-GSource *
-g_main_context_find_source_by_user_data (GMainContext *context,
- gpointer user_data)
-{
- GSource *source;
-
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- source = context->source_list;
- while (source)
- {
- if (!SOURCE_DESTROYED (source) &&
- source->callback_funcs)
- {
- GSourceFunc callback;
- gpointer callback_data = NULL;
-
- source->callback_funcs->get (source->callback_data, source, &callback, &callback_data);
-
- if (callback_data == user_data)
- break;
- }
- source = source->next;
- }
-
- UNLOCK_CONTEXT (context);
-
- return source;
-}
-
-/**
- * g_source_remove:
- * @tag: the ID of the source to remove.
- *
- * Removes the source with the given id from the default main context.
- * The id of
- * a #GSource is given by g_source_get_id(), or will be returned by the
- * functions g_source_attach(), g_idle_add(), g_idle_add_full(),
- * g_timeout_add(), g_timeout_add_full(), g_child_watch_add(),
- * g_child_watch_add_full(), g_io_add_watch(), and g_io_add_watch_full().
- *
- * See also g_source_destroy(). You must use g_source_destroy() for sources
- * added to a non-default main context.
- *
- * Return value: %TRUE if the source was found and removed.
- **/
-gboolean
-g_source_remove (guint tag)
-{
- GSource *source;
-
- g_return_val_if_fail (tag > 0, FALSE);
-
- source = g_main_context_find_source_by_id (NULL, tag);
- if (source)
- g_source_destroy (source);
-
- return source != NULL;
-}
-
-/**
- * g_source_remove_by_user_data:
- * @user_data: the user_data for the callback.
- *
- * Removes a source from the default main loop context given the user
- * data for the callback. If multiple sources exist with the same user
- * data, only one will be destroyed.
- *
- * Return value: %TRUE if a source was found and removed.
- **/
-gboolean
-g_source_remove_by_user_data (gpointer user_data)
-{
- GSource *source;
-
- source = g_main_context_find_source_by_user_data (NULL, user_data);
- if (source)
- {
- g_source_destroy (source);
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**
- * g_source_remove_by_funcs_user_data:
- * @funcs: The @source_funcs passed to g_source_new()
- * @user_data: the user data for the callback
- *
- * Removes a source from the default main loop context given the
- * source functions and user data. If multiple sources exist with the
- * same source functions and user data, only one will be destroyed.
- *
- * Return value: %TRUE if a source was found and removed.
- **/
-gboolean
-g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
- gpointer user_data)
-{
- GSource *source;
-
- g_return_val_if_fail (funcs != NULL, FALSE);
-
- source = g_main_context_find_source_by_funcs_user_data (NULL, funcs, user_data);
- if (source)
- {
- g_source_destroy (source);
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**
- * g_get_current_time:
- * @result: #GTimeVal structure in which to store current time.
- *
- * Equivalent to the UNIX gettimeofday() function, but portable.
- **/
-void
-g_get_current_time (GTimeVal *result)
-{
-#ifndef G_OS_WIN32
- struct timeval r;
-
- g_return_if_fail (result != NULL);
-
- /*this is required on alpha, there the timeval structs are int's
- not longs and a cast only would fail horribly*/
- gettimeofday (&r, NULL);
- result->tv_sec = r.tv_sec;
- result->tv_usec = r.tv_usec;
-#else
- FILETIME ft;
- guint64 time64;
-
- g_return_if_fail (result != NULL);
-
- GetSystemTimeAsFileTime (&ft);
- memmove (&time64, &ft, sizeof (FILETIME));
-
- /* Convert from 100s of nanoseconds since 1601-01-01
- * to Unix epoch. Yes, this is Y2038 unsafe.
- */
- time64 -= G_GINT64_CONSTANT (116444736000000000);
- time64 /= 10;
-
- result->tv_sec = time64 / 1000000;
- result->tv_usec = time64 % 1000000;
-#endif
-}
-
-static void
-g_main_dispatch_free (gpointer dispatch)
-{
- g_slice_free (GMainDispatch, dispatch);
-}
-
-/* Running the main loop */
-
-static GMainDispatch *
-get_dispatch (void)
-{
- static GStaticPrivate depth_private = G_STATIC_PRIVATE_INIT;
- GMainDispatch *dispatch = g_static_private_get (&depth_private);
- if (!dispatch)
- {
- dispatch = g_slice_new0 (GMainDispatch);
- g_static_private_set (&depth_private, dispatch, g_main_dispatch_free);
- }
-
- return dispatch;
-}
-
-/**
- * g_main_depth:
- *
- * Returns the depth of the stack of calls to
- * g_main_context_dispatch() on any #GMainContext in the current thread.
- * That is, when called from the toplevel, it gives 0. When
- * called from within a callback from g_main_context_iteration()
- * (or g_main_loop_run(), etc.) it returns 1. When called from within
- * a callback to a recursive call to g_main_context_iterate(),
- * it returns 2. And so forth.
- *
- * This function is useful in a situation like the following:
- * Imagine an extremely simple "garbage collected" system.
- *
- * |[
- * static GList *free_list;
- *
- * gpointer
- * allocate_memory (gsize size)
- * {
- * gpointer result = g_malloc (size);
- * free_list = g_list_prepend (free_list, result);
- * return result;
- * }
- *
- * void
- * free_allocated_memory (void)
- * {
- * GList *l;
- * for (l = free_list; l; l = l->next);
- * g_free (l->data);
- * g_list_free (free_list);
- * free_list = NULL;
- * }
- *
- * [...]
- *
- * while (TRUE);
- * {
- * g_main_context_iteration (NULL, TRUE);
- * free_allocated_memory();
- * }
- * ]|
- *
- * This works from an application, however, if you want to do the same
- * thing from a library, it gets more difficult, since you no longer
- * control the main loop. You might think you can simply use an idle
- * function to make the call to free_allocated_memory(), but that
- * doesn't work, since the idle function could be called from a
- * recursive callback. This can be fixed by using g_main_depth()
- *
- * |[
- * gpointer
- * allocate_memory (gsize size)
- * {
- * FreeListBlock *block = g_new (FreeListBlock, 1);
- * block->mem = g_malloc (size);
- * block->depth = g_main_depth ();
- * free_list = g_list_prepend (free_list, block);
- * return block->mem;
- * }
- *
- * void
- * free_allocated_memory (void)
- * {
- * GList *l;
- *
- * int depth = g_main_depth ();
- * for (l = free_list; l; );
- * {
- * GList *next = l->next;
- * FreeListBlock *block = l->data;
- * if (block->depth > depth)
- * {
- * g_free (block->mem);
- * g_free (block);
- * free_list = g_list_delete_link (free_list, l);
- * }
- *
- * l = next;
- * }
- * }
- * ]|
- *
- * There is a temptation to use g_main_depth() to solve
- * problems with reentrancy. For instance, while waiting for data
- * to be received from the network in response to a menu item,
- * the menu item might be selected again. It might seem that
- * one could make the menu item's callback return immediately
- * and do nothing if g_main_depth() returns a value greater than 1.
- * However, this should be avoided since the user then sees selecting
- * the menu item do nothing. Furthermore, you'll find yourself adding
- * these checks all over your code, since there are doubtless many,
- * many things that the user could do. Instead, you can use the
- * following techniques:
- *
- * <orderedlist>
- * <listitem>
- * <para>
- * Use gtk_widget_set_sensitive() or modal dialogs to prevent
- * the user from interacting with elements while the main
- * loop is recursing.
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * Avoid main loop recursion in situations where you can't handle
- * arbitrary callbacks. Instead, structure your code so that you
- * simply return to the main loop and then get called again when
- * there is more work to do.
- * </para>
- * </listitem>
- * </orderedlist>
- *
- * Return value: The main loop recursion level in the current thread
- **/
-int
-g_main_depth (void)
-{
- GMainDispatch *dispatch = get_dispatch ();
- return dispatch->depth;
-}
-
-/**
- * g_main_current_source:
- *
- * Returns the currently firing source for this thread.
- *
- * Return value: The currently firing source or %NULL.
- *
- * Since: 2.12
- */
-GSource *
-g_main_current_source (void)
-{
- GMainDispatch *dispatch = get_dispatch ();
- return dispatch->dispatching_sources ? dispatch->dispatching_sources->data : NULL;
-}
-
-/**
- * g_source_is_destroyed:
- * @source: a #GSource
- *
- * Returns whether @source has been destroyed.
- *
- * This is important when you operate upon your objects
- * from within idle handlers, but may have freed the object
- * before the dispatch of your idle handler.
- *
- * |[
- * static gboolean
- * idle_callback (gpointer data)
- * {
- * SomeWidget *self = data;
- *
- * GDK_THREADS_ENTER (<!-- -->);
- * /<!-- -->* do stuff with self *<!-- -->/
- * self->idle_id = 0;
- * GDK_THREADS_LEAVE (<!-- -->);
- *
- * return FALSE;
- * }
- *
- * static void
- * some_widget_do_stuff_later (SomeWidget *self)
- * {
- * self->idle_id = g_idle_add (idle_callback, self);
- * }
- *
- * static void
- * some_widget_finalize (GObject *object)
- * {
- * SomeWidget *self = SOME_WIDGET (object);
- *
- * if (self->idle_id)
- * g_source_remove (self->idle_id);
- *
- * G_OBJECT_CLASS (parent_class)->finalize (object);
- * }
- * ]|
- *
- * This will fail in a multi-threaded application if the
- * widget is destroyed before the idle handler fires due
- * to the use after free in the callback. A solution, to
- * this particular problem, is to check to if the source
- * has already been destroy within the callback.
- *
- * |[
- * static gboolean
- * idle_callback (gpointer data)
- * {
- * SomeWidget *self = data;
- *
- * GDK_THREADS_ENTER ();
- * if (!g_source_is_destroyed (g_main_current_source ()))
- * {
- * /<!-- -->* do stuff with self *<!-- -->/
- * }
- * GDK_THREADS_LEAVE ();
- *
- * return FALSE;
- * }
- * ]|
- *
- * Return value: %TRUE if the source has been destroyed
- *
- * Since: 2.12
- */
-gboolean
-g_source_is_destroyed (GSource *source)
-{
- return SOURCE_DESTROYED (source);
-}
-
-/* Temporarily remove all this source's file descriptors from the
- * poll(), so that if data comes available for one of the file descriptors
- * we don't continually spin in the poll()
- */
-/* HOLDS: source->context's lock */
-static void
-block_source (GSource *source)
-{
- GSList *tmp_list;
-
- g_return_if_fail (!SOURCE_BLOCKED (source));
-
- tmp_list = source->poll_fds;
- while (tmp_list)
- {
- g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
- tmp_list = tmp_list->next;
- }
-}
-
-/* HOLDS: source->context's lock */
-static void
-unblock_source (GSource *source)
-{
- GSList *tmp_list;
-
- g_return_if_fail (!SOURCE_BLOCKED (source)); /* Source already unblocked */
- g_return_if_fail (!SOURCE_DESTROYED (source));
-
- tmp_list = source->poll_fds;
- while (tmp_list)
- {
- g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
- tmp_list = tmp_list->next;
- }
-}
-
-/* HOLDS: context's lock */
-static void
-g_main_dispatch (GMainContext *context)
-{
- GMainDispatch *current = get_dispatch ();
- guint i;
-
- for (i = 0; i < context->pending_dispatches->len; i++)
- {
- GSource *source = context->pending_dispatches->pdata[i];
-
- context->pending_dispatches->pdata[i] = NULL;
- g_assert (source);
-
- source->flags &= ~G_SOURCE_READY;
-
- if (!SOURCE_DESTROYED (source))
- {
- gboolean was_in_call;
- gpointer user_data = NULL;
- GSourceFunc callback = NULL;
- GSourceCallbackFuncs *cb_funcs;
- gpointer cb_data;
- gboolean need_destroy;
-
- gboolean (*dispatch) (GSource *,
- GSourceFunc,
- gpointer);
- GSList current_source_link;
-
- dispatch = source->source_funcs->dispatch;
- cb_funcs = source->callback_funcs;
- cb_data = source->callback_data;
-
- if (cb_funcs)
- cb_funcs->ref (cb_data);
-
- if ((source->flags & G_SOURCE_CAN_RECURSE) == 0)
- block_source (source);
-
- was_in_call = source->flags & G_HOOK_FLAG_IN_CALL;
- source->flags |= G_HOOK_FLAG_IN_CALL;
-
- if (cb_funcs)
- cb_funcs->get (cb_data, source, &callback, &user_data);
-
- UNLOCK_CONTEXT (context);
-
- current->depth++;
- /* The on-stack allocation of the GSList is unconventional, but
- * we know that the lifetime of the link is bounded to this
- * function as the link is kept in a thread specific list and
- * not manipulated outside of this function and its descendants.
- * Avoiding the overhead of a g_slist_alloc() is useful as many
- * applications do little more than dispatch events.
- *
- * This is a performance hack - do not revert to g_slist_prepend()!
- */
- current_source_link.data = source;
- current_source_link.next = current->dispatching_sources;
- current->dispatching_sources = ¤t_source_link;
- need_destroy = ! dispatch (source,
- callback,
- user_data);
- g_assert (current->dispatching_sources == ¤t_source_link);
- current->dispatching_sources = current_source_link.next;
- current->depth--;
-
- if (cb_funcs)
- cb_funcs->unref (cb_data);
-
- LOCK_CONTEXT (context);
-
- if (!was_in_call)
- source->flags &= ~G_HOOK_FLAG_IN_CALL;
-
- if ((source->flags & G_SOURCE_CAN_RECURSE) == 0 &&
- !SOURCE_DESTROYED (source))
- unblock_source (source);
-
- /* Note: this depends on the fact that we can't switch
- * sources from one main context to another
- */
- if (need_destroy && !SOURCE_DESTROYED (source))
- {
- g_assert (source->context == context);
- g_source_destroy_internal (source, context, TRUE);
- }
- }
-
- SOURCE_UNREF (source, context);
- }
-
- g_ptr_array_set_size (context->pending_dispatches, 0);
-}
-
-/* Holds context's lock */
-static inline GSource *
-next_valid_source (GMainContext *context,
- GSource *source)
-{
- GSource *new_source = source ? source->next : context->source_list;
-
- while (new_source)
- {
- if (!SOURCE_DESTROYED (new_source))
- {
- new_source->ref_count++;
- break;
- }
-
- new_source = new_source->next;
- }
-
- if (source)
- SOURCE_UNREF (source, context);
-
- return new_source;
-}
-
-/**
- * g_main_context_acquire:
- * @context: a #GMainContext
- *
- * Tries to become the owner of the specified context.
- * If some other thread is the owner of the context,
- * returns %FALSE immediately. Ownership is properly
- * recursive: the owner can require ownership again
- * and will release ownership when g_main_context_release()
- * is called as many times as g_main_context_acquire().
- *
- * You must be the owner of a context before you
- * can call g_main_context_prepare(), g_main_context_query(),
- * g_main_context_check(), g_main_context_dispatch().
- *
- * Return value: %TRUE if the operation succeeded, and
- * this thread is now the owner of @context.
- **/
-gboolean
-g_main_context_acquire (GMainContext *context)
-{
-#ifdef G_THREADS_ENABLED
- gboolean result = FALSE;
- GThread *self = G_THREAD_SELF;
-
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- if (!context->owner)
- {
- context->owner = self;
- g_assert (context->owner_count == 0);
- }
-
- if (context->owner == self)
- {
- context->owner_count++;
- result = TRUE;
- }
-
- UNLOCK_CONTEXT (context);
-
- return result;
-#else /* !G_THREADS_ENABLED */
- return TRUE;
-#endif /* G_THREADS_ENABLED */
-}
-
-/**
- * g_main_context_release:
- * @context: a #GMainContext
- *
- * Releases ownership of a context previously acquired by this thread
- * with g_main_context_acquire(). If the context was acquired multiple
- * times, the ownership will be released only when g_main_context_release()
- * is called as many times as it was acquired.
- **/
-void
-g_main_context_release (GMainContext *context)
-{
-#ifdef G_THREADS_ENABLED
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- context->owner_count--;
- if (context->owner_count == 0)
- {
- context->owner = NULL;
-
- if (context->waiters)
- {
- GMainWaiter *waiter = context->waiters->data;
- gboolean loop_internal_waiter =
- (waiter->mutex == g_static_mutex_get_mutex (&context->mutex));
- context->waiters = g_slist_delete_link (context->waiters,
- context->waiters);
- if (!loop_internal_waiter)
- g_mutex_lock (waiter->mutex);
-
- g_cond_signal (waiter->cond);
-
- if (!loop_internal_waiter)
- g_mutex_unlock (waiter->mutex);
- }
- }
-
- UNLOCK_CONTEXT (context);
-#endif /* G_THREADS_ENABLED */
-}
-
-/**
- * g_main_context_wait:
- * @context: a #GMainContext
- * @cond: a condition variable
- * @mutex: a mutex, currently held
- *
- * Tries to become the owner of the specified context,
- * as with g_main_context_acquire(). But if another thread
- * is the owner, atomically drop @mutex and wait on @cond until
- * that owner releases ownership or until @cond is signaled, then
- * try again (once) to become the owner.
- *
- * Return value: %TRUE if the operation succeeded, and
- * this thread is now the owner of @context.
- **/
-gboolean
-g_main_context_wait (GMainContext *context,
- GCond *cond,
- GMutex *mutex)
-{
-#ifdef G_THREADS_ENABLED
- gboolean result = FALSE;
- GThread *self = G_THREAD_SELF;
- gboolean loop_internal_waiter;
-
- if (context == NULL)
- context = g_main_context_default ();
-
- loop_internal_waiter = (mutex == g_static_mutex_get_mutex (&context->mutex));
-
- if (!loop_internal_waiter)
- LOCK_CONTEXT (context);
-
- if (context->owner && context->owner != self)
- {
- GMainWaiter waiter;
-
- waiter.cond = cond;
- waiter.mutex = mutex;
-
- context->waiters = g_slist_append (context->waiters, &waiter);
-
- if (!loop_internal_waiter)
- UNLOCK_CONTEXT (context);
- g_cond_wait (cond, mutex);
- if (!loop_internal_waiter)
- LOCK_CONTEXT (context);
-
- context->waiters = g_slist_remove (context->waiters, &waiter);
- }
-
- if (!context->owner)
- {
- context->owner = self;
- g_assert (context->owner_count == 0);
- }
-
- if (context->owner == self)
- {
- context->owner_count++;
- result = TRUE;
- }
-
- if (!loop_internal_waiter)
- UNLOCK_CONTEXT (context);
-
- return result;
-#else /* !G_THREADS_ENABLED */
- return TRUE;
-#endif /* G_THREADS_ENABLED */
-}
-
-/**
- * g_main_context_prepare:
- * @context: a #GMainContext
- * @priority: location to store priority of highest priority
- * source already ready.
- *
- * Prepares to poll sources within a main loop. The resulting information
- * for polling is determined by calling g_main_context_query ().
- *
- * Return value: %TRUE if some source is ready to be dispatched
- * prior to polling.
- **/
-gboolean
-g_main_context_prepare (GMainContext *context,
- gint *priority)
-{
- gint i;
- gint n_ready = 0;
- gint current_priority = G_MAXINT;
- GSource *source;
-
- if (context == NULL)
- context = g_main_context_default ();
-
- LOCK_CONTEXT (context);
-
- context->time_is_current = FALSE;
-
- if (context->in_check_or_prepare)
- {
- g_warning ("g_main_context_prepare() called recursively from within a source's check() or "
- "prepare() member.");
- UNLOCK_CONTEXT (context);
- return FALSE;
- }
-
-#ifdef G_THREADS_ENABLED
- if (context->poll_waiting)
- {
- g_warning("g_main_context_prepare(): main loop already active in another thread");
- UNLOCK_CONTEXT (context);
- return FALSE;
- }
-
- context->poll_waiting = TRUE;
-#endif /* G_THREADS_ENABLED */
-
-#if 0
- /* If recursing, finish up current dispatch, before starting over */
- if (context->pending_dispatches)
- {
- if (dispatch)
- g_main_dispatch (context, ¤t_time);
-
- UNLOCK_CONTEXT (context);
- return TRUE;
- }
-#endif
-
- /* If recursing, clear list of pending dispatches */
-
- for (i = 0; i < context->pending_dispatches->len; i++)
- {
- if (context->pending_dispatches->pdata[i])
- SOURCE_UNREF ((GSource *)context->pending_dispatches->pdata[i], context);
- }
- g_ptr_array_set_size (context->pending_dispatches, 0);
-
- /* Prepare all sources */
-
- context->timeout = -1;
-
- source = next_valid_source (context, NULL);
- while (source)
- {
- gint source_timeout = -1;
-
- if ((n_ready > 0) && (source->priority > current_priority))
- {
- SOURCE_UNREF (source, context);
- break;
- }
- if (SOURCE_BLOCKED (source))
- goto next;
-
- if (!(source->flags & G_SOURCE_READY))
- {
- gboolean result;
- gboolean (*prepare) (GSource *source,
- gint *timeout);
-
- prepare = source->source_funcs->prepare;
- context->in_check_or_prepare++;
- UNLOCK_CONTEXT (context);
-
- result = (*prepare) (source, &source_timeout);
-
- LOCK_CONTEXT (context);
- context->in_check_or_prepare--;
-
- if (result)
- source->flags |= G_SOURCE_READY;
- }
-
- if (source->flags & G_SOURCE_READY)
- {
- n_ready++;
- current_priority = source->priority;
- context->timeout = 0;
- }
-
- if (source_timeout >= 0)
- {
- if (context->timeout < 0)
- context->timeout = source_timeout;
- else
- context->timeout = MIN (context->timeout, source_timeout);
- }
-
- next:
- source = next_valid_source (context, source);
- }
-
- UNLOCK_CONTEXT (context);
-
- if (priority)
- *priority = current_priority;
-
- return (n_ready > 0);
-}
-
-/**
- * g_main_context_query:
- * @context: a #GMainContext
- * @max_priority: maximum priority source to check
- * @timeout_: location to store timeout to be used in polling
- * @fds: location to store #GPollFD records that need to be polled.
- * @n_fds: length of @fds.
- *
- * Determines information necessary to poll this main loop.
- *
- * Return value: the number of records actually stored in @fds,
- * or, if more than @n_fds records need to be stored, the number
- * of records that need to be stored.
- **/
-gint
-g_main_context_query (GMainContext *context,
- gint max_priority,
- gint *timeout,
- GPollFD *fds,
- gint n_fds)
-{
- gint n_poll;
- GPollRec *pollrec;
-
- LOCK_CONTEXT (context);
-
- pollrec = context->poll_records;
- n_poll = 0;
- while (pollrec && max_priority >= pollrec->priority)
- {
- /* We need to include entries with fd->events == 0 in the array because
- * otherwise if the application changes fd->events behind our back and
- * makes it non-zero, we'll be out of sync when we check the fds[] array.
- * (Changing fd->events after adding an FD wasn't an anticipated use of
- * this API, but it occurs in practice.) */
- if (n_poll < n_fds)
- {
- fds[n_poll].fd = pollrec->fd->fd;
- /* In direct contradiction to the Unix98 spec, IRIX runs into
- * difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
- * flags in the events field of the pollfd while it should
- * just ignoring them. So we mask them out here.
- */
- fds[n_poll].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
- fds[n_poll].revents = 0;
- }
-
- pollrec = pollrec->next;
- n_poll++;
- }
-
-#ifdef G_THREADS_ENABLED
- context->poll_changed = FALSE;
-#endif
-
- if (timeout)
- {
- *timeout = context->timeout;
- if (*timeout != 0)
- context->time_is_current = FALSE;
- }
-
- UNLOCK_CONTEXT (context);
-
- return n_poll;
-}
-
-/**
- * g_main_context_check:
- * @context: a #GMainContext
- * @max_priority: the maximum numerical priority of sources to check
- * @fds: array of #GPollFD's that was passed to the last call to
- * g_main_context_query()
- * @n_fds: return value of g_main_context_query()
- *
- * Passes the results of polling back to the main loop.
- *
- * Return value: %TRUE if some sources are ready to be dispatched.
- **/
-gboolean
-g_main_context_check (GMainContext *context,
- gint max_priority,
- GPollFD *fds,
- gint n_fds)
-{
- GSource *source;
- GPollRec *pollrec;
- gint n_ready = 0;
- gint i;
-
- LOCK_CONTEXT (context);
-
- if (context->in_check_or_prepare)
- {
- g_warning ("g_main_context_check() called recursively from within a source's check() or "
- "prepare() member.");
- UNLOCK_CONTEXT (context);
- return FALSE;
- }
-
-#ifdef G_THREADS_ENABLED
- if (!context->poll_waiting)
- {
-#ifndef G_OS_WIN32
- gchar a;
- read (context->wake_up_pipe[0], &a, 1);
-#endif
- }
- else
- context->poll_waiting = FALSE;
-
- /* If the set of poll file descriptors changed, bail out
- * and let the main loop rerun
- */
- if (context->poll_changed)
- {
- UNLOCK_CONTEXT (context);
- return FALSE;
- }
-#endif /* G_THREADS_ENABLED */
-
- pollrec = context->poll_records;
- i = 0;
- while (i < n_fds)
- {
- if (pollrec->fd->events)
- pollrec->fd->revents = fds[i].revents;
-
- pollrec = pollrec->next;
- i++;
- }
-
- source = next_valid_source (context, NULL);
- while (source)
- {
- if ((n_ready > 0) && (source->priority > max_priority))
- {
- SOURCE_UNREF (source, context);
- break;
- }
- if (SOURCE_BLOCKED (source))
- goto next;
-
- if (!(source->flags & G_SOURCE_READY))
- {
- gboolean result;
- gboolean (*check) (GSource *source);
-
- check = source->source_funcs->check;
-
- context->in_check_or_prepare++;
- UNLOCK_CONTEXT (context);
-
- result = (*check) (source);
-
- LOCK_CONTEXT (context);
- context->in_check_or_prepare--;
-
- if (result)
- source->flags |= G_SOURCE_READY;
- }
-
- if (source->flags & G_SOURCE_READY)
- {
- source->ref_count++;
- g_ptr_array_add (context->pending_dispatches, source);
-
- n_ready++;
-
- /* never dispatch sources with less priority than the first
- * one we choose to dispatch
- */
- max_priority = source->priority;
- }
-
- next:
- source = next_valid_source (context, source);
- }
-
- UNLOCK_CONTEXT (context);
-
- return n_ready > 0;
-}
-
-/**
- * g_main_context_dispatch:
- * @context: a #GMainContext
- *
- * Dispatches all pending sources.
- **/
-void
-g_main_context_dispatch (GMainContext *context)
-{
- LOCK_CONTEXT (context);
-
- if (context->pending_dispatches->len > 0)
- {
- g_main_dispatch (context);
- }
-
- UNLOCK_CONTEXT (context);
-}
-
-/* HOLDS context lock */
-static gboolean
-g_main_context_iterate (GMainContext *context,
- gboolean block,
- gboolean dispatch,
- GThread *self)
-{
- gint max_priority;
- gint timeout;
- gboolean some_ready;
- gint nfds, allocated_nfds;
- GPollFD *fds = NULL;
-
- UNLOCK_CONTEXT (context);
-
-#ifdef G_THREADS_ENABLED
- if (!g_main_context_acquire (context))
- {
- gboolean got_ownership;
-
- LOCK_CONTEXT (context);
-
- g_return_val_if_fail (g_thread_supported (), FALSE);
-
- if (!block)
- return FALSE;
-
- if (!context->cond)
- context->cond = g_cond_new ();
-
- got_ownership = g_main_context_wait (context,
- context->cond,
- g_static_mutex_get_mutex (&context->mutex));
-
- if (!got_ownership)
- return FALSE;
- }
- else
- LOCK_CONTEXT (context);
-#endif /* G_THREADS_ENABLED */
-
- if (!context->cached_poll_array)
- {
- context->cached_poll_array_size = context->n_poll_records;
- context->cached_poll_array = g_new (GPollFD, context->n_poll_records);
- }
-
- allocated_nfds = context->cached_poll_array_size;
- fds = context->cached_poll_array;
-
- UNLOCK_CONTEXT (context);
-
- g_main_context_prepare (context, &max_priority);
-
- while ((nfds = g_main_context_query (context, max_priority, &timeout, fds,
- allocated_nfds)) > allocated_nfds)
- {
- LOCK_CONTEXT (context);
- g_free (fds);
- context->cached_poll_array_size = allocated_nfds = nfds;
- context->cached_poll_array = fds = g_new (GPollFD, nfds);
- UNLOCK_CONTEXT (context);
- }
-
- if (!block)
- timeout = 0;
-
- g_main_context_poll (context, timeout, max_priority, fds, nfds);
-
- some_ready = g_main_context_check (context, max_priority, fds, nfds);
-
- if (dispatch)
- g_main_context_dispatch (context);
-
-#ifdef G_THREADS_ENABLED
- g_main_context_release (context);
-#endif /* G_THREADS_ENABLED */
-
- LOCK_CONTEXT (context);
-
- return some_ready;
-}
-
-/**
- * g_main_context_pending:
- * @context: a #GMainContext (if %NULL, the default context will be used)
- *
- * Checks if any sources have pending events for the given context.
- *
- * Return value: %TRUE if events are pending.
- **/
-gboolean
-g_main_context_pending (GMainContext *context)
-{
- gboolean retval;
-
- if (!context)
- context = g_main_context_default();
-
- LOCK_CONTEXT (context);
- retval = g_main_context_iterate (context, FALSE, FALSE, G_THREAD_SELF);
- UNLOCK_CONTEXT (context);
-
- return retval;
-}
-
-/**
- * g_main_context_iteration:
- * @context: a #GMainContext (if %NULL, the default context will be used)
- * @may_block: whether the call may block.
- *
- * Runs a single iteration for the given main loop. This involves
- * checking to see if any event sources are ready to be processed,
- * then if no events sources are ready and @may_block is %TRUE, waiting
- * for a source to become ready, then dispatching the highest priority
- * events sources that are ready. Otherwise, if @may_block is %FALSE
- * sources are not waited to become ready, only those highest priority
- * events sources will be dispatched (if any), that are ready at this
- * given moment without further waiting.
- *
- * Note that even when @may_block is %TRUE, it is still possible for
- * g_main_context_iteration() to return %FALSE, since the the wait may
- * be interrupted for other reasons than an event source becoming ready.
- *
- * Return value: %TRUE if events were dispatched.
- **/
-gboolean
-g_main_context_iteration (GMainContext *context, gboolean may_block)
-{
- gboolean retval;
-
- if (!context)
- context = g_main_context_default();
-
- LOCK_CONTEXT (context);
- retval = g_main_context_iterate (context, may_block, TRUE, G_THREAD_SELF);
- UNLOCK_CONTEXT (context);
-
- return retval;
-}
-
-/**
- * g_main_loop_new:
- * @context: a #GMainContext (if %NULL, the default context will be used).
- * @is_running: set to %TRUE to indicate that the loop is running. This
- * is not very important since calling g_main_loop_run() will set this to
- * %TRUE anyway.
- *
- * Creates a new #GMainLoop structure.
- *
- * Return value: a new #GMainLoop.
- **/
-GMainLoop *
-g_main_loop_new (GMainContext *context,
- gboolean is_running)
-{
- GMainLoop *loop;
-
- if (!context)
- context = g_main_context_default();
-
- g_main_context_ref (context);
-
- loop = g_new0 (GMainLoop, 1);
- loop->context = context;
- loop->is_running = is_running != FALSE;
- loop->ref_count = 1;
-
- return loop;
-}
-
-/**
- * g_main_loop_ref:
- * @loop: a #GMainLoop
- *
- * Increases the reference count on a #GMainLoop object by one.
- *
- * Return value: @loop
- **/
-GMainLoop *
-g_main_loop_ref (GMainLoop *loop)
-{
- g_return_val_if_fail (loop != NULL, NULL);
- g_return_val_if_fail (g_atomic_int_get (&loop->ref_count) > 0, NULL);
-
- g_atomic_int_inc (&loop->ref_count);
-
- return loop;
-}
-
-/**
- * g_main_loop_unref:
- * @loop: a #GMainLoop
- *
- * Decreases the reference count on a #GMainLoop object by one. If
- * the result is zero, free the loop and free all associated memory.
- **/
-void
-g_main_loop_unref (GMainLoop *loop)
-{
- g_return_if_fail (loop != NULL);
- g_return_if_fail (g_atomic_int_get (&loop->ref_count) > 0);
-
- if (!g_atomic_int_dec_and_test (&loop->ref_count))
- return;
-
- g_main_context_unref (loop->context);
- g_free (loop);
-}
-
-/**
- * g_main_loop_run:
- * @loop: a #GMainLoop
- *
- * Runs a main loop until g_main_loop_quit() is called on the loop.
- * If this is called for the thread of the loop's #GMainContext,
- * it will process events from the loop, otherwise it will
- * simply wait.
- **/
-void
-g_main_loop_run (GMainLoop *loop)
-{
- GThread *self = G_THREAD_SELF;
-
- g_return_if_fail (loop != NULL);
- g_return_if_fail (g_atomic_int_get (&loop->ref_count) > 0);
-
-#ifdef G_THREADS_ENABLED
- if (!g_main_context_acquire (loop->context))
- {
- gboolean got_ownership = FALSE;
-
- /* Another thread owns this context */
- if (!g_thread_supported ())
- {
- g_warning ("g_main_loop_run() was called from second thread but "
- "g_thread_init() was never called.");
- return;
- }
-
- LOCK_CONTEXT (loop->context);
-
- g_atomic_int_inc (&loop->ref_count);
-
- if (!loop->is_running)
- loop->is_running = TRUE;
-
- if (!loop->context->cond)
- loop->context->cond = g_cond_new ();
-
- while (loop->is_running && !got_ownership)
- got_ownership = g_main_context_wait (loop->context,
- loop->context->cond,
- g_static_mutex_get_mutex (&loop->context->mutex));
-
- if (!loop->is_running)
- {
- UNLOCK_CONTEXT (loop->context);
- if (got_ownership)
- g_main_context_release (loop->context);
- g_main_loop_unref (loop);
- return;
- }
-
- g_assert (got_ownership);
- }
- else
- LOCK_CONTEXT (loop->context);
-#endif /* G_THREADS_ENABLED */
-
- if (loop->context->in_check_or_prepare)
- {
- g_warning ("g_main_loop_run(): called recursively from within a source's "
- "check() or prepare() member, iteration not possible.");
- return;
- }
-
- g_atomic_int_inc (&loop->ref_count);
- loop->is_running = TRUE;
- while (loop->is_running)
- g_main_context_iterate (loop->context, TRUE, TRUE, self);
-
- UNLOCK_CONTEXT (loop->context);
-
-#ifdef G_THREADS_ENABLED
- g_main_context_release (loop->context);
-#endif /* G_THREADS_ENABLED */
-
- g_main_loop_unref (loop);
-}
-
-/**
- * g_main_loop_quit:
- * @loop: a #GMainLoop
- *
- * Stops a #GMainLoop from running. Any calls to g_main_loop_run()
- * for the loop will return.
- *
- * Note that sources that have already been dispatched when
- * g_main_loop_quit() is called will still be executed.
- **/
-void
-g_main_loop_quit (GMainLoop *loop)
-{
- g_return_if_fail (loop != NULL);
- g_return_if_fail (g_atomic_int_get (&loop->ref_count) > 0);
-
- LOCK_CONTEXT (loop->context);
- loop->is_running = FALSE;
- g_main_context_wakeup_unlocked (loop->context);
-
-#ifdef G_THREADS_ENABLED
- if (loop->context->cond)
- g_cond_broadcast (loop->context->cond);
-#endif /* G_THREADS_ENABLED */
-
- UNLOCK_CONTEXT (loop->context);
-}
-
-/**
- * g_main_loop_is_running:
- * @loop: a #GMainLoop.
- *
- * Checks to see if the main loop is currently being run via g_main_loop_run().
- *
- * Return value: %TRUE if the mainloop is currently being run.
- **/
-gboolean
-g_main_loop_is_running (GMainLoop *loop)
-{
- g_return_val_if_fail (loop != NULL, FALSE);
- g_return_val_if_fail (g_atomic_int_get (&loop->ref_count) > 0, FALSE);
-
- return loop->is_running;
-}
-
-/**
- * g_main_loop_get_context:
- * @loop: a #GMainLoop.
- *
- * Returns the #GMainContext of @loop.
- *
- * Return value: the #GMainContext of @loop
- **/
-GMainContext *
-g_main_loop_get_context (GMainLoop *loop)
-{
- g_return_val_if_fail (loop != NULL, NULL);
- g_return_val_if_fail (g_atomic_int_get (&loop->ref_count) > 0, NULL);
-
- return loop->context;
-}
-
-/* HOLDS: context's lock */
-static void
-g_main_context_poll (GMainContext *context,
- gint timeout,
- gint priority,
- GPollFD *fds,
- gint n_fds)
-{
-#ifdef G_MAIN_POLL_DEBUG
- GTimer *poll_timer;
- GPollRec *pollrec;
- gint i;
-#endif
-
- GPollFunc poll_func;
-
- if (n_fds || timeout != 0)
- {
-#ifdef G_MAIN_POLL_DEBUG
- if (_g_main_poll_debug)
- {
- g_print ("polling context=%p n=%d timeout=%d\n",
- context, n_fds, timeout);
- poll_timer = g_timer_new ();
- }
-#endif
-
- LOCK_CONTEXT (context);
-
- poll_func = context->poll_func;
-
- UNLOCK_CONTEXT (context);
- if ((*poll_func) (fds, n_fds, timeout) < 0 && errno != EINTR)
- {
-#ifndef G_OS_WIN32
- g_warning ("poll(2) failed due to: %s.",
- g_strerror (errno));
-#else
- /* If g_poll () returns -1, it has already called g_warning() */
-#endif
- }
-
-#ifdef G_MAIN_POLL_DEBUG
- if (_g_main_poll_debug)
- {
- LOCK_CONTEXT (context);
-
- g_print ("g_main_poll(%d) timeout: %d - elapsed %12.10f seconds",
- n_fds,
- timeout,
- g_timer_elapsed (poll_timer, NULL));
- g_timer_destroy (poll_timer);
- pollrec = context->poll_records;
-
- while (pollrec != NULL)
- {
- i = 0;
- while (i < n_fds)
- {
- if (fds[i].fd == pollrec->fd->fd &&
- pollrec->fd->events &&
- fds[i].revents)
- {
- g_print (" [" G_POLLFD_FORMAT " :", fds[i].fd);
- if (fds[i].revents & G_IO_IN)
- g_print ("i");
- if (fds[i].revents & G_IO_OUT)
- g_print ("o");
- if (fds[i].revents & G_IO_PRI)
- g_print ("p");
- if (fds[i].revents & G_IO_ERR)
- g_print ("e");
- if (fds[i].revents & G_IO_HUP)
- g_print ("h");
- if (fds[i].revents & G_IO_NVAL)
- g_print ("n");
- g_print ("]");
- }
- i++;
- }
- pollrec = pollrec->next;
- }
- g_print ("\n");
-
- UNLOCK_CONTEXT (context);
- }
-#endif
- } /* if (n_fds || timeout != 0) */
-}
-
-/**
- * g_main_context_add_poll:
- * @context: a #GMainContext (or %NULL for the default context)
- * @fd: a #GPollFD structure holding information about a file
- * descriptor to watch.
- * @priority: the priority for this file descriptor which should be
- * the same as the priority used for g_source_attach() to ensure that the
- * file descriptor is polled whenever the results may be needed.
- *
- * Adds a file descriptor to the set of file descriptors polled for
- * this context. This will very seldomly be used directly. Instead
- * a typical event source will use g_source_add_poll() instead.
- **/
-void
-g_main_context_add_poll (GMainContext *context,
- GPollFD *fd,
- gint priority)
-{
- if (!context)
- context = g_main_context_default ();
-
- g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
- g_return_if_fail (fd);
-
- LOCK_CONTEXT (context);
- g_main_context_add_poll_unlocked (context, priority, fd);
- UNLOCK_CONTEXT (context);
-}
-
-/* HOLDS: main_loop_lock */
-static void
-g_main_context_add_poll_unlocked (GMainContext *context,
- gint priority,
- GPollFD *fd)
-{
- GPollRec *lastrec, *pollrec;
- GPollRec *newrec = g_slice_new (GPollRec);
-
- /* This file descriptor may be checked before we ever poll */
- fd->revents = 0;
- newrec->fd = fd;
- newrec->priority = priority;
-
- lastrec = NULL;
- pollrec = context->poll_records;
- while (pollrec && priority >= pollrec->priority)
- {
- lastrec = pollrec;
- pollrec = pollrec->next;
- }
-
- if (lastrec)
- lastrec->next = newrec;
- else
- context->poll_records = newrec;
-
- newrec->next = pollrec;
-
- context->n_poll_records++;
-
-#ifdef G_THREADS_ENABLED
- context->poll_changed = TRUE;
-
- /* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
-#endif
-}
-
-/**
- * g_main_context_remove_poll:
- * @context:a #GMainContext
- * @fd: a #GPollFD descriptor previously added with g_main_context_add_poll()
- *
- * Removes file descriptor from the set of file descriptors to be
- * polled for a particular context.
- **/
-void
-g_main_context_remove_poll (GMainContext *context,
- GPollFD *fd)
-{
- if (!context)
- context = g_main_context_default ();
-
- g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
- g_return_if_fail (fd);
-
- LOCK_CONTEXT (context);
- g_main_context_remove_poll_unlocked (context, fd);
- UNLOCK_CONTEXT (context);
-}
-
-static void
-g_main_context_remove_poll_unlocked (GMainContext *context,
- GPollFD *fd)
-{
- GPollRec *pollrec, *lastrec;
-
- lastrec = NULL;
- pollrec = context->poll_records;
-
- while (pollrec)
- {
- if (pollrec->fd == fd)
- {
- if (lastrec != NULL)
- lastrec->next = pollrec->next;
- else
- context->poll_records = pollrec->next;
-
- g_slice_free (GPollRec, pollrec);
-
- context->n_poll_records--;
- break;
- }
- lastrec = pollrec;
- pollrec = pollrec->next;
- }
-
-#ifdef G_THREADS_ENABLED
- context->poll_changed = TRUE;
-
- /* Now wake up the main loop if it is waiting in the poll() */
- g_main_context_wakeup_unlocked (context);
-#endif
-}
-
-/**
- * g_source_get_current_time:
- * @source: a #GSource
- * @timeval: #GTimeVal structure in which to store current time.
- *
- * Gets the "current time" to be used when checking
- * this source. The advantage of calling this function over
- * calling g_get_current_time() directly is that when
- * checking multiple sources, GLib can cache a single value
- * instead of having to repeatedly get the system time.
- **/
-void
-g_source_get_current_time (GSource *source,
- GTimeVal *timeval)
-{
- GMainContext *context;
-
- g_return_if_fail (source->context != NULL);
-
- context = source->context;
-
- LOCK_CONTEXT (context);
-
- if (!context->time_is_current)
- {
- g_get_current_time (&context->current_time);
- context->time_is_current = TRUE;
- }
-
- *timeval = context->current_time;
-
- UNLOCK_CONTEXT (context);
-}
-
-/**
- * g_main_context_set_poll_func:
- * @context: a #GMainContext
- * @func: the function to call to poll all file descriptors
- *
- * Sets the function to use to handle polling of file descriptors. It
- * will be used instead of the poll() system call
- * (or GLib's replacement function, which is used where
- * poll() isn't available).
- *
- * This function could possibly be used to integrate the GLib event
- * loop with an external event loop.
- **/
-void
-g_main_context_set_poll_func (GMainContext *context,
- GPollFunc func)
-{
- if (!context)
- context = g_main_context_default ();
-
- g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
-
- LOCK_CONTEXT (context);
-
- if (func)
- context->poll_func = func;
- else
- context->poll_func = g_poll;
-
- UNLOCK_CONTEXT (context);
-}
-
-/**
- * g_main_context_get_poll_func:
- * @context: a #GMainContext
- *
- * Gets the poll function set by g_main_context_set_poll_func().
- *
- * Return value: the poll function
- **/
-GPollFunc
-g_main_context_get_poll_func (GMainContext *context)
-{
- GPollFunc result;
-
- if (!context)
- context = g_main_context_default ();
-
- g_return_val_if_fail (g_atomic_int_get (&context->ref_count) > 0, NULL);
-
- LOCK_CONTEXT (context);
- result = context->poll_func;
- UNLOCK_CONTEXT (context);
-
- return result;
-}
-
-/* HOLDS: context's lock */
-/* Wake the main loop up from a poll() */
-static void
-g_main_context_wakeup_unlocked (GMainContext *context)
-{
-#ifdef G_THREADS_ENABLED
- if (g_thread_supported() && context->poll_waiting)
- {
- context->poll_waiting = FALSE;
-#ifndef G_OS_WIN32
- write (context->wake_up_pipe[1], "A", 1);
-#else
- ReleaseSemaphore (context->wake_up_semaphore, 1, NULL);
-#endif
- }
-#endif
-}
-
-/**
- * g_main_context_wakeup:
- * @context: a #GMainContext
- *
- * If @context is currently waiting in a poll(), interrupt
- * the poll(), and continue the iteration process.
- **/
-void
-g_main_context_wakeup (GMainContext *context)
-{
- if (!context)
- context = g_main_context_default ();
-
- g_return_if_fail (g_atomic_int_get (&context->ref_count) > 0);
-
- LOCK_CONTEXT (context);
- g_main_context_wakeup_unlocked (context);
- UNLOCK_CONTEXT (context);
-}
-
-/**
- * g_main_context_is_owner:
- * @context: a #GMainContext
- *
- * Determines whether this thread holds the (recursive)
- * ownership of this #GMaincontext. This is useful to
- * know before waiting on another thread that may be
- * blocking to get ownership of @context.
- *
- * Returns: %TRUE if current thread is owner of @context.
- *
- * Since: 2.10
- **/
-gboolean
-g_main_context_is_owner (GMainContext *context)
-{
- gboolean is_owner;
-
- if (!context)
- context = g_main_context_default ();
-
-#ifdef G_THREADS_ENABLED
- LOCK_CONTEXT (context);
- is_owner = context->owner == G_THREAD_SELF;
- UNLOCK_CONTEXT (context);
-#else
- is_owner = TRUE;
-#endif
-
- return is_owner;
-}
-
-/* Timeouts */
-
-static void
-g_timeout_set_expiration (GTimeoutSource *timeout_source,
- GTimeVal *current_time)
-{
- guint seconds = timeout_source->interval / 1000;
- guint msecs = timeout_source->interval - seconds * 1000;
-
- timeout_source->expiration.tv_sec = current_time->tv_sec + seconds;
- timeout_source->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
- if (timeout_source->expiration.tv_usec >= 1000000)
- {
- timeout_source->expiration.tv_usec -= 1000000;
- timeout_source->expiration.tv_sec++;
- }
- if (timer_perturb==-1)
- {
- /*
- * we want a per machine/session unique 'random' value; try the dbus
- * address first, that has a UUID in it. If there is no dbus, use the
- * hostname for hashing.
- */
- const char *session_bus_address = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
- if (!session_bus_address)
- session_bus_address = g_getenv ("HOSTNAME");
- if (session_bus_address)
- timer_perturb = ABS ((gint) g_str_hash (session_bus_address));
- else
- timer_perturb = 0;
- }
- if (timeout_source->granularity)
- {
- gint remainder;
- gint gran; /* in usecs */
- gint perturb;
-
- gran = timeout_source->granularity * 1000;
- perturb = timer_perturb % gran;
- /*
- * We want to give each machine a per machine pertubation;
- * shift time back first, and forward later after the rounding
- */
-
- timeout_source->expiration.tv_usec -= perturb;
- if (timeout_source->expiration.tv_usec < 0)
- {
- timeout_source->expiration.tv_usec += 1000000;
- timeout_source->expiration.tv_sec--;
- }
-
- remainder = timeout_source->expiration.tv_usec % gran;
- if (remainder >= gran/4) /* round up */
- timeout_source->expiration.tv_usec += gran;
- timeout_source->expiration.tv_usec -= remainder;
- /* shift back */
- timeout_source->expiration.tv_usec += perturb;
-
- /* the rounding may have overflown tv_usec */
- while (timeout_source->expiration.tv_usec > 1000000)
- {
- timeout_source->expiration.tv_usec -= 1000000;
- timeout_source->expiration.tv_sec++;
- }
- }
-}
-
-static gboolean
-g_timeout_prepare (GSource *source,
- gint *timeout)
-{
- glong sec;
- glong msec;
- GTimeVal current_time;
-
- GTimeoutSource *timeout_source = (GTimeoutSource *)source;
-
- g_source_get_current_time (source, ¤t_time);
-
- sec = timeout_source->expiration.tv_sec - current_time.tv_sec;
- msec = (timeout_source->expiration.tv_usec - current_time.tv_usec) / 1000;
-
- /* We do the following in a rather convoluted fashion to deal with
- * the fact that we don't have an integral type big enough to hold
- * the difference of two timevals in millseconds.
- */
- if (sec < 0 || (sec == 0 && msec < 0))
- msec = 0;
- else
- {
- glong interval_sec = timeout_source->interval / 1000;
- glong interval_msec = timeout_source->interval % 1000;
-
- if (msec < 0)
- {
- msec += 1000;
- sec -= 1;
- }
-
- if (sec > interval_sec ||
- (sec == interval_sec && msec > interval_msec))
- {
- /* The system time has been set backwards, so we
- * reset the expiration time to now + timeout_source->interval;
- * this at least avoids hanging for long periods of time.
- */
- g_timeout_set_expiration (timeout_source, ¤t_time);
- msec = MIN (G_MAXINT, timeout_source->interval);
- }
- else
- {
- msec = MIN (G_MAXINT, (guint)msec + 1000 * (guint)sec);
- }
- }
-
- *timeout = (gint)msec;
-
- return msec == 0;
-}
-
-static gboolean
-g_timeout_check (GSource *source)
-{
- GTimeVal current_time;
- GTimeoutSource *timeout_source = (GTimeoutSource *)source;
-
- g_source_get_current_time (source, ¤t_time);
-
- return ((timeout_source->expiration.tv_sec < current_time.tv_sec) ||
- ((timeout_source->expiration.tv_sec == current_time.tv_sec) &&
- (timeout_source->expiration.tv_usec <= current_time.tv_usec)));
-}
-
-static gboolean
-g_timeout_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- GTimeoutSource *timeout_source = (GTimeoutSource *)source;
-
- if (!callback)
- {
- g_warning ("Timeout source dispatched without callback\n"
- "You must call g_source_set_callback().");
- return FALSE;
- }
-
- if (callback (user_data))
- {
- GTimeVal current_time;
-
- g_source_get_current_time (source, ¤t_time);
- g_timeout_set_expiration (timeout_source, ¤t_time);
-
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**
- * g_timeout_source_new:
- * @interval: the timeout interval in milliseconds.
- *
- * Creates a new timeout source.
- *
- * The source will not initially be associated with any #GMainContext
- * and must be added to one with g_source_attach() before it will be
- * executed.
- *
- * Return value: the newly-created timeout source
- **/
-GSource *
-g_timeout_source_new (guint interval)
-{
- GSource *source = g_source_new (&g_timeout_funcs, sizeof (GTimeoutSource));
- GTimeoutSource *timeout_source = (GTimeoutSource *)source;
- GTimeVal current_time;
-
- timeout_source->interval = interval;
-
- g_get_current_time (¤t_time);
- g_timeout_set_expiration (timeout_source, ¤t_time);
-
- return source;
-}
-
-/**
- * g_timeout_source_new_seconds:
- * @interval: the timeout interval in seconds
- *
- * Creates a new timeout source.
- *
- * The source will not initially be associated with any #GMainContext
- * and must be added to one with g_source_attach() before it will be
- * executed.
- *
- * The scheduling granularity/accuracy of this timeout source will be
- * in seconds.
- *
- * Return value: the newly-created timeout source
- *
- * Since: 2.14
- **/
-GSource *
-g_timeout_source_new_seconds (guint interval)
-{
- GSource *source = g_source_new (&g_timeout_funcs, sizeof (GTimeoutSource));
- GTimeoutSource *timeout_source = (GTimeoutSource *)source;
- GTimeVal current_time;
-
- timeout_source->interval = 1000*interval;
- timeout_source->granularity = 1000;
-
- g_get_current_time (¤t_time);
- g_timeout_set_expiration (timeout_source, ¤t_time);
-
- return source;
-}
-
-
-/**
- * g_timeout_add_full:
- * @priority: the priority of the timeout source. Typically this will be in
- * the range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH.
- * @interval: the time between calls to the function, in milliseconds
- * (1/1000ths of a second)
- * @function: function to call
- * @data: data to pass to @function
- * @notify: function to call when the timeout is removed, or %NULL
- *
- * Sets a function to be called at regular intervals, with the given
- * priority. The function is called repeatedly until it returns
- * %FALSE, at which point the timeout is automatically destroyed and
- * the function will not be called again. The @notify function is
- * called when the timeout is destroyed. The first call to the
- * function will be at the end of the first @interval.
- *
- * Note that timeout functions may be delayed, due to the processing of other
- * event sources. Thus they should not be relied on for precise timing.
- * After each call to the timeout function, the time of the next
- * timeout is recalculated based on the current time and the given interval
- * (it does not try to 'catch up' time lost in delays).
- *
- * This internally creates a main loop source using g_timeout_source_new()
- * and attaches it to the main loop context using g_source_attach(). You can
- * do these steps manually if you need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- **/
-guint
-g_timeout_add_full (gint priority,
- guint interval,
- GSourceFunc function,
- gpointer data,
- GDestroyNotify notify)
-{
- GSource *source;
- guint id;
-
- g_return_val_if_fail (function != NULL, 0);
-
- source = g_timeout_source_new (interval);
-
- if (priority != G_PRIORITY_DEFAULT)
- g_source_set_priority (source, priority);
-
- g_source_set_callback (source, function, data, notify);
- id = g_source_attach (source, NULL);
- g_source_unref (source);
-
- return id;
-}
-
-/**
- * g_timeout_add:
- * @interval: the time between calls to the function, in milliseconds
- * (1/1000ths of a second)
- * @function: function to call
- * @data: data to pass to @function
- *
- * Sets a function to be called at regular intervals, with the default
- * priority, #G_PRIORITY_DEFAULT. The function is called repeatedly
- * until it returns %FALSE, at which point the timeout is automatically
- * destroyed and the function will not be called again. The first call
- * to the function will be at the end of the first @interval.
- *
- * Note that timeout functions may be delayed, due to the processing of other
- * event sources. Thus they should not be relied on for precise timing.
- * After each call to the timeout function, the time of the next
- * timeout is recalculated based on the current time and the given interval
- * (it does not try to 'catch up' time lost in delays).
- *
- * If you want to have a timer in the "seconds" range and do not care
- * about the exact time of the first call of the timer, use the
- * g_timeout_add_seconds() function; this function allows for more
- * optimizations and more efficient system power usage.
- *
- * This internally creates a main loop source using g_timeout_source_new()
- * and attaches it to the main loop context using g_source_attach(). You can
- * do these steps manually if you need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- **/
-guint
-g_timeout_add (guint32 interval,
- GSourceFunc function,
- gpointer data)
-{
- return g_timeout_add_full (G_PRIORITY_DEFAULT,
- interval, function, data, NULL);
-}
-
-/**
- * g_timeout_add_seconds_full:
- * @priority: the priority of the timeout source. Typically this will be in
- * the range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH.
- * @interval: the time between calls to the function, in seconds
- * @function: function to call
- * @data: data to pass to @function
- * @notify: function to call when the timeout is removed, or %NULL
- *
- * Sets a function to be called at regular intervals, with @priority.
- * The function is called repeatedly until it returns %FALSE, at which
- * point the timeout is automatically destroyed and the function will
- * not be called again.
- *
- * Unlike g_timeout_add(), this function operates at whole second granularity.
- * The initial starting point of the timer is determined by the implementation
- * and the implementation is expected to group multiple timers together so that
- * they fire all at the same time.
- * To allow this grouping, the @interval to the first timer is rounded
- * and can deviate up to one second from the specified interval.
- * Subsequent timer iterations will generally run at the specified interval.
- *
- * Note that timeout functions may be delayed, due to the processing of other
- * event sources. Thus they should not be relied on for precise timing.
- * After each call to the timeout function, the time of the next
- * timeout is recalculated based on the current time and the given @interval
- *
- * If you want timing more precise than whole seconds, use g_timeout_add()
- * instead.
- *
- * The grouping of timers to fire at the same time results in a more power
- * and CPU efficient behavior so if your timer is in multiples of seconds
- * and you don't require the first timer exactly one second from now, the
- * use of g_timeout_add_seconds() is preferred over g_timeout_add().
- *
- * This internally creates a main loop source using
- * g_timeout_source_new_seconds() and attaches it to the main loop context
- * using g_source_attach(). You can do these steps manually if you need
- * greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- *
- * Since: 2.14
- **/
-guint
-g_timeout_add_seconds_full (gint priority,
- guint32 interval,
- GSourceFunc function,
- gpointer data,
- GDestroyNotify notify)
-{
- GSource *source;
- guint id;
-
- g_return_val_if_fail (function != NULL, 0);
-
- source = g_timeout_source_new_seconds (interval);
-
- if (priority != G_PRIORITY_DEFAULT)
- g_source_set_priority (source, priority);
-
- g_source_set_callback (source, function, data, notify);
- id = g_source_attach (source, NULL);
- g_source_unref (source);
-
- return id;
-}
-
-/**
- * g_timeout_add_seconds:
- * @interval: the time between calls to the function, in seconds
- * @function: function to call
- * @data: data to pass to @function
- *
- * Sets a function to be called at regular intervals with the default
- * priority, #G_PRIORITY_DEFAULT. The function is called repeatedly until
- * it returns %FALSE, at which point the timeout is automatically destroyed
- * and the function will not be called again.
- *
- * This internally creates a main loop source using
- * g_timeout_source_new_seconds() and attaches it to the main loop context
- * using g_source_attach(). You can do these steps manually if you need
- * greater control. Also see g_timout_add_seconds_full().
- *
- * Return value: the ID (greater than 0) of the event source.
- *
- * Since: 2.14
- **/
-guint
-g_timeout_add_seconds (guint interval,
- GSourceFunc function,
- gpointer data)
-{
- g_return_val_if_fail (function != NULL, 0);
-
- return g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, interval, function, data, NULL);
-}
-
-/* Child watch functions */
-
-#ifdef G_OS_WIN32
-
-static gboolean
-g_child_watch_prepare (GSource *source,
- gint *timeout)
-{
- *timeout = -1;
- return FALSE;
-}
-
-
-static gboolean
-g_child_watch_check (GSource *source)
-{
- GChildWatchSource *child_watch_source;
- gboolean child_exited;
-
- child_watch_source = (GChildWatchSource *) source;
-
- child_exited = child_watch_source->poll.revents & G_IO_IN;
-
- if (child_exited)
- {
- DWORD child_status;
-
- /*
- * Note: We do _not_ check for the special value of STILL_ACTIVE
- * since we know that the process has exited and doing so runs into
- * problems if the child process "happens to return STILL_ACTIVE(259)"
- * as Microsoft's Platform SDK puts it.
- */
- if (!GetExitCodeProcess (child_watch_source->pid, &child_status))
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
- g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg);
- g_free (emsg);
-
- child_watch_source->child_status = -1;
- }
- else
- child_watch_source->child_status = child_status;
- }
-
- return child_exited;
-}
-
-#else /* G_OS_WIN32 */
-
-static gboolean
-check_for_child_exited (GSource *source)
-{
- GChildWatchSource *child_watch_source;
- gint count;
-
- /* protect against another SIGCHLD in the middle of this call */
- count = child_watch_count;
-
- child_watch_source = (GChildWatchSource *) source;
-
- if (child_watch_source->child_exited)
- return TRUE;
-
- if (child_watch_source->count < count)
- {
- gint child_status;
-
- if (waitpid (child_watch_source->pid, &child_status, WNOHANG) > 0)
- {
- child_watch_source->child_status = child_status;
- child_watch_source->child_exited = TRUE;
- }
- child_watch_source->count = count;
- }
-
- return child_watch_source->child_exited;
-}
-
-static gboolean
-g_child_watch_prepare (GSource *source,
- gint *timeout)
-{
- *timeout = -1;
-
- return check_for_child_exited (source);
-}
-
-
-static gboolean
-g_child_watch_check (GSource *source)
-{
- return check_for_child_exited (source);
-}
-
-#endif /* G_OS_WIN32 */
-
-static gboolean
-g_child_watch_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- GChildWatchSource *child_watch_source;
- GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback;
-
- child_watch_source = (GChildWatchSource *) source;
-
- if (!callback)
- {
- g_warning ("Child watch source dispatched without callback\n"
- "You must call g_source_set_callback().");
- return FALSE;
- }
-
- (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data);
-
- /* We never keep a child watch source around as the child is gone */
- return FALSE;
-}
-
-#ifndef G_OS_WIN32
-
-static void
-g_child_watch_signal_handler (int signum)
-{
- child_watch_count ++;
-
- if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED)
- {
- write (child_watch_wake_up_pipe[1], "B", 1);
- }
- else
- {
- /* We count on the signal interrupting the poll in the same thread.
- */
- }
-}
-
-static void
-g_child_watch_source_init_single (void)
-{
- struct sigaction action;
-
- g_assert (! g_thread_supported());
- g_assert (child_watch_init_state == CHILD_WATCH_UNINITIALIZED);
-
- child_watch_init_state = CHILD_WATCH_INITIALIZED_SINGLE;
-
- action.sa_handler = g_child_watch_signal_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = SA_NOCLDSTOP;
- sigaction (SIGCHLD, &action, NULL);
-}
-
-G_GNUC_NORETURN static gpointer
-child_watch_helper_thread (gpointer data)
-{
- while (1)
- {
- gchar b[20];
- GSList *list;
-
- read (child_watch_wake_up_pipe[0], b, 20);
-
- /* We were woken up. Wake up all other contexts in all other threads */
- G_LOCK (main_context_list);
- for (list = main_context_list; list; list = list->next)
- {
- GMainContext *context;
-
- context = list->data;
- if (g_atomic_int_get (&context->ref_count) > 0)
- /* Due to racing conditions we can find ref_count == 0, in
- * that case, however, the context is still not destroyed
- * and no poll can be active, otherwise the ref_count
- * wouldn't be 0 */
- g_main_context_wakeup (context);
- }
- G_UNLOCK (main_context_list);
- }
-}
-
-static void
-g_child_watch_source_init_multi_threaded (void)
-{
- GError *error = NULL;
- struct sigaction action;
-
- g_assert (g_thread_supported());
-
- if (pipe (child_watch_wake_up_pipe) < 0)
- g_error ("Cannot create wake up pipe: %s\n", g_strerror (errno));
- fcntl (child_watch_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (child_watch_wake_up_pipe[1], F_GETFL));
-
- /* We create a helper thread that polls on the wakeup pipe indefinitely */
- /* FIXME: Think this through for races */
- if (g_thread_create (child_watch_helper_thread, NULL, FALSE, &error) == NULL)
- g_error ("Cannot create a thread to monitor child exit status: %s\n", error->message);
- child_watch_init_state = CHILD_WATCH_INITIALIZED_THREADED;
-
- action.sa_handler = g_child_watch_signal_handler;
- sigemptyset (&action.sa_mask);
- action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
- sigaction (SIGCHLD, &action, NULL);
-}
-
-static void
-g_child_watch_source_init_promote_single_to_threaded (void)
-{
- g_child_watch_source_init_multi_threaded ();
-}
-
-static void
-g_child_watch_source_init (void)
-{
- if (g_thread_supported())
- {
- if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED)
- g_child_watch_source_init_multi_threaded ();
- else if (child_watch_init_state == CHILD_WATCH_INITIALIZED_SINGLE)
- g_child_watch_source_init_promote_single_to_threaded ();
- }
- else
- {
- if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED)
- g_child_watch_source_init_single ();
- }
-}
-
-#endif /* !G_OS_WIN32 */
-
-/**
- * g_child_watch_source_new:
- * @pid: process to watch. On POSIX the pid of a child process. On
- * Windows a handle for a process (which doesn't have to be a child).
- *
- * Creates a new child_watch source.
- *
- * The source will not initially be associated with any #GMainContext
- * and must be added to one with g_source_attach() before it will be
- * executed.
- *
- * Note that child watch sources can only be used in conjunction with
- * <literal>g_spawn...</literal> when the %G_SPAWN_DO_NOT_REAP_CHILD
- * flag is used.
- *
- * Note that on platforms where #GPid must be explicitly closed
- * (see g_spawn_close_pid()) @pid must not be closed while the
- * source is still active. Typically, you will want to call
- * g_spawn_close_pid() in the callback function for the source.
- *
- * Note further that using g_child_watch_source_new() is not
- * compatible with calling <literal>waitpid(-1)</literal> in
- * the application. Calling waitpid() for individual pids will
- * still work fine.
- *
- * Return value: the newly-created child watch source
- *
- * Since: 2.4
- **/
-GSource *
-g_child_watch_source_new (GPid pid)
-{
- GSource *source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource));
- GChildWatchSource *child_watch_source = (GChildWatchSource *)source;
-
-#ifdef G_OS_WIN32
- child_watch_source->poll.fd = (gintptr) pid;
- child_watch_source->poll.events = G_IO_IN;
-
- g_source_add_poll (source, &child_watch_source->poll);
-#else /* G_OS_WIN32 */
- g_child_watch_source_init ();
-#endif /* G_OS_WIN32 */
-
- child_watch_source->pid = pid;
-
- return source;
-}
-
-/**
- * g_child_watch_add_full:
- * @priority: the priority of the idle source. Typically this will be in the
- * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
- * @pid: process to watch. On POSIX the pid of a child process. On
- * Windows a handle for a process (which doesn't have to be a child).
- * @function: function to call
- * @data: data to pass to @function
- * @notify: function to call when the idle is removed, or %NULL
- *
- * Sets a function to be called when the child indicated by @pid
- * exits, at the priority @priority.
- *
- * If you obtain @pid from g_spawn_async() or g_spawn_async_with_pipes()
- * you will need to pass #G_SPAWN_DO_NOT_REAP_CHILD as flag to
- * the spawn function for the child watching to work.
- *
- * Note that on platforms where #GPid must be explicitly closed
- * (see g_spawn_close_pid()) @pid must not be closed while the
- * source is still active. Typically, you will want to call
- * g_spawn_close_pid() in the callback function for the source.
- *
- * GLib supports only a single callback per process id.
- *
- * This internally creates a main loop source using
- * g_child_watch_source_new() and attaches it to the main loop context
- * using g_source_attach(). You can do these steps manually if you
- * need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- *
- * Since: 2.4
- **/
-guint
-g_child_watch_add_full (gint priority,
- GPid pid,
- GChildWatchFunc function,
- gpointer data,
- GDestroyNotify notify)
-{
- GSource *source;
- guint id;
-
- g_return_val_if_fail (function != NULL, 0);
-
- source = g_child_watch_source_new (pid);
-
- if (priority != G_PRIORITY_DEFAULT)
- g_source_set_priority (source, priority);
-
- g_source_set_callback (source, (GSourceFunc) function, data, notify);
- id = g_source_attach (source, NULL);
- g_source_unref (source);
-
- return id;
-}
-
-/**
- * g_child_watch_add:
- * @pid: process id to watch. On POSIX the pid of a child process. On
- * Windows a handle for a process (which doesn't have to be a child).
- * @function: function to call
- * @data: data to pass to @function
- *
- * Sets a function to be called when the child indicated by @pid
- * exits, at a default priority, #G_PRIORITY_DEFAULT.
- *
- * If you obtain @pid from g_spawn_async() or g_spawn_async_with_pipes()
- * you will need to pass #G_SPAWN_DO_NOT_REAP_CHILD as flag to
- * the spawn function for the child watching to work.
- *
- * Note that on platforms where #GPid must be explicitly closed
- * (see g_spawn_close_pid()) @pid must not be closed while the
- * source is still active. Typically, you will want to call
- * g_spawn_close_pid() in the callback function for the source.
- *
- * GLib supports only a single callback per process id.
- *
- * This internally creates a main loop source using
- * g_child_watch_source_new() and attaches it to the main loop context
- * using g_source_attach(). You can do these steps manually if you
- * need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- *
- * Since: 2.4
- **/
-guint
-g_child_watch_add (GPid pid,
- GChildWatchFunc function,
- gpointer data)
-{
- return g_child_watch_add_full (G_PRIORITY_DEFAULT, pid, function, data, NULL);
-}
-
-
-/* Idle functions */
-
-static gboolean
-g_idle_prepare (GSource *source,
- gint *timeout)
-{
- *timeout = 0;
-
- return TRUE;
-}
-
-static gboolean
-g_idle_check (GSource *source)
-{
- return TRUE;
-}
-
-static gboolean
-g_idle_dispatch (GSource *source,
- GSourceFunc callback,
- gpointer user_data)
-{
- if (!callback)
- {
- g_warning ("Idle source dispatched without callback\n"
- "You must call g_source_set_callback().");
- return FALSE;
- }
-
- return callback (user_data);
-}
-
-/**
- * g_idle_source_new:
- *
- * Creates a new idle source.
- *
- * The source will not initially be associated with any #GMainContext
- * and must be added to one with g_source_attach() before it will be
- * executed. Note that the default priority for idle sources is
- * %G_PRIORITY_DEFAULT_IDLE, as compared to other sources which
- * have a default priority of %G_PRIORITY_DEFAULT.
- *
- * Return value: the newly-created idle source
- **/
-GSource *
-g_idle_source_new (void)
-{
- GSource *source;
-
- source = g_source_new (&g_idle_funcs, sizeof (GSource));
- g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE);
-
- return source;
-}
-
-/**
- * g_idle_add_full:
- * @priority: the priority of the idle source. Typically this will be in the
- * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
- * @function: function to call
- * @data: data to pass to @function
- * @notify: function to call when the idle is removed, or %NULL
- *
- * Adds a function to be called whenever there are no higher priority
- * events pending. If the function returns %FALSE it is automatically
- * removed from the list of event sources and will not be called again.
- *
- * This internally creates a main loop source using g_idle_source_new()
- * and attaches it to the main loop context using g_source_attach().
- * You can do these steps manually if you need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- **/
-guint
-g_idle_add_full (gint priority,
- GSourceFunc function,
- gpointer data,
- GDestroyNotify notify)
-{
- GSource *source;
- guint id;
-
- g_return_val_if_fail (function != NULL, 0);
-
- source = g_idle_source_new ();
-
- if (priority != G_PRIORITY_DEFAULT_IDLE)
- g_source_set_priority (source, priority);
-
- g_source_set_callback (source, function, data, notify);
- id = g_source_attach (source, NULL);
- g_source_unref (source);
-
- return id;
-}
-
-/**
- * g_idle_add:
- * @function: function to call
- * @data: data to pass to @function.
- *
- * Adds a function to be called whenever there are no higher priority
- * events pending to the default main loop. The function is given the
- * default idle priority, #G_PRIORITY_DEFAULT_IDLE. If the function
- * returns %FALSE it is automatically removed from the list of event
- * sources and will not be called again.
- *
- * This internally creates a main loop source using g_idle_source_new()
- * and attaches it to the main loop context using g_source_attach().
- * You can do these steps manually if you need greater control.
- *
- * Return value: the ID (greater than 0) of the event source.
- **/
-guint
-g_idle_add (GSourceFunc function,
- gpointer data)
-{
- return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL);
-}
-
-/**
- * g_idle_remove_by_data:
- * @data: the data for the idle source's callback.
- *
- * Removes the idle function with the given data.
- *
- * Return value: %TRUE if an idle source was found and removed.
- **/
-gboolean
-g_idle_remove_by_data (gpointer data)
-{
- return g_source_remove_by_funcs_user_data (&g_idle_funcs, data);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * gmappedfile.c: Simplified wrapper around the mmap() function.
- *
- * Copyright 2005 Matthias Clasen
- *
- * 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 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 "config.h"
-
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
-#endif
-
-#include "glibconfig.h"
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#include <io.h>
-
-#define fstat(a,b) _fstati64(a,b)
-#define stat _stati64
-
-#endif
-
-#include "gconvert.h"
-#include "gerror.h"
-#include "gfileutils.h"
-#include "gmappedfile.h"
-#include "gmem.h"
-#include "gmessages.h"
-#include "gstdio.h"
-#include "gstrfuncs.h"
-#include "gatomic.h"
-#include "gbuffer.h"
-
-#include "glibintl.h"
-
-
-#ifndef _O_BINARY
-#define _O_BINARY 0
-#endif
-
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *) -1)
-#endif
-
-struct _GMappedFile
-{
- gchar *contents;
- gsize length;
- gpointer free_func;
- int ref_count;
-#ifdef G_OS_WIN32
- HANDLE mapping;
-#endif
-};
-
-/* Make sure the layout of GMappedFile is the same as GBuffer's */
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, contents) ==
- G_STRUCT_OFFSET (GBuffer, data));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, length) ==
- G_STRUCT_OFFSET (GBuffer, size));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, ref_count) ==
- G_STRUCT_OFFSET (GBuffer, ref_count));
-G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, free_func) ==
- G_STRUCT_OFFSET (GBuffer, free_func));
-
-static void
-g_mapped_file_destroy (GMappedFile *file)
-{
- if (file->length)
- {
-#ifdef HAVE_MMAP
- munmap (file->contents, file->length);
-#endif
-#ifdef G_OS_WIN32
- UnmapViewOfFile (file->contents);
- CloseHandle (file->mapping);
-#endif
- }
-
- g_slice_free (GMappedFile, file);
-}
-
-/**
- * g_mapped_file_new:
- * @filename: The path of the file to load, in the GLib filename encoding
- * @writable: whether the mapping should be writable
- * @error: return location for a #GError, or %NULL
- *
- * Maps a file into memory. On UNIX, this is using the mmap() function.
- *
- * If @writable is %TRUE, the mapped buffer may be modified, otherwise
- * it is an error to modify the mapped buffer. Modifications to the buffer
- * are not visible to other processes mapping the same file, and are not
- * written back to the file.
- *
- * Note that modifications of the underlying file might affect the contents
- * of the #GMappedFile. Therefore, mapping should only be used if the file
- * will not be modified, or if all modifications of the file are done
- * atomically (e.g. using g_file_set_contents()).
- *
- * Return value: a newly allocated #GMappedFile which must be unref'd
- * with g_mapped_file_unref(), or %NULL if the mapping failed.
- *
- * Since: 2.8
- */
-GMappedFile *
-g_mapped_file_new (const gchar *filename,
- gboolean writable,
- GError **error)
-{
- GMappedFile *file;
- int fd;
- struct stat st;
-
- g_return_val_if_fail (filename != NULL, NULL);
- g_return_val_if_fail (!error || *error == NULL, NULL);
-
- fd = g_open (filename, (writable ? O_RDWR : O_RDONLY) | _O_BINARY, 0);
- if (fd == -1)
- {
- int save_errno = errno;
- gchar *display_filename = g_filename_display_name (filename);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to open file '%s': open() failed: %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
- return NULL;
- }
-
- file = g_slice_new0 (GMappedFile);
- file->ref_count = 1;
- file->free_func = g_mapped_file_destroy;
-
- if (fstat (fd, &st) == -1)
- {
- int save_errno = errno;
- gchar *display_filename = g_filename_display_name (filename);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to get attributes of file '%s': fstat() failed: %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
- goto out;
- }
-
- if (st.st_size == 0)
- {
- file->length = 0;
- file->contents = NULL;
- close (fd);
- return file;
- }
-
- file->contents = MAP_FAILED;
-
-#ifdef HAVE_MMAP
- if (st.st_size > G_MAXSIZE)
- {
- errno = EINVAL;
- }
- else
- {
- file->length = (gsize) st.st_size;
- file->contents = (gchar *) mmap (NULL, file->length,
- writable ? PROT_READ|PROT_WRITE : PROT_READ,
- MAP_PRIVATE, fd, 0);
- }
-#endif
-#ifdef G_OS_WIN32
- file->length = st.st_size;
- file->mapping = CreateFileMapping ((HANDLE) _get_osfhandle (fd), NULL,
- writable ? PAGE_WRITECOPY : PAGE_READONLY,
- 0, 0,
- NULL);
- if (file->mapping != NULL)
- {
- file->contents = MapViewOfFile (file->mapping,
- writable ? FILE_MAP_COPY : FILE_MAP_READ,
- 0, 0,
- 0);
- if (file->contents == NULL)
- {
- file->contents = MAP_FAILED;
- CloseHandle (file->mapping);
- file->mapping = NULL;
- }
- }
-#endif
-
-
- if (file->contents == MAP_FAILED)
- {
- int save_errno = errno;
- gchar *display_filename = g_filename_display_name (filename);
-
- g_set_error (error,
- G_FILE_ERROR,
- g_file_error_from_errno (save_errno),
- _("Failed to map file '%s': mmap() failed: %s"),
- display_filename,
- g_strerror (save_errno));
- g_free (display_filename);
- goto out;
- }
-
- close (fd);
- return file;
-
- out:
- close (fd);
- g_slice_free (GMappedFile, file);
-
- return NULL;
-}
-
-/**
- * g_mapped_file_get_length:
- * @file: a #GMappedFile
- *
- * Returns the length of the contents of a #GMappedFile.
- *
- * Returns: the length of the contents of @file.
- *
- * Since: 2.8
- */
-gsize
-g_mapped_file_get_length (GMappedFile *file)
-{
- g_return_val_if_fail (file != NULL, 0);
-
- return file->length;
-}
-
-/**
- * g_mapped_file_get_contents:
- * @file: a #GMappedFile
- *
- * Returns the contents of a #GMappedFile.
- *
- * Note that the contents may not be zero-terminated,
- * even if the #GMappedFile is backed by a text file.
- *
- * If the file is empty then %NULL is returned.
- *
- * Returns: the contents of @file, or %NULL.
- *
- * Since: 2.8
- */
-gchar *
-g_mapped_file_get_contents (GMappedFile *file)
-{
- g_return_val_if_fail (file != NULL, NULL);
-
- return file->contents;
-}
-
-/**
- * g_mapped_file_free:
- * @file: a #GMappedFile
- *
- * This call existed before #GMappedFile had refcounting and is currently
- * exactly the same as g_mapped_file_unref().
- *
- * Since: 2.8
- * Deprecated:2.22: Use g_mapped_file_unref() instead.
- */
-void
-g_mapped_file_free (GMappedFile *file)
-{
- g_mapped_file_unref (file);
-}
-
-/**
- * g_mapped_file_ref:
- * @file: a #GMappedFile
- *
- * Increments the reference count of @file by one. It is safe to call
- * this function from any thread.
- *
- * Return value: the passed in #GMappedFile.
- *
- * Since: 2.22
- **/
-GMappedFile *
-g_mapped_file_ref (GMappedFile *file)
-{
- g_return_val_if_fail (file != NULL, NULL);
- g_return_val_if_fail (file->ref_count > 0, file);
-
- g_atomic_int_inc (&file->ref_count);
-
- return file;
-}
-
-/**
- * g_mapped_file_unref:
- * @file: a #GMappedFile
- *
- * Decrements the reference count of @file by one. If the reference count
- * drops to 0, unmaps the buffer of @file and frees it.
- *
- * It is safe to call this function from any thread.
- *
- * Since 2.22
- **/
-void
-g_mapped_file_unref (GMappedFile *file)
-{
- g_return_if_fail (file != NULL);
- g_return_if_fail (file->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&file->ref_count))
- g_mapped_file_destroy (file);
-}
+++ /dev/null
-/* gmarkup.c - Simple XML-like parser
- *
- * Copyright 2000, 2003 Red Hat, Inc.
- * Copyright 2007, 2008 Ryan Lortie <desrt@desrt.ca>
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "gmarkup.h"
-
-#include "galloca.h"
-#include "gstrfuncs.h"
-#include "gstring.h"
-#include "gtestutils.h"
-#include "glibintl.h"
-
-GQuark
-g_markup_error_quark (void)
-{
- return g_quark_from_static_string ("g-markup-error-quark");
-}
-
-typedef enum
-{
- STATE_START,
- STATE_AFTER_OPEN_ANGLE,
- STATE_AFTER_CLOSE_ANGLE,
- STATE_AFTER_ELISION_SLASH, /* the slash that obviates need for end element */
- STATE_INSIDE_OPEN_TAG_NAME,
- STATE_INSIDE_ATTRIBUTE_NAME,
- STATE_AFTER_ATTRIBUTE_NAME,
- STATE_BETWEEN_ATTRIBUTES,
- STATE_AFTER_ATTRIBUTE_EQUALS_SIGN,
- STATE_INSIDE_ATTRIBUTE_VALUE_SQ,
- STATE_INSIDE_ATTRIBUTE_VALUE_DQ,
- STATE_INSIDE_TEXT,
- STATE_AFTER_CLOSE_TAG_SLASH,
- STATE_INSIDE_CLOSE_TAG_NAME,
- STATE_AFTER_CLOSE_TAG_NAME,
- STATE_INSIDE_PASSTHROUGH,
- STATE_ERROR
-} GMarkupParseState;
-
-typedef struct
-{
- const char *prev_element;
- const GMarkupParser *prev_parser;
- gpointer prev_user_data;
-} GMarkupRecursionTracker;
-
-struct _GMarkupParseContext
-{
- const GMarkupParser *parser;
-
- GMarkupParseFlags flags;
-
- gint line_number;
- gint char_number;
-
- gpointer user_data;
- GDestroyNotify dnotify;
-
- /* A piece of character data or an element that
- * hasn't "ended" yet so we haven't yet called
- * the callback for it.
- */
- GString *partial_chunk;
- GSList *spare_chunks;
-
- GMarkupParseState state;
- GSList *tag_stack;
- GSList *tag_stack_gstr;
- GSList *spare_list_nodes;
-
- GString **attr_names;
- GString **attr_values;
- gint cur_attr;
- gint alloc_attrs;
-
- const gchar *current_text;
- gssize current_text_len;
- const gchar *current_text_end;
-
- /* used to save the start of the last interesting thingy */
- const gchar *start;
-
- const gchar *iter;
-
- guint document_empty : 1;
- guint parsing : 1;
- guint awaiting_pop : 1;
- gint balance;
-
- /* subparser support */
- GSList *subparser_stack; /* (GMarkupRecursionTracker *) */
- const char *subparser_element;
- gpointer held_user_data;
-};
-
-/*
- * Helpers to reduce our allocation overhead, we have
- * a well defined allocation lifecycle.
- */
-static GSList *
-get_list_node (GMarkupParseContext *context, gpointer data)
-{
- GSList *node;
- if (context->spare_list_nodes != NULL)
- {
- node = context->spare_list_nodes;
- context->spare_list_nodes = g_slist_remove_link (context->spare_list_nodes, node);
- }
- else
- node = g_slist_alloc();
- node->data = data;
- return node;
-}
-
-static void
-free_list_node (GMarkupParseContext *context, GSList *node)
-{
- node->data = NULL;
- context->spare_list_nodes = g_slist_concat (node, context->spare_list_nodes);
-}
-
-static inline void
-string_blank (GString *string)
-{
- string->str[0] = '\0';
- string->len = 0;
-}
-
-/**
- * g_markup_parse_context_new:
- * @parser: a #GMarkupParser
- * @flags: one or more #GMarkupParseFlags
- * @user_data: user data to pass to #GMarkupParser functions
- * @user_data_dnotify: user data destroy notifier called when the parse context is freed
- *
- * Creates a new parse context. A parse context is used to parse
- * marked-up documents. You can feed any number of documents into
- * a context, as long as no errors occur; once an error occurs,
- * the parse context can't continue to parse text (you have to free it
- * and create a new parse context).
- *
- * Return value: a new #GMarkupParseContext
- **/
-GMarkupParseContext *
-g_markup_parse_context_new (const GMarkupParser *parser,
- GMarkupParseFlags flags,
- gpointer user_data,
- GDestroyNotify user_data_dnotify)
-{
- GMarkupParseContext *context;
-
- g_return_val_if_fail (parser != NULL, NULL);
-
- context = g_new (GMarkupParseContext, 1);
-
- context->parser = parser;
- context->flags = flags;
- context->user_data = user_data;
- context->dnotify = user_data_dnotify;
-
- context->line_number = 1;
- context->char_number = 1;
-
- context->partial_chunk = NULL;
- context->spare_chunks = NULL;
- context->spare_list_nodes = NULL;
-
- context->state = STATE_START;
- context->tag_stack = NULL;
- context->tag_stack_gstr = NULL;
- context->attr_names = NULL;
- context->attr_values = NULL;
- context->cur_attr = -1;
- context->alloc_attrs = 0;
-
- context->current_text = NULL;
- context->current_text_len = -1;
- context->current_text_end = NULL;
-
- context->start = NULL;
- context->iter = NULL;
-
- context->document_empty = TRUE;
- context->parsing = FALSE;
-
- context->awaiting_pop = FALSE;
- context->subparser_stack = NULL;
- context->subparser_element = NULL;
-
- /* this is only looked at if awaiting_pop = TRUE. initialise anyway. */
- context->held_user_data = NULL;
-
- context->balance = 0;
-
- return context;
-}
-
-static void
-string_full_free (gpointer ptr, gpointer user_data)
-{
- g_string_free (ptr, TRUE);
-}
-
-static void clear_attributes (GMarkupParseContext *context);
-
-/**
- * g_markup_parse_context_free:
- * @context: a #GMarkupParseContext
- *
- * Frees a #GMarkupParseContext. Can't be called from inside
- * one of the #GMarkupParser functions. Can't be called while
- * a subparser is pushed.
- **/
-void
-g_markup_parse_context_free (GMarkupParseContext *context)
-{
- g_return_if_fail (context != NULL);
- g_return_if_fail (!context->parsing);
- g_return_if_fail (!context->subparser_stack);
- g_return_if_fail (!context->awaiting_pop);
-
- if (context->dnotify)
- (* context->dnotify) (context->user_data);
-
- clear_attributes (context);
- g_free (context->attr_names);
- g_free (context->attr_values);
-
- g_slist_foreach (context->tag_stack_gstr, string_full_free, NULL);
- g_slist_free (context->tag_stack_gstr);
- g_slist_free (context->tag_stack);
-
- g_slist_foreach (context->spare_chunks, string_full_free, NULL);
- g_slist_free (context->spare_chunks);
- g_slist_free (context->spare_list_nodes);
-
- if (context->partial_chunk)
- g_string_free (context->partial_chunk, TRUE);
-
- g_free (context);
-}
-
-static void pop_subparser_stack (GMarkupParseContext *context);
-
-static void
-mark_error (GMarkupParseContext *context,
- GError *error)
-{
- context->state = STATE_ERROR;
-
- if (context->parser->error)
- (*context->parser->error) (context, error, context->user_data);
-
- /* report the error all the way up to free all the user-data */
- while (context->subparser_stack)
- {
- pop_subparser_stack (context);
- context->awaiting_pop = FALSE; /* already been freed */
-
- if (context->parser->error)
- (*context->parser->error) (context, error, context->user_data);
- }
-}
-
-static void set_error (GMarkupParseContext *context,
- GError **error,
- GMarkupError code,
- const gchar *format,
- ...) G_GNUC_PRINTF (4, 5);
-
-static void
-set_error_literal (GMarkupParseContext *context,
- GError **error,
- GMarkupError code,
- const gchar *message)
-{
- GError *tmp_error;
-
- tmp_error = g_error_new_literal (G_MARKUP_ERROR, code, message);
-
- g_prefix_error (&tmp_error,
- _("Error on line %d char %d: "),
- context->line_number,
- context->char_number);
-
- mark_error (context, tmp_error);
-
- g_propagate_error (error, tmp_error);
-}
-
-static void
-set_error (GMarkupParseContext *context,
- GError **error,
- GMarkupError code,
- const gchar *format,
- ...)
-{
- gchar *s;
- gchar *s_valid;
- va_list args;
-
- va_start (args, format);
- s = g_strdup_vprintf (format, args);
- va_end (args);
-
- /* Make sure that the GError message is valid UTF-8 even if it is
- * complaining about invalid UTF-8 in the markup: */
- s_valid = _g_utf8_make_valid (s);
- set_error_literal (context, error, code, s);
-
- g_free (s);
- g_free (s_valid);
-}
-
-static void
-propagate_error (GMarkupParseContext *context,
- GError **dest,
- GError *src)
-{
- if (context->flags & G_MARKUP_PREFIX_ERROR_POSITION)
- g_prefix_error (&src,
- _("Error on line %d char %d: "),
- context->line_number,
- context->char_number);
-
- mark_error (context, src);
-
- g_propagate_error (dest, src);
-}
-
-#define IS_COMMON_NAME_END_CHAR(c) \
- ((c) == '=' || (c) == '/' || (c) == '>' || (c) == ' ')
-
-static gboolean
-slow_name_validate (GMarkupParseContext *context, const char *name, GError **error)
-{
- const char *p = name;
-
- if (!g_utf8_validate (name, strlen (name), NULL))
- {
- set_error (context, error, G_MARKUP_ERROR_BAD_UTF8,
- _("Invalid UTF-8 encoded text in name - not valid '%s'"), name);
- return FALSE;
- }
-
- if (!(g_ascii_isalpha (*p) ||
- (!IS_COMMON_NAME_END_CHAR (*p) &&
- (*p == '_' ||
- *p == ':' ||
- g_unichar_isalpha (g_utf8_get_char (p))))))
- {
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("'%s' is not a valid name "), name);
- return FALSE;
- }
-
- for (p = g_utf8_next_char (name); *p != '\0'; p = g_utf8_next_char (p))
- {
- /* is_name_char */
- if (!(g_ascii_isalnum (*p) ||
- (!IS_COMMON_NAME_END_CHAR (*p) &&
- (*p == '.' ||
- *p == '-' ||
- *p == '_' ||
- *p == ':' ||
- g_unichar_isalpha (g_utf8_get_char (p))))))
- {
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("'%s' is not a valid name: '%c' "), name, *p);
- return FALSE;
- }
- }
- return TRUE;
-}
-
-/*
- * Use me for elements, attributes etc.
- */
-static gboolean
-name_validate (GMarkupParseContext *context, const char *name, GError **error)
-{
- char mask;
- const char *p;
-
- /* name start char */
- p = name;
- if (G_UNLIKELY (IS_COMMON_NAME_END_CHAR (*p) ||
- !(g_ascii_isalpha (*p) || *p == '_' || *p == ':')))
- goto slow_validate;
-
- for (mask = *p++; *p != '\0'; p++)
- {
- mask |= *p;
-
- /* is_name_char */
- if (G_UNLIKELY (!(g_ascii_isalnum (*p) ||
- (!IS_COMMON_NAME_END_CHAR (*p) &&
- (*p == '.' ||
- *p == '-' ||
- *p == '_' ||
- *p == ':')))))
- goto slow_validate;
- }
-
- if (mask & 0x80) /* un-common / non-ascii */
- goto slow_validate;
-
- return TRUE;
-
- slow_validate:
- return slow_name_validate (context, name, error);
-}
-
-static gboolean
-text_validate (GMarkupParseContext *context, const char *p, int len, GError **error)
-{
- if (!g_utf8_validate (p, len, NULL))
- {
- set_error (context, error, G_MARKUP_ERROR_BAD_UTF8,
- _("Invalid UTF-8 encoded text in name - not valid '%s'"), p);
- return FALSE;
- }
- else
- return TRUE;
-}
-
-static gchar*
-char_str (gunichar c,
- gchar *buf)
-{
- memset (buf, 0, 8);
- g_unichar_to_utf8 (c, buf);
- return buf;
-}
-
-static gchar*
-utf8_str (const gchar *utf8,
- gchar *buf)
-{
- char_str (g_utf8_get_char (utf8), buf);
- return buf;
-}
-
-static void
-set_unescape_error (GMarkupParseContext *context,
- GError **error,
- const gchar *remaining_text,
- GMarkupError code,
- const gchar *format,
- ...)
-{
- GError *tmp_error;
- gchar *s;
- va_list args;
- gint remaining_newlines;
- const gchar *p;
-
- remaining_newlines = 0;
- p = remaining_text;
- while (*p != '\0')
- {
- if (*p == '\n')
- ++remaining_newlines;
- ++p;
- }
-
- va_start (args, format);
- s = g_strdup_vprintf (format, args);
- va_end (args);
-
- tmp_error = g_error_new (G_MARKUP_ERROR,
- code,
- _("Error on line %d: %s"),
- context->line_number - remaining_newlines,
- s);
-
- g_free (s);
-
- mark_error (context, tmp_error);
-
- g_propagate_error (error, tmp_error);
-}
-
-/*
- * re-write the GString in-place, unescaping anything that escaped.
- * most XML does not contain entities, or escaping.
- */
-static gboolean
-unescape_gstring_inplace (GMarkupParseContext *context,
- GString *string,
- gboolean *is_ascii,
- GError **error)
-{
- char mask, *to;
- int line_num = 1;
- const char *from;
- gboolean normalize_attribute;
-
- *is_ascii = FALSE;
-
- /* are we unescaping an attribute or not ? */
- if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ ||
- context->state == STATE_INSIDE_ATTRIBUTE_VALUE_DQ)
- normalize_attribute = TRUE;
- else
- normalize_attribute = FALSE;
-
- /*
- * Meeks' theorum: unescaping can only shrink text.
- * for < etc. this is obvious, for  more
- * thought is required, but this is patently so.
- */
- mask = 0;
- for (from = to = string->str; *from != '\0'; from++, to++)
- {
- *to = *from;
-
- mask |= *to;
- if (*to == '\n')
- line_num++;
- if (normalize_attribute && (*to == '\t' || *to == '\n'))
- *to = ' ';
- if (*to == '\r')
- {
- *to = normalize_attribute ? ' ' : '\n';
- if (from[1] == '\n')
- from++;
- }
- if (*from == '&')
- {
- from++;
- if (*from == '#')
- {
- gboolean is_hex = FALSE;
- gulong l;
- gchar *end = NULL;
-
- from++;
-
- if (*from == 'x')
- {
- is_hex = TRUE;
- from++;
- }
-
- /* digit is between start and p */
- errno = 0;
- if (is_hex)
- l = strtoul (from, &end, 16);
- else
- l = strtoul (from, &end, 10);
-
- if (end == from || errno != 0)
- {
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Failed to parse '%-.*s', which "
- "should have been a digit "
- "inside a character reference "
- "(ê for example) - perhaps "
- "the digit is too large"),
- end - from, from);
- return FALSE;
- }
- else if (*end != ';')
- {
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Character reference did not end with a "
- "semicolon; "
- "most likely you used an ampersand "
- "character without intending to start "
- "an entity - escape ampersand as &"));
- return FALSE;
- }
- else
- {
- /* characters XML 1.1 permits */
- if ((0 < l && l <= 0xD7FF) ||
- (0xE000 <= l && l <= 0xFFFD) ||
- (0x10000 <= l && l <= 0x10FFFF))
- {
- gchar buf[8];
- char_str (l, buf);
- strcpy (to, buf);
- to += strlen (buf) - 1;
- from = end;
- if (l >= 0x80) /* not ascii */
- mask |= 0x80;
- }
- else
- {
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Character reference '%-.*s' does not "
- "encode a permitted character"),
- end - from, from);
- return FALSE;
- }
- }
- }
-
- else if (strncmp (from, "lt;", 3) == 0)
- {
- *to = '<';
- from += 2;
- }
- else if (strncmp (from, "gt;", 3) == 0)
- {
- *to = '>';
- from += 2;
- }
- else if (strncmp (from, "amp;", 4) == 0)
- {
- *to = '&';
- from += 3;
- }
- else if (strncmp (from, "quot;", 5) == 0)
- {
- *to = '"';
- from += 4;
- }
- else if (strncmp (from, "apos;", 5) == 0)
- {
- *to = '\'';
- from += 4;
- }
- else
- {
- if (*from == ';')
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Empty entity '&;' seen; valid "
- "entities are: & " < > '"));
- else
- {
- const char *end = strchr (from, ';');
- if (end)
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Entity name '%-.*s' is not known"),
- end-from, from);
- else
- set_unescape_error (context, error,
- from, G_MARKUP_ERROR_PARSE,
- _("Entity did not end with a semicolon; "
- "most likely you used an ampersand "
- "character without intending to start "
- "an entity - escape ampersand as &"));
- }
- return FALSE;
- }
- }
- }
-
- g_assert (to - string->str <= string->len);
- if (to - string->str != string->len)
- g_string_truncate (string, to - string->str);
-
- *is_ascii = !(mask & 0x80);
-
- return TRUE;
-}
-
-static inline gboolean
-advance_char (GMarkupParseContext *context)
-{
- context->iter++;
- context->char_number++;
-
- if (G_UNLIKELY (context->iter == context->current_text_end))
- return FALSE;
-
- else if (G_UNLIKELY (*context->iter == '\n'))
- {
- context->line_number++;
- context->char_number = 1;
- }
-
- return TRUE;
-}
-
-static inline gboolean
-xml_isspace (char c)
-{
- return c == ' ' || c == '\t' || c == '\n' || c == '\r';
-}
-
-static void
-skip_spaces (GMarkupParseContext *context)
-{
- do
- {
- if (!xml_isspace (*context->iter))
- return;
- }
- while (advance_char (context));
-}
-
-static void
-advance_to_name_end (GMarkupParseContext *context)
-{
- do
- {
- if (IS_COMMON_NAME_END_CHAR (*(context->iter)))
- return;
- if (xml_isspace (*(context->iter)))
- return;
- }
- while (advance_char (context));
-}
-
-static void
-release_chunk (GMarkupParseContext *context, GString *str)
-{
- GSList *node;
- if (!str)
- return;
- if (str->allocated_len > 256)
- { /* large strings are unusual and worth freeing */
- g_string_free (str, TRUE);
- return;
- }
- string_blank (str);
- node = get_list_node (context, str);
- context->spare_chunks = g_slist_concat (node, context->spare_chunks);
-}
-
-static void
-add_to_partial (GMarkupParseContext *context,
- const gchar *text_start,
- const gchar *text_end)
-{
- if (context->partial_chunk == NULL)
- { /* allocate a new chunk to parse into */
-
- if (context->spare_chunks != NULL)
- {
- GSList *node = context->spare_chunks;
- context->spare_chunks = g_slist_remove_link (context->spare_chunks, node);
- context->partial_chunk = node->data;
- free_list_node (context, node);
- }
- else
- context->partial_chunk = g_string_sized_new (MAX (28, text_end - text_start));
- }
-
- if (text_start != text_end)
- g_string_insert_len (context->partial_chunk, -1,
- text_start, text_end - text_start);
-}
-
-static inline void
-truncate_partial (GMarkupParseContext *context)
-{
- if (context->partial_chunk != NULL)
- string_blank (context->partial_chunk);
-}
-
-static inline const gchar*
-current_element (GMarkupParseContext *context)
-{
- return context->tag_stack->data;
-}
-
-static void
-pop_subparser_stack (GMarkupParseContext *context)
-{
- GMarkupRecursionTracker *tracker;
-
- g_assert (context->subparser_stack);
-
- tracker = context->subparser_stack->data;
-
- context->awaiting_pop = TRUE;
- context->held_user_data = context->user_data;
-
- context->user_data = tracker->prev_user_data;
- context->parser = tracker->prev_parser;
- context->subparser_element = tracker->prev_element;
- g_slice_free (GMarkupRecursionTracker, tracker);
-
- context->subparser_stack = g_slist_delete_link (context->subparser_stack,
- context->subparser_stack);
-}
-
-static void
-push_partial_as_tag (GMarkupParseContext *context)
-{
- GString *str = context->partial_chunk;
- /* sadly, this is exported by gmarkup_get_element_stack as-is */
- context->tag_stack = g_slist_concat (get_list_node (context, str->str), context->tag_stack);
- context->tag_stack_gstr = g_slist_concat (get_list_node (context, str), context->tag_stack_gstr);
- context->partial_chunk = NULL;
-}
-
-static void
-pop_tag (GMarkupParseContext *context)
-{
- GSList *nodea, *nodeb;
-
- nodea = context->tag_stack;
- nodeb = context->tag_stack_gstr;
- release_chunk (context, nodeb->data);
- context->tag_stack = g_slist_remove_link (context->tag_stack, nodea);
- context->tag_stack_gstr = g_slist_remove_link (context->tag_stack_gstr, nodeb);
- free_list_node (context, nodea);
- free_list_node (context, nodeb);
-}
-
-static void
-possibly_finish_subparser (GMarkupParseContext *context)
-{
- if (current_element (context) == context->subparser_element)
- pop_subparser_stack (context);
-}
-
-static void
-ensure_no_outstanding_subparser (GMarkupParseContext *context)
-{
- if (context->awaiting_pop)
- g_critical ("During the first end_element call after invoking a "
- "subparser you must pop the subparser stack and handle "
- "the freeing of the subparser user_data. This can be "
- "done by calling the end function of the subparser. "
- "Very probably, your program just leaked memory.");
-
- /* let valgrind watch the pointer disappear... */
- context->held_user_data = NULL;
- context->awaiting_pop = FALSE;
-}
-
-static const gchar*
-current_attribute (GMarkupParseContext *context)
-{
- g_assert (context->cur_attr >= 0);
- return context->attr_names[context->cur_attr]->str;
-}
-
-static void
-add_attribute (GMarkupParseContext *context, GString *str)
-{
- if (context->cur_attr + 2 >= context->alloc_attrs)
- {
- context->alloc_attrs += 5; /* silly magic number */
- context->attr_names = g_realloc (context->attr_names, sizeof(GString*)*context->alloc_attrs);
- context->attr_values = g_realloc (context->attr_values, sizeof(GString*)*context->alloc_attrs);
- }
- context->cur_attr++;
- context->attr_names[context->cur_attr] = str;
- context->attr_values[context->cur_attr] = NULL;
- context->attr_names[context->cur_attr+1] = NULL;
- context->attr_values[context->cur_attr+1] = NULL;
-}
-
-static void
-clear_attributes (GMarkupParseContext *context)
-{
- /* Go ahead and free the attributes. */
- for (; context->cur_attr >= 0; context->cur_attr--)
- {
- int pos = context->cur_attr;
- release_chunk (context, context->attr_names[pos]);
- release_chunk (context, context->attr_values[pos]);
- context->attr_names[pos] = context->attr_values[pos] = NULL;
- }
- g_assert (context->cur_attr == -1);
- g_assert (context->attr_names == NULL ||
- context->attr_names[0] == NULL);
- g_assert (context->attr_values == NULL ||
- context->attr_values[0] == NULL);
-}
-
-/* This has to be a separate function to ensure the alloca's
- are unwound on exit - otherwise we grow & blow the stack
- with large documents */
-static inline void
-emit_start_element (GMarkupParseContext *context, GError **error)
-{
- int i;
- const gchar *start_name;
- const gchar **attr_names;
- const gchar **attr_values;
- GError *tmp_error;
-
- attr_names = g_newa (const gchar *, context->cur_attr + 2);
- attr_values = g_newa (const gchar *, context->cur_attr + 2);
- for (i = 0; i < context->cur_attr + 1; i++)
- {
- attr_names[i] = context->attr_names[i]->str;
- attr_values[i] = context->attr_values[i]->str;
- }
- attr_names[i] = NULL;
- attr_values[i] = NULL;
-
- /* Call user callback for element start */
- tmp_error = NULL;
- start_name = current_element (context);
-
- if (context->parser->start_element &&
- name_validate (context, start_name, error))
- (* context->parser->start_element) (context,
- start_name,
- (const gchar **)attr_names,
- (const gchar **)attr_values,
- context->user_data,
- &tmp_error);
- clear_attributes (context);
-
- if (tmp_error != NULL)
- propagate_error (context, error, tmp_error);
-}
-
-/**
- * g_markup_parse_context_parse:
- * @context: a #GMarkupParseContext
- * @text: chunk of text to parse
- * @text_len: length of @text in bytes
- * @error: return location for a #GError
- *
- * Feed some data to the #GMarkupParseContext. The data need not
- * be valid UTF-8; an error will be signaled if it's invalid.
- * The data need not be an entire document; you can feed a document
- * into the parser incrementally, via multiple calls to this function.
- * Typically, as you receive data from a network connection or file,
- * you feed each received chunk of data into this function, aborting
- * the process if an error occurs. Once an error is reported, no further
- * data may be fed to the #GMarkupParseContext; all errors are fatal.
- *
- * Return value: %FALSE if an error occurred, %TRUE on success
- **/
-gboolean
-g_markup_parse_context_parse (GMarkupParseContext *context,
- const gchar *text,
- gssize text_len,
- GError **error)
-{
- g_return_val_if_fail (context != NULL, FALSE);
- g_return_val_if_fail (text != NULL, FALSE);
- g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
- g_return_val_if_fail (!context->parsing, FALSE);
-
- if (text_len < 0)
- text_len = strlen (text);
-
- if (text_len == 0)
- return TRUE;
-
- context->parsing = TRUE;
-
-
- context->current_text = text;
- context->current_text_len = text_len;
- context->current_text_end = context->current_text + text_len;
- context->iter = context->current_text;
- context->start = context->iter;
-
- if (context->current_text_len == 0)
- goto finished;
-
- while (context->iter != context->current_text_end)
- {
- switch (context->state)
- {
- case STATE_START:
- /* Possible next state: AFTER_OPEN_ANGLE */
-
- g_assert (context->tag_stack == NULL);
-
- /* whitespace is ignored outside of any elements */
- skip_spaces (context);
-
- if (context->iter != context->current_text_end)
- {
- if (*context->iter == '<')
- {
- /* Move after the open angle */
- advance_char (context);
-
- context->state = STATE_AFTER_OPEN_ANGLE;
-
- /* this could start a passthrough */
- context->start = context->iter;
-
- /* document is now non-empty */
- context->document_empty = FALSE;
- }
- else
- {
- set_error_literal (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Document must begin with an element (e.g. <book>)"));
- }
- }
- break;
-
- case STATE_AFTER_OPEN_ANGLE:
- /* Possible next states: INSIDE_OPEN_TAG_NAME,
- * AFTER_CLOSE_TAG_SLASH, INSIDE_PASSTHROUGH
- */
- if (*context->iter == '?' ||
- *context->iter == '!')
- {
- /* include < in the passthrough */
- const gchar *openangle = "<";
- add_to_partial (context, openangle, openangle + 1);
- context->start = context->iter;
- context->balance = 1;
- context->state = STATE_INSIDE_PASSTHROUGH;
- }
- else if (*context->iter == '/')
- {
- /* move after it */
- advance_char (context);
-
- context->state = STATE_AFTER_CLOSE_TAG_SLASH;
- }
- else if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
- {
- context->state = STATE_INSIDE_OPEN_TAG_NAME;
-
- /* start of tag name */
- context->start = context->iter;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("'%s' is not a valid character following "
- "a '<' character; it may not begin an "
- "element name"),
- utf8_str (context->iter, buf));
- }
- break;
-
- /* The AFTER_CLOSE_ANGLE state is actually sort of
- * broken, because it doesn't correspond to a range
- * of characters in the input stream as the others do,
- * and thus makes things harder to conceptualize
- */
- case STATE_AFTER_CLOSE_ANGLE:
- /* Possible next states: INSIDE_TEXT, STATE_START */
- if (context->tag_stack == NULL)
- {
- context->start = NULL;
- context->state = STATE_START;
- }
- else
- {
- context->start = context->iter;
- context->state = STATE_INSIDE_TEXT;
- }
- break;
-
- case STATE_AFTER_ELISION_SLASH:
- /* Possible next state: AFTER_CLOSE_ANGLE */
-
- {
- /* We need to pop the tag stack and call the end_element
- * function, since this is the close tag
- */
- GError *tmp_error = NULL;
-
- g_assert (context->tag_stack != NULL);
-
- possibly_finish_subparser (context);
-
- tmp_error = NULL;
- if (context->parser->end_element)
- (* context->parser->end_element) (context,
- current_element (context),
- context->user_data,
- &tmp_error);
-
- ensure_no_outstanding_subparser (context);
-
- if (tmp_error)
- {
- mark_error (context, tmp_error);
- g_propagate_error (error, tmp_error);
- }
- else
- {
- if (*context->iter == '>')
- {
- /* move after the close angle */
- advance_char (context);
- context->state = STATE_AFTER_CLOSE_ANGLE;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Odd character '%s', expected a '>' character "
- "to end the empty-element tag '%s'"),
- utf8_str (context->iter, buf),
- current_element (context));
- }
- }
- pop_tag (context);
- }
- break;
-
- case STATE_INSIDE_OPEN_TAG_NAME:
- /* Possible next states: BETWEEN_ATTRIBUTES */
-
- /* if there's a partial chunk then it's the first part of the
- * tag name. If there's a context->start then it's the start
- * of the tag name in current_text, the partial chunk goes
- * before that start though.
- */
- advance_to_name_end (context);
-
- if (context->iter == context->current_text_end)
- {
- /* The name hasn't necessarily ended. Merge with
- * partial chunk, leave state unchanged.
- */
- add_to_partial (context, context->start, context->iter);
- }
- else
- {
- /* The name has ended. Combine it with the partial chunk
- * if any; push it on the stack; enter next state.
- */
- add_to_partial (context, context->start, context->iter);
- push_partial_as_tag (context);
-
- context->state = STATE_BETWEEN_ATTRIBUTES;
- context->start = NULL;
- }
- break;
-
- case STATE_INSIDE_ATTRIBUTE_NAME:
- /* Possible next states: AFTER_ATTRIBUTE_NAME */
-
- advance_to_name_end (context);
- add_to_partial (context, context->start, context->iter);
-
- /* read the full name, if we enter the equals sign state
- * then add the attribute to the list (without the value),
- * otherwise store a partial chunk to be prepended later.
- */
- if (context->iter != context->current_text_end)
- context->state = STATE_AFTER_ATTRIBUTE_NAME;
- break;
-
- case STATE_AFTER_ATTRIBUTE_NAME:
- /* Possible next states: AFTER_ATTRIBUTE_EQUALS_SIGN */
-
- skip_spaces (context);
-
- if (context->iter != context->current_text_end)
- {
- /* The name has ended. Combine it with the partial chunk
- * if any; push it on the stack; enter next state.
- */
- if (!name_validate (context, context->partial_chunk->str, error))
- break;
-
- add_attribute (context, context->partial_chunk);
-
- context->partial_chunk = NULL;
- context->start = NULL;
-
- if (*context->iter == '=')
- {
- advance_char (context);
- context->state = STATE_AFTER_ATTRIBUTE_EQUALS_SIGN;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Odd character '%s', expected a '=' after "
- "attribute name '%s' of element '%s'"),
- utf8_str (context->iter, buf),
- current_attribute (context),
- current_element (context));
-
- }
- }
- break;
-
- case STATE_BETWEEN_ATTRIBUTES:
- /* Possible next states: AFTER_CLOSE_ANGLE,
- * AFTER_ELISION_SLASH, INSIDE_ATTRIBUTE_NAME
- */
- skip_spaces (context);
-
- if (context->iter != context->current_text_end)
- {
- if (*context->iter == '/')
- {
- advance_char (context);
- context->state = STATE_AFTER_ELISION_SLASH;
- }
- else if (*context->iter == '>')
- {
- advance_char (context);
- context->state = STATE_AFTER_CLOSE_ANGLE;
- }
- else if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
- {
- context->state = STATE_INSIDE_ATTRIBUTE_NAME;
- /* start of attribute name */
- context->start = context->iter;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Odd character '%s', expected a '>' or '/' "
- "character to end the start tag of "
- "element '%s', or optionally an attribute; "
- "perhaps you used an invalid character in "
- "an attribute name"),
- utf8_str (context->iter, buf),
- current_element (context));
- }
-
- /* If we're done with attributes, invoke
- * the start_element callback
- */
- if (context->state == STATE_AFTER_ELISION_SLASH ||
- context->state == STATE_AFTER_CLOSE_ANGLE)
- emit_start_element (context, error);
- }
- break;
-
- case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
- /* Possible next state: INSIDE_ATTRIBUTE_VALUE_[SQ/DQ] */
-
- skip_spaces (context);
-
- if (context->iter != context->current_text_end)
- {
- if (*context->iter == '"')
- {
- advance_char (context);
- context->state = STATE_INSIDE_ATTRIBUTE_VALUE_DQ;
- context->start = context->iter;
- }
- else if (*context->iter == '\'')
- {
- advance_char (context);
- context->state = STATE_INSIDE_ATTRIBUTE_VALUE_SQ;
- context->start = context->iter;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Odd character '%s', expected an open quote mark "
- "after the equals sign when giving value for "
- "attribute '%s' of element '%s'"),
- utf8_str (context->iter, buf),
- current_attribute (context),
- current_element (context));
- }
- }
- break;
-
- case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
- case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
- /* Possible next states: BETWEEN_ATTRIBUTES */
- {
- gchar delim;
-
- if (context->state == STATE_INSIDE_ATTRIBUTE_VALUE_SQ)
- {
- delim = '\'';
- }
- else
- {
- delim = '"';
- }
-
- do
- {
- if (*context->iter == delim)
- break;
- }
- while (advance_char (context));
- }
- if (context->iter == context->current_text_end)
- {
- /* The value hasn't necessarily ended. Merge with
- * partial chunk, leave state unchanged.
- */
- add_to_partial (context, context->start, context->iter);
- }
- else
- {
- gboolean is_ascii;
- /* The value has ended at the quote mark. Combine it
- * with the partial chunk if any; set it for the current
- * attribute.
- */
- add_to_partial (context, context->start, context->iter);
-
- g_assert (context->cur_attr >= 0);
-
- if (unescape_gstring_inplace (context, context->partial_chunk, &is_ascii, error) &&
- (is_ascii || text_validate (context, context->partial_chunk->str,
- context->partial_chunk->len, error)))
- {
- /* success, advance past quote and set state. */
- context->attr_values[context->cur_attr] = context->partial_chunk;
- context->partial_chunk = NULL;
- advance_char (context);
- context->state = STATE_BETWEEN_ATTRIBUTES;
- context->start = NULL;
- }
-
- truncate_partial (context);
- }
- break;
-
- case STATE_INSIDE_TEXT:
- /* Possible next states: AFTER_OPEN_ANGLE */
- do
- {
- if (*context->iter == '<')
- break;
- }
- while (advance_char (context));
-
- /* The text hasn't necessarily ended. Merge with
- * partial chunk, leave state unchanged.
- */
-
- add_to_partial (context, context->start, context->iter);
-
- if (context->iter != context->current_text_end)
- {
- gboolean is_ascii;
-
- /* The text has ended at the open angle. Call the text
- * callback.
- */
-
- if (unescape_gstring_inplace (context, context->partial_chunk, &is_ascii, error) &&
- (is_ascii || text_validate (context, context->partial_chunk->str,
- context->partial_chunk->len, error)))
- {
- GError *tmp_error = NULL;
-
- if (context->parser->text)
- (*context->parser->text) (context,
- context->partial_chunk->str,
- context->partial_chunk->len,
- context->user_data,
- &tmp_error);
-
- if (tmp_error == NULL)
- {
- /* advance past open angle and set state. */
- advance_char (context);
- context->state = STATE_AFTER_OPEN_ANGLE;
- /* could begin a passthrough */
- context->start = context->iter;
- }
- else
- propagate_error (context, error, tmp_error);
- }
-
- truncate_partial (context);
- }
- break;
-
- case STATE_AFTER_CLOSE_TAG_SLASH:
- /* Possible next state: INSIDE_CLOSE_TAG_NAME */
- if (!IS_COMMON_NAME_END_CHAR (*(context->iter)))
- {
- context->state = STATE_INSIDE_CLOSE_TAG_NAME;
-
- /* start of tag name */
- context->start = context->iter;
- }
- else
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("'%s' is not a valid character following "
- "the characters '</'; '%s' may not begin an "
- "element name"),
- utf8_str (context->iter, buf),
- utf8_str (context->iter, buf));
- }
- break;
-
- case STATE_INSIDE_CLOSE_TAG_NAME:
- /* Possible next state: AFTER_CLOSE_TAG_NAME */
- advance_to_name_end (context);
- add_to_partial (context, context->start, context->iter);
-
- if (context->iter != context->current_text_end)
- context->state = STATE_AFTER_CLOSE_TAG_NAME;
- break;
-
- case STATE_AFTER_CLOSE_TAG_NAME:
- /* Possible next state: AFTER_CLOSE_TAG_SLASH */
-
- skip_spaces (context);
-
- if (context->iter != context->current_text_end)
- {
- GString *close_name;
-
- close_name = context->partial_chunk;
- context->partial_chunk = NULL;
-
- if (*context->iter != '>')
- {
- gchar buf[8];
-
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("'%s' is not a valid character following "
- "the close element name '%s'; the allowed "
- "character is '>'"),
- utf8_str (context->iter, buf),
- close_name->str);
- }
- else if (context->tag_stack == NULL)
- {
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Element '%s' was closed, no element "
- "is currently open"),
- close_name->str);
- }
- else if (strcmp (close_name->str, current_element (context)) != 0)
- {
- set_error (context,
- error,
- G_MARKUP_ERROR_PARSE,
- _("Element '%s' was closed, but the currently "
- "open element is '%s'"),
- close_name->str,
- current_element (context));
- }
- else
- {
- GError *tmp_error;
- advance_char (context);
- context->state = STATE_AFTER_CLOSE_ANGLE;
- context->start = NULL;
-
- possibly_finish_subparser (context);
-
- /* call the end_element callback */
- tmp_error = NULL;
- if (context->parser->end_element)
- (* context->parser->end_element) (context,
- close_name->str,
- context->user_data,
- &tmp_error);
-
- ensure_no_outstanding_subparser (context);
- pop_tag (context);
-
- if (tmp_error)
- propagate_error (context, error, tmp_error);
- }
- context->partial_chunk = close_name;
- truncate_partial (context);
- }
- break;
-
- case STATE_INSIDE_PASSTHROUGH:
- /* Possible next state: AFTER_CLOSE_ANGLE */
- do
- {
- if (*context->iter == '<')
- context->balance++;
- if (*context->iter == '>')
- {
- gchar *str;
- gsize len;
-
- context->balance--;
- add_to_partial (context, context->start, context->iter);
- context->start = context->iter;
-
- str = context->partial_chunk->str;
- len = context->partial_chunk->len;
-
- if (str[1] == '?' && str[len - 1] == '?')
- break;
- if (strncmp (str, "<!--", 4) == 0 &&
- strcmp (str + len - 2, "--") == 0)
- break;
- if (strncmp (str, "<![CDATA[", 9) == 0 &&
- strcmp (str + len - 2, "]]") == 0)
- break;
- if (strncmp (str, "<!DOCTYPE", 9) == 0 &&
- context->balance == 0)
- break;
- }
- }
- while (advance_char (context));
-
- if (context->iter == context->current_text_end)
- {
- /* The passthrough hasn't necessarily ended. Merge with
- * partial chunk, leave state unchanged.
- */
- add_to_partial (context, context->start, context->iter);
- }
- else
- {
- /* The passthrough has ended at the close angle. Combine
- * it with the partial chunk if any. Call the passthrough
- * callback. Note that the open/close angles are
- * included in the text of the passthrough.
- */
- GError *tmp_error = NULL;
-
- advance_char (context); /* advance past close angle */
- add_to_partial (context, context->start, context->iter);
-
- if (context->flags & G_MARKUP_TREAT_CDATA_AS_TEXT &&
- strncmp (context->partial_chunk->str, "<![CDATA[", 9) == 0)
- {
- if (context->parser->text &&
- text_validate (context,
- context->partial_chunk->str + 9,
- context->partial_chunk->len - 12,
- error))
- (*context->parser->text) (context,
- context->partial_chunk->str + 9,
- context->partial_chunk->len - 12,
- context->user_data,
- &tmp_error);
- }
- else if (context->parser->passthrough &&
- text_validate (context,
- context->partial_chunk->str,
- context->partial_chunk->len,
- error))
- (*context->parser->passthrough) (context,
- context->partial_chunk->str,
- context->partial_chunk->len,
- context->user_data,
- &tmp_error);
-
- truncate_partial (context);
-
- if (tmp_error == NULL)
- {
- context->state = STATE_AFTER_CLOSE_ANGLE;
- context->start = context->iter; /* could begin text */
- }
- else
- propagate_error (context, error, tmp_error);
- }
- break;
-
- case STATE_ERROR:
- goto finished;
- break;
-
- default:
- g_assert_not_reached ();
- break;
- }
- }
-
- finished:
- context->parsing = FALSE;
-
- return context->state != STATE_ERROR;
-}
-
-/**
- * g_markup_parse_context_end_parse:
- * @context: a #GMarkupParseContext
- * @error: return location for a #GError
- *
- * Signals to the #GMarkupParseContext that all data has been
- * fed into the parse context with g_markup_parse_context_parse().
- * This function reports an error if the document isn't complete,
- * for example if elements are still open.
- *
- * Return value: %TRUE on success, %FALSE if an error was set
- **/
-gboolean
-g_markup_parse_context_end_parse (GMarkupParseContext *context,
- GError **error)
-{
- g_return_val_if_fail (context != NULL, FALSE);
- g_return_val_if_fail (!context->parsing, FALSE);
- g_return_val_if_fail (context->state != STATE_ERROR, FALSE);
-
- if (context->partial_chunk != NULL)
- {
- g_string_free (context->partial_chunk, TRUE);
- context->partial_chunk = NULL;
- }
-
- if (context->document_empty)
- {
- set_error_literal (context, error, G_MARKUP_ERROR_EMPTY,
- _("Document was empty or contained only whitespace"));
- return FALSE;
- }
-
- context->parsing = TRUE;
-
- switch (context->state)
- {
- case STATE_START:
- /* Nothing to do */
- break;
-
- case STATE_AFTER_OPEN_ANGLE:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly just after an open angle bracket '<'"));
- break;
-
- case STATE_AFTER_CLOSE_ANGLE:
- if (context->tag_stack != NULL)
- {
- /* Error message the same as for INSIDE_TEXT */
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly with elements still open - "
- "'%s' was the last element opened"),
- current_element (context));
- }
- break;
-
- case STATE_AFTER_ELISION_SLASH:
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly, expected to see a close angle "
- "bracket ending the tag <%s/>"), current_element (context));
- break;
-
- case STATE_INSIDE_OPEN_TAG_NAME:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly inside an element name"));
- break;
-
- case STATE_INSIDE_ATTRIBUTE_NAME:
- case STATE_AFTER_ATTRIBUTE_NAME:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly inside an attribute name"));
- break;
-
- case STATE_BETWEEN_ATTRIBUTES:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly inside an element-opening "
- "tag."));
- break;
-
- case STATE_AFTER_ATTRIBUTE_EQUALS_SIGN:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly after the equals sign "
- "following an attribute name; no attribute value"));
- break;
-
- case STATE_INSIDE_ATTRIBUTE_VALUE_SQ:
- case STATE_INSIDE_ATTRIBUTE_VALUE_DQ:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly while inside an attribute "
- "value"));
- break;
-
- case STATE_INSIDE_TEXT:
- g_assert (context->tag_stack != NULL);
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly with elements still open - "
- "'%s' was the last element opened"),
- current_element (context));
- break;
-
- case STATE_AFTER_CLOSE_TAG_SLASH:
- case STATE_INSIDE_CLOSE_TAG_NAME:
- case STATE_AFTER_CLOSE_TAG_NAME:
- set_error (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly inside the close tag for "
- "element '%s'"), current_element (context));
- break;
-
- case STATE_INSIDE_PASSTHROUGH:
- set_error_literal (context, error, G_MARKUP_ERROR_PARSE,
- _("Document ended unexpectedly inside a comment or "
- "processing instruction"));
- break;
-
- case STATE_ERROR:
- default:
- g_assert_not_reached ();
- break;
- }
-
- context->parsing = FALSE;
-
- return context->state != STATE_ERROR;
-}
-
-/**
- * g_markup_parse_context_get_element:
- * @context: a #GMarkupParseContext
- * @returns: the name of the currently open element, or %NULL
- *
- * Retrieves the name of the currently open element.
- *
- * If called from the start_element or end_element handlers this will
- * give the element_name as passed to those functions. For the parent
- * elements, see g_markup_parse_context_get_element_stack().
- *
- * Since: 2.2
- **/
-G_CONST_RETURN gchar *
-g_markup_parse_context_get_element (GMarkupParseContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
-
- if (context->tag_stack == NULL)
- return NULL;
- else
- return current_element (context);
-}
-
-/**
- * g_markup_parse_context_get_element_stack:
- * @context: a #GMarkupParseContext
- *
- * Retrieves the element stack from the internal state of the parser.
- * The returned #GSList is a list of strings where the first item is
- * the currently open tag (as would be returned by
- * g_markup_parse_context_get_element()) and the next item is its
- * immediate parent.
- *
- * This function is intended to be used in the start_element and
- * end_element handlers where g_markup_parse_context_get_element()
- * would merely return the name of the element that is being
- * processed.
- *
- * Returns: the element stack, which must not be modified
- *
- * Since: 2.16
- **/
-G_CONST_RETURN GSList *
-g_markup_parse_context_get_element_stack (GMarkupParseContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
- return context->tag_stack;
-}
-
-/**
- * g_markup_parse_context_get_position:
- * @context: a #GMarkupParseContext
- * @line_number: return location for a line number, or %NULL
- * @char_number: return location for a char-on-line number, or %NULL
- *
- * Retrieves the current line number and the number of the character on
- * that line. Intended for use in error messages; there are no strict
- * semantics for what constitutes the "current" line number other than
- * "the best number we could come up with for error messages."
- *
- **/
-void
-g_markup_parse_context_get_position (GMarkupParseContext *context,
- gint *line_number,
- gint *char_number)
-{
- g_return_if_fail (context != NULL);
-
- if (line_number)
- *line_number = context->line_number;
-
- if (char_number)
- *char_number = context->char_number;
-}
-
-/**
- * g_markup_parse_context_get_user_data:
- * @context: a #GMarkupParseContext
- *
- * Returns the user_data associated with @context. This will either
- * be the user_data that was provided to g_markup_parse_context_new()
- * or to the most recent call of g_markup_parse_context_push().
- *
- * Returns: the provided user_data. The returned data belongs to
- * the markup context and will be freed when g_markup_context_free()
- * is called.
- *
- * Since: 2.18
- **/
-gpointer
-g_markup_parse_context_get_user_data (GMarkupParseContext *context)
-{
- return context->user_data;
-}
-
-/**
- * g_markup_parse_context_push:
- * @context: a #GMarkupParseContext
- * @parser: a #GMarkupParser
- * @user_data: user data to pass to #GMarkupParser functions
- *
- * Temporarily redirects markup data to a sub-parser.
- *
- * This function may only be called from the start_element handler of
- * a #GMarkupParser. It must be matched with a corresponding call to
- * g_markup_parse_context_pop() in the matching end_element handler
- * (except in the case that the parser aborts due to an error).
- *
- * All tags, text and other data between the matching tags is
- * redirected to the subparser given by @parser. @user_data is used
- * as the user_data for that parser. @user_data is also passed to the
- * error callback in the event that an error occurs. This includes
- * errors that occur in subparsers of the subparser.
- *
- * The end tag matching the start tag for which this call was made is
- * handled by the previous parser (which is given its own user_data)
- * which is why g_markup_parse_context_pop() is provided to allow "one
- * last access" to the @user_data provided to this function. In the
- * case of error, the @user_data provided here is passed directly to
- * the error callback of the subparser and g_markup_parse_context()
- * should not be called. In either case, if @user_data was allocated
- * then it ought to be freed from both of these locations.
- *
- * This function is not intended to be directly called by users
- * interested in invoking subparsers. Instead, it is intended to be
- * used by the subparsers themselves to implement a higher-level
- * interface.
- *
- * As an example, see the following implementation of a simple
- * parser that counts the number of tags encountered.
- *
- * |[
- * typedef struct
- * {
- * gint tag_count;
- * } CounterData;
- *
- * static void
- * counter_start_element (GMarkupParseContext *context,
- * const gchar *element_name,
- * const gchar **attribute_names,
- * const gchar **attribute_values,
- * gpointer user_data,
- * GError **error)
- * {
- * CounterData *data = user_data;
- *
- * data->tag_count++;
- * }
- *
- * static void
- * counter_error (GMarkupParseContext *context,
- * GError *error,
- * gpointer user_data)
- * {
- * CounterData *data = user_data;
- *
- * g_slice_free (CounterData, data);
- * }
- *
- * static GMarkupParser counter_subparser =
- * {
- * counter_start_element,
- * NULL,
- * NULL,
- * NULL,
- * counter_error
- * };
- * ]|
- *
- * In order to allow this parser to be easily used as a subparser, the
- * following interface is provided:
- *
- * |[
- * void
- * start_counting (GMarkupParseContext *context)
- * {
- * CounterData *data = g_slice_new (CounterData);
- *
- * data->tag_count = 0;
- * g_markup_parse_context_push (context, &counter_subparser, data);
- * }
- *
- * gint
- * end_counting (GMarkupParseContext *context)
- * {
- * CounterData *data = g_markup_parse_context_pop (context);
- * int result;
- *
- * result = data->tag_count;
- * g_slice_free (CounterData, data);
- *
- * return result;
- * }
- * ]|
- *
- * The subparser would then be used as follows:
- *
- * |[
- * static void start_element (context, element_name, ...)
- * {
- * if (strcmp (element_name, "count-these") == 0)
- * start_counting (context);
- *
- * /* else, handle other tags... */
- * }
- *
- * static void end_element (context, element_name, ...)
- * {
- * if (strcmp (element_name, "count-these") == 0)
- * g_print ("Counted %d tags\n", end_counting (context));
- *
- * /* else, handle other tags... */
- * }
- * ]|
- *
- * Since: 2.18
- **/
-void
-g_markup_parse_context_push (GMarkupParseContext *context,
- const GMarkupParser *parser,
- gpointer user_data)
-{
- GMarkupRecursionTracker *tracker;
-
- tracker = g_slice_new (GMarkupRecursionTracker);
- tracker->prev_element = context->subparser_element;
- tracker->prev_parser = context->parser;
- tracker->prev_user_data = context->user_data;
-
- context->subparser_element = current_element (context);
- context->parser = parser;
- context->user_data = user_data;
-
- context->subparser_stack = g_slist_prepend (context->subparser_stack,
- tracker);
-}
-
-/**
- * g_markup_parse_context_pop:
- * @context: a #GMarkupParseContext
- *
- * Completes the process of a temporary sub-parser redirection.
- *
- * This function exists to collect the user_data allocated by a
- * matching call to g_markup_parse_context_push(). It must be called
- * in the end_element handler corresponding to the start_element
- * handler during which g_markup_parse_context_push() was called. You
- * must not call this function from the error callback -- the
- * @user_data is provided directly to the callback in that case.
- *
- * This function is not intended to be directly called by users
- * interested in invoking subparsers. Instead, it is intended to be
- * used by the subparsers themselves to implement a higher-level
- * interface.
- *
- * Returns: the user_data passed to g_markup_parse_context_push().
- *
- * Since: 2.18
- **/
-gpointer
-g_markup_parse_context_pop (GMarkupParseContext *context)
-{
- gpointer user_data;
-
- if (!context->awaiting_pop)
- possibly_finish_subparser (context);
-
- g_assert (context->awaiting_pop);
-
- context->awaiting_pop = FALSE;
-
- /* valgrind friendliness */
- user_data = context->held_user_data;
- context->held_user_data = NULL;
-
- return user_data;
-}
-
-static void
-append_escaped_text (GString *str,
- const gchar *text,
- gssize length)
-{
- const gchar *p;
- const gchar *end;
- gunichar c;
-
- p = text;
- end = text + length;
-
- while (p != end)
- {
- const gchar *next;
- next = g_utf8_next_char (p);
-
- switch (*p)
- {
- case '&':
- g_string_append (str, "&");
- break;
-
- case '<':
- g_string_append (str, "<");
- break;
-
- case '>':
- g_string_append (str, ">");
- break;
-
- case '\'':
- g_string_append (str, "'");
- break;
-
- case '"':
- g_string_append (str, """);
- break;
-
- default:
- c = g_utf8_get_char (p);
- if ((0x1 <= c && c <= 0x8) ||
- (0xb <= c && c <= 0xc) ||
- (0xe <= c && c <= 0x1f) ||
- (0x7f <= c && c <= 0x84) ||
- (0x86 <= c && c <= 0x9f))
- g_string_append_printf (str, "&#x%x;", c);
- else
- g_string_append_len (str, p, next - p);
- break;
- }
-
- p = next;
- }
-}
-
-/**
- * g_markup_escape_text:
- * @text: some valid UTF-8 text
- * @length: length of @text in bytes, or -1 if the text is nul-terminated
- *
- * Escapes text so that the markup parser will parse it verbatim.
- * Less than, greater than, ampersand, etc. are replaced with the
- * corresponding entities. This function would typically be used
- * when writing out a file to be parsed with the markup parser.
- *
- * Note that this function doesn't protect whitespace and line endings
- * from being processed according to the XML rules for normalization
- * of line endings and attribute values.
- *
- * Note also that this function will produce character references in
- * the range of &#x1; ... &#x1f; for all control sequences
- * except for tabstop, newline and carriage return. The character
- * references in this range are not valid XML 1.0, but they are
- * valid XML 1.1 and will be accepted by the GMarkup parser.
- *
- * Return value: a newly allocated string with the escaped text
- **/
-gchar*
-g_markup_escape_text (const gchar *text,
- gssize length)
-{
- GString *str;
-
- g_return_val_if_fail (text != NULL, NULL);
-
- if (length < 0)
- length = strlen (text);
-
- /* prealloc at least as long as original text */
- str = g_string_sized_new (length);
- append_escaped_text (str, text, length);
-
- return g_string_free (str, FALSE);
-}
-
-/**
- * find_conversion:
- * @format: a printf-style format string
- * @after: location to store a pointer to the character after
- * the returned conversion. On a %NULL return, returns the
- * pointer to the trailing NUL in the string
- *
- * Find the next conversion in a printf-style format string.
- * Partially based on code from printf-parser.c,
- * Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
- *
- * Return value: pointer to the next conversion in @format,
- * or %NULL, if none.
- **/
-static const char *
-find_conversion (const char *format,
- const char **after)
-{
- const char *start = format;
- const char *cp;
-
- while (*start != '\0' && *start != '%')
- start++;
-
- if (*start == '\0')
- {
- *after = start;
- return NULL;
- }
-
- cp = start + 1;
-
- if (*cp == '\0')
- {
- *after = cp;
- return NULL;
- }
-
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- cp = np + 1;
- }
-
- /* Skip the flags. */
- for (;;)
- {
- if (*cp == '\'' ||
- *cp == '-' ||
- *cp == '+' ||
- *cp == ' ' ||
- *cp == '#' ||
- *cp == '0')
- cp++;
- else
- break;
- }
-
- /* Skip the field width. */
- if (*cp == '*')
- {
- cp++;
-
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- cp = np + 1;
- }
- }
- else
- {
- for (; *cp >= '0' && *cp <= '9'; cp++)
- ;
- }
-
- /* Skip the precision. */
- if (*cp == '.')
- {
- cp++;
- if (*cp == '*')
- {
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- cp = np + 1;
- }
- }
- else
- {
- for (; *cp >= '0' && *cp <= '9'; cp++)
- ;
- }
- }
-
- /* Skip argument type/size specifiers. */
- while (*cp == 'h' ||
- *cp == 'L' ||
- *cp == 'l' ||
- *cp == 'j' ||
- *cp == 'z' ||
- *cp == 'Z' ||
- *cp == 't')
- cp++;
-
- /* Skip the conversion character. */
- cp++;
-
- *after = cp;
- return start;
-}
-
-/**
- * g_markup_vprintf_escaped:
- * @format: printf() style format string
- * @args: variable argument list, similar to vprintf()
- *
- * Formats the data in @args according to @format, escaping
- * all string and character arguments in the fashion
- * of g_markup_escape_text(). See g_markup_printf_escaped().
- *
- * Return value: newly allocated result from formatting
- * operation. Free with g_free().
- *
- * Since: 2.4
- **/
-char *
-g_markup_vprintf_escaped (const char *format,
- va_list args)
-{
- GString *format1;
- GString *format2;
- GString *result = NULL;
- gchar *output1 = NULL;
- gchar *output2 = NULL;
- const char *p, *op1, *op2;
- va_list args2;
-
- /* The technique here, is that we make two format strings that
- * have the identical conversions in the identical order to the
- * original strings, but differ in the text in-between. We
- * then use the normal g_strdup_vprintf() to format the arguments
- * with the two new format strings. By comparing the results,
- * we can figure out what segments of the output come from
- * the the original format string, and what from the arguments,
- * and thus know what portions of the string to escape.
- *
- * For instance, for:
- *
- * g_markup_printf_escaped ("%s ate %d apples", "Susan & Fred", 5);
- *
- * We form the two format strings "%sX%dX" and %sY%sY". The results
- * of formatting with those two strings are
- *
- * "%sX%dX" => "Susan & FredX5X"
- * "%sY%dY" => "Susan & FredY5Y"
- *
- * To find the span of the first argument, we find the first position
- * where the two arguments differ, which tells us that the first
- * argument formatted to "Susan & Fred". We then escape that
- * to "Susan & Fred" and join up with the intermediate portions
- * of the format string and the second argument to get
- * "Susan & Fred ate 5 apples".
- */
-
- /* Create the two modified format strings
- */
- format1 = g_string_new (NULL);
- format2 = g_string_new (NULL);
- p = format;
- while (TRUE)
- {
- const char *after;
- const char *conv = find_conversion (p, &after);
- if (!conv)
- break;
-
- g_string_append_len (format1, conv, after - conv);
- g_string_append_c (format1, 'X');
- g_string_append_len (format2, conv, after - conv);
- g_string_append_c (format2, 'Y');
-
- p = after;
- }
-
- /* Use them to format the arguments
- */
- G_VA_COPY (args2, args);
-
- output1 = g_strdup_vprintf (format1->str, args);
- if (!output1)
- {
- va_end (args2);
- goto cleanup;
- }
-
- output2 = g_strdup_vprintf (format2->str, args2);
- va_end (args2);
- if (!output2)
- goto cleanup;
-
- result = g_string_new (NULL);
-
- /* Iterate through the original format string again,
- * copying the non-conversion portions and the escaped
- * converted arguments to the output string.
- */
- op1 = output1;
- op2 = output2;
- p = format;
- while (TRUE)
- {
- const char *after;
- const char *output_start;
- const char *conv = find_conversion (p, &after);
- char *escaped;
-
- if (!conv) /* The end, after points to the trailing \0 */
- {
- g_string_append_len (result, p, after - p);
- break;
- }
-
- g_string_append_len (result, p, conv - p);
- output_start = op1;
- while (*op1 == *op2)
- {
- op1++;
- op2++;
- }
-
- escaped = g_markup_escape_text (output_start, op1 - output_start);
- g_string_append (result, escaped);
- g_free (escaped);
-
- p = after;
- op1++;
- op2++;
- }
-
- cleanup:
- g_string_free (format1, TRUE);
- g_string_free (format2, TRUE);
- g_free (output1);
- g_free (output2);
-
- if (result)
- return g_string_free (result, FALSE);
- else
- return NULL;
-}
-
-/**
- * g_markup_printf_escaped:
- * @format: printf() style format string
- * @Varargs: the arguments to insert in the format string
- *
- * Formats arguments according to @format, escaping
- * all string and character arguments in the fashion
- * of g_markup_escape_text(). This is useful when you
- * want to insert literal strings into XML-style markup
- * output, without having to worry that the strings
- * might themselves contain markup.
- *
- * |[
- * const char *store = "Fortnum & Mason";
- * const char *item = "Tea";
- * char *output;
- *
- * output = g_markup_printf_escaped ("<purchase>"
- * "<store>%s</store>"
- * "<item>%s</item>"
- * "</purchase>",
- * store, item);
- * ]|
- *
- * Return value: newly allocated result from formatting
- * operation. Free with g_free().
- *
- * Since: 2.4
- **/
-char *
-g_markup_printf_escaped (const char *format, ...)
-{
- char *result;
- va_list args;
-
- va_start (args, format);
- result = g_markup_vprintf_escaped (format, args);
- va_end (args);
-
- return result;
-}
-
-static gboolean
-g_markup_parse_boolean (const char *string,
- gboolean *value)
-{
- char const * const falses[] = { "false", "f", "no", "n", "0" };
- char const * const trues[] = { "true", "t", "yes", "y", "1" };
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (falses); i++)
- {
- if (g_ascii_strcasecmp (string, falses[i]) == 0)
- {
- if (value != NULL)
- *value = FALSE;
-
- return TRUE;
- }
- }
-
- for (i = 0; i < G_N_ELEMENTS (trues); i++)
- {
- if (g_ascii_strcasecmp (string, trues[i]) == 0)
- {
- if (value != NULL)
- *value = TRUE;
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- * GMarkupCollectType:
- * @G_MARKUP_COLLECT_INVALID: used to terminate the list of attributes
- * to collect.
- * @G_MARKUP_COLLECT_STRING: collect the string pointer directly from
- * the attribute_values[] array. Expects a
- * parameter of type (const char **). If
- * %G_MARKUP_COLLECT_OPTIONAL is specified
- * and the attribute isn't present then the
- * pointer will be set to %NULL.
- * @G_MARKUP_COLLECT_STRDUP: as with %G_MARKUP_COLLECT_STRING, but
- * expects a parameter of type (char **) and
- * g_strdup()s the returned pointer. The
- * pointer must be freed with g_free().
- * @G_MARKUP_COLLECT_BOOLEAN: expects a parameter of type (gboolean *)
- * and parses the attribute value as a
- * boolean. Sets %FALSE if the attribute
- * isn't present. Valid boolean values
- * consist of (case insensitive) "false",
- * "f", "no", "n", "0" and "true", "t",
- * "yes", "y", "1".
- * @G_MARKUP_COLLECT_TRISTATE: as with %G_MARKUP_COLLECT_BOOLEAN, but
- * in the case of a missing attribute a
- * value is set that compares equal to
- * neither %FALSE nor %TRUE.
- * G_MARKUP_COLLECT_OPTIONAL is implied.
- * @G_MARKUP_COLLECT_OPTIONAL: can be bitwise ORed with the other
- * fields. If present, allows the
- * attribute not to appear. A default
- * value is set depending on what value
- * type is used.
- *
- * A mixed enumerated type and flags field. You must specify one type
- * (string, strdup, boolean, tristate). Additionally, you may
- * optionally bitwise OR the type with the flag
- * %G_MARKUP_COLLECT_OPTIONAL.
- *
- * It is likely that this enum will be extended in the future to
- * support other types.
- **/
-
-/**
- * g_markup_collect_attributes:
- * @element_name: the current tag name
- * @attribute_names: the attribute names
- * @attribute_values: the attribute values
- * @error: a pointer to a #GError or %NULL
- * @first_type: the #GMarkupCollectType of the
- * first attribute
- * @first_attr: the name of the first attribute
- * @...: a pointer to the storage location of the
- * first attribute (or %NULL), followed by
- * more types names and pointers, ending
- * with %G_MARKUP_COLLECT_INVALID.
- *
- * Collects the attributes of the element from the
- * data passed to the #GMarkupParser start_element
- * function, dealing with common error conditions
- * and supporting boolean values.
- *
- * This utility function is not required to write
- * a parser but can save a lot of typing.
- *
- * The @element_name, @attribute_names,
- * @attribute_values and @error parameters passed
- * to the start_element callback should be passed
- * unmodified to this function.
- *
- * Following these arguments is a list of
- * "supported" attributes to collect. It is an
- * error to specify multiple attributes with the
- * same name. If any attribute not in the list
- * appears in the @attribute_names array then an
- * unknown attribute error will result.
- *
- * The #GMarkupCollectType field allows specifying
- * the type of collection to perform and if a
- * given attribute must appear or is optional.
- *
- * The attribute name is simply the name of the
- * attribute to collect.
- *
- * The pointer should be of the appropriate type
- * (see the descriptions under
- * #GMarkupCollectType) and may be %NULL in case a
- * particular attribute is to be allowed but
- * ignored.
- *
- * This function deals with issuing errors for missing attributes
- * (of type %G_MARKUP_ERROR_MISSING_ATTRIBUTE), unknown attributes
- * (of type %G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE) and duplicate
- * attributes (of type %G_MARKUP_ERROR_INVALID_CONTENT) as well
- * as parse errors for boolean-valued attributes (again of type
- * %G_MARKUP_ERROR_INVALID_CONTENT). In all of these cases %FALSE
- * will be returned and @error will be set as appropriate.
- *
- * Return value: %TRUE if successful
- *
- * Since: 2.16
- **/
-gboolean
-g_markup_collect_attributes (const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- GError **error,
- GMarkupCollectType first_type,
- const gchar *first_attr,
- ...)
-{
- GMarkupCollectType type;
- const gchar *attr;
- guint64 collected;
- int written;
- va_list ap;
- int i;
-
- type = first_type;
- attr = first_attr;
- collected = 0;
- written = 0;
-
- va_start (ap, first_attr);
- while (type != G_MARKUP_COLLECT_INVALID)
- {
- gboolean mandatory;
- const gchar *value;
-
- mandatory = !(type & G_MARKUP_COLLECT_OPTIONAL);
- type &= (G_MARKUP_COLLECT_OPTIONAL - 1);
-
- /* tristate records a value != TRUE and != FALSE
- * for the case where the attribute is missing
- */
- if (type == G_MARKUP_COLLECT_TRISTATE)
- mandatory = FALSE;
-
- for (i = 0; attribute_names[i]; i++)
- if (i >= 40 || !(collected & (G_GUINT64_CONSTANT(1) << i)))
- if (!strcmp (attribute_names[i], attr))
- break;
-
- /* ISO C99 only promises that the user can pass up to 127 arguments.
- * Subtracting the first 4 arguments plus the final NULL and dividing
- * by 3 arguments per collected attribute, we are left with a maximum
- * number of supported attributes of (127 - 5) / 3 = 40.
- *
- * In reality, nobody is ever going to call us with anywhere close to
- * 40 attributes to collect, so it is safe to assume that if i > 40
- * then the user has given some invalid or repeated arguments. These
- * problems will be caught and reported at the end of the function.
- *
- * We know at this point that we have an error, but we don't know
- * what error it is, so just continue...
- */
- if (i < 40)
- collected |= (G_GUINT64_CONSTANT(1) << i);
-
- value = attribute_values[i];
-
- if (value == NULL && mandatory)
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "element '%s' requires attribute '%s'",
- element_name, attr);
-
- va_end (ap);
- goto failure;
- }
-
- switch (type)
- {
- case G_MARKUP_COLLECT_STRING:
- {
- const char **str_ptr;
-
- str_ptr = va_arg (ap, const char **);
-
- if (str_ptr != NULL)
- *str_ptr = value;
- }
- break;
-
- case G_MARKUP_COLLECT_STRDUP:
- {
- char **str_ptr;
-
- str_ptr = va_arg (ap, char **);
-
- if (str_ptr != NULL)
- *str_ptr = g_strdup (value);
- }
- break;
-
- case G_MARKUP_COLLECT_BOOLEAN:
- case G_MARKUP_COLLECT_TRISTATE:
- if (value == NULL)
- {
- gboolean *bool_ptr;
-
- bool_ptr = va_arg (ap, gboolean *);
-
- if (bool_ptr != NULL)
- {
- if (type == G_MARKUP_COLLECT_TRISTATE)
- /* constructivists rejoice!
- * neither false nor true...
- */
- *bool_ptr = -1;
-
- else /* G_MARKUP_COLLECT_BOOLEAN */
- *bool_ptr = FALSE;
- }
- }
- else
- {
- if (!g_markup_parse_boolean (value, va_arg (ap, gboolean *)))
- {
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "element '%s', attribute '%s', value '%s' "
- "cannot be parsed as a boolean value",
- element_name, attr, value);
-
- va_end (ap);
- goto failure;
- }
- }
-
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- type = va_arg (ap, GMarkupCollectType);
- attr = va_arg (ap, const char *);
- written++;
- }
- va_end (ap);
-
- /* ensure we collected all the arguments */
- for (i = 0; attribute_names[i]; i++)
- if ((collected & (G_GUINT64_CONSTANT(1) << i)) == 0)
- {
- /* attribute not collected: could be caused by two things.
- *
- * 1) it doesn't exist in our list of attributes
- * 2) it existed but was matched by a duplicate attribute earlier
- *
- * find out.
- */
- int j;
-
- for (j = 0; j < i; j++)
- if (strcmp (attribute_names[i], attribute_names[j]) == 0)
- /* duplicate! */
- break;
-
- /* j is now the first occurrence of attribute_names[i] */
- if (i == j)
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
- "attribute '%s' invalid for element '%s'",
- attribute_names[i], element_name);
- else
- g_set_error (error, G_MARKUP_ERROR,
- G_MARKUP_ERROR_INVALID_CONTENT,
- "attribute '%s' given multiple times for element '%s'",
- attribute_names[i], element_name);
-
- goto failure;
- }
-
- return TRUE;
-
-failure:
- /* replay the above to free allocations */
- type = first_type;
- attr = first_attr;
-
- va_start (ap, first_attr);
- while (type != G_MARKUP_COLLECT_INVALID)
- {
- gpointer ptr;
-
- ptr = va_arg (ap, gpointer);
-
- if (ptr == NULL)
- continue;
-
- switch (type & (G_MARKUP_COLLECT_OPTIONAL - 1))
- {
- case G_MARKUP_COLLECT_STRDUP:
- if (written)
- g_free (*(char **) ptr);
-
- case G_MARKUP_COLLECT_STRING:
- *(char **) ptr = NULL;
- break;
-
- case G_MARKUP_COLLECT_BOOLEAN:
- *(gboolean *) ptr = FALSE;
- break;
-
- case G_MARKUP_COLLECT_TRISTATE:
- *(gboolean *) ptr = -1;
- break;
- }
-
- type = va_arg (ap, GMarkupCollectType);
- attr = va_arg (ap, const char *);
-
- if (written)
- written--;
- }
- va_end (ap);
-
- return FALSE;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gmem.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-
-#include "gbacktrace.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "glib_trace.h"
-
-
-#define MEM_PROFILE_TABLE_SIZE 4096
-
-
-/* notes on macros:
- * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and
- * g_mem_profile().
- * REALLOC_0_WORKS is defined if g_realloc (NULL, x) works.
- * SANE_MALLOC_PROTOS is defined if the systems malloc() and friends functions
- * match the corresponding GLib prototypes, keep configure.ac and gmem.h in sync here.
- * g_mem_gc_friendly is TRUE, freed memory should be 0-wiped.
- */
-
-/* --- prototypes --- */
-static gboolean g_mem_initialized = FALSE;
-static void g_mem_init_nomessage (void);
-
-
-/* --- malloc wrappers --- */
-#ifndef REALLOC_0_WORKS
-static gpointer
-standard_realloc (gpointer mem,
- gsize n_bytes)
-{
- if (!mem)
- return malloc (n_bytes);
- else
- return realloc (mem, n_bytes);
-}
-#endif /* !REALLOC_0_WORKS */
-
-#ifdef SANE_MALLOC_PROTOS
-# define standard_malloc malloc
-# ifdef REALLOC_0_WORKS
-# define standard_realloc realloc
-# endif /* REALLOC_0_WORKS */
-# define standard_free free
-# define standard_calloc calloc
-# define standard_try_malloc malloc
-# define standard_try_realloc realloc
-#else /* !SANE_MALLOC_PROTOS */
-static gpointer
-standard_malloc (gsize n_bytes)
-{
- return malloc (n_bytes);
-}
-# ifdef REALLOC_0_WORKS
-static gpointer
-standard_realloc (gpointer mem,
- gsize n_bytes)
-{
- return realloc (mem, n_bytes);
-}
-# endif /* REALLOC_0_WORKS */
-static void
-standard_free (gpointer mem)
-{
- free (mem);
-}
-static gpointer
-standard_calloc (gsize n_blocks,
- gsize n_bytes)
-{
- return calloc (n_blocks, n_bytes);
-}
-#define standard_try_malloc standard_malloc
-#define standard_try_realloc standard_realloc
-#endif /* !SANE_MALLOC_PROTOS */
-
-
-/* --- variables --- */
-static GMemVTable glib_mem_vtable = {
- standard_malloc,
- standard_realloc,
- standard_free,
- standard_calloc,
- standard_try_malloc,
- standard_try_realloc,
-};
-
-/**
- * SECTION:memory
- * @Short_Description: general memory-handling
- * @Title: Memory Allocation
- *
- * These functions provide support for allocating and freeing memory.
- *
- * <note>
- * If any call to allocate memory fails, the application is terminated.
- * This also means that there is no need to check if the call succeeded.
- * </note>
- *
- * <note>
- * It's important to match g_malloc() with g_free(), plain malloc() with free(),
- * and (if you're using C++) new with delete and new[] with delete[]. Otherwise
- * bad things can happen, since these allocators may use different memory
- * pools (and new/delete call constructors and destructors). See also
- * g_mem_set_vtable().
- * </note>
- */
-
-/* --- functions --- */
-/**
- * g_malloc:
- * @n_bytes: the number of bytes to allocate
- *
- * Allocates @n_bytes bytes of memory.
- * If @n_bytes is 0 it returns %NULL.
- *
- * Returns: a pointer to the allocated memory
- */
-gpointer
-g_malloc (gsize n_bytes)
-{
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- {
- gpointer mem;
-
- mem = glib_mem_vtable.malloc (n_bytes);
- TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0));
- if (mem)
- return mem;
-
- g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_bytes);
- }
-
- TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0));
-
- return NULL;
-}
-
-/**
- * g_malloc0:
- * @n_bytes: the number of bytes to allocate
- *
- * Allocates @n_bytes bytes of memory, initialized to 0's.
- * If @n_bytes is 0 it returns %NULL.
- *
- * Returns: a pointer to the allocated memory
- */
-gpointer
-g_malloc0 (gsize n_bytes)
-{
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- {
- gpointer mem;
-
- mem = glib_mem_vtable.calloc (1, n_bytes);
- TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0));
- if (mem)
- return mem;
-
- g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_bytes);
- }
-
- TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0));
-
- return NULL;
-}
-
-/**
- * g_realloc:
- * @mem: the memory to reallocate
- * @n_bytes: new size of the memory in bytes
- *
- * Reallocates the memory pointed to by @mem, so that it now has space for
- * @n_bytes bytes of memory. It returns the new address of the memory, which may
- * have been moved. @mem may be %NULL, in which case it's considered to
- * have zero-length. @n_bytes may be 0, in which case %NULL will be returned
- * and @mem will be freed unless it is %NULL.
- *
- * Returns: the new address of the allocated memory
- */
-gpointer
-g_realloc (gpointer mem,
- gsize n_bytes)
-{
- gpointer newmem;
-
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- {
- newmem = glib_mem_vtable.realloc (mem, n_bytes);
- TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0));
- if (newmem)
- return newmem;
-
- g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_bytes);
- }
-
- if (mem)
- glib_mem_vtable.free (mem);
-
- TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0));
-
- return NULL;
-}
-
-/**
- * g_free:
- * @mem: the memory to free
- *
- * Frees the memory pointed to by @mem.
- * If @mem is %NULL it simply returns.
- */
-void
-g_free (gpointer mem)
-{
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (mem))
- glib_mem_vtable.free (mem);
- TRACE(GLIB_MEM_FREE((void*) mem));
-}
-
-/**
- * g_try_malloc:
- * @n_bytes: number of bytes to allocate.
- *
- * Attempts to allocate @n_bytes, and returns %NULL on failure.
- * Contrast with g_malloc(), which aborts the program on failure.
- *
- * Returns: the allocated memory, or %NULL.
- */
-gpointer
-g_try_malloc (gsize n_bytes)
-{
- gpointer mem;
-
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- mem = glib_mem_vtable.try_malloc (n_bytes);
- else
- mem = NULL;
-
- TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 1));
-
- return mem;
-}
-
-/**
- * g_try_malloc0:
- * @n_bytes: number of bytes to allocate
- *
- * Attempts to allocate @n_bytes, initialized to 0's, and returns %NULL on
- * failure. Contrast with g_malloc0(), which aborts the program on failure.
- *
- * Since: 2.8
- * Returns: the allocated memory, or %NULL
- */
-gpointer
-g_try_malloc0 (gsize n_bytes)
-{
- gpointer mem;
-
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- mem = glib_mem_vtable.try_malloc (n_bytes);
- else
- mem = NULL;
-
- if (mem)
- memset (mem, 0, n_bytes);
-
- return mem;
-}
-
-/**
- * g_try_realloc:
- * @mem: previously-allocated memory, or %NULL.
- * @n_bytes: number of bytes to allocate.
- *
- * Attempts to realloc @mem to a new size, @n_bytes, and returns %NULL
- * on failure. Contrast with g_realloc(), which aborts the program
- * on failure. If @mem is %NULL, behaves the same as g_try_malloc().
- *
- * Returns: the allocated memory, or %NULL.
- */
-gpointer
-g_try_realloc (gpointer mem,
- gsize n_bytes)
-{
- gpointer newmem;
-
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
- if (G_LIKELY (n_bytes))
- newmem = glib_mem_vtable.try_realloc (mem, n_bytes);
- else
- {
- newmem = NULL;
- if (mem)
- glib_mem_vtable.free (mem);
- }
-
- TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1));
-
- return newmem;
-}
-
-
-#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b)))
-
-/**
- * g_malloc_n:
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: a pointer to the allocated memory
- */
-gpointer
-g_malloc_n (gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- {
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
-
- g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_blocks, n_block_bytes);
- }
-
- return g_malloc (n_blocks * n_block_bytes);
-}
-
-/**
- * g_malloc0_n:
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: a pointer to the allocated memory
- */
-gpointer
-g_malloc0_n (gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- {
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
-
- g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_blocks, n_block_bytes);
- }
-
- return g_malloc0 (n_blocks * n_block_bytes);
-}
-
-/**
- * g_realloc_n:
- * @mem: the memory to reallocate
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: the new address of the allocated memory
- */
-gpointer
-g_realloc_n (gpointer mem,
- gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- {
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
-
- g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes",
- G_STRLOC, n_blocks, n_block_bytes);
- }
-
- return g_realloc (mem, n_blocks * n_block_bytes);
-}
-
-/**
- * g_try_malloc_n:
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_try_malloc(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: the allocated memory, or %NULL.
- */
-gpointer
-g_try_malloc_n (gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- return NULL;
-
- return g_try_malloc (n_blocks * n_block_bytes);
-}
-
-/**
- * g_try_malloc0_n:
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_try_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: the allocated memory, or %NULL
- */
-gpointer
-g_try_malloc0_n (gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- return NULL;
-
- return g_try_malloc0 (n_blocks * n_block_bytes);
-}
-
-/**
- * g_try_realloc_n:
- * @mem: previously-allocated memory, or %NULL.
- * @n_blocks: the number of blocks to allocate
- * @n_block_bytes: the size of each block in bytes
- *
- * This function is similar to g_try_realloc(), allocating (@n_blocks * @n_block_bytes) bytes,
- * but care is taken to detect possible overflow during multiplication.
- *
- * Since: 2.24
- * Returns: the allocated memory, or %NULL.
- */
-gpointer
-g_try_realloc_n (gpointer mem,
- gsize n_blocks,
- gsize n_block_bytes)
-{
- if (SIZE_OVERFLOWS (n_blocks, n_block_bytes))
- return NULL;
-
- return g_try_realloc (mem, n_blocks * n_block_bytes);
-}
-
-
-
-static gpointer
-fallback_calloc (gsize n_blocks,
- gsize n_block_bytes)
-{
- gsize l = n_blocks * n_block_bytes;
- gpointer mem = glib_mem_vtable.malloc (l);
-
- if (mem)
- memset (mem, 0, l);
-
- return mem;
-}
-
-static gboolean vtable_set = FALSE;
-
-/**
- * g_mem_is_system_malloc
- *
- * Checks whether the allocator used by g_malloc() is the system's
- * malloc implementation. If it returns %TRUE memory allocated with
- * malloc() can be used interchangeable with memory allocated using g_malloc().
- * This function is useful for avoiding an extra copy of allocated memory returned
- * by a non-GLib-based API.
- *
- * A different allocator can be set using g_mem_set_vtable().
- *
- * Return value: if %TRUE, malloc() and g_malloc() can be mixed.
- **/
-gboolean
-g_mem_is_system_malloc (void)
-{
- return !vtable_set;
-}
-
-/**
- * g_mem_set_vtable:
- * @vtable: table of memory allocation routines.
- *
- * Sets the #GMemVTable to use for memory allocation. You can use this to provide
- * custom memory allocation routines. <emphasis>This function must be called
- * before using any other GLib functions.</emphasis> The @vtable only needs to
- * provide malloc(), realloc(), and free() functions; GLib can provide default
- * implementations of the others. The malloc() and realloc() implementations
- * should return %NULL on failure, GLib will handle error-checking for you.
- * @vtable is copied, so need not persist after this function has been called.
- */
-void
-g_mem_set_vtable (GMemVTable *vtable)
-{
- if (!vtable_set)
- {
- if (vtable->malloc && vtable->realloc && vtable->free)
- {
- glib_mem_vtable.malloc = vtable->malloc;
- glib_mem_vtable.realloc = vtable->realloc;
- glib_mem_vtable.free = vtable->free;
- glib_mem_vtable.calloc = vtable->calloc ? vtable->calloc : fallback_calloc;
- glib_mem_vtable.try_malloc = vtable->try_malloc ? vtable->try_malloc : glib_mem_vtable.malloc;
- glib_mem_vtable.try_realloc = vtable->try_realloc ? vtable->try_realloc : glib_mem_vtable.realloc;
- vtable_set = TRUE;
- }
- else
- g_warning (G_STRLOC ": memory allocation vtable lacks one of malloc(), realloc() or free()");
- }
- else
- g_warning (G_STRLOC ": memory allocation vtable can only be set once at startup");
-}
-
-
-/* --- memory profiling and checking --- */
-#ifdef G_DISABLE_CHECKS
-/**
- * glib_mem_profiler_table:
- *
- * A #GMemVTable containing profiling variants of the memory
- * allocation functions. Use them together with g_mem_profile()
- * in order to get information about the memory allocation pattern
- * of your program.
- */
-GMemVTable *glib_mem_profiler_table = &glib_mem_vtable;
-void
-g_mem_profile (void)
-{
-}
-#else /* !G_DISABLE_CHECKS */
-typedef enum {
- PROFILER_FREE = 0,
- PROFILER_ALLOC = 1,
- PROFILER_RELOC = 2,
- PROFILER_ZINIT = 4
-} ProfilerJob;
-static guint *profile_data = NULL;
-static gsize profile_allocs = 0;
-static gsize profile_zinit = 0;
-static gsize profile_frees = 0;
-static GMutex *gmem_profile_mutex = NULL;
-#ifdef G_ENABLE_DEBUG
-static volatile gsize g_trap_free_size = 0;
-static volatile gsize g_trap_realloc_size = 0;
-static volatile gsize g_trap_malloc_size = 0;
-#endif /* G_ENABLE_DEBUG */
-
-#define PROFILE_TABLE(f1,f2,f3) ( ( ((f3) << 2) | ((f2) << 1) | (f1) ) * (MEM_PROFILE_TABLE_SIZE + 1))
-
-static void
-profiler_log (ProfilerJob job,
- gsize n_bytes,
- gboolean success)
-{
- g_mutex_lock (gmem_profile_mutex);
- if (!profile_data)
- {
- profile_data = standard_calloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8,
- sizeof (profile_data[0]));
- if (!profile_data) /* memory system kiddin' me, eh? */
- {
- g_mutex_unlock (gmem_profile_mutex);
- return;
- }
- }
-
- if (n_bytes < MEM_PROFILE_TABLE_SIZE)
- profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0,
- (job & PROFILER_RELOC) != 0,
- success != 0)] += 1;
- else
- profile_data[MEM_PROFILE_TABLE_SIZE + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0,
- (job & PROFILER_RELOC) != 0,
- success != 0)] += 1;
- if (success)
- {
- if (job & PROFILER_ALLOC)
- {
- profile_allocs += n_bytes;
- if (job & PROFILER_ZINIT)
- profile_zinit += n_bytes;
- }
- else
- profile_frees += n_bytes;
- }
- g_mutex_unlock (gmem_profile_mutex);
-}
-
-static void
-profile_print_locked (guint *local_data,
- gboolean success)
-{
- gboolean need_header = TRUE;
- guint i;
-
- for (i = 0; i <= MEM_PROFILE_TABLE_SIZE; i++)
- {
- glong t_malloc = local_data[i + PROFILE_TABLE (1, 0, success)];
- glong t_realloc = local_data[i + PROFILE_TABLE (1, 1, success)];
- glong t_free = local_data[i + PROFILE_TABLE (0, 0, success)];
- glong t_refree = local_data[i + PROFILE_TABLE (0, 1, success)];
-
- if (!t_malloc && !t_realloc && !t_free && !t_refree)
- continue;
- else if (need_header)
- {
- need_header = FALSE;
- g_print (" blocks of | allocated | freed | allocated | freed | n_bytes \n");
- g_print (" n_bytes | n_times by | n_times by | n_times by | n_times by | remaining \n");
- g_print (" | malloc() | free() | realloc() | realloc() | \n");
- g_print ("===========|============|============|============|============|===========\n");
- }
- if (i < MEM_PROFILE_TABLE_SIZE)
- g_print ("%10u | %10ld | %10ld | %10ld | %10ld |%+11ld\n",
- i, t_malloc, t_free, t_realloc, t_refree,
- (t_malloc - t_free + t_realloc - t_refree) * i);
- else if (i >= MEM_PROFILE_TABLE_SIZE)
- g_print (" >%6u | %10ld | %10ld | %10ld | %10ld | ***\n",
- i, t_malloc, t_free, t_realloc, t_refree);
- }
- if (need_header)
- g_print (" --- none ---\n");
-}
-
-/**
- * g_mem_profile:
- * @void:
- *
- * Outputs a summary of memory usage.
- *
- * It outputs the frequency of allocations of different sizes,
- * the total number of bytes which have been allocated,
- * the total number of bytes which have been freed,
- * and the difference between the previous two values, i.e. the number of bytes
- * still in use.
- *
- * Note that this function will not output anything unless you have
- * previously installed the #glib_mem_profiler_table with g_mem_set_vtable().
- */
-
-void
-g_mem_profile (void)
-{
- guint local_data[(MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])];
- gsize local_allocs;
- gsize local_zinit;
- gsize local_frees;
-
- if (G_UNLIKELY (!g_mem_initialized))
- g_mem_init_nomessage();
-
- g_mutex_lock (gmem_profile_mutex);
-
- local_allocs = profile_allocs;
- local_zinit = profile_zinit;
- local_frees = profile_frees;
-
- if (!profile_data)
- {
- g_mutex_unlock (gmem_profile_mutex);
- return;
- }
-
- memcpy (local_data, profile_data,
- (MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0]));
-
- g_mutex_unlock (gmem_profile_mutex);
-
- g_print ("GLib Memory statistics (successful operations):\n");
- profile_print_locked (local_data, TRUE);
- g_print ("GLib Memory statistics (failing operations):\n");
- profile_print_locked (local_data, FALSE);
- g_print ("Total bytes: allocated=%"G_GSIZE_FORMAT", "
- "zero-initialized=%"G_GSIZE_FORMAT" (%.2f%%), "
- "freed=%"G_GSIZE_FORMAT" (%.2f%%), "
- "remaining=%"G_GSIZE_FORMAT"\n",
- local_allocs,
- local_zinit,
- ((gdouble) local_zinit) / local_allocs * 100.0,
- local_frees,
- ((gdouble) local_frees) / local_allocs * 100.0,
- local_allocs - local_frees);
-}
-
-static gpointer
-profiler_try_malloc (gsize n_bytes)
-{
- gsize *p;
-
-#ifdef G_ENABLE_DEBUG
- if (g_trap_malloc_size == n_bytes)
- G_BREAKPOINT ();
-#endif /* G_ENABLE_DEBUG */
-
- p = standard_malloc (sizeof (gsize) * 2 + n_bytes);
-
- if (p)
- {
- p[0] = 0; /* free count */
- p[1] = n_bytes; /* length */
- profiler_log (PROFILER_ALLOC, n_bytes, TRUE);
- p += 2;
- }
- else
- profiler_log (PROFILER_ALLOC, n_bytes, FALSE);
-
- return p;
-}
-
-static gpointer
-profiler_malloc (gsize n_bytes)
-{
- gpointer mem = profiler_try_malloc (n_bytes);
-
- if (!mem)
- g_mem_profile ();
-
- return mem;
-}
-
-static gpointer
-profiler_calloc (gsize n_blocks,
- gsize n_block_bytes)
-{
- gsize l = n_blocks * n_block_bytes;
- gsize *p;
-
-#ifdef G_ENABLE_DEBUG
- if (g_trap_malloc_size == l)
- G_BREAKPOINT ();
-#endif /* G_ENABLE_DEBUG */
-
- p = standard_calloc (1, sizeof (gsize) * 2 + l);
-
- if (p)
- {
- p[0] = 0; /* free count */
- p[1] = l; /* length */
- profiler_log (PROFILER_ALLOC | PROFILER_ZINIT, l, TRUE);
- p += 2;
- }
- else
- {
- profiler_log (PROFILER_ALLOC | PROFILER_ZINIT, l, FALSE);
- g_mem_profile ();
- }
-
- return p;
-}
-
-static void
-profiler_free (gpointer mem)
-{
- gsize *p = mem;
-
- p -= 2;
- if (p[0]) /* free count */
- {
- g_warning ("free(%p): memory has been freed %"G_GSIZE_FORMAT" times already",
- p + 2, p[0]);
- profiler_log (PROFILER_FREE,
- p[1], /* length */
- FALSE);
- }
- else
- {
-#ifdef G_ENABLE_DEBUG
- if (g_trap_free_size == p[1])
- G_BREAKPOINT ();
-#endif /* G_ENABLE_DEBUG */
-
- profiler_log (PROFILER_FREE,
- p[1], /* length */
- TRUE);
- memset (p + 2, 0xaa, p[1]);
-
- /* for all those that miss standard_free (p); in this place, yes,
- * we do leak all memory when profiling, and that is intentional
- * to catch double frees. patch submissions are futile.
- */
- }
- p[0] += 1;
-}
-
-static gpointer
-profiler_try_realloc (gpointer mem,
- gsize n_bytes)
-{
- gsize *p = mem;
-
- p -= 2;
-
-#ifdef G_ENABLE_DEBUG
- if (g_trap_realloc_size == n_bytes)
- G_BREAKPOINT ();
-#endif /* G_ENABLE_DEBUG */
-
- if (mem && p[0]) /* free count */
- {
- g_warning ("realloc(%p, %"G_GSIZE_FORMAT"): "
- "memory has been freed %"G_GSIZE_FORMAT" times already",
- p + 2, (gsize) n_bytes, p[0]);
- profiler_log (PROFILER_ALLOC | PROFILER_RELOC, n_bytes, FALSE);
-
- return NULL;
- }
- else
- {
- p = standard_realloc (mem ? p : NULL, sizeof (gsize) * 2 + n_bytes);
-
- if (p)
- {
- if (mem)
- profiler_log (PROFILER_FREE | PROFILER_RELOC, p[1], TRUE);
- p[0] = 0;
- p[1] = n_bytes;
- profiler_log (PROFILER_ALLOC | PROFILER_RELOC, p[1], TRUE);
- p += 2;
- }
- else
- profiler_log (PROFILER_ALLOC | PROFILER_RELOC, n_bytes, FALSE);
-
- return p;
- }
-}
-
-static gpointer
-profiler_realloc (gpointer mem,
- gsize n_bytes)
-{
- mem = profiler_try_realloc (mem, n_bytes);
-
- if (!mem)
- g_mem_profile ();
-
- return mem;
-}
-
-static GMemVTable profiler_table = {
- profiler_malloc,
- profiler_realloc,
- profiler_free,
- profiler_calloc,
- profiler_try_malloc,
- profiler_try_realloc,
-};
-GMemVTable *glib_mem_profiler_table = &profiler_table;
-
-#endif /* !G_DISABLE_CHECKS */
-
-/* --- MemChunks --- */
-/**
- * SECTION: allocators
- * @title: Memory Allocators
- * @short_description: deprecated way to allocate chunks of memory for
- * GList, GSList and GNode
- *
- * Prior to 2.10, #GAllocator was used as an efficient way to allocate
- * small pieces of memory for use with the #GList, #GSList and #GNode
- * data structures. Since 2.10, it has been completely replaced by the
- * <link linkend="glib-Memory-Slices">slice allocator</link> and
- * deprecated.
- **/
-
-/**
- * SECTION: memory_chunks
- * @title: Memory Chunks
- * @short_description: deprecated way to allocate groups of equal-sized
- * chunks of memory
- *
- * Memory chunks provide an space-efficient way to allocate equal-sized
- * pieces of memory, called atoms. However, due to the administrative
- * overhead (in particular for #G_ALLOC_AND_FREE, and when used from
- * multiple threads), they are in practise often slower than direct use
- * of g_malloc(). Therefore, memory chunks have been deprecated in
- * favor of the <link linkend="glib-Memory-Slices">slice
- * allocator</link>, which has been added in 2.10. All internal uses of
- * memory chunks in GLib have been converted to the
- * <literal>g_slice</literal> API.
- *
- * There are two types of memory chunks, #G_ALLOC_ONLY, and
- * #G_ALLOC_AND_FREE. <itemizedlist> <listitem><para> #G_ALLOC_ONLY
- * chunks only allow allocation of atoms. The atoms can never be freed
- * individually. The memory chunk can only be free in its entirety.
- * </para></listitem> <listitem><para> #G_ALLOC_AND_FREE chunks do
- * allow atoms to be freed individually. The disadvantage of this is
- * that the memory chunk has to keep track of which atoms have been
- * freed. This results in more memory being used and a slight
- * degradation in performance. </para></listitem> </itemizedlist>
- *
- * To create a memory chunk use g_mem_chunk_new() or the convenience
- * macro g_mem_chunk_create().
- *
- * To allocate a new atom use g_mem_chunk_alloc(),
- * g_mem_chunk_alloc0(), or the convenience macros g_chunk_new() or
- * g_chunk_new0().
- *
- * To free an atom use g_mem_chunk_free(), or the convenience macro
- * g_chunk_free(). (Atoms can only be freed if the memory chunk is
- * created with the type set to #G_ALLOC_AND_FREE.)
- *
- * To free any blocks of memory which are no longer being used, use
- * g_mem_chunk_clean(). To clean all memory chunks, use g_blow_chunks().
- *
- * To reset the memory chunk, freeing all of the atoms, use
- * g_mem_chunk_reset().
- *
- * To destroy a memory chunk, use g_mem_chunk_destroy().
- *
- * To help debug memory chunks, use g_mem_chunk_info() and
- * g_mem_chunk_print().
- *
- * <example>
- * <title>Using a #GMemChunk</title>
- * <programlisting>
- * GMemChunk *mem_chunk;
- * gchar *mem[10000];
- * gint i;
- *
- * /<!-- -->* Create a GMemChunk with atoms 50 bytes long, and memory
- * blocks holding 100 bytes. Note that this means that only 2 atoms
- * fit into each memory block and so isn't very efficient. *<!-- -->/
- * mem_chunk = g_mem_chunk_new ("test mem chunk", 50, 100, G_ALLOC_AND_FREE);
- * /<!-- -->* Now allocate 10000 atoms. *<!-- -->/
- * for (i = 0; i < 10000; i++)
- * {
- * mem[i] = g_chunk_new (gchar, mem_chunk);
- * /<!-- -->* Fill in the atom memory with some junk. *<!-- -->/
- * for (j = 0; j < 50; j++)
- * mem[i][j] = i * j;
- * }
- * /<!-- -->* Now free all of the atoms. Note that since we are going to
- * destroy the GMemChunk, this wouldn't normally be used. *<!-- -->/
- * for (i = 0; i < 10000; i++)
- * {
- * g_mem_chunk_free (mem_chunk, mem[i]);
- * }
- * /<!-- -->* We are finished with the GMemChunk, so we destroy it. *<!-- -->/
- * g_mem_chunk_destroy (mem_chunk);
- * </programlisting>
- * </example>
- *
- * <example>
- * <title>Using a #GMemChunk with data structures</title>
- * <programlisting>
- * GMemChunk *array_mem_chunk;
- * GRealArray *array;
- * /<!-- -->* Create a GMemChunk to hold GRealArray structures, using
- * the g_mem_chunk_create(<!-- -->) convenience macro. We want 1024 atoms in each
- * memory block, and we want to be able to free individual atoms. *<!-- -->/
- * array_mem_chunk = g_mem_chunk_create (GRealArray, 1024, G_ALLOC_AND_FREE);
- * /<!-- -->* Allocate one atom, using the g_chunk_new(<!-- -->) convenience macro. *<!-- -->/
- * array = g_chunk_new (GRealArray, array_mem_chunk);
- * /<!-- -->* We can now use array just like a normal pointer to a structure. *<!-- -->/
- * array->data = NULL;
- * array->len = 0;
- * array->alloc = 0;
- * array->zero_terminated = (zero_terminated ? 1 : 0);
- * array->clear = (clear ? 1 : 0);
- * array->elt_size = elt_size;
- * /<!-- -->* We can free the element, so it can be reused. *<!-- -->/
- * g_chunk_free (array, array_mem_chunk);
- * /<!-- -->* We destroy the GMemChunk when we are finished with it. *<!-- -->/
- * g_mem_chunk_destroy (array_mem_chunk);
- * </programlisting>
- * </example>
- **/
-
-#ifndef G_ALLOC_AND_FREE
-
-/**
- * GAllocator:
- *
- * The #GAllocator struct contains private data. and should only be
- * accessed using the following functions.
- **/
-typedef struct _GAllocator GAllocator;
-
-/**
- * GMemChunk:
- *
- * The #GMemChunk struct is an opaque data structure representing a
- * memory chunk. It should be accessed only through the use of the
- * following functions.
- **/
-typedef struct _GMemChunk GMemChunk;
-
-/**
- * G_ALLOC_ONLY:
- *
- * Specifies the type of a #GMemChunk. Used in g_mem_chunk_new() and
- * g_mem_chunk_create() to specify that atoms will never be freed
- * individually.
- **/
-#define G_ALLOC_ONLY 1
-
-/**
- * G_ALLOC_AND_FREE:
- *
- * Specifies the type of a #GMemChunk. Used in g_mem_chunk_new() and
- * g_mem_chunk_create() to specify that atoms will be freed
- * individually.
- **/
-#define G_ALLOC_AND_FREE 2
-#endif
-
-struct _GMemChunk {
- guint alloc_size; /* the size of an atom */
-};
-
-/**
- * g_mem_chunk_new:
- * @name: a string to identify the #GMemChunk. It is not copied so it
- * should be valid for the lifetime of the #GMemChunk. It is
- * only used in g_mem_chunk_print(), which is used for debugging.
- * @atom_size: the size, in bytes, of each element in the #GMemChunk.
- * @area_size: the size, in bytes, of each block of memory allocated to
- * contain the atoms.
- * @type: the type of the #GMemChunk. #G_ALLOC_AND_FREE is used if the
- * atoms will be freed individually. #G_ALLOC_ONLY should be
- * used if atoms will never be freed individually.
- * #G_ALLOC_ONLY is quicker, since it does not need to track
- * free atoms, but it obviously wastes memory if you no longer
- * need many of the atoms.
- * @Returns: the new #GMemChunk.
- *
- * Creates a new #GMemChunk.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-GMemChunk*
-g_mem_chunk_new (const gchar *name,
- gint atom_size,
- gsize area_size,
- gint type)
-{
- GMemChunk *mem_chunk;
- g_return_val_if_fail (atom_size > 0, NULL);
-
- mem_chunk = g_slice_new (GMemChunk);
- mem_chunk->alloc_size = atom_size;
- return mem_chunk;
-}
-
-/**
- * g_mem_chunk_destroy:
- * @mem_chunk: a #GMemChunk.
- *
- * Frees all of the memory allocated for a #GMemChunk.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void
-g_mem_chunk_destroy (GMemChunk *mem_chunk)
-{
- g_return_if_fail (mem_chunk != NULL);
-
- g_slice_free (GMemChunk, mem_chunk);
-}
-
-/**
- * g_mem_chunk_alloc:
- * @mem_chunk: a #GMemChunk.
- * @Returns: a pointer to the allocated atom.
- *
- * Allocates an atom of memory from a #GMemChunk.
- *
- * Deprecated:2.10: Use g_slice_alloc() instead
- **/
-gpointer
-g_mem_chunk_alloc (GMemChunk *mem_chunk)
-{
- g_return_val_if_fail (mem_chunk != NULL, NULL);
-
- return g_slice_alloc (mem_chunk->alloc_size);
-}
-
-/**
- * g_mem_chunk_alloc0:
- * @mem_chunk: a #GMemChunk.
- * @Returns: a pointer to the allocated atom.
- *
- * Allocates an atom of memory from a #GMemChunk, setting the memory to
- * 0.
- *
- * Deprecated:2.10: Use g_slice_alloc0() instead
- **/
-gpointer
-g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
-{
- g_return_val_if_fail (mem_chunk != NULL, NULL);
-
- return g_slice_alloc0 (mem_chunk->alloc_size);
-}
-
-/**
- * g_mem_chunk_free:
- * @mem_chunk: a #GMemChunk.
- * @mem: a pointer to the atom to free.
- *
- * Frees an atom in a #GMemChunk. This should only be called if the
- * #GMemChunk was created with #G_ALLOC_AND_FREE. Otherwise it will
- * simply return.
- *
- * Deprecated:2.10: Use g_slice_free1() instead
- **/
-void
-g_mem_chunk_free (GMemChunk *mem_chunk,
- gpointer mem)
-{
- g_return_if_fail (mem_chunk != NULL);
-
- g_slice_free1 (mem_chunk->alloc_size, mem);
-}
-
-/**
- * g_mem_chunk_clean:
- * @mem_chunk: a #GMemChunk.
- *
- * Frees any blocks in a #GMemChunk which are no longer being used.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void g_mem_chunk_clean (GMemChunk *mem_chunk) {}
-
-/**
- * g_mem_chunk_reset:
- * @mem_chunk: a #GMemChunk.
- *
- * Resets a GMemChunk to its initial state. It frees all of the
- * currently allocated blocks of memory.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void g_mem_chunk_reset (GMemChunk *mem_chunk) {}
-
-
-/**
- * g_mem_chunk_print:
- * @mem_chunk: a #GMemChunk.
- *
- * Outputs debugging information for a #GMemChunk. It outputs the name
- * of the #GMemChunk (set with g_mem_chunk_new()), the number of bytes
- * used, and the number of blocks of memory allocated.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void g_mem_chunk_print (GMemChunk *mem_chunk) {}
-
-
-/**
- * g_mem_chunk_info:
- *
- * Outputs debugging information for all #GMemChunk objects currently
- * in use. It outputs the number of #GMemChunk objects currently
- * allocated, and calls g_mem_chunk_print() to output information on
- * each one.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void g_mem_chunk_info (void) {}
-
-/**
- * g_blow_chunks:
- *
- * Calls g_mem_chunk_clean() on all #GMemChunk objects.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void g_blow_chunks (void) {}
-
-/**
- * g_chunk_new0:
- * @type: the type of the #GMemChunk atoms, typically a structure name.
- * @chunk: a #GMemChunk.
- * @Returns: a pointer to the allocated atom, cast to a pointer to
- * @type.
- *
- * A convenience macro to allocate an atom of memory from a #GMemChunk.
- * It calls g_mem_chunk_alloc0() and casts the returned atom to a
- * pointer to the given type, avoiding a type cast in the source code.
- *
- * Deprecated:2.10: Use g_slice_new0() instead
- **/
-
-/**
- * g_chunk_free:
- * @mem: a pointer to the atom to be freed.
- * @mem_chunk: a #GMemChunk.
- *
- * A convenience macro to free an atom of memory from a #GMemChunk. It
- * simply switches the arguments and calls g_mem_chunk_free() It is
- * included simply to complement the other convenience macros,
- * g_chunk_new() and g_chunk_new0().
- *
- * Deprecated:2.10: Use g_slice_free() instead
- **/
-
-/**
- * g_chunk_new:
- * @type: the type of the #GMemChunk atoms, typically a structure name.
- * @chunk: a #GMemChunk.
- * @Returns: a pointer to the allocated atom, cast to a pointer to
- * @type.
- *
- * A convenience macro to allocate an atom of memory from a #GMemChunk.
- * It calls g_mem_chunk_alloc() and casts the returned atom to a
- * pointer to the given type, avoiding a type cast in the source code.
- *
- * Deprecated:2.10: Use g_slice_new() instead
- **/
-
-/**
- * g_mem_chunk_create:
- * @type: the type of the atoms, typically a structure name.
- * @pre_alloc: the number of atoms to store in each block of memory.
- * @alloc_type: the type of the #GMemChunk. #G_ALLOC_AND_FREE is used
- * if the atoms will be freed individually. #G_ALLOC_ONLY
- * should be used if atoms will never be freed
- * individually. #G_ALLOC_ONLY is quicker, since it does
- * not need to track free atoms, but it obviously wastes
- * memory if you no longer need many of the atoms.
- * @Returns: the new #GMemChunk.
- *
- * A convenience macro for creating a new #GMemChunk. It calls
- * g_mem_chunk_new(), using the given type to create the #GMemChunk
- * name. The atom size is determined using
- * <function>sizeof()</function>, and the area size is calculated by
- * multiplying the @pre_alloc parameter with the atom size.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-
-
-/**
- * g_allocator_new:
- * @name: the name of the #GAllocator. This name is used to set the
- * name of the #GMemChunk used by the #GAllocator, and is only
- * used for debugging.
- * @n_preallocs: the number of elements in each block of memory
- * allocated. Larger blocks mean less calls to
- * g_malloc(), but some memory may be wasted. (GLib uses
- * 128 elements per block by default.) The value must be
- * between 1 and 65535.
- * @Returns: a new #GAllocator.
- *
- * Creates a new #GAllocator.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-GAllocator*
-g_allocator_new (const gchar *name,
- guint n_preallocs)
-{
- static struct _GAllocator {
- gchar *name;
- guint16 n_preallocs;
- guint is_unused : 1;
- guint type : 4;
- GAllocator *last;
- GMemChunk *mem_chunk;
- gpointer free_list;
- } dummy = {
- "GAllocator is deprecated", 1, TRUE, 0, NULL, NULL, NULL,
- };
- /* some (broken) GAllocator uses depend on non-NULL allocators */
- return (void*) &dummy;
-}
-
-/**
- * g_allocator_free:
- * @allocator: a #GAllocator.
- *
- * Frees all of the memory allocated by the #GAllocator.
- *
- * Deprecated:2.10: Use the <link linkend="glib-Memory-Slices">slice
- * allocator</link> instead
- **/
-void
-g_allocator_free (GAllocator *allocator)
-{
-}
-
-#ifdef ENABLE_GC_FRIENDLY_DEFAULT
-gboolean g_mem_gc_friendly = TRUE;
-#else
-/**
- * g_mem_gc_friendly:
- *
- * This variable is %TRUE if the <envar>G_DEBUG</envar> environment variable
- * includes the key <link linkend="G_DEBUG">gc-friendly</link>.
- */
-gboolean g_mem_gc_friendly = FALSE;
-#endif
-
-static void
-g_mem_init_nomessage (void)
-{
- gchar buffer[1024];
- const gchar *val;
- const GDebugKey keys[] = {
- { "gc-friendly", 1 },
- };
- gint flags;
- if (g_mem_initialized)
- return;
- /* don't use g_malloc/g_message here */
- val = _g_getenv_nomalloc ("G_DEBUG", buffer);
- flags = !val ? 0 : g_parse_debug_string (val, keys, G_N_ELEMENTS (keys));
- if (flags & 1) /* gc-friendly */
- {
- g_mem_gc_friendly = TRUE;
- }
- g_mem_initialized = TRUE;
-}
-
-void
-_g_mem_thread_init_noprivate_nomessage (void)
-{
- /* we may only create mutexes here, locking/
- * unlocking a mutex does not yet work.
- */
- g_mem_init_nomessage();
-#ifndef G_DISABLE_CHECKS
- gmem_profile_mutex = g_mutex_new ();
-#endif
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <signal.h>
-#include <locale.h>
-#include <errno.h>
-
-#ifdef G_OS_WIN32
-#include <process.h> /* For getpid() */
-#include <io.h>
-# define STRICT /* Strict typing, please */
-# define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
-# include <windows.h>
-# undef STRICT
-#endif
-
-#include "gmessages.h"
-
-#include "gbacktrace.h"
-#include "gconvert.h"
-#include "gdebug.h"
-#include "gmem.h"
-#include "gprintfint.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gthreadprivate.h"
-#include "gstrfuncs.h"
-#include "gstring.h"
-
-
-/* --- structures --- */
-typedef struct _GLogDomain GLogDomain;
-typedef struct _GLogHandler GLogHandler;
-struct _GLogDomain
-{
- gchar *log_domain;
- GLogLevelFlags fatal_mask;
- GLogHandler *handlers;
- GLogDomain *next;
-};
-struct _GLogHandler
-{
- guint id;
- GLogLevelFlags log_level;
- GLogFunc log_func;
- gpointer data;
- GLogHandler *next;
-};
-
-
-/* --- variables --- */
-static GMutex *g_messages_lock = NULL;
-static GLogDomain *g_log_domains = NULL;
-static GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
-static GPrintFunc glib_print_func = NULL;
-static GPrintFunc glib_printerr_func = NULL;
-static GPrivate *g_log_depth = NULL;
-static GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
-static GLogFunc default_log_func = g_log_default_handler;
-static gpointer default_log_data = NULL;
-static GTestLogFatalFunc fatal_log_func = NULL;
-static gpointer fatal_log_data;
-
-/* --- functions --- */
-#ifdef G_OS_WIN32
-# define STRICT
-# include <windows.h>
-# undef STRICT
-static gboolean win32_keep_fatal_message = FALSE;
-
-/* This default message will usually be overwritten. */
-/* Yes, a fixed size buffer is bad. So sue me. But g_error() is never
- * called with huge strings, is it?
- */
-static gchar fatal_msg_buf[1000] = "Unspecified fatal error encountered, aborting.";
-static gchar *fatal_msg_ptr = fatal_msg_buf;
-
-#undef write
-static inline int
-dowrite (int fd,
- const void *buf,
- unsigned int len)
-{
- if (win32_keep_fatal_message)
- {
- memcpy (fatal_msg_ptr, buf, len);
- fatal_msg_ptr += len;
- *fatal_msg_ptr = 0;
- return len;
- }
-
- write (fd, buf, len);
-
- return len;
-}
-#define write(fd, buf, len) dowrite(fd, buf, len)
-
-#endif
-
-static void
-write_string (int fd,
- const gchar *string)
-{
- write (fd, string, strlen (string));
-}
-
-static void
-g_messages_prefixed_init (void)
-{
- static gboolean initialized = FALSE;
-
- if (!initialized)
- {
- const gchar *val;
-
- initialized = TRUE;
- val = g_getenv ("G_MESSAGES_PREFIXED");
-
- if (val)
- {
- const GDebugKey keys[] = {
- { "error", G_LOG_LEVEL_ERROR },
- { "critical", G_LOG_LEVEL_CRITICAL },
- { "warning", G_LOG_LEVEL_WARNING },
- { "message", G_LOG_LEVEL_MESSAGE },
- { "info", G_LOG_LEVEL_INFO },
- { "debug", G_LOG_LEVEL_DEBUG }
- };
-
- g_log_msg_prefix = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys));
- }
- }
-}
-
-static GLogDomain*
-g_log_find_domain_L (const gchar *log_domain)
-{
- register GLogDomain *domain;
-
- domain = g_log_domains;
- while (domain)
- {
- if (strcmp (domain->log_domain, log_domain) == 0)
- return domain;
- domain = domain->next;
- }
- return NULL;
-}
-
-static GLogDomain*
-g_log_domain_new_L (const gchar *log_domain)
-{
- register GLogDomain *domain;
-
- domain = g_new (GLogDomain, 1);
- domain->log_domain = g_strdup (log_domain);
- domain->fatal_mask = G_LOG_FATAL_MASK;
- domain->handlers = NULL;
-
- domain->next = g_log_domains;
- g_log_domains = domain;
-
- return domain;
-}
-
-static void
-g_log_domain_check_free_L (GLogDomain *domain)
-{
- if (domain->fatal_mask == G_LOG_FATAL_MASK &&
- domain->handlers == NULL)
- {
- register GLogDomain *last, *work;
-
- last = NULL;
-
- work = g_log_domains;
- while (work)
- {
- if (work == domain)
- {
- if (last)
- last->next = domain->next;
- else
- g_log_domains = domain->next;
- g_free (domain->log_domain);
- g_free (domain);
- break;
- }
- last = work;
- work = last->next;
- }
- }
-}
-
-static GLogFunc
-g_log_domain_get_handler_L (GLogDomain *domain,
- GLogLevelFlags log_level,
- gpointer *data)
-{
- if (domain && log_level)
- {
- register GLogHandler *handler;
-
- handler = domain->handlers;
- while (handler)
- {
- if ((handler->log_level & log_level) == log_level)
- {
- *data = handler->data;
- return handler->log_func;
- }
- handler = handler->next;
- }
- }
-
- *data = default_log_data;
- return default_log_func;
-}
-
-GLogLevelFlags
-g_log_set_always_fatal (GLogLevelFlags fatal_mask)
-{
- GLogLevelFlags old_mask;
-
- /* restrict the global mask to levels that are known to glib
- * since this setting applies to all domains
- */
- fatal_mask &= (1 << G_LOG_LEVEL_USER_SHIFT) - 1;
- /* force errors to be fatal */
- fatal_mask |= G_LOG_LEVEL_ERROR;
- /* remove bogus flag */
- fatal_mask &= ~G_LOG_FLAG_FATAL;
-
- g_mutex_lock (g_messages_lock);
- old_mask = g_log_always_fatal;
- g_log_always_fatal = fatal_mask;
- g_mutex_unlock (g_messages_lock);
-
- return old_mask;
-}
-
-GLogLevelFlags
-g_log_set_fatal_mask (const gchar *log_domain,
- GLogLevelFlags fatal_mask)
-{
- GLogLevelFlags old_flags;
- register GLogDomain *domain;
-
- if (!log_domain)
- log_domain = "";
-
- /* force errors to be fatal */
- fatal_mask |= G_LOG_LEVEL_ERROR;
- /* remove bogus flag */
- fatal_mask &= ~G_LOG_FLAG_FATAL;
-
- g_mutex_lock (g_messages_lock);
-
- domain = g_log_find_domain_L (log_domain);
- if (!domain)
- domain = g_log_domain_new_L (log_domain);
- old_flags = domain->fatal_mask;
-
- domain->fatal_mask = fatal_mask;
- g_log_domain_check_free_L (domain);
-
- g_mutex_unlock (g_messages_lock);
-
- return old_flags;
-}
-
-guint
-g_log_set_handler (const gchar *log_domain,
- GLogLevelFlags log_levels,
- GLogFunc log_func,
- gpointer user_data)
-{
- static guint handler_id = 0;
- GLogDomain *domain;
- GLogHandler *handler;
-
- g_return_val_if_fail ((log_levels & G_LOG_LEVEL_MASK) != 0, 0);
- g_return_val_if_fail (log_func != NULL, 0);
-
- if (!log_domain)
- log_domain = "";
-
- handler = g_new (GLogHandler, 1);
-
- g_mutex_lock (g_messages_lock);
-
- domain = g_log_find_domain_L (log_domain);
- if (!domain)
- domain = g_log_domain_new_L (log_domain);
-
- handler->id = ++handler_id;
- handler->log_level = log_levels;
- handler->log_func = log_func;
- handler->data = user_data;
- handler->next = domain->handlers;
- domain->handlers = handler;
-
- g_mutex_unlock (g_messages_lock);
-
- return handler_id;
-}
-
-GLogFunc
-g_log_set_default_handler (GLogFunc log_func,
- gpointer user_data)
-{
- GLogFunc old_log_func;
-
- g_mutex_lock (g_messages_lock);
- old_log_func = default_log_func;
- default_log_func = log_func;
- default_log_data = user_data;
- g_mutex_unlock (g_messages_lock);
-
- return old_log_func;
-}
-
-/**
- * g_test_log_set_fatal_handler:
- * @log_func: the log handler function.
- * @user_data: data passed to the log handler.
- *
- * Installs a non-error fatal log handler which can be
- * used to decide whether log messages which are counted
- * as fatal abort the program.
- *
- * The use case here is that you are running a test case
- * that depends on particular libraries or circumstances
- * and cannot prevent certain known critical or warning
- * messages. So you install a handler that compares the
- * domain and message to precisely not abort in such a case.
- *
- * Note that the handler is reset at the beginning of
- * any test case, so you have to set it inside each test
- * function which needs the special behavior.
- *
- * This handler has no effect on g_error messages.
- *
- * Since: 2.22
- **/
-void
-g_test_log_set_fatal_handler (GTestLogFatalFunc log_func,
- gpointer user_data)
-{
- g_mutex_lock (g_messages_lock);
- fatal_log_func = log_func;
- fatal_log_data = user_data;
- g_mutex_unlock (g_messages_lock);
-}
-
-void
-g_log_remove_handler (const gchar *log_domain,
- guint handler_id)
-{
- register GLogDomain *domain;
-
- g_return_if_fail (handler_id > 0);
-
- if (!log_domain)
- log_domain = "";
-
- g_mutex_lock (g_messages_lock);
- domain = g_log_find_domain_L (log_domain);
- if (domain)
- {
- GLogHandler *work, *last;
-
- last = NULL;
- work = domain->handlers;
- while (work)
- {
- if (work->id == handler_id)
- {
- if (last)
- last->next = work->next;
- else
- domain->handlers = work->next;
- g_log_domain_check_free_L (domain);
- g_mutex_unlock (g_messages_lock);
- g_free (work);
- return;
- }
- last = work;
- work = last->next;
- }
- }
- g_mutex_unlock (g_messages_lock);
- g_warning ("%s: could not find handler with id `%d' for domain \"%s\"",
- G_STRLOC, handler_id, log_domain);
-}
-
-void
-g_logv (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *format,
- va_list args1)
-{
- gboolean was_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
- gboolean was_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
- gint i;
-
- log_level &= G_LOG_LEVEL_MASK;
- if (!log_level)
- return;
-
- for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i))
- {
- register GLogLevelFlags test_level;
-
- test_level = 1 << i;
- if (log_level & test_level)
- {
- guint depth = GPOINTER_TO_UINT (g_private_get (g_log_depth));
- GLogDomain *domain;
- GLogFunc log_func;
- GLogLevelFlags domain_fatal_mask;
- gpointer data = NULL;
- gboolean masquerade_fatal = FALSE;
-
- if (was_fatal)
- test_level |= G_LOG_FLAG_FATAL;
- if (was_recursion)
- test_level |= G_LOG_FLAG_RECURSION;
-
- /* check recursion and lookup handler */
- g_mutex_lock (g_messages_lock);
- domain = g_log_find_domain_L (log_domain ? log_domain : "");
- if (depth)
- test_level |= G_LOG_FLAG_RECURSION;
- depth++;
- domain_fatal_mask = domain ? domain->fatal_mask : G_LOG_FATAL_MASK;
- if ((domain_fatal_mask | g_log_always_fatal) & test_level)
- test_level |= G_LOG_FLAG_FATAL;
- if (test_level & G_LOG_FLAG_RECURSION)
- log_func = _g_log_fallback_handler;
- else
- log_func = g_log_domain_get_handler_L (domain, test_level, &data);
- domain = NULL;
- g_mutex_unlock (g_messages_lock);
-
- g_private_set (g_log_depth, GUINT_TO_POINTER (depth));
-
- /* had to defer debug initialization until we can keep track of recursion */
- if (!(test_level & G_LOG_FLAG_RECURSION) && !_g_debug_initialized)
- {
- GLogLevelFlags orig_test_level = test_level;
-
- _g_debug_init ();
- if ((domain_fatal_mask | g_log_always_fatal) & test_level)
- test_level |= G_LOG_FLAG_FATAL;
- if (test_level != orig_test_level)
- {
- /* need a relookup, not nice, but not too bad either */
- g_mutex_lock (g_messages_lock);
- domain = g_log_find_domain_L (log_domain ? log_domain : "");
- log_func = g_log_domain_get_handler_L (domain, test_level, &data);
- domain = NULL;
- g_mutex_unlock (g_messages_lock);
- }
- }
-
- if (test_level & G_LOG_FLAG_RECURSION)
- {
- /* we use a stack buffer of fixed size, since we're likely
- * in an out-of-memory situation
- */
- gchar buffer[1025];
- gsize size;
- va_list args2;
-
- G_VA_COPY (args2, args1);
- size = _g_vsnprintf (buffer, 1024, format, args2);
- va_end (args2);
-
- log_func (log_domain, test_level, buffer, data);
- }
- else
- {
- gchar *msg;
- va_list args2;
-
- G_VA_COPY (args2, args1);
- msg = g_strdup_vprintf (format, args2);
- va_end (args2);
-
- log_func (log_domain, test_level, msg, data);
-
- if ((test_level & G_LOG_FLAG_FATAL)
- && !(test_level & G_LOG_LEVEL_ERROR))
- {
- masquerade_fatal = fatal_log_func
- && !fatal_log_func (log_domain, test_level, msg, data);
- }
-
- g_free (msg);
- }
-
- if ((test_level & G_LOG_FLAG_FATAL) && !masquerade_fatal)
- {
-#ifdef G_OS_WIN32
- gchar *locale_msg = g_locale_from_utf8 (fatal_msg_buf, -1, NULL, NULL, NULL);
-
- MessageBox (NULL, locale_msg, NULL,
- MB_ICONERROR|MB_SETFOREGROUND);
- if (IsDebuggerPresent () && !(test_level & G_LOG_FLAG_RECURSION))
- G_BREAKPOINT ();
- else
- abort ();
-#else
-#if defined (G_ENABLE_DEBUG) && defined (SIGTRAP)
- if (!(test_level & G_LOG_FLAG_RECURSION))
- G_BREAKPOINT ();
- else
- abort ();
-#else /* !G_ENABLE_DEBUG || !SIGTRAP */
- abort ();
-#endif /* !G_ENABLE_DEBUG || !SIGTRAP */
-#endif /* !G_OS_WIN32 */
- }
-
- depth--;
- g_private_set (g_log_depth, GUINT_TO_POINTER (depth));
- }
- }
-}
-
-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_return_if_fail_warning (const char *log_domain,
- const char *pretty_function,
- const char *expression)
-{
- g_log (log_domain,
- G_LOG_LEVEL_CRITICAL,
- "%s: assertion `%s' failed",
- pretty_function,
- expression);
-}
-
-void
-g_warn_message (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *warnexpr)
-{
- char *s, lstr[32];
- g_snprintf (lstr, 32, "%d", line);
- if (warnexpr)
- s = g_strconcat ("(", file, ":", lstr, "):",
- func, func[0] ? ":" : "",
- " runtime check failed: (", warnexpr, ")", NULL);
- else
- s = g_strconcat ("(", file, ":", lstr, "):",
- func, func[0] ? ":" : "",
- " ", "code should not be reached", NULL);
- g_log (domain, G_LOG_LEVEL_WARNING, "%s", s);
- g_free (s);
-}
-
-void
-g_assert_warning (const char *log_domain,
- const char *file,
- const int line,
- const char *pretty_function,
- const char *expression)
-{
- g_log (log_domain,
- G_LOG_LEVEL_ERROR,
- expression
- ? "file %s: line %d (%s): assertion failed: (%s)"
- : "file %s: line %d (%s): should not be reached",
- file,
- line,
- pretty_function,
- expression);
- abort ();
-}
-
-#define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \
- (wc == 0x7f) || \
- (wc >= 0x80 && wc < 0xa0)))
-
-static gchar*
-strdup_convert (const gchar *string,
- const gchar *charset)
-{
- if (!g_utf8_validate (string, -1, NULL))
- {
- GString *gstring = g_string_new ("[Invalid UTF-8] ");
- guchar *p;
-
- for (p = (guchar *)string; *p; p++)
- {
- if (CHAR_IS_SAFE(*p) &&
- !(*p == '\r' && *(p + 1) != '\n') &&
- *p < 0x80)
- g_string_append_c (gstring, *p);
- else
- g_string_append_printf (gstring, "\\x%02x", (guint)(guchar)*p);
- }
-
- return g_string_free (gstring, FALSE);
- }
- else
- {
- GError *err = NULL;
-
- gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err);
- if (result)
- return result;
- else
- {
- /* Not thread-safe, but doesn't matter if we print the warning twice
- */
- static gboolean warned = FALSE;
- if (!warned)
- {
- warned = TRUE;
- _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message);
- }
- g_error_free (err);
-
- return g_strdup (string);
- }
- }
-}
-
-/* For a radix of 8 we need at most 3 output bytes for 1 input
- * byte. Additionally we might need up to 2 output bytes for the
- * readix prefix and 1 byte for the trailing NULL.
- */
-#define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3)
-
-static void
-format_unsigned (gchar *buf,
- gulong num,
- guint radix)
-{
- gulong tmp;
- gchar c;
- gint i, n;
-
- /* we may not call _any_ GLib functions here (or macros like g_return_if_fail()) */
-
- if (radix != 8 && radix != 10 && radix != 16)
- {
- *buf = '\000';
- return;
- }
-
- if (!num)
- {
- *buf++ = '0';
- *buf = '\000';
- return;
- }
-
- if (radix == 16)
- {
- *buf++ = '0';
- *buf++ = 'x';
- }
- else if (radix == 8)
- {
- *buf++ = '0';
- }
-
- n = 0;
- tmp = num;
- while (tmp)
- {
- tmp /= radix;
- n++;
- }
-
- i = n;
-
- /* Again we can't use g_assert; actually this check should _never_ fail. */
- if (n > FORMAT_UNSIGNED_BUFSIZE - 3)
- {
- *buf = '\000';
- return;
- }
-
- while (num)
- {
- i--;
- c = (num % radix);
- if (c < 10)
- buf[i] = c + '0';
- else
- buf[i] = c + 'a' - 10;
- num /= radix;
- }
-
- buf[n] = '\000';
-}
-
-/* string size big enough to hold level prefix */
-#define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32)
-
-#define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)
-
-static int
-mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE],
- GLogLevelFlags log_level)
-{
- gboolean to_stdout = TRUE;
-
- /* we may not call _any_ GLib functions here */
-
- switch (log_level & G_LOG_LEVEL_MASK)
- {
- case G_LOG_LEVEL_ERROR:
- strcpy (level_prefix, "ERROR");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_CRITICAL:
- strcpy (level_prefix, "CRITICAL");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_WARNING:
- strcpy (level_prefix, "WARNING");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_MESSAGE:
- strcpy (level_prefix, "Message");
- to_stdout = FALSE;
- break;
- case G_LOG_LEVEL_INFO:
- strcpy (level_prefix, "INFO");
- break;
- case G_LOG_LEVEL_DEBUG:
- strcpy (level_prefix, "DEBUG");
- break;
- default:
- if (log_level)
- {
- strcpy (level_prefix, "LOG-");
- format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16);
- }
- else
- strcpy (level_prefix, "LOG");
- break;
- }
- if (log_level & G_LOG_FLAG_RECURSION)
- strcat (level_prefix, " (recursed)");
- if (log_level & ALERT_LEVELS)
- strcat (level_prefix, " **");
-
-#ifdef G_OS_WIN32
- win32_keep_fatal_message = (log_level & G_LOG_FLAG_FATAL) != 0;
-#endif
- return to_stdout ? 1 : 2;
-}
-
-void
-_g_log_fallback_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer unused_data)
-{
- gchar level_prefix[STRING_BUFFER_SIZE];
-#ifndef G_OS_WIN32
- gchar pid_string[FORMAT_UNSIGNED_BUFSIZE];
-#endif
- gboolean is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
- int fd;
-
- /* we can not call _any_ GLib functions in this fallback handler,
- * which is why we skip UTF-8 conversion, etc.
- * since we either recursed or ran out of memory, we're in a pretty
- * pathologic situation anyways, what we can do is giving the
- * the process ID unconditionally however.
- */
-
- fd = mklevel_prefix (level_prefix, log_level);
- if (!message)
- message = "(NULL) message";
-
-#ifndef G_OS_WIN32
- format_unsigned (pid_string, getpid (), 10);
-#endif
-
- if (log_domain)
- write_string (fd, "\n");
- else
- write_string (fd, "\n** ");
-
-#ifndef G_OS_WIN32
- write_string (fd, "(process:");
- write_string (fd, pid_string);
- write_string (fd, "): ");
-#endif
-
- if (log_domain)
- {
- write_string (fd, log_domain);
- write_string (fd, "-");
- }
- write_string (fd, level_prefix);
- write_string (fd, ": ");
- write_string (fd, message);
- if (is_fatal)
- write_string (fd, "\naborting...\n");
- else
- write_string (fd, "\n");
-}
-
-static void
-escape_string (GString *string)
-{
- const char *p = string->str;
- gunichar wc;
-
- while (p < string->str + string->len)
- {
- gboolean safe;
-
- wc = g_utf8_get_char_validated (p, -1);
- if (wc == (gunichar)-1 || wc == (gunichar)-2)
- {
- gchar *tmp;
- guint pos;
-
- pos = p - string->str;
-
- /* Emit invalid UTF-8 as hex escapes
- */
- tmp = g_strdup_printf ("\\x%02x", (guint)(guchar)*p);
- g_string_erase (string, pos, 1);
- g_string_insert (string, pos, tmp);
-
- p = string->str + (pos + 4); /* Skip over escape sequence */
-
- g_free (tmp);
- continue;
- }
- if (wc == '\r')
- {
- safe = *(p + 1) == '\n';
- }
- else
- {
- safe = CHAR_IS_SAFE (wc);
- }
-
- if (!safe)
- {
- gchar *tmp;
- guint pos;
-
- pos = p - string->str;
-
- /* Largest char we escape is 0x0a, so we don't have to worry
- * about 8-digit \Uxxxxyyyy
- */
- tmp = g_strdup_printf ("\\u%04x", wc);
- g_string_erase (string, pos, g_utf8_next_char (p) - p);
- g_string_insert (string, pos, tmp);
- g_free (tmp);
-
- p = string->str + (pos + 6); /* Skip over escape sequence */
- }
- else
- p = g_utf8_next_char (p);
- }
-}
-
-#ifdef BUILD_WITH_ANDROID
-#include <android/log.h>
-void g_log_default_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer unused_data)
-{
- gint pri = ANDROID_LOG_UNKNOWN;
-
- if (log_level & G_LOG_FLAG_FATAL)
- pri = ANDROID_LOG_FATAL;
- else if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL))
- pri = ANDROID_LOG_ERROR;
- else if (log_level & G_LOG_LEVEL_WARNING)
- pri = ANDROID_LOG_WARN;
- else if (log_level & (G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO))
- pri = ANDROID_LOG_INFO;
- else if (log_level & G_LOG_LEVEL_DEBUG)
- pri = ANDROID_LOG_DEBUG;
-
- __android_log_print (pri, log_domain == NULL ? "GLib-NULL" : log_domain, message);
-}
-#else
-void
-g_log_default_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer unused_data)
-{
- gboolean is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
- gchar level_prefix[STRING_BUFFER_SIZE], *string;
- GString *gstring;
- int fd;
-
- /* we can be called externally with recursion for whatever reason */
- if (log_level & G_LOG_FLAG_RECURSION)
- {
- _g_log_fallback_handler (log_domain, log_level, message, unused_data);
- return;
- }
-
- g_messages_prefixed_init ();
-
- fd = mklevel_prefix (level_prefix, log_level);
-
- gstring = g_string_new (NULL);
- if (log_level & ALERT_LEVELS)
- g_string_append (gstring, "\n");
- if (!log_domain)
- g_string_append (gstring, "** ");
-
- if ((g_log_msg_prefix & log_level) == log_level)
- {
- const gchar *prg_name = g_get_prgname ();
-
- if (!prg_name)
- g_string_append_printf (gstring, "(process:%lu): ", (gulong)getpid ());
- else
- g_string_append_printf (gstring, "(%s:%lu): ", prg_name, (gulong)getpid ());
- }
-
- if (log_domain)
- {
- g_string_append (gstring, log_domain);
- g_string_append_c (gstring, '-');
- }
- g_string_append (gstring, level_prefix);
-
- g_string_append (gstring, ": ");
- if (!message)
- g_string_append (gstring, "(NULL) message");
- else
- {
- GString *msg;
- const gchar *charset;
-
- msg = g_string_new (message);
- escape_string (msg);
-
- if (g_get_charset (&charset))
- g_string_append (gstring, msg->str); /* charset is UTF-8 already */
- else
- {
- string = strdup_convert (msg->str, charset);
- g_string_append (gstring, string);
- g_free (string);
- }
-
- g_string_free (msg, TRUE);
- }
- if (is_fatal)
- g_string_append (gstring, "\naborting...\n");
- else
- g_string_append (gstring, "\n");
-
- string = g_string_free (gstring, FALSE);
-
- write_string (fd, string);
- g_free (string);
-}
-#endif
-
-GPrintFunc
-g_set_print_handler (GPrintFunc func)
-{
- GPrintFunc old_print_func;
-
- g_mutex_lock (g_messages_lock);
- old_print_func = glib_print_func;
- glib_print_func = func;
- g_mutex_unlock (g_messages_lock);
-
- return old_print_func;
-}
-
-void
-g_print (const gchar *format,
- ...)
-{
- va_list args;
- gchar *string;
- GPrintFunc local_glib_print_func;
-
- g_return_if_fail (format != NULL);
-
- va_start (args, format);
- string = g_strdup_vprintf (format, args);
- va_end (args);
-
- g_mutex_lock (g_messages_lock);
- local_glib_print_func = glib_print_func;
- g_mutex_unlock (g_messages_lock);
-
- if (local_glib_print_func)
- local_glib_print_func (string);
- else
- {
- const gchar *charset;
-
- if (g_get_charset (&charset))
- fputs (string, stdout); /* charset is UTF-8 already */
- else
- {
- gchar *lstring = strdup_convert (string, charset);
-
- fputs (lstring, stdout);
- g_free (lstring);
- }
- fflush (stdout);
- }
- g_free (string);
-}
-
-GPrintFunc
-g_set_printerr_handler (GPrintFunc func)
-{
- GPrintFunc old_printerr_func;
-
- g_mutex_lock (g_messages_lock);
- old_printerr_func = glib_printerr_func;
- glib_printerr_func = func;
- g_mutex_unlock (g_messages_lock);
-
- return old_printerr_func;
-}
-
-void
-g_printerr (const gchar *format,
- ...)
-{
- va_list args;
- gchar *string;
- GPrintFunc local_glib_printerr_func;
-
- g_return_if_fail (format != NULL);
-
- va_start (args, format);
- string = g_strdup_vprintf (format, args);
- va_end (args);
-
- g_mutex_lock (g_messages_lock);
- local_glib_printerr_func = glib_printerr_func;
- g_mutex_unlock (g_messages_lock);
-
- if (local_glib_printerr_func)
- local_glib_printerr_func (string);
- else
- {
- const gchar *charset;
-
- if (g_get_charset (&charset))
- fputs (string, stderr); /* charset is UTF-8 already */
- else
- {
- gchar *lstring = strdup_convert (string, charset);
-
- fputs (lstring, stderr);
- g_free (lstring);
- }
- fflush (stderr);
- }
- g_free (string);
-}
-
-gsize
-g_printf_string_upper_bound (const gchar *format,
- va_list args)
-{
- gchar c;
- return _g_vsnprintf (&c, 1, format, args) + 1;
-}
-
-void
-_g_messages_thread_init_nomessage (void)
-{
- g_messages_lock = g_mutex_new ();
- g_log_depth = g_private_new (NULL);
- g_messages_prefixed_init ();
- _g_debug_init ();
-}
-
-gboolean _g_debug_initialized = FALSE;
-guint _g_debug_flags = 0;
-
-void
-_g_debug_init (void)
-{
- const gchar *val;
-
- _g_debug_initialized = TRUE;
-
- val = g_getenv ("G_DEBUG");
- if (val != NULL)
- {
- const GDebugKey keys[] = {
- {"fatal_warnings", G_DEBUG_FATAL_WARNINGS},
- {"fatal_criticals", G_DEBUG_FATAL_CRITICALS}
- };
-
- _g_debug_flags = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys));
- }
-
- if (_g_debug_flags & G_DEBUG_FATAL_WARNINGS)
- {
- GLogLevelFlags fatal_mask;
-
- fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
- fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
- g_log_set_always_fatal (fatal_mask);
- }
-
- if (_g_debug_flags & G_DEBUG_FATAL_CRITICALS)
- {
- GLogLevelFlags fatal_mask;
-
- fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
- fatal_mask |= G_LOG_LEVEL_CRITICAL;
- g_log_set_always_fatal (fatal_mask);
- }
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GNode: N-way tree implementation.
- * Copyright (C) 1998 Tim Janik
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gnode.h"
-
-#include "gtestutils.h"
-
-/**
- * SECTION: trees-nary
- * @title: N-ary Trees
- * @short_description: trees of data with any number of branches
- *
- * The #GNode struct and its associated functions provide a N-ary tree
- * data structure, where nodes in the tree can contain arbitrary data.
- *
- * To create a new tree use g_node_new().
- *
- * To insert a node into a tree use g_node_insert(),
- * g_node_insert_before(), g_node_append() and g_node_prepend().
- *
- * To create a new node and insert it into a tree use
- * g_node_insert_data(), g_node_insert_data_before(),
- * g_node_append_data() and g_node_prepend_data().
- *
- * To reverse the children of a node use g_node_reverse_children().
- *
- * To find a node use g_node_get_root(), g_node_find(),
- * g_node_find_child(), g_node_child_index(), g_node_child_position(),
- * g_node_first_child(), g_node_last_child(), g_node_nth_child(),
- * g_node_first_sibling(), g_node_prev_sibling(), g_node_next_sibling()
- * or g_node_last_sibling().
- *
- * To get information about a node or tree use G_NODE_IS_LEAF(),
- * G_NODE_IS_ROOT(), g_node_depth(), g_node_n_nodes(),
- * g_node_n_children(), g_node_is_ancestor() or g_node_max_height().
- *
- * To traverse a tree, calling a function for each node visited in the
- * traversal, use g_node_traverse() or g_node_children_foreach().
- *
- * To remove a node or subtree from a tree use g_node_unlink() or
- * g_node_destroy().
- **/
-
-/**
- * GNode:
- * @data: contains the actual data of the node.
- * @next: points to the node's next sibling (a sibling is another
- * #GNode with the same parent).
- * @prev: points to the node's previous sibling.
- * @parent: points to the parent of the #GNode, or is %NULL if the
- * #GNode is the root of the tree.
- * @children: points to the first child of the #GNode. The other
- * children are accessed by using the @next pointer of each
- * child.
- *
- * The #GNode struct represents one node in a
- * <link linkend="glib-N-ary-Trees">N-ary Tree</link>. fields
- **/
-
-/**
- * g_node_push_allocator:
- * @dummy: the #GAllocator to use when allocating #GNode elements.
- *
- * Sets the allocator to use to allocate #GNode elements. Use
- * g_node_pop_allocator() to restore the previous allocator.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated:2.10: It does nothing, since #GNode has been converted to
- * the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_node_push_allocator (gpointer dummy) { /* present for binary compat only */ }
-
-/**
- * g_node_pop_allocator:
- *
- * Restores the previous #GAllocator, used when allocating #GNode
- * elements.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated:2.10: It does nothing, since #GNode has been converted to
- * the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_node_pop_allocator (void) { /* present for binary compat only */ }
-
-#define g_node_alloc0() g_slice_new0 (GNode)
-#define g_node_free(node) g_slice_free (GNode, node)
-
-/* --- functions --- */
-/**
- * g_node_new:
- * @data: the data of the new node
- *
- * Creates a new #GNode containing the given data.
- * Used to create the first node in a tree.
- *
- * Returns: a new #GNode
- */
-GNode*
-g_node_new (gpointer data)
-{
- GNode *node = g_node_alloc0 ();
- node->data = data;
- return node;
-}
-
-static void
-g_nodes_free (GNode *node)
-{
- while (node)
- {
- GNode *next = node->next;
- if (node->children)
- g_nodes_free (node->children);
- g_node_free (node);
- node = next;
- }
-}
-
-/**
- * g_node_destroy:
- * @root: the root of the tree/subtree to destroy
- *
- * Removes @root and its children from the tree, freeing any memory
- * allocated.
- */
-void
-g_node_destroy (GNode *root)
-{
- g_return_if_fail (root != NULL);
-
- if (!G_NODE_IS_ROOT (root))
- g_node_unlink (root);
-
- g_nodes_free (root);
-}
-
-/**
- * g_node_unlink:
- * @node: the #GNode to unlink, which becomes the root of a new tree
- *
- * Unlinks a #GNode from a tree, resulting in two separate trees.
- */
-void
-g_node_unlink (GNode *node)
-{
- g_return_if_fail (node != NULL);
-
- if (node->prev)
- node->prev->next = node->next;
- else if (node->parent)
- node->parent->children = node->next;
- node->parent = NULL;
- if (node->next)
- {
- node->next->prev = node->prev;
- node->next = NULL;
- }
- node->prev = NULL;
-}
-
-/**
- * g_node_copy_deep:
- * @node: a #GNode
- * @copy_func: the function which is called to copy the data inside each node,
- * or %NULL to use the original data.
- * @data: data to pass to @copy_func
- *
- * Recursively copies a #GNode and its data.
- *
- * Return value: a new #GNode containing copies of the data in @node.
- *
- * Since: 2.4
- **/
-GNode*
-g_node_copy_deep (GNode *node,
- GCopyFunc copy_func,
- gpointer data)
-{
- GNode *new_node = NULL;
-
- if (copy_func == NULL)
- return g_node_copy (node);
-
- if (node)
- {
- GNode *child, *new_child;
-
- new_node = g_node_new (copy_func (node->data, data));
-
- for (child = g_node_last_child (node); child; child = child->prev)
- {
- new_child = g_node_copy_deep (child, copy_func, data);
- g_node_prepend (new_node, new_child);
- }
- }
-
- return new_node;
-}
-
-/**
- * g_node_copy:
- * @node: a #GNode
- *
- * Recursively copies a #GNode (but does not deep-copy the data inside the
- * nodes, see g_node_copy_deep() if you need that).
- *
- * Returns: a new #GNode containing the same data pointers
- */
-GNode*
-g_node_copy (GNode *node)
-{
- GNode *new_node = NULL;
-
- if (node)
- {
- GNode *child;
-
- new_node = g_node_new (node->data);
-
- for (child = g_node_last_child (node); child; child = child->prev)
- g_node_prepend (new_node, g_node_copy (child));
- }
-
- return new_node;
-}
-
-/**
- * g_node_insert:
- * @parent: the #GNode to place @node under
- * @position: the position to place @node at, with respect to its siblings
- * If position is -1, @node is inserted as the last child of @parent
- * @node: the #GNode to insert
- *
- * Inserts a #GNode beneath the parent at the given position.
- *
- * Returns: the inserted #GNode
- */
-GNode*
-g_node_insert (GNode *parent,
- gint position,
- GNode *node)
-{
- g_return_val_if_fail (parent != NULL, node);
- g_return_val_if_fail (node != NULL, node);
- g_return_val_if_fail (G_NODE_IS_ROOT (node), node);
-
- if (position > 0)
- return g_node_insert_before (parent,
- g_node_nth_child (parent, position),
- node);
- else if (position == 0)
- return g_node_prepend (parent, node);
- else /* if (position < 0) */
- return g_node_append (parent, node);
-}
-
-/**
- * g_node_insert_before:
- * @parent: the #GNode to place @node under
- * @sibling: the sibling #GNode to place @node before.
- * If sibling is %NULL, the node is inserted as the last child of @parent.
- * @node: the #GNode to insert
- *
- * Inserts a #GNode beneath the parent before the given sibling.
- *
- * Returns: the inserted #GNode
- */
-GNode*
-g_node_insert_before (GNode *parent,
- GNode *sibling,
- GNode *node)
-{
- g_return_val_if_fail (parent != NULL, node);
- g_return_val_if_fail (node != NULL, node);
- g_return_val_if_fail (G_NODE_IS_ROOT (node), node);
- if (sibling)
- g_return_val_if_fail (sibling->parent == parent, node);
-
- node->parent = parent;
-
- if (sibling)
- {
- if (sibling->prev)
- {
- node->prev = sibling->prev;
- node->prev->next = node;
- node->next = sibling;
- sibling->prev = node;
- }
- else
- {
- node->parent->children = node;
- node->next = sibling;
- sibling->prev = node;
- }
- }
- else
- {
- if (parent->children)
- {
- sibling = parent->children;
- while (sibling->next)
- sibling = sibling->next;
- node->prev = sibling;
- sibling->next = node;
- }
- else
- node->parent->children = node;
- }
-
- return node;
-}
-
-/**
- * g_node_insert_after:
- * @parent: the #GNode to place @node under
- * @sibling: the sibling #GNode to place @node after.
- * If sibling is %NULL, the node is inserted as the first child of @parent.
- * @node: the #GNode to insert
- *
- * Inserts a #GNode beneath the parent after the given sibling.
- *
- * Returns: the inserted #GNode
- */
-GNode*
-g_node_insert_after (GNode *parent,
- GNode *sibling,
- GNode *node)
-{
- g_return_val_if_fail (parent != NULL, node);
- g_return_val_if_fail (node != NULL, node);
- g_return_val_if_fail (G_NODE_IS_ROOT (node), node);
- if (sibling)
- g_return_val_if_fail (sibling->parent == parent, node);
-
- node->parent = parent;
-
- if (sibling)
- {
- if (sibling->next)
- {
- sibling->next->prev = node;
- }
- node->next = sibling->next;
- node->prev = sibling;
- sibling->next = node;
- }
- else
- {
- if (parent->children)
- {
- node->next = parent->children;
- parent->children->prev = node;
- }
- parent->children = node;
- }
-
- return node;
-}
-
-/**
- * g_node_prepend:
- * @parent: the #GNode to place the new #GNode under
- * @node: the #GNode to insert
- *
- * Inserts a #GNode as the first child of the given parent.
- *
- * Returns: the inserted #GNode
- */
-GNode*
-g_node_prepend (GNode *parent,
- GNode *node)
-{
- g_return_val_if_fail (parent != NULL, node);
-
- return g_node_insert_before (parent, parent->children, node);
-}
-
-/**
- * g_node_get_root:
- * @node: a #GNode
- *
- * Gets the root of a tree.
- *
- * Returns: the root of the tree
- */
-GNode*
-g_node_get_root (GNode *node)
-{
- g_return_val_if_fail (node != NULL, NULL);
-
- while (node->parent)
- node = node->parent;
-
- return node;
-}
-
-/**
- * g_node_is_ancestor:
- * @node: a #GNode
- * @descendant: a #GNode
- *
- * Returns %TRUE if @node is an ancestor of @descendant.
- * This is true if node is the parent of @descendant,
- * or if node is the grandparent of @descendant etc.
- *
- * Returns: %TRUE if @node is an ancestor of @descendant
- */
-gboolean
-g_node_is_ancestor (GNode *node,
- GNode *descendant)
-{
- g_return_val_if_fail (node != NULL, FALSE);
- g_return_val_if_fail (descendant != NULL, FALSE);
-
- while (descendant)
- {
- if (descendant->parent == node)
- return TRUE;
-
- descendant = descendant->parent;
- }
-
- return FALSE;
-}
-
-/**
- * g_node_depth:
- * @node: a #GNode
- *
- * Gets the depth of a #GNode.
- *
- * If @node is %NULL the depth is 0. The root node has a depth of 1.
- * For the children of the root node the depth is 2. And so on.
- *
- * Returns: the depth of the #GNode
- */
-guint
-g_node_depth (GNode *node)
-{
- guint depth = 0;
-
- while (node)
- {
- depth++;
- node = node->parent;
- }
-
- return depth;
-}
-
-/**
- * g_node_reverse_children:
- * @node: a #GNode.
- *
- * Reverses the order of the children of a #GNode.
- * (It doesn't change the order of the grandchildren.)
- */
-void
-g_node_reverse_children (GNode *node)
-{
- GNode *child;
- GNode *last;
-
- g_return_if_fail (node != NULL);
-
- child = node->children;
- last = NULL;
- while (child)
- {
- last = child;
- child = last->next;
- last->next = last->prev;
- last->prev = child;
- }
- node->children = last;
-}
-
-/**
- * g_node_max_height:
- * @root: a #GNode
- *
- * Gets the maximum height of all branches beneath a #GNode.
- * This is the maximum distance from the #GNode to all leaf nodes.
- *
- * If @root is %NULL, 0 is returned. If @root has no children,
- * 1 is returned. If @root has children, 2 is returned. And so on.
- *
- * Returns: the maximum height of the tree beneath @root
- */
-guint
-g_node_max_height (GNode *root)
-{
- GNode *child;
- guint max_height = 0;
-
- if (!root)
- return 0;
-
- child = root->children;
- while (child)
- {
- guint tmp_height;
-
- tmp_height = g_node_max_height (child);
- if (tmp_height > max_height)
- max_height = tmp_height;
- child = child->next;
- }
-
- return max_height + 1;
-}
-
-static gboolean
-g_node_traverse_pre_order (GNode *node,
- GTraverseFlags flags,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- GNode *child;
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- child = node->children;
- while (child)
- {
- GNode *current;
-
- current = child;
- child = current->next;
- if (g_node_traverse_pre_order (current, flags, func, data))
- return TRUE;
- }
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_depth_traverse_pre_order (GNode *node,
- GTraverseFlags flags,
- guint depth,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- GNode *child;
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- depth--;
- if (!depth)
- return FALSE;
-
- child = node->children;
- while (child)
- {
- GNode *current;
-
- current = child;
- child = current->next;
- if (g_node_depth_traverse_pre_order (current, flags, depth, func, data))
- return TRUE;
- }
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_traverse_post_order (GNode *node,
- GTraverseFlags flags,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- GNode *child;
-
- child = node->children;
- while (child)
- {
- GNode *current;
-
- current = child;
- child = current->next;
- if (g_node_traverse_post_order (current, flags, func, data))
- return TRUE;
- }
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_depth_traverse_post_order (GNode *node,
- GTraverseFlags flags,
- guint depth,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- depth--;
- if (depth)
- {
- GNode *child;
-
- child = node->children;
- while (child)
- {
- GNode *current;
-
- current = child;
- child = current->next;
- if (g_node_depth_traverse_post_order (current, flags, depth, func, data))
- return TRUE;
- }
- }
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_traverse_in_order (GNode *node,
- GTraverseFlags flags,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- GNode *child;
- GNode *current;
-
- child = node->children;
- current = child;
- child = current->next;
-
- if (g_node_traverse_in_order (current, flags, func, data))
- return TRUE;
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- while (child)
- {
- current = child;
- child = current->next;
- if (g_node_traverse_in_order (current, flags, func, data))
- return TRUE;
- }
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_depth_traverse_in_order (GNode *node,
- GTraverseFlags flags,
- guint depth,
- GNodeTraverseFunc func,
- gpointer data)
-{
- if (node->children)
- {
- depth--;
- if (depth)
- {
- GNode *child;
- GNode *current;
-
- child = node->children;
- current = child;
- child = current->next;
-
- if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
- return TRUE;
-
- if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
-
- while (child)
- {
- current = child;
- child = current->next;
- if (g_node_depth_traverse_in_order (current, flags, depth, func, data))
- return TRUE;
- }
- }
- else if ((flags & G_TRAVERSE_NON_LEAFS) &&
- func (node, data))
- return TRUE;
- }
- else if ((flags & G_TRAVERSE_LEAFS) &&
- func (node, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-g_node_traverse_level (GNode *node,
- GTraverseFlags flags,
- guint level,
- GNodeTraverseFunc func,
- gpointer data,
- gboolean *more_levels)
-{
- if (level == 0)
- {
- if (node->children)
- {
- *more_levels = TRUE;
- return (flags & G_TRAVERSE_NON_LEAFS) && func (node, data);
- }
- else
- {
- return (flags & G_TRAVERSE_LEAFS) && func (node, data);
- }
- }
- else
- {
- node = node->children;
-
- while (node)
- {
- if (g_node_traverse_level (node, flags, level - 1, func, data, more_levels))
- return TRUE;
-
- node = node->next;
- }
- }
-
- return FALSE;
-}
-
-static gboolean
-g_node_depth_traverse_level (GNode *node,
- GTraverseFlags flags,
- guint depth,
- GNodeTraverseFunc func,
- gpointer data)
-{
- guint level;
- gboolean more_levels;
-
- level = 0;
- while (level != depth)
- {
- more_levels = FALSE;
- if (g_node_traverse_level (node, flags, level, func, data, &more_levels))
- return TRUE;
- if (!more_levels)
- break;
- level++;
- }
- return FALSE;
-}
-
-/**
- * g_node_traverse:
- * @root: the root #GNode of the tree to traverse
- * @order: the order in which nodes are visited - %G_IN_ORDER,
- * %G_PRE_ORDER, %G_POST_ORDER, or %G_LEVEL_ORDER.
- * @flags: which types of children are to be visited, one of
- * %G_TRAVERSE_ALL, %G_TRAVERSE_LEAVES and %G_TRAVERSE_NON_LEAVES
- * @max_depth: the maximum depth of the traversal. Nodes below this
- * depth will not be visited. If max_depth is -1 all nodes in
- * the tree are visited. If depth is 1, only the root is visited.
- * If depth is 2, the root and its children are visited. And so on.
- * @func: the function to call for each visited #GNode
- * @data: user data to pass to the function
- *
- * Traverses a tree starting at the given root #GNode.
- * It calls the given function for each node visited.
- * The traversal can be halted at any point by returning %TRUE from @func.
- */
-/**
- * GTraverseFlags:
- * @G_TRAVERSE_LEAVES: only leaf nodes should be visited. This name has
- * been introduced in 2.6, for older version use
- * %G_TRAVERSE_LEAFS.
- * @G_TRAVERSE_NON_LEAVES: only non-leaf nodes should be visited. This
- * name has been introduced in 2.6, for older
- * version use %G_TRAVERSE_NON_LEAFS.
- * @G_TRAVERSE_ALL: all nodes should be visited.
- * @G_TRAVERSE_MASK: a mask of all traverse flags.
- * @G_TRAVERSE_LEAFS: identical to %G_TRAVERSE_LEAVES.
- * @G_TRAVERSE_NON_LEAFS: identical to %G_TRAVERSE_NON_LEAVES.
- *
- * Specifies which nodes are visited during several of the tree
- * functions, including g_node_traverse() and g_node_find().
- **/
-/**
- * GNodeTraverseFunc:
- * @node: a #GNode.
- * @data: user data passed to g_node_traverse().
- * @Returns: %TRUE to stop the traversal.
- *
- * Specifies the type of function passed to g_node_traverse(). The
- * function is called with each of the nodes visited, together with the
- * user data passed to g_node_traverse(). If the function returns
- * %TRUE, then the traversal is stopped.
- **/
-void
-g_node_traverse (GNode *root,
- GTraverseType order,
- GTraverseFlags flags,
- gint depth,
- GNodeTraverseFunc func,
- gpointer data)
-{
- g_return_if_fail (root != NULL);
- g_return_if_fail (func != NULL);
- g_return_if_fail (order <= G_LEVEL_ORDER);
- g_return_if_fail (flags <= G_TRAVERSE_MASK);
- g_return_if_fail (depth == -1 || depth > 0);
-
- switch (order)
- {
- case G_PRE_ORDER:
- if (depth < 0)
- g_node_traverse_pre_order (root, flags, func, data);
- else
- g_node_depth_traverse_pre_order (root, flags, depth, func, data);
- break;
- case G_POST_ORDER:
- if (depth < 0)
- g_node_traverse_post_order (root, flags, func, data);
- else
- g_node_depth_traverse_post_order (root, flags, depth, func, data);
- break;
- case G_IN_ORDER:
- if (depth < 0)
- g_node_traverse_in_order (root, flags, func, data);
- else
- g_node_depth_traverse_in_order (root, flags, depth, func, data);
- break;
- case G_LEVEL_ORDER:
- g_node_depth_traverse_level (root, flags, depth, func, data);
- break;
- }
-}
-
-static gboolean
-g_node_find_func (GNode *node,
- gpointer data)
-{
- gpointer *d = data;
-
- if (*d != node->data)
- return FALSE;
-
- *(++d) = node;
-
- return TRUE;
-}
-
-/**
- * g_node_find:
- * @root: the root #GNode of the tree to search
- * @order: the order in which nodes are visited - %G_IN_ORDER,
- * %G_PRE_ORDER, %G_POST_ORDER, or %G_LEVEL_ORDER
- * @flags: which types of children are to be searched, one of
- * %G_TRAVERSE_ALL, %G_TRAVERSE_LEAVES and %G_TRAVERSE_NON_LEAVES
- * @data: the data to find
- *
- * Finds a #GNode in a tree.
- *
- * Returns: the found #GNode, or %NULL if the data is not found
- */
-GNode*
-g_node_find (GNode *root,
- GTraverseType order,
- GTraverseFlags flags,
- gpointer data)
-{
- gpointer d[2];
-
- g_return_val_if_fail (root != NULL, NULL);
- g_return_val_if_fail (order <= G_LEVEL_ORDER, NULL);
- g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
-
- d[0] = data;
- d[1] = NULL;
-
- g_node_traverse (root, order, flags, -1, g_node_find_func, d);
-
- return d[1];
-}
-
-static void
-g_node_count_func (GNode *node,
- GTraverseFlags flags,
- guint *n)
-{
- if (node->children)
- {
- GNode *child;
-
- if (flags & G_TRAVERSE_NON_LEAFS)
- (*n)++;
-
- child = node->children;
- while (child)
- {
- g_node_count_func (child, flags, n);
- child = child->next;
- }
- }
- else if (flags & G_TRAVERSE_LEAFS)
- (*n)++;
-}
-
-/**
- * g_node_n_nodes:
- * @root: a #GNode
- * @flags: which types of children are to be counted, one of
- * %G_TRAVERSE_ALL, %G_TRAVERSE_LEAVES and %G_TRAVERSE_NON_LEAVES
- *
- * Gets the number of nodes in a tree.
- *
- * Returns: the number of nodes in the tree
- */
-guint
-g_node_n_nodes (GNode *root,
- GTraverseFlags flags)
-{
- guint n = 0;
-
- g_return_val_if_fail (root != NULL, 0);
- g_return_val_if_fail (flags <= G_TRAVERSE_MASK, 0);
-
- g_node_count_func (root, flags, &n);
-
- return n;
-}
-
-/**
- * g_node_last_child:
- * @node: a #GNode (must not be %NULL)
- *
- * Gets the last child of a #GNode.
- *
- * Returns: the last child of @node, or %NULL if @node has no children
- */
-GNode*
-g_node_last_child (GNode *node)
-{
- g_return_val_if_fail (node != NULL, NULL);
-
- node = node->children;
- if (node)
- while (node->next)
- node = node->next;
-
- return node;
-}
-
-/**
- * g_node_nth_child:
- * @node: a #GNode
- * @n: the index of the desired child
- *
- * Gets a child of a #GNode, using the given index.
- * The first child is at index 0. If the index is
- * too big, %NULL is returned.
- *
- * Returns: the child of @node at index @n
- */
-GNode*
-g_node_nth_child (GNode *node,
- guint n)
-{
- g_return_val_if_fail (node != NULL, NULL);
-
- node = node->children;
- if (node)
- while ((n-- > 0) && node)
- node = node->next;
-
- return node;
-}
-
-/**
- * g_node_n_children:
- * @node: a #GNode
- *
- * Gets the number of children of a #GNode.
- *
- * Returns: the number of children of @node
- */
-guint
-g_node_n_children (GNode *node)
-{
- guint n = 0;
-
- g_return_val_if_fail (node != NULL, 0);
-
- node = node->children;
- while (node)
- {
- n++;
- node = node->next;
- }
-
- return n;
-}
-
-/**
- * g_node_find_child:
- * @node: a #GNode
- * @flags: which types of children are to be searched, one of
- * %G_TRAVERSE_ALL, %G_TRAVERSE_LEAVES and %G_TRAVERSE_NON_LEAVES
- * @data: the data to find
- *
- * Finds the first child of a #GNode with the given data.
- *
- * Returns: the found child #GNode, or %NULL if the data is not found
- */
-GNode*
-g_node_find_child (GNode *node,
- GTraverseFlags flags,
- gpointer data)
-{
- g_return_val_if_fail (node != NULL, NULL);
- g_return_val_if_fail (flags <= G_TRAVERSE_MASK, NULL);
-
- node = node->children;
- while (node)
- {
- if (node->data == data)
- {
- if (G_NODE_IS_LEAF (node))
- {
- if (flags & G_TRAVERSE_LEAFS)
- return node;
- }
- else
- {
- if (flags & G_TRAVERSE_NON_LEAFS)
- return node;
- }
- }
- node = node->next;
- }
-
- return NULL;
-}
-
-/**
- * g_node_child_position:
- * @node: a #GNode
- * @child: a child of @node
- *
- * Gets the position of a #GNode with respect to its siblings.
- * @child must be a child of @node. The first child is numbered 0,
- * the second 1, and so on.
- *
- * Returns: the position of @child with respect to its siblings
- */
-gint
-g_node_child_position (GNode *node,
- GNode *child)
-{
- guint n = 0;
-
- g_return_val_if_fail (node != NULL, -1);
- g_return_val_if_fail (child != NULL, -1);
- g_return_val_if_fail (child->parent == node, -1);
-
- node = node->children;
- while (node)
- {
- if (node == child)
- return n;
- n++;
- node = node->next;
- }
-
- return -1;
-}
-
-/**
- * g_node_child_index:
- * @node: a #GNode
- * @data: the data to find
- *
- * Gets the position of the first child of a #GNode
- * which contains the given data.
- *
- * Returns: the index of the child of @node which contains
- * @data, or -1 if the data is not found
- */
-gint
-g_node_child_index (GNode *node,
- gpointer data)
-{
- guint n = 0;
-
- g_return_val_if_fail (node != NULL, -1);
-
- node = node->children;
- while (node)
- {
- if (node->data == data)
- return n;
- n++;
- node = node->next;
- }
-
- return -1;
-}
-
-/**
- * g_node_first_sibling:
- * @node: a #GNode
- *
- * Gets the first sibling of a #GNode.
- * This could possibly be the node itself.
- *
- * Returns: the first sibling of @node
- */
-GNode*
-g_node_first_sibling (GNode *node)
-{
- g_return_val_if_fail (node != NULL, NULL);
-
- if (node->parent)
- return node->parent->children;
-
- while (node->prev)
- node = node->prev;
-
- return node;
-}
-
-/**
- * g_node_last_sibling:
- * @node: a #GNode
- *
- * Gets the last sibling of a #GNode.
- * This could possibly be the node itself.
- *
- * Returns: the last sibling of @node
- */
-GNode*
-g_node_last_sibling (GNode *node)
-{
- g_return_val_if_fail (node != NULL, NULL);
-
- while (node->next)
- node = node->next;
-
- return node;
-}
-
-/**
- * g_node_children_foreach:
- * @node: a #GNode
- * @flags: which types of children are to be visited, one of
- * %G_TRAVERSE_ALL, %G_TRAVERSE_LEAVES and %G_TRAVERSE_NON_LEAVES
- * @func: the function to call for each visited node
- * @data: user data to pass to the function
- *
- * Calls a function for each of the children of a #GNode.
- * Note that it doesn't descend beneath the child nodes.
- */
-/**
- * GNodeForeachFunc:
- * @node: a #GNode.
- * @data: user data passed to g_node_children_foreach().
- *
- * Specifies the type of function passed to g_node_children_foreach().
- * The function is called with each child node, together with the user
- * data passed to g_node_children_foreach().
- **/
-void
-g_node_children_foreach (GNode *node,
- GTraverseFlags flags,
- GNodeForeachFunc func,
- gpointer data)
-{
- g_return_if_fail (node != NULL);
- g_return_if_fail (flags <= G_TRAVERSE_MASK);
- g_return_if_fail (func != NULL);
-
- node = node->children;
- while (node)
- {
- GNode *current;
-
- current = node;
- node = current->next;
- if (G_NODE_IS_LEAF (current))
- {
- if (flags & G_TRAVERSE_LEAFS)
- func (current, data);
- }
- else
- {
- if (flags & G_TRAVERSE_NON_LEAFS)
- func (current, data);
- }
- }
-}
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-include $(top_srcdir)/Makefile.decl
-
-INCLUDES = $(glib_INCLUDES) -DG_LOG_DOMAIN=\"GLib\" \
- $(GLIB_DEBUG_FLAGS) -DG_DISABLE_DEPRECATED -DGLIB_COMPILATION
-
-noinst_LTLIBRARIES = libgnulib.la
-
-libgnulib_la_SOURCES = \
- asnprintf.c \
- printf-args.c \
- printf-args.h \
- printf-parse.c \
- printf-parse.h \
- vasnprintf.c \
- vasnprintf.h \
- printf.c \
- printf.h \
- g-gnulib.h
-
-
-EXTRA_DIST += makefile.msc
+++ /dev/null
-The files
-
- asnprintf.c
- printf-args.c
- printf-args.h
- printf-parse.c
- printf-parse.h
- vasnprintf.c
- vasnprintf.h
-
-are taken from the vasnprintf module of the GNUlib package, which can
-be found at:
-
- http://www.gnu.org/software/gnulib/
-
-All files have been modified to include g-gnulib.h.
-
-vasnprintf.c has also been modified to include support for long long
-printing if the system printf doesn't. This code is protected by
-#ifndef HAVE_LONG_LONG_FORMAT.
-
-Code has been added to printf-args.[ch], printf-parse.c and vasnprintf.c
-to support printing of __int64 values with the I64 format modifier. This
-is protected by #ifdef HAVE_INT64_AND_I64.
-
-The files
-
- printf.h
- printf.c
- g-gnulib.h
-
-have been written by me. printf.[hc] contain implementations of the
-remaining functions in the printf family based on vasnprintf.
-g-gnulib.h is included by all source files in order to move all
-exported functions to the _g_gnulib namespace, replace malloc by
-g_malloc and make sure that snprintf is only used if it implements
-C99 return value semantics.
-
-Matthias Clasen
-November 1, 2003
-
-
-
-
+++ /dev/null
-/* Formatted output to strings.
- Copyright (C) 1999, 2002 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "g-gnulib.h"
-
-/* Specification. */
-#include "vasnprintf.h"
-
-#include <stdarg.h>
-
-char *
-asnprintf(char *resultbuf, size_t *lengthp, const char *format, ...)
-{
- va_list args;
- char *result;
-
- va_start(args, format);
- result = vasnprintf(resultbuf, lengthp, format, args);
- va_end(args);
- return result;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 2003 Matthias Clasen
- *
- * 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 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 __G_GNULIB_H__
-
-#include "config.h"
-#include <stdlib.h>
-#include "glib/glib.h"
-
-/* Private namespace for gnulib functions */
-#define asnprintf _g_gnulib_asnprintf
-#define vasnprintf _g_gnulib_vasnprintf
-#define printf_parse _g_gnulib_printf_parse
-#define printf_fetchargs _g_gnulib_printf_fetchargs
-
-/* Use GLib memory allocation */
-#undef malloc
-#undef realloc
-#undef free
-#define malloc g_malloc
-#define realloc g_realloc
-#define free g_free
-
-/* Ensure only C99 snprintf gets used */
-#undef HAVE_SNPRINTF
-#ifdef HAVE_C99_SNPRINTF
-#define HAVE_SNPRINTF 1
-#else
-#define HAVE_SNPRINTF 0
-#endif
-
-#endif /* __G_GNULIB_H__ */
-
+++ /dev/null
-TOP = ..\..\..
-!INCLUDE ..\..\build\win32\make.msc
-
-INCLUDES = -I ..\.. -I ..
-DEFINES = -DHAVE_CONFIG_H -DHAVE_LONG_LONG_FORMAT
-
-OBJECTS = \
- asnprintf.obj \
- printf.obj \
- printf-args.obj \
- printf-parse.obj \
- vasnprintf.obj
-
-all : gnulib.lib
-
-gnulib.lib : $(OBJECTS)
- lib -out:gnulib.lib $(OBJECTS)
-
+++ /dev/null
-/* Decomposed printf argument list.
- Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "g-gnulib.h"
-
-/* Specification. */
-#include "printf-args.h"
-
-#ifdef STATIC
-STATIC
-#endif
-int printf_fetchargs(va_list args, arguments *a)
-{
- unsigned int i;
- argument *ap;
-
- for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++)
- switch (ap->type)
- {
- case TYPE_SCHAR:
- ap->a.a_schar = va_arg (args, /*signed char*/int);
- break;
- case TYPE_UCHAR:
- ap->a.a_uchar = va_arg (args, /*unsigned char*/int);
- break;
- case TYPE_SHORT:
- ap->a.a_short = va_arg (args, /*short*/int);
- break;
- case TYPE_USHORT:
- ap->a.a_ushort = va_arg (args, /*unsigned short*/int);
- break;
- case TYPE_INT:
- ap->a.a_int = va_arg (args, int);
- break;
- case TYPE_UINT:
- ap->a.a_uint = va_arg (args, unsigned int);
- break;
- case TYPE_LONGINT:
- ap->a.a_longint = va_arg (args, long int);
- break;
- case TYPE_ULONGINT:
- ap->a.a_ulongint = va_arg (args, unsigned long int);
- break;
-#ifdef HAVE_LONG_LONG
- case TYPE_LONGLONGINT:
- ap->a.a_longlongint = va_arg (args, long long int);
- break;
- case TYPE_ULONGLONGINT:
- ap->a.a_ulonglongint = va_arg (args, unsigned long long int);
- break;
-#endif
-#ifdef HAVE_INT64_AND_I64
- case TYPE_INT64:
- ap->a.a_int64 = va_arg (args, __int64);
- break;
- case TYPE_UINT64:
- ap->a.a_uint64 = va_arg (args, unsigned __int64);
- break;
-#endif
- case TYPE_DOUBLE:
- ap->a.a_double = va_arg (args, double);
- break;
-#ifdef HAVE_LONG_DOUBLE
- case TYPE_LONGDOUBLE:
- ap->a.a_longdouble = va_arg (args, long double);
- break;
-#endif
- case TYPE_CHAR:
- ap->a.a_char = va_arg (args, int);
- break;
-#ifdef HAVE_WINT_T
- case TYPE_WIDE_CHAR:
-#ifdef _WIN32
- ap->a.a_wide_char = va_arg (args, int);
-#else
- ap->a.a_wide_char = va_arg (args, wint_t);
-#endif
- break;
-#endif
- case TYPE_STRING:
- ap->a.a_string = va_arg (args, const char *);
- break;
-#ifdef HAVE_WCHAR_T
- case TYPE_WIDE_STRING:
- ap->a.a_wide_string = va_arg (args, const wchar_t *);
- break;
-#endif
- case TYPE_POINTER:
- ap->a.a_pointer = va_arg (args, void *);
- break;
- case TYPE_COUNT_SCHAR_POINTER:
- ap->a.a_count_schar_pointer = va_arg (args, signed char *);
- break;
- case TYPE_COUNT_SHORT_POINTER:
- ap->a.a_count_short_pointer = va_arg (args, short *);
- break;
- case TYPE_COUNT_INT_POINTER:
- ap->a.a_count_int_pointer = va_arg (args, int *);
- break;
- case TYPE_COUNT_LONGINT_POINTER:
- ap->a.a_count_longint_pointer = va_arg (args, long int *);
- break;
-#ifdef HAVE_LONG_LONG
- case TYPE_COUNT_LONGLONGINT_POINTER:
- ap->a.a_count_longlongint_pointer = va_arg (args, long long int *);
- break;
-#endif
- default:
- /* Unknown type. */
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/* Decomposed printf argument list.
- Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifndef _PRINTF_ARGS_H
-#define _PRINTF_ARGS_H
-
-/* Get wchar_t. */
-#ifdef HAVE_WCHAR_T
-# include <stddef.h>
-#endif
-
-/* Get wint_t. */
-#ifdef HAVE_WINT_T
-# include <wchar.h>
-#endif
-
-/* Get va_list. */
-#include <stdarg.h>
-
-/* Argument types */
-typedef enum
-{
- TYPE_NONE,
- TYPE_SCHAR,
- TYPE_UCHAR,
- TYPE_SHORT,
- TYPE_USHORT,
- TYPE_INT,
- TYPE_UINT,
- TYPE_LONGINT,
- TYPE_ULONGINT,
-#ifdef HAVE_LONG_LONG
- TYPE_LONGLONGINT,
- TYPE_ULONGLONGINT,
-#endif
-#ifdef HAVE_INT64_AND_I64
- TYPE_INT64,
- TYPE_UINT64,
-#endif
- TYPE_DOUBLE,
-#ifdef HAVE_LONG_DOUBLE
- TYPE_LONGDOUBLE,
-#endif
- TYPE_CHAR,
-#ifdef HAVE_WINT_T
- TYPE_WIDE_CHAR,
-#endif
- TYPE_STRING,
-#ifdef HAVE_WCHAR_T
- TYPE_WIDE_STRING,
-#endif
- TYPE_POINTER,
- TYPE_COUNT_SCHAR_POINTER,
- TYPE_COUNT_SHORT_POINTER,
- TYPE_COUNT_INT_POINTER,
- TYPE_COUNT_LONGINT_POINTER
-#ifdef HAVE_LONG_LONG
- , TYPE_COUNT_LONGLONGINT_POINTER
-#endif
-} arg_type;
-
- /* Polymorphic argument */
-typedef struct
-{
- arg_type type;
- union
- {
- signed char a_schar;
- unsigned char a_uchar;
- short a_short;
- unsigned short a_ushort;
- int a_int;
- unsigned int a_uint;
- long int a_longint;
- unsigned long int a_ulongint;
-#ifdef HAVE_LONG_LONG
- long long int a_longlongint;
- unsigned long long int a_ulonglongint;
-#endif
-#ifdef HAVE_INT64_AND_I64
- __int64 a_int64;
- unsigned __int64 a_uint64;
-#endif
- float a_float;
- double a_double;
-#ifdef HAVE_LONG_DOUBLE
- long double a_longdouble;
-#endif
- int a_char;
-#ifdef HAVE_WINT_T
- wint_t a_wide_char;
-#endif
- const char* a_string;
-#ifdef HAVE_WCHAR_T
- const wchar_t* a_wide_string;
-#endif
- void* a_pointer;
- signed char * a_count_schar_pointer;
- short * a_count_short_pointer;
- int * a_count_int_pointer;
- long int * a_count_longint_pointer;
-#ifdef HAVE_LONG_LONG
- long long int * a_count_longlongint_pointer;
-#endif
- } a;
-} argument;
-
-typedef struct
-{
- unsigned int count;
- argument *arg;
-} arguments;
-
-/* Fetch the arguments, putting them into a. */
-#ifdef STATIC
-STATIC
-#else
-extern
-#endif
-int printf_fetchargs(va_list args, arguments *a);
-
-#endif /* _PRINTF_ARGS_H */
+++ /dev/null
-/* Formatted output to strings.
- Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include "g-gnulib.h"
-
-/* Specification. */
-#include "printf-parse.h"
-
-/* Get size_t, NULL. */
-#include <stddef.h>
-
-/* Get intmax_t. */
-#if HAVE_STDINT_H_WITH_UINTMAX
-# include <stdint.h>
-#endif
-#if HAVE_INTTYPES_H_WITH_UINTMAX
-# include <inttypes.h>
-#endif
-
-/* malloc(), realloc(), free(). */
-#include <stdlib.h>
-
-#ifdef STATIC
-STATIC
-#endif
-int printf_parse(const char *format, char_directives *d, arguments *a)
-{
- const char *cp = format; /* pointer into format */
- int arg_posn = 0; /* number of regular arguments consumed */
- unsigned int d_allocated; /* allocated elements of d->dir */
- unsigned int a_allocated; /* allocated elements of a->arg */
- unsigned int max_width_length = 0;
- unsigned int max_precision_length = 0;
-
- d->count = 0;
- d_allocated = 1;
- d->dir = malloc(d_allocated * sizeof(char_directive));
- if (d->dir == NULL)
- /* Out of memory. */
- return -1;
-
- a->count = 0;
- a_allocated = 0;
- a->arg = NULL;
-
-#define REGISTER_ARG(_index_,_type_) \
- { \
- unsigned int n = (_index_); \
- if (n >= a_allocated) \
- { \
- argument *memory; \
- a_allocated = 2 * a_allocated; \
- if (a_allocated <= n) \
- a_allocated = n + 1; \
- memory = (a->arg \
- ? realloc (a->arg, a_allocated * sizeof (argument)) \
- : malloc (a_allocated * sizeof (argument))); \
- if (memory == NULL) \
- /* Out of memory. */ \
- goto error; \
- a->arg = memory; \
- } \
- while (a->count <= n) \
- a->arg[a->count++].type = TYPE_NONE; \
- if (a->arg[n].type == TYPE_NONE) \
- a->arg[n].type = (_type_); \
- else if (a->arg[n].type != (_type_)) \
- /* Ambiguous type for positional argument. */ \
- goto error; \
- }
-
- while (*cp != '\0')
- {
- char c = *cp++;
- if (c == '%')
- {
- int arg_index = -1;
- char_directive *dp = &d->dir[d->count];/* pointer to next directive */
-
- /* Initialize the next directive. */
- dp->dir_start = cp - 1;
- dp->flags = 0;
- dp->width_start = NULL;
- dp->width_end = NULL;
- dp->width_arg_index = -1;
- dp->precision_start = NULL;
- dp->precision_end = NULL;
- dp->precision_arg_index = -1;
- dp->arg_index = -1;
-
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- {
- unsigned int n = 0;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- n = 10 * n + (*np - '0');
- if (n == 0)
- /* Positional argument 0. */
- goto error;
- arg_index = n - 1;
- cp = np + 1;
- }
- }
-
- /* Read the flags. */
- for (;;)
- {
- if (*cp == '\'')
- {
- dp->flags |= FLAG_GROUP;
- cp++;
- }
- else if (*cp == '-')
- {
- dp->flags |= FLAG_LEFT;
- cp++;
- }
- else if (*cp == '+')
- {
- dp->flags |= FLAG_SHOWSIGN;
- cp++;
- }
- else if (*cp == ' ')
- {
- dp->flags |= FLAG_SPACE;
- cp++;
- }
- else if (*cp == '#')
- {
- dp->flags |= FLAG_ALT;
- cp++;
- }
- else if (*cp == '0')
- {
- dp->flags |= FLAG_ZERO;
- cp++;
- }
- else
- break;
- }
-
- /* Parse the field width. */
- if (*cp == '*')
- {
- dp->width_start = cp;
- cp++;
- dp->width_end = cp;
- if (max_width_length < 1)
- max_width_length = 1;
-
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- {
- unsigned int n = 0;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- n = 10 * n + (*np - '0');
- if (n == 0)
- /* Positional argument 0. */
- goto error;
- dp->width_arg_index = n - 1;
- cp = np + 1;
- }
- }
- if (dp->width_arg_index < 0)
- dp->width_arg_index = arg_posn++;
- REGISTER_ARG(dp->width_arg_index, TYPE_INT);
- }
- else if (*cp >= '0' && *cp <= '9')
- {
- unsigned int width_length;
-
- dp->width_start = cp;
- for (; *cp >= '0' && *cp <= '9'; cp++)
- ;
- dp->width_end = cp;
- width_length = dp->width_end - dp->width_start;
- if (max_width_length < width_length)
- max_width_length = width_length;
- }
-
- /* Parse the precision. */
- if (*cp == '.')
- {
- cp++;
- if (*cp == '*')
- {
- dp->precision_start = cp - 1;
- cp++;
- dp->precision_end = cp;
- if (max_precision_length < 2)
- max_precision_length = 2;
-
- /* Test for positional argument. */
- if (*cp >= '0' && *cp <= '9')
- {
- const char *np;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- ;
- if (*np == '$')
- {
- unsigned int n = 0;
-
- for (np = cp; *np >= '0' && *np <= '9'; np++)
- n = 10 * n + (*np - '0');
- if (n == 0)
- /* Positional argument 0. */
- goto error;
- dp->precision_arg_index = n - 1;
- cp = np + 1;
- }
- }
- if (dp->precision_arg_index < 0)
- dp->precision_arg_index = arg_posn++;
- REGISTER_ARG(dp->precision_arg_index, TYPE_INT);
- }
- else
- {
- unsigned int precision_length;
-
- dp->precision_start = cp - 1;
- for (; *cp >= '0' && *cp <= '9'; cp++)
- ;
- dp->precision_end = cp;
- precision_length = dp->precision_end - dp->precision_start;
- if (max_precision_length < precision_length)
- max_precision_length = precision_length;
- }
- }
-
- {
- arg_type type;
-
- /* Parse argument type/size specifiers. */
- {
- int flags = 0;
-
- for (;;)
- {
- if (*cp == 'h')
- {
- flags |= (1 << (flags & 1));
- cp++;
- }
- else if (*cp == 'L')
- {
- flags |= 4;
- cp++;
- }
- else if (*cp == 'l')
- {
- flags += 8;
- cp++;
- }
-#ifdef HAVE_INT64_AND_I64
- else if (cp[0] == 'I' &&
- cp[1] == '6' &&
- cp[2] == '4')
- {
- flags = 64;
- cp += 3;
- }
-#endif
-#ifdef HAVE_INTMAX_T
- else if (*cp == 'j')
- {
- if (sizeof(intmax_t) > sizeof(long))
- {
- /* intmax_t = long long */
- flags += 16;
- }
- else if (sizeof(intmax_t) > sizeof(int))
- {
- /* intmax_t = long */
- flags += 8;
- }
- cp++;
- }
-#endif
- else if (*cp == 'z' || *cp == 'Z')
- {
- /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
- because the warning facility in gcc-2.95.2 understands
- only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
- if (sizeof(size_t) > sizeof(long))
- {
- /* size_t = long long */
- flags += 16;
- }
- else if (sizeof(size_t) > sizeof(int))
- {
- /* size_t = long */
- flags += 8;
- }
- cp++;
- }
- else if (*cp == 't')
- {
- if (sizeof(ptrdiff_t) > sizeof(long))
- {
- /* ptrdiff_t = long long */
- flags += 16;
- }
- else if (sizeof(ptrdiff_t) > sizeof(int))
- {
- /* ptrdiff_t = long */
- flags += 8;
- }
- cp++;
- }
- else
- break;
- }
-
- /* Read the conversion character. */
- c = *cp++;
- switch (c)
- {
- case 'd':
- case 'i':
-#ifdef HAVE_INT64_AND_I64
- if (flags == 64)
- type = TYPE_INT64;
- else
-#endif
-#ifdef HAVE_LONG_LONG
- if (flags >= 16 || (flags & 4))
- type = TYPE_LONGLONGINT;
- else
-#endif
- if (flags >= 8)
- type = TYPE_LONGINT;
- else if (flags & 2)
- type = TYPE_SCHAR;
- else if (flags & 1)
- type = TYPE_SHORT;
- else
- type = TYPE_INT;
- break;
- case 'o':
- case 'u':
- case 'x':
- case 'X':
-#ifdef HAVE_INT64_AND_I64
- if (flags == 64)
- type = TYPE_UINT64;
- else
-#endif
-#ifdef HAVE_LONG_LONG
- if (flags >= 16 || (flags & 4))
- type = TYPE_ULONGLONGINT;
- else
-#endif
- if (flags >= 8)
- type = TYPE_ULONGINT;
- else if (flags & 2)
- type = TYPE_UCHAR;
- else if (flags & 1)
- type = TYPE_USHORT;
- else
- type = TYPE_UINT;
- break;
- case 'f':
- case 'F':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- case 'a':
- case 'A':
-#ifdef HAVE_LONG_DOUBLE
- if (flags >= 16 || (flags & 4))
- type = TYPE_LONGDOUBLE;
- else
-#endif
- type = TYPE_DOUBLE;
- break;
- case 'c':
- if (flags >= 8)
-#ifdef HAVE_WINT_T
- type = TYPE_WIDE_CHAR;
-#else
- goto error;
-#endif
- else
- type = TYPE_CHAR;
- break;
-#ifdef HAVE_WINT_T
- case 'C':
- type = TYPE_WIDE_CHAR;
- c = 'c';
- break;
-#endif
- case 's':
- if (flags >= 8)
-#ifdef HAVE_WCHAR_T
- type = TYPE_WIDE_STRING;
-#else
- goto error;
-#endif
- else
- type = TYPE_STRING;
- break;
-#ifdef HAVE_WCHAR_T
- case 'S':
- type = TYPE_WIDE_STRING;
- c = 's';
- break;
-#endif
- case 'p':
- type = TYPE_POINTER;
- break;
- case 'n':
-#ifdef HAVE_LONG_LONG
- if (flags >= 16 || (flags & 4))
- type = TYPE_COUNT_LONGLONGINT_POINTER;
- else
-#endif
- if (flags >= 8)
- type = TYPE_COUNT_LONGINT_POINTER;
- else if (flags & 2)
- type = TYPE_COUNT_SCHAR_POINTER;
- else if (flags & 1)
- type = TYPE_COUNT_SHORT_POINTER;
- else
- type = TYPE_COUNT_INT_POINTER;
- break;
- case '%':
- type = TYPE_NONE;
- break;
- default:
- /* Unknown conversion character. */
- goto error;
- }
- }
-
- if (type != TYPE_NONE)
- {
- dp->arg_index = arg_index;
- if (dp->arg_index < 0)
- dp->arg_index = arg_posn++;
- REGISTER_ARG(dp->arg_index, type);
- }
- dp->conversion = c;
- dp->dir_end = cp;
- }
-
- d->count++;
- if (d->count >= d_allocated)
- {
- char_directive *memory;
-
- d_allocated = 2 * d_allocated;
- memory = realloc(d->dir, d_allocated * sizeof(char_directive));
- if (memory == NULL)
- /* Out of memory. */
- goto error;
- d->dir = memory;
- }
- }
- }
- d->dir[d->count].dir_start = cp;
-
- d->max_width_length = max_width_length;
- d->max_precision_length = max_precision_length;
- return 0;
-
- error: if (a->arg)
- free(a->arg);
- if (d->dir)
- free(d->dir);
- return -1;
-}
+++ /dev/null
-/* Parse printf format string.
- Copyright (C) 1999, 2002 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifndef _PRINTF_PARSE_H
-#define _PRINTF_PARSE_H
-
-#include "printf-args.h"
-
-/* Private namespace for gnulib functions */
-#define printf_parse _g_gnulib_printf_parse
-
-/* Flags */
-#define FLAG_GROUP 1 /* ' flag */
-#define FLAG_LEFT 2 /* - flag */
-#define FLAG_SHOWSIGN 4 /* + flag */
-#define FLAG_SPACE 8 /* space flag */
-#define FLAG_ALT 16 /* # flag */
-#define FLAG_ZERO 32
-
-/* A parsed directive. */
-typedef struct
-{
- const char* dir_start;
- const char* dir_end;
- int flags;
- const char* width_start;
- const char* width_end;
- int width_arg_index;
- const char* precision_start;
- const char* precision_end;
- int precision_arg_index;
- char conversion; /* d i o u x X f e E g G c s p n U % but not C S */
- int arg_index;
-} char_directive;
-
-/* A parsed format string. */
-typedef struct
-{
- unsigned int count;
- char_directive *dir;
- unsigned int max_width_length;
- unsigned int max_precision_length;
-} char_directives;
-
-/* Parses the format string. Fills in the number N of directives, and fills
- in directives[0], ..., directives[N-1], and sets directives[N].dir_start
- to the end of the format string. Also fills in the arg_type fields of the
- arguments and the needed count of arguments. */
-#ifdef STATIC
-STATIC
-#else
-extern
-#endif
-int printf_parse(const char *format, char_directives *d, arguments *a);
-
-#endif /* _PRINTF_PARSE_H */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 2003 Matthias Clasen
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 2003. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "g-gnulib.h"
-#include "vasnprintf.h"
-#include "printf.h"
-
-int _g_gnulib_printf(char const *format, ...)
-{
- va_list args;
- int retval;
-
- va_start(args, format);
- retval = _g_gnulib_vprintf(format, args);
- va_end(args);
-
- return retval;
-}
-
-int _g_gnulib_fprintf(FILE *file, char const *format, ...)
-{
- va_list args;
- int retval;
-
- va_start(args, format);
- retval = _g_gnulib_vfprintf(file, format, args);
- va_end(args);
-
- return retval;
-}
-
-int _g_gnulib_sprintf(char *string, char const *format, ...)
-{
- va_list args;
- int retval;
-
- va_start(args, format);
- retval = _g_gnulib_vsprintf(string, format, args);
- va_end(args);
-
- return retval;
-}
-
-int _g_gnulib_snprintf(char *string, size_t n, char const *format, ...)
-{
- va_list args;
- int retval;
-
- va_start(args, format);
- retval = _g_gnulib_vsnprintf(string, n, format, args);
- va_end(args);
-
- return retval;
-}
-
-int _g_gnulib_vprintf(char const *format, va_list args)
-{
- return _g_gnulib_vfprintf(stdout, format, args);
-}
-
-int _g_gnulib_vfprintf(FILE *file, char const *format, va_list args)
-{
- char *result;
- size_t length;
-
- result = vasnprintf(NULL, &length, format, args);
- if (result == NULL)
- return -1;
-
- fwrite(result, 1, length, file);
- free(result);
-
- return length;
-}
-
-int _g_gnulib_vsprintf(char *string, char const *format, va_list args)
-{
- char *result;
- size_t length;
-
- result = vasnprintf(NULL, &length, format, args);
- if (result == NULL)
- return -1;
-
- memcpy(string, result, length + 1);
- free(result);
-
- return length;
-}
-
-int _g_gnulib_vsnprintf(char *string, size_t n, char const *format, va_list args)
-{
- char *result;
- size_t length;
-
- result = vasnprintf(NULL, &length, format, args);
- if (result == NULL)
- return -1;
-
- if (n > 0)
- {
- memcpy(string, result, MIN(length + 1, n));
- string[n - 1] = 0;
- }
-
- free(result);
-
- return length;
-}
-
-int _g_gnulib_vasprintf(char **result, char const *format, va_list args)
-{
- size_t length;
-
- *result = vasnprintf(NULL, &length, format, args);
- if (*result == NULL)
- return -1;
-
- return length;
-}
-
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 2003 Matthias Clasen
- *
- * 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 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 __GNULIB_PRINTF_H__
-#define __GNULIB_PRINTF_H__
-
-#include <stdarg.h>
-#include <stdio.h>
-
-int _g_gnulib_printf(char const *format, ...);
-int _g_gnulib_fprintf(FILE *file, char const *format, ...);
-int _g_gnulib_sprintf(char *string, char const *format, ...);
-int _g_gnulib_snprintf(char *string, size_t n, char const *format, ...);
-int _g_gnulib_vprintf(char const *format, va_list args);
-int _g_gnulib_vfprintf(FILE *file, char const *format, va_list args);
-int _g_gnulib_vsprintf(char *string, char const *format, va_list args);
-int _g_gnulib_vsnprintf(char *string, size_t n, char const *format, va_list args);
-int _g_gnulib_vasprintf(char **result, char const *format, va_list args);
-
-#endif /* __GNULIB_PRINTF_H__ */
-
+++ /dev/null
-/* vsprintf with automatic memory allocation.
- Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifndef _WIN32
-/* Tell glibc's <stdio.h> to provide a prototype for snprintf().
- This must come before <config.h> because <config.h> may include
- <features.h>, and once <features.h> has been included, it's too late. */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE 1
-#endif
-#endif
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-#include "glib/galloca.h"
-
-#include "g-gnulib.h"
-
-/* Specification. */
-#include "vasnprintf.h"
-
-#include <stdio.h> /* snprintf(), sprintf() */
-#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
-#include <string.h> /* memcpy(), strlen() */
-#include <errno.h> /* errno */
-#include <limits.h> /* CHAR_BIT */
-#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
-#include "printf-parse.h"
-
-#ifdef HAVE_WCHAR_T
-# ifdef HAVE_WCSLEN
-# define local_wcslen wcslen
-# else
-/* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
- a dependency towards this library, here is a local substitute.
- Define this substitute only once, even if this file is included
- twice in the same compilation unit. */
-# ifndef local_wcslen_defined
-# define local_wcslen_defined 1
-static size_t
-local_wcslen (const wchar_t *s)
-{
- const wchar_t *ptr;
-
- for (ptr = s; *ptr != (wchar_t) 0; ptr++)
- ;
- return ptr - s;
-}
-# endif
-# endif
-#endif
-
-/* For those losing systems which don't have 'alloca' we have to add
- some additional code emulating it. */
-#ifdef HAVE_ALLOCA
-# define freea(p) /* nothing */
-#else
-# define alloca(n) malloc (n)
-# define freea(p) free (p)
-#endif
-
-#ifndef HAVE_LONG_LONG_FORMAT
-static int print_long_long(char *buf, int len, int width, int precision, unsigned long flags,
- char conversion, unsigned long long number)
-{
- int negative = FALSE;
- char buffer[128];
- char *bufferend;
- char *pointer;
- int base;
- static const char *upper = "0123456789ABCDEFX";
- static const char *lower = "0123456789abcdefx";
- const char *digits;
- int i;
- char *p;
- int count;
-
-#define EMIT(c) \
- if (p - buf == len - 1) \
- { \
- *p++ = '\0'; \
- return len; \
- } \
- else \
- *p++ = c;
-
- p = buf;
-
- switch (conversion)
- {
- case 'o':
- base = 8;
- digits = lower;
- negative = FALSE;
- break;
- case 'x':
- base = 16;
- digits = lower;
- negative = FALSE;
- break;
- case 'X':
- base = 16;
- digits = upper;
- negative = FALSE;
- break;
- default:
- base = 10;
- digits = lower;
- negative = (long long) number < 0;
- if (negative)
- number = -((long long) number);
- break;
- }
-
- /* Build number */
- pointer = bufferend = &buffer[sizeof(buffer) - 1];
- *pointer-- = '\0';
- for (i = 1; i < (int) sizeof(buffer); i++)
- {
- *pointer-- = digits[number % base];
- number /= base;
- if (number == 0)
- break;
- }
-
- /* Adjust width */
- width -= (bufferend - pointer) - 1;
-
- /* Adjust precision */
- if (precision != -1)
- {
- precision -= (bufferend - pointer) - 1;
- if (precision < 0)
- precision = 0;
- flags |= FLAG_ZERO;
- }
-
- /* Adjust width further */
- if (negative || (flags & FLAG_SHOWSIGN) || (flags & FLAG_SPACE))
- width--;
- if (flags & FLAG_ALT)
- {
- switch (base)
- {
- case 16:
- width -= 2;
- break;
- case 8:
- width--;
- break;
- default:
- break;
- }
- }
-
- /* Output prefixes spaces if needed */
- if (!((flags & FLAG_LEFT) || ((flags & FLAG_ZERO) && (precision == -1))))
- {
- count = (precision == -1) ? 0 : precision;
- while (width-- > count)
- *p++ = ' ';
- }
-
- /* width has been adjusted for signs and alternatives */
- if (negative)
- {
- EMIT('-');
- }
- else if (flags & FLAG_SHOWSIGN)
- {
- EMIT('+');
- }
- else if (flags & FLAG_SPACE)
- {
- EMIT(' ');
- }
-
- if (flags & FLAG_ALT)
- {
- switch (base)
- {
- case 8:
- EMIT('0');
- break;
- case 16:
- EMIT('0');
- EMIT(digits[16]);
- break;
- default:
- break;
- } /* switch base */
- }
-
- /* Output prefixed zero padding if needed */
- if (flags & FLAG_ZERO)
- {
- if (precision == -1)
- precision = width;
- while (precision-- > 0)
- {
- EMIT('0');
- width--;
- }
- }
-
- /* Output the number itself */
- while (*(++pointer))
- {
- EMIT(*pointer);
- }
-
- /* Output trailing spaces if needed */
- if (flags & FLAG_LEFT)
- {
- while (width-- > 0)
- EMIT(' ');
- }
-
- EMIT('\0');
-
- return p - buf - 1;
-}
-#endif
-
-char *
-vasnprintf(char *resultbuf, size_t *lengthp, const char *format, va_list args)
-{
- char_directives d;
- arguments a;
-
- if (printf_parse(format, &d, &a) < 0)
- {
- errno = EINVAL;
- return NULL;
- }
-
-#define CLEANUP() \
- free (d.dir); \
- if (a.arg) \
- free (a.arg);
-
- if (printf_fetchargs(args, &a) < 0)
- {
- CLEANUP ();
- errno = EINVAL;
- return NULL;
- }
-
- {
- char *buf = (char *) alloca(7 + d.max_width_length + d.max_precision_length + 6);
- const char *cp;
- unsigned int i;
- char_directive *dp;
- /* Output string accumulator. */
- char *result;
- size_t allocated;
- size_t length;
-
- if (resultbuf != NULL)
- {
- result = resultbuf;
- allocated = *lengthp;
- }
- else
- {
- result = NULL;
- allocated = 0;
- }
- length = 0;
- /* Invariants:
- result is either == resultbuf or == NULL or malloc-allocated.
- If length > 0, then result != NULL. */
-
-#define ENSURE_ALLOCATION(needed) \
- if ((needed) > allocated) \
- { \
- char *memory; \
- \
- allocated = (allocated > 0 ? 2 * allocated : 12); \
- if ((needed) > allocated) \
- allocated = (needed); \
- if (result == resultbuf || result == NULL) \
- memory = (char *) malloc (allocated); \
- else \
- memory = (char *) realloc (result, allocated); \
- \
- if (memory == NULL) \
- { \
- if (!(result == resultbuf || result == NULL)) \
- free (result); \
- freea (buf); \
- CLEANUP (); \
- errno = ENOMEM; \
- return NULL; \
- } \
- if (result == resultbuf && length > 0) \
- memcpy (memory, result, length); \
- result = memory; \
- }
-
- for (cp = format, i = 0, dp = &d.dir[0];; cp = dp->dir_end, i++, dp++)
- {
- if (cp != dp->dir_start)
- {
- size_t n = dp->dir_start - cp;
-
- ENSURE_ALLOCATION(length + n);
- memcpy(result + length, cp, n);
- length += n;
- }
- if (i == d.count)
- break;
-
- /* Execute a single directive. */
- if (dp->conversion == '%')
- {
- if (!(dp->arg_index < 0))
- abort();
- ENSURE_ALLOCATION(length + 1);
- result[length] = '%';
- length += 1;
- }
- else
- {
- if (!(dp->arg_index >= 0))
- abort();
-
- if (dp->conversion == 'n')
- {
- switch (a.arg[dp->arg_index].type)
- {
- case TYPE_COUNT_SCHAR_POINTER:
- *a.arg[dp->arg_index].a.a_count_schar_pointer = length;
- break;
- case TYPE_COUNT_SHORT_POINTER:
- *a.arg[dp->arg_index].a.a_count_short_pointer = length;
- break;
- case TYPE_COUNT_INT_POINTER:
- *a.arg[dp->arg_index].a.a_count_int_pointer = length;
- break;
- case TYPE_COUNT_LONGINT_POINTER:
- *a.arg[dp->arg_index].a.a_count_longint_pointer = length;
- break;
-#ifdef HAVE_LONG_LONG
- case TYPE_COUNT_LONGLONGINT_POINTER:
- *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
- break;
-#endif
- default:
- abort();
- }
- }
- else
- {
- arg_type type = a.arg[dp->arg_index].type;
- char *p;
- unsigned int prefix_count;
- int prefixes[2];
-#if !HAVE_SNPRINTF
- unsigned int tmp_length;
- char tmpbuf[700];
- char *tmp;
-
- /* Allocate a temporary buffer of sufficient size for calling
- sprintf. */
- {
- unsigned int width;
- unsigned int precision;
-
- width = 0;
- if (dp->width_start != dp->width_end)
- {
- if (dp->width_arg_index >= 0)
- {
- int arg;
-
- if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
- abort();
- arg = a.arg[dp->width_arg_index].a.a_int;
- width = (arg < 0 ? -arg : arg);
- }
- else
- {
- const char *digitp = dp->width_start;
-
- do
- width = width * 10 + (*digitp++ - '0');
- while (digitp != dp->width_end);
- }
- }
-
- precision = 6;
- if (dp->precision_start != dp->precision_end)
- {
- if (dp->precision_arg_index >= 0)
- {
- int arg;
-
- if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
- abort();
- arg = a.arg[dp->precision_arg_index].a.a_int;
- precision = (arg < 0 ? 0 : arg);
- }
- else
- {
- const char *digitp = dp->precision_start + 1;
-
- precision = 0;
- while (digitp != dp->precision_end)
- precision = precision * 10 + (*digitp++ - '0');
- }
- }
-
- switch (dp->conversion)
- {
- case 'd':
- case 'i':
- case 'u':
-# ifdef HAVE_LONG_LONG
- if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long long)
- * CHAR_BIT * 0.30103 /* binary -> decimal */
- * 2 /* estimate for FLAG_GROUP */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- else
-# endif
- if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long) * CHAR_BIT
- * 0.30103 /* binary -> decimal */
- * 2 /* estimate for FLAG_GROUP */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- else
- tmp_length = (unsigned int) (sizeof(unsigned int) * CHAR_BIT
- * 0.30103 /* binary -> decimal */
- * 2 /* estimate for FLAG_GROUP */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- break;
-
- case 'o':
-# ifdef HAVE_LONG_LONG
- if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long long)
- * CHAR_BIT * 0.333334 /* binary -> octal */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- else
-# endif
- if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long) * CHAR_BIT
- * 0.333334 /* binary -> octal */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- else
- tmp_length = (unsigned int) (sizeof(unsigned int) * CHAR_BIT
- * 0.333334 /* binary -> octal */
- ) + 1 /* turn floor into ceil */
- + 1; /* account for leading sign */
- break;
-
- case 'x':
- case 'X':
-# ifdef HAVE_LONG_LONG
- if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long long)
- * CHAR_BIT * 0.25 /* binary -> hexadecimal */
- ) + 1 /* turn floor into ceil */
- + 2; /* account for leading sign or alternate form */
- else
-# endif
-# ifdef HAVE_INT64_AND_I64
- if (type == TYPE_INT64 || type == TYPE_UINT64)
- tmp_length =
- (unsigned int) (sizeof (unsigned __int64) * CHAR_BIT
- * 0.25 /* binary -> hexadecimal */
- )
- + 1 /* turn floor into ceil */
- + 2; /* account for leading sign or alternate form */
- else
-# endif
- if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
- tmp_length = (unsigned int) (sizeof(unsigned long) * CHAR_BIT
- * 0.25 /* binary -> hexadecimal */
- ) + 1 /* turn floor into ceil */
- + 2; /* account for leading sign or alternate form */
- else
- tmp_length = (unsigned int) (sizeof(unsigned int) * CHAR_BIT
- * 0.25 /* binary -> hexadecimal */
- ) + 1 /* turn floor into ceil */
- + 2; /* account for leading sign or alternate form */
- break;
-
- case 'f':
- case 'F':
-# ifdef HAVE_LONG_DOUBLE
- if (type == TYPE_LONGDOUBLE)
- tmp_length = (unsigned int) (LDBL_MAX_EXP * 0.30103 /* binary -> decimal */
- * 2 /* estimate for FLAG_GROUP */
- ) + 1 /* turn floor into ceil */
- + precision + 10; /* sign, decimal point etc. */
- else
-# endif
- tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */
- * 2 /* estimate for FLAG_GROUP */
- ) + 1 /* turn floor into ceil */
- + precision + 10; /* sign, decimal point etc. */
- break;
-
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- case 'a':
- case 'A':
- tmp_length = precision + 12; /* sign, decimal point, exponent etc. */
- break;
-
- case 'c':
-# ifdef HAVE_WINT_T
- if (type == TYPE_WIDE_CHAR)
- tmp_length = MB_CUR_MAX;
- else
-# endif
- tmp_length = 1;
- break;
-
- case 's':
-# ifdef HAVE_WCHAR_T
- if (type == TYPE_WIDE_STRING)
- tmp_length =
- (a.arg[dp->arg_index].a.a_wide_string == NULL
- ? 6 /* wcslen(L"(null)") */
- : local_wcslen (a.arg[dp->arg_index].a.a_wide_string))
- * MB_CUR_MAX;
- else
-# endif
- tmp_length =
- a.arg[dp->arg_index].a.a_string == NULL ?
- 6 /* strlen("(null)") */
- :
- strlen(a.arg[dp->arg_index].a.a_string);
- break;
-
- case 'p':
- tmp_length = (unsigned int) (sizeof(void *) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
- ) + 1 /* turn floor into ceil */
- + 2; /* account for leading 0x */
- break;
-
- default:
- abort();
- }
-
- if (tmp_length < width)
- tmp_length = width;
-
- tmp_length++; /* account for trailing NUL */
- }
-
- if (tmp_length <= sizeof(tmpbuf))
- tmp = tmpbuf;
- else
- {
- tmp = (char *) malloc(tmp_length);
- if (tmp == NULL)
- {
- /* Out of memory. */
- if (!(result == resultbuf || result == NULL))
- free(result);
- freea (buf);
- CLEANUP ();
- errno = ENOMEM;
- return NULL;
- }
- }
-#endif
-
- /* Construct the format string for calling snprintf or
- sprintf. */
- p = buf;
- *p++ = '%';
- if (dp->flags & FLAG_GROUP)
- *p++ = '\'';
- if (dp->flags & FLAG_LEFT)
- *p++ = '-';
- if (dp->flags & FLAG_SHOWSIGN)
- *p++ = '+';
- if (dp->flags & FLAG_SPACE)
- *p++ = ' ';
- if (dp->flags & FLAG_ALT)
- *p++ = '#';
- if (dp->flags & FLAG_ZERO)
- *p++ = '0';
- if (dp->width_start != dp->width_end)
- {
- size_t n = dp->width_end - dp->width_start;
- memcpy(p, dp->width_start, n);
- p += n;
- }
- if (dp->precision_start != dp->precision_end)
- {
- size_t n = dp->precision_end - dp->precision_start;
- memcpy(p, dp->precision_start, n);
- p += n;
- }
-
- switch (type)
- {
-#ifdef HAVE_INT64_AND_I64
- case TYPE_INT64:
- case TYPE_UINT64:
- *p++ = 'I';
- *p++ = '6';
- *p++ = '4';
- break;
-#endif
-#ifdef HAVE_LONG_LONG
- case TYPE_LONGLONGINT:
- case TYPE_ULONGLONGINT:
-#ifdef HAVE_INT64_AND_I64 /* The system (sn)printf uses %I64. Also assume
- * that long long == __int64.
- */
- *p++ = 'I';
- *p++ = '6';
- *p++ = '4';
- break;
-#else
- *p++ = 'l';
- /*FALLTHROUGH*/
-#endif
-#endif
- case TYPE_LONGINT:
- case TYPE_ULONGINT:
-#ifdef HAVE_WINT_T
- case TYPE_WIDE_CHAR:
-#endif
-#ifdef HAVE_WCHAR_T
- case TYPE_WIDE_STRING:
-#endif
- *p++ = 'l';
- break;
-#ifdef HAVE_LONG_DOUBLE
- case TYPE_LONGDOUBLE:
- *p++ = 'L';
- break;
-#endif
- default:
- break;
- }
- *p = dp->conversion;
-#if HAVE_SNPRINTF
- p[1] = '%';
- p[2] = 'n';
- p[3] = '\0';
-#else
- p[1] = '\0';
-#endif
-
- /* Construct the arguments for calling snprintf or sprintf. */
- prefix_count = 0;
- if (dp->width_arg_index >= 0)
- {
- if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
- abort();
- prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int;
- }
- if (dp->precision_arg_index >= 0)
- {
- if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
- abort();
- prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int;
- }
-
-#if HAVE_SNPRINTF
- /* Prepare checking whether snprintf returns the count
- via %n. */
- ENSURE_ALLOCATION (length + 1);
- result[length] = '\0';
-#endif
-
- for (;;)
- {
- size_t maxlen;
- int count;
- int retcount;
-
- maxlen = allocated - length;
- count = -1;
- retcount = 0;
-
-#if HAVE_SNPRINTF
-#define SNPRINTF_BUF(arg) \
- switch (prefix_count) \
- { \
- case 0: \
- retcount = snprintf (result + length, maxlen, buf, \
- arg, &count); \
- break; \
- case 1: \
- retcount = snprintf (result + length, maxlen, buf, \
- prefixes[0], arg, &count); \
- break; \
- case 2: \
- retcount = snprintf (result + length, maxlen, buf, \
- prefixes[0], prefixes[1], arg, \
- &count); \
- break; \
- default: \
- abort (); \
- }
-#else
-#define SNPRINTF_BUF(arg) \
- switch (prefix_count) \
- { \
- case 0: \
- count = sprintf (tmp, buf, arg); \
- break; \
- case 1: \
- count = sprintf (tmp, buf, prefixes[0], arg); \
- break; \
- case 2: \
- count = sprintf (tmp, buf, prefixes[0], prefixes[1],\
- arg); \
- break; \
- default: \
- abort (); \
- }
-#endif
-
- switch (type)
- {
- case TYPE_SCHAR:
- {
- int arg = a.arg[dp->arg_index].a.a_schar;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_UCHAR:
- {
- unsigned int arg = a.arg[dp->arg_index].a.a_uchar;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_SHORT:
- {
- int arg = a.arg[dp->arg_index].a.a_short;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_USHORT:
- {
- unsigned int arg = a.arg[dp->arg_index].a.a_ushort;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_INT:
- {
- int arg = a.arg[dp->arg_index].a.a_int;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_UINT:
- {
- unsigned int arg = a.arg[dp->arg_index].a.a_uint;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_LONGINT:
- {
- long int arg = a.arg[dp->arg_index].a.a_longint;
- SNPRINTF_BUF(arg);
- }
- break;
- case TYPE_ULONGINT:
- {
- unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint;
- SNPRINTF_BUF(arg);
- }
- break;
-#ifdef HAVE_INT64_AND_I64
- case TYPE_INT64:
- {
- __int64 arg = a.arg[dp->arg_index].a.a_int64;
- SNPRINTF_BUF (arg);
- }
- break;
- case TYPE_UINT64:
- {
- unsigned __int64 arg = a.arg[dp->arg_index].a.a_uint64;
- SNPRINTF_BUF (arg);
- }
- break;
-#endif
-#ifdef HAVE_LONG_LONG
-#ifndef HAVE_LONG_LONG_FORMAT
- case TYPE_LONGLONGINT:
- case TYPE_ULONGLONGINT:
- {
- unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint;
- int width;
- int precision;
-
- width = 0;
- if (dp->width_start != dp->width_end)
- {
- if (dp->width_arg_index >= 0)
- {
- int arg;
-
- if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
- abort();
- arg = a.arg[dp->width_arg_index].a.a_int;
- width = (arg < 0 ? -arg : arg);
- }
- else
- {
- const char *digitp = dp->width_start;
-
- do
- width = width * 10 + (*digitp++ - '0');
- while (digitp != dp->width_end);
- }
- }
-
- precision = -1;
- if (dp->precision_start != dp->precision_end)
- {
- if (dp->precision_arg_index >= 0)
- {
- int arg;
-
- if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
- abort();
- arg = a.arg[dp->precision_arg_index].a.a_int;
- precision = (arg < 0 ? 0 : arg);
- }
- else
- {
- const char *digitp = dp->precision_start + 1;
-
- precision = 0;
- do
- precision = precision * 10 + (*digitp++ - '0');
- while (digitp != dp->precision_end);
- }
- }
-
-#if HAVE_SNPRINTF
- count = print_long_long (result + length, maxlen,
- width, precision,
- dp->flags,
- dp->conversion,
- arg);
-#else
- count = print_long_long(tmp, tmp_length, width, precision,
- dp->flags, dp->conversion, arg);
-#endif
- }
- break;
-#else
- case TYPE_LONGLONGINT:
- {
- long long int arg = a.arg[dp->arg_index].a.a_longlongint;
- SNPRINTF_BUF (arg);
- }
- break;
- case TYPE_ULONGLONGINT:
- {
- unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint;
- SNPRINTF_BUF (arg);
- }
- break;
-#endif
-#endif
- case TYPE_DOUBLE:
- {
- double arg = a.arg[dp->arg_index].a.a_double;
- SNPRINTF_BUF(arg);
- }
- break;
-#ifdef HAVE_LONG_DOUBLE
- case TYPE_LONGDOUBLE:
- {
- long double arg = a.arg[dp->arg_index].a.a_longdouble;
- SNPRINTF_BUF(arg);
- }
- break;
-#endif
- case TYPE_CHAR:
- {
- int arg = a.arg[dp->arg_index].a.a_char;
- SNPRINTF_BUF(arg);
- }
- break;
-#ifdef HAVE_WINT_T
- case TYPE_WIDE_CHAR:
- {
- wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
- SNPRINTF_BUF (arg);
- }
- break;
-#endif
- case TYPE_STRING:
- {
- const char *arg =
- a.arg[dp->arg_index].a.a_string == NULL ?
- "(null)" : a.arg[dp->arg_index].a.a_string;
- SNPRINTF_BUF(arg);
- }
- break;
-#ifdef HAVE_WCHAR_T
- case TYPE_WIDE_STRING:
- {
- const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string == NULL
- ? L"(null)"
- : a.arg[dp->arg_index].a.a_wide_string;
- SNPRINTF_BUF (arg);
- }
- break;
-#endif
- case TYPE_POINTER:
- {
- void *arg = a.arg[dp->arg_index].a.a_pointer;
- SNPRINTF_BUF(arg);
- }
- break;
- default:
- abort();
- }
-
-#if HAVE_SNPRINTF
- /* Portability: Not all implementations of snprintf()
- are ISO C 99 compliant. Determine the number of
- bytes that snprintf() has produced or would have
- produced. */
- if (count >= 0)
- {
- /* Verify that snprintf() has NUL-terminated its
- result. */
- if (count < maxlen && result[length + count] != '\0')
- abort ();
- /* Portability hack. */
- if (retcount > count)
- count = retcount;
- }
- else
- {
- /* snprintf() doesn't understand the '%n'
- directive. */
- if (p[1] != '\0')
- {
- /* Don't use the '%n' directive; instead, look
- at the snprintf() return value. */
- p[1] = '\0';
- continue;
- }
- count = retcount;
- }
-#endif
-
- /* Attempt to handle failure. */
- if (count < 0)
- {
- if (!(result == resultbuf || result == NULL))
- free(result);
- freea (buf);
- CLEANUP ();
- errno = EINVAL;
- return NULL;
- }
-
-#if !HAVE_SNPRINTF
- if (count >= tmp_length)
- /* tmp_length was incorrectly calculated - fix the
- code above! */
- abort();
-#endif
-
- /* Make room for the result. */
- if (count >= maxlen)
- {
- /* Need at least count bytes. But allocate
- proportionally, to avoid looping eternally if
- snprintf() reports a too small count. */
- size_t n = length + count;
-
- if (n < 2 * allocated)
- n = 2 * allocated;
-
- ENSURE_ALLOCATION(n);
-#if HAVE_SNPRINTF
- continue;
-#endif
- }
-
-#if HAVE_SNPRINTF
- /* The snprintf() result did fit. */
-#else
- /* Append the sprintf() result. */
- memcpy(result + length, tmp, count);
- if (tmp != tmpbuf)
- free(tmp);
-#endif
-
- length += count;
- break;
- }
- }
- }
- }
-
- /* Add the final NUL. */
- ENSURE_ALLOCATION(length + 1);
- result[length] = '\0';
-
- if (result != resultbuf && length + 1 < allocated)
- {
- /* Shrink the allocated memory if possible. */
- char *memory;
-
- memory = (char *) realloc(result, length + 1);
- if (memory != NULL)
- result = memory;
- }
-
- freea (buf);
- CLEANUP ();
- *lengthp = length;
- return result;
- }
-}
+++ /dev/null
-/* vsprintf with automatic memory allocation.
- Copyright (C) 2002-2003 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-#ifndef _VASNPRINTF_H
-#define _VASNPRINTF_H
-
-/* Get va_list. */
-#include <stdarg.h>
-
-/* Get size_t. */
-#include <stddef.h>
-
-#ifndef __attribute__
-/* This feature is available in gcc versions 2.5 and later. */
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
-# define __attribute__(Spec) /* empty */
-# endif
-/* The __-protected variants of `format' and `printf' attributes
- are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
-# define __format__ format
-# define __printf__ printf
-# endif
-#endif
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
- /* Write formatted output to a string dynamically allocated with malloc().
- You can pass a preallocated buffer for the result in RESULTBUF and its
- size in *LENGTHP; otherwise you pass RESULTBUF = NULL.
- If successful, return the address of the string (this may be = RESULTBUF
- if no dynamic memory allocation was necessary) and set *LENGTHP to the
- number of resulting bytes, excluding the trailing NUL. Upon error, set
- errno and return NULL. */
- extern char * asnprintf(char *resultbuf, size_t *lengthp, const char *format, ...)
- __attribute__ ((__format__ (__printf__, 3, 4)));
- extern char * vasnprintf(char *resultbuf, size_t *lengthp, const char *format, va_list args)
- __attribute__ ((__format__ (__printf__, 3, 0)));
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _VASNPRINTF_H */
+++ /dev/null
-/* goption.c - Option parser
- *
- * Copyright (C) 1999, 2003 Red Hat Software
- * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library 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.
- */
-
-/**
- * SECTION:option
- * @Short_description: parses commandline options
- * @Title: Commandline option parser
- *
- * The GOption commandline parser is intended to be a simpler replacement
- * for the popt library. It supports short and long commandline options,
- * as shown in the following example:
- *
- * <literal>testtreemodel -r 1 --max-size 20 --rand --display=:1.0 -vb -- file1 file2</literal>
- *
- * The example demonstrates a number of features of the GOption
- * commandline parser
- * <itemizedlist><listitem><para>
- * Options can be single letters, prefixed by a single dash. Multiple
- * short options can be grouped behind a single dash.
- * </para></listitem><listitem><para>
- * Long options are prefixed by two consecutive dashes.
- * </para></listitem><listitem><para>
- * Options can have an extra argument, which can be a number, a string or
- * a filename. For long options, the extra argument can be appended with
- * an equals sign after the option name, which is useful if the extra
- * argument starts with a dash, which would otherwise cause it to be
- * interpreted as another option.
- * </para></listitem><listitem><para>
- * Non-option arguments are returned to the application as rest arguments.
- * </para></listitem><listitem><para>
- * An argument consisting solely of two dashes turns off further parsing,
- * any remaining arguments (even those starting with a dash) are returned
- * to the application as rest arguments.
- * </para></listitem></itemizedlist>
- *
- * Another important feature of GOption is that it can automatically
- * generate nicely formatted help output. Unless it is explicitly turned
- * off with g_option_context_set_help_enabled(), GOption will recognize
- * the <option>--help</option>, <option>-?</option>,
- * <option>--help-all</option> and
- * <option>--help-</option><replaceable>groupname</replaceable> options
- * (where <replaceable>groupname</replaceable> is the name of a
- * #GOptionGroup) and write a text similar to the one shown in the
- * following example to stdout.
- *
- * <informalexample><screen>
- * Usage:
- * testtreemodel [OPTION...] - test tree model performance
- *
- * Help Options:
- * -h, --help Show help options
- * --help-all Show all help options
- * --help-gtk Show GTK+ Options
- *
- * Application Options:
- * -r, --repeats=N Average over N repetitions
- * -m, --max-size=M Test up to 2^M items
- * --display=DISPLAY X display to use
- * -v, --verbose Be verbose
- * -b, --beep Beep when done
- * --rand Randomize the data
- * </screen></informalexample>
- *
- * GOption groups options in #GOptionGroup<!-- -->s, which makes it easy to
- * incorporate options from multiple sources. The intended use for this is
- * to let applications collect option groups from the libraries it uses,
- * add them to their #GOptionContext, and parse all options by a single call
- * to g_option_context_parse(). See gtk_get_option_group() for an example.
- *
- * If an option is declared to be of type string or filename, GOption takes
- * care of converting it to the right encoding; strings are returned in
- * UTF-8, filenames are returned in the GLib filename encoding. Note that
- * this only works if setlocale() has been called before
- * g_option_context_parse().
- *
- * Here is a complete example of setting up GOption to parse the example
- * commandline above and produce the example help output.
- *
- * <informalexample><programlisting>
- * static gint repeats = 2;
- * static gint max_size = 8;
- * static gboolean verbose = FALSE;
- * static gboolean beep = FALSE;
- * static gboolean rand = FALSE;
- *
- * static GOptionEntry entries[] =
- * {
- * { "repeats", 'r', 0, G_OPTION_ARG_INT, &repeats, "Average over N repetitions", "N" },
- * { "max-size", 'm', 0, G_OPTION_ARG_INT, &max_size, "Test up to 2^M items", "M" },
- * { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL },
- * { "beep", 'b', 0, G_OPTION_ARG_NONE, &beep, "Beep when done", NULL },
- * { "rand", 0, 0, G_OPTION_ARG_NONE, &rand, "Randomize the data", NULL },
- * { NULL }
- * };
- *
- * int
- * main (int argc, char *argv[])
- * {
- * GError *error = NULL;
- * GOptionContext *context;
- *
- * context = g_option_context_new ("- test tree model performance");
- * g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
- * g_option_context_add_group (context, gtk_get_option_group (TRUE));
- * if (!g_option_context_parse (context, &argc, &argv, &error))
- * {
- * g_print ("option parsing failed: %s\n", error->message);
- * exit (1);
- * }
- *
- * /* ... */
- *
- * }
- * </programlisting></informalexample>
- */
-
-#include "config.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include "goption.h"
-
-#include "gprintf.h"
-#include "glibintl.h"
-
-#define TRANSLATE(group, str) (((group)->translate_func ? (* (group)->translate_func) ((str), (group)->translate_data) : (str)))
-
-#define NO_ARG(entry) ((entry)->arg == G_OPTION_ARG_NONE || \
- ((entry)->arg == G_OPTION_ARG_CALLBACK && \
- ((entry)->flags & G_OPTION_FLAG_NO_ARG)))
-
-#define OPTIONAL_ARG(entry) ((entry)->arg == G_OPTION_ARG_CALLBACK && \
- (entry)->flags & G_OPTION_FLAG_OPTIONAL_ARG)
-
-typedef struct
-{
- GOptionArg arg_type;
- gpointer arg_data;
- union
- {
- gboolean bool;
- gint integer;
- gchar *str;
- gchar **array;
- gdouble dbl;
- gint64 int64;
- } prev;
- union
- {
- gchar *str;
- struct
- {
- gint len;
- gchar **data;
- } array;
- } allocated;
-} Change;
-
-typedef struct
-{
- gchar **ptr;
- gchar *value;
-} PendingNull;
-
-struct _GOptionContext
-{
- GList *groups;
-
- gchar *parameter_string;
- gchar *summary;
- gchar *description;
-
- GTranslateFunc translate_func;
- GDestroyNotify translate_notify;
- gpointer translate_data;
-
- guint help_enabled : 1;
- guint ignore_unknown : 1;
-
- GOptionGroup *main_group;
-
- /* We keep a list of change so we can revert them */
- GList *changes;
-
- /* We also keep track of all argv elements
- * that should be NULLed or modified.
- */
- GList *pending_nulls;
-};
-
-struct _GOptionGroup
-{
- gchar *name;
- gchar *description;
- gchar *help_description;
-
- GDestroyNotify destroy_notify;
- gpointer user_data;
-
- GTranslateFunc translate_func;
- GDestroyNotify translate_notify;
- gpointer translate_data;
-
- GOptionEntry *entries;
- gint n_entries;
-
- GOptionParseFunc pre_parse_func;
- GOptionParseFunc post_parse_func;
- GOptionErrorFunc error_func;
-};
-
-static void free_changes_list (GOptionContext *context,
- gboolean revert);
-static void free_pending_nulls (GOptionContext *context,
- gboolean perform_nulls);
-
-
-static int
-_g_unichar_get_width (gunichar c)
-{
- if (G_UNLIKELY (g_unichar_iszerowidth (c)))
- return 0;
-
- /* we ignore the fact that we should call g_unichar_iswide_cjk() under
- * some locales (legacy East Asian ones) */
- if (g_unichar_iswide (c))
- return 2;
-
- return 1;
-}
-
-static glong
-_g_utf8_strwidth (const gchar *p,
- gssize max)
-{
- glong len = 0;
- const gchar *start = p;
- g_return_val_if_fail (p != NULL || max == 0, 0);
-
- if (max < 0)
- {
- while (*p)
- {
- len += _g_unichar_get_width (g_utf8_get_char (p));
- p = g_utf8_next_char (p);
- }
- }
- else
- {
- if (max == 0 || !*p)
- return 0;
-
- /* this case may not be quite correct */
-
- len += _g_unichar_get_width (g_utf8_get_char (p));
- p = g_utf8_next_char (p);
-
- while (p - start < max && *p)
- {
- len += _g_unichar_get_width (g_utf8_get_char (p));
- p = g_utf8_next_char (p);
- }
- }
-
- return len;
-}
-
-
-GQuark
-g_option_error_quark (void)
-{
- return g_quark_from_static_string ("g-option-context-error-quark");
-}
-
-/**
- * g_option_context_new:
- * @parameter_string: a string which is displayed in
- * the first line of <option>--help</option> output, after the
- * usage summary
- * <literal><replaceable>programname</replaceable> [OPTION...]</literal>
- *
- * Creates a new option context.
- *
- * The @parameter_string can serve multiple purposes. It can be used
- * to add descriptions for "rest" arguments, which are not parsed by
- * the #GOptionContext, typically something like "FILES" or
- * "FILE1 FILE2...". If you are using #G_OPTION_REMAINING for
- * collecting "rest" arguments, GLib handles this automatically by
- * using the @arg_description of the corresponding #GOptionEntry in
- * the usage summary.
- *
- * Another usage is to give a short summary of the program
- * functionality, like " - frob the strings", which will be displayed
- * in the same line as the usage. For a longer description of the
- * program functionality that should be displayed as a paragraph
- * below the usage line, use g_option_context_set_summary().
- *
- * Note that the @parameter_string is translated using the
- * function set with g_option_context_set_translate_func(), so
- * it should normally be passed untranslated.
- *
- * Returns: a newly created #GOptionContext, which must be
- * freed with g_option_context_free() after use.
- *
- * Since: 2.6
- */
-GOptionContext *
-g_option_context_new (const gchar *parameter_string)
-
-{
- GOptionContext *context;
-
- context = g_new0 (GOptionContext, 1);
-
- context->parameter_string = g_strdup (parameter_string);
- context->help_enabled = TRUE;
- context->ignore_unknown = FALSE;
-
- return context;
-}
-
-/**
- * g_option_context_free:
- * @context: a #GOptionContext
- *
- * Frees context and all the groups which have been
- * added to it.
- *
- * Please note that parsed arguments need to be freed separately (see
- * #GOptionEntry).
- *
- * Since: 2.6
- */
-void g_option_context_free (GOptionContext *context)
-{
- g_return_if_fail (context != NULL);
-
- g_list_foreach (context->groups, (GFunc)g_option_group_free, NULL);
- g_list_free (context->groups);
-
- if (context->main_group)
- g_option_group_free (context->main_group);
-
- free_changes_list (context, FALSE);
- free_pending_nulls (context, FALSE);
-
- g_free (context->parameter_string);
- g_free (context->summary);
- g_free (context->description);
-
- if (context->translate_notify)
- (* context->translate_notify) (context->translate_data);
-
- g_free (context);
-}
-
-
-/**
- * g_option_context_set_help_enabled:
- * @context: a #GOptionContext
- * @help_enabled: %TRUE to enable <option>--help</option>, %FALSE to disable it
- *
- * Enables or disables automatic generation of <option>--help</option>
- * output. By default, g_option_context_parse() recognizes
- * <option>--help</option>, <option>-h</option>,
- * <option>-?</option>, <option>--help-all</option>
- * and <option>--help-</option><replaceable>groupname</replaceable> and creates
- * suitable output to stdout.
- *
- * Since: 2.6
- */
-void g_option_context_set_help_enabled (GOptionContext *context,
- gboolean help_enabled)
-
-{
- g_return_if_fail (context != NULL);
-
- context->help_enabled = help_enabled;
-}
-
-/**
- * g_option_context_get_help_enabled:
- * @context: a #GOptionContext
- *
- * Returns whether automatic <option>--help</option> generation
- * is turned on for @context. See g_option_context_set_help_enabled().
- *
- * Returns: %TRUE if automatic help generation is turned on.
- *
- * Since: 2.6
- */
-gboolean
-g_option_context_get_help_enabled (GOptionContext *context)
-{
- g_return_val_if_fail (context != NULL, FALSE);
-
- return context->help_enabled;
-}
-
-/**
- * g_option_context_set_ignore_unknown_options:
- * @context: a #GOptionContext
- * @ignore_unknown: %TRUE to ignore unknown options, %FALSE to produce
- * an error when unknown options are met
- *
- * Sets whether to ignore unknown options or not. If an argument is
- * ignored, it is left in the @argv array after parsing. By default,
- * g_option_context_parse() treats unknown options as error.
- *
- * This setting does not affect non-option arguments (i.e. arguments
- * which don't start with a dash). But note that GOption cannot reliably
- * determine whether a non-option belongs to a preceding unknown option.
- *
- * Since: 2.6
- **/
-void
-g_option_context_set_ignore_unknown_options (GOptionContext *context,
- gboolean ignore_unknown)
-{
- g_return_if_fail (context != NULL);
-
- context->ignore_unknown = ignore_unknown;
-}
-
-/**
- * g_option_context_get_ignore_unknown_options:
- * @context: a #GOptionContext
- *
- * Returns whether unknown options are ignored or not. See
- * g_option_context_set_ignore_unknown_options().
- *
- * Returns: %TRUE if unknown options are ignored.
- *
- * Since: 2.6
- **/
-gboolean
-g_option_context_get_ignore_unknown_options (GOptionContext *context)
-{
- g_return_val_if_fail (context != NULL, FALSE);
-
- return context->ignore_unknown;
-}
-
-/**
- * g_option_context_add_group:
- * @context: a #GOptionContext
- * @group: the group to add
- *
- * Adds a #GOptionGroup to the @context, so that parsing with @context
- * will recognize the options in the group. Note that the group will
- * be freed together with the context when g_option_context_free() is
- * called, so you must not free the group yourself after adding it
- * to a context.
- *
- * Since: 2.6
- **/
-void
-g_option_context_add_group (GOptionContext *context,
- GOptionGroup *group)
-{
- GList *list;
-
- g_return_if_fail (context != NULL);
- g_return_if_fail (group != NULL);
- g_return_if_fail (group->name != NULL);
- g_return_if_fail (group->description != NULL);
- g_return_if_fail (group->help_description != NULL);
-
- for (list = context->groups; list; list = list->next)
- {
- GOptionGroup *g = (GOptionGroup *)list->data;
-
- if ((group->name == NULL && g->name == NULL) ||
- (group->name && g->name && strcmp (group->name, g->name) == 0))
- g_warning ("A group named \"%s\" is already part of this GOptionContext",
- group->name);
- }
-
- context->groups = g_list_append (context->groups, group);
-}
-
-/**
- * g_option_context_set_main_group:
- * @context: a #GOptionContext
- * @group: the group to set as main group
- *
- * Sets a #GOptionGroup as main group of the @context.
- * This has the same effect as calling g_option_context_add_group(),
- * the only difference is that the options in the main group are
- * treated differently when generating <option>--help</option> output.
- *
- * Since: 2.6
- **/
-void
-g_option_context_set_main_group (GOptionContext *context,
- GOptionGroup *group)
-{
- g_return_if_fail (context != NULL);
- g_return_if_fail (group != NULL);
-
- if (context->main_group)
- {
- g_warning ("This GOptionContext already has a main group");
-
- return;
- }
-
- context->main_group = group;
-}
-
-/**
- * g_option_context_get_main_group:
- * @context: a #GOptionContext
- *
- * Returns a pointer to the main group of @context.
- *
- * Return value: the main group of @context, or %NULL if @context doesn't
- * have a main group. Note that group belongs to @context and should
- * not be modified or freed.
- *
- * Since: 2.6
- **/
-GOptionGroup *
-g_option_context_get_main_group (GOptionContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
-
- return context->main_group;
-}
-
-/**
- * g_option_context_add_main_entries:
- * @context: a #GOptionContext
- * @entries: a %NULL-terminated array of #GOptionEntry<!-- -->s
- * @translation_domain: a translation domain to use for translating
- * the <option>--help</option> output for the options in @entries
- * with gettext(), or %NULL
- *
- * A convenience function which creates a main group if it doesn't
- * exist, adds the @entries to it and sets the translation domain.
- *
- * Since: 2.6
- **/
-void
-g_option_context_add_main_entries (GOptionContext *context,
- const GOptionEntry *entries,
- const gchar *translation_domain)
-{
- g_return_if_fail (entries != NULL);
-
- if (!context->main_group)
- context->main_group = g_option_group_new (NULL, NULL, NULL, NULL, NULL);
-
- g_option_group_add_entries (context->main_group, entries);
- g_option_group_set_translation_domain (context->main_group, translation_domain);
-}
-
-static gint
-calculate_max_length (GOptionGroup *group)
-{
- GOptionEntry *entry;
- gint i, len, max_length;
-
- max_length = 0;
-
- for (i = 0; i < group->n_entries; i++)
- {
- entry = &group->entries[i];
-
- if (entry->flags & G_OPTION_FLAG_HIDDEN)
- continue;
-
- len = _g_utf8_strwidth (entry->long_name, -1);
-
- if (entry->short_name)
- len += 4;
-
- if (!NO_ARG (entry) && entry->arg_description)
- len += 1 + _g_utf8_strwidth (TRANSLATE (group, entry->arg_description), -1);
-
- max_length = MAX (max_length, len);
- }
-
- return max_length;
-}
-
-static void
-print_entry (GOptionGroup *group,
- gint max_length,
- const GOptionEntry *entry,
- GString *string)
-{
- GString *str;
-
- if (entry->flags & G_OPTION_FLAG_HIDDEN)
- return;
-
- if (entry->long_name[0] == 0)
- return;
-
- str = g_string_new (NULL);
-
- if (entry->short_name)
- g_string_append_printf (str, " -%c, --%s", entry->short_name, entry->long_name);
- else
- g_string_append_printf (str, " --%s", entry->long_name);
-
- if (entry->arg_description)
- g_string_append_printf (str, "=%s", TRANSLATE (group, entry->arg_description));
-
- g_string_append_printf (string, "%s%*s %s\n", str->str,
- (int) (max_length + 4 - _g_utf8_strwidth (str->str, -1)), "",
- entry->description ? TRANSLATE (group, entry->description) : "");
- g_string_free (str, TRUE);
-}
-
-static gboolean
-group_has_visible_entries (GOptionContext *context,
- GOptionGroup *group,
- gboolean main_entries)
-{
- GOptionFlags reject_filter = G_OPTION_FLAG_HIDDEN;
- GOptionEntry *entry;
- gint i, l;
- gboolean main_group = group == context->main_group;
-
- if (!main_entries)
- reject_filter |= G_OPTION_FLAG_IN_MAIN;
-
- for (i = 0, l = (group ? group->n_entries : 0); i < l; i++)
- {
- entry = &group->entries[i];
-
- if (main_entries && !main_group && !(entry->flags & G_OPTION_FLAG_IN_MAIN))
- continue;
- if (!(entry->flags & reject_filter))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-group_list_has_visible_entires (GOptionContext *context,
- GList *group_list,
- gboolean main_entries)
-{
- while (group_list)
- {
- if (group_has_visible_entries (context, group_list->data, main_entries))
- return TRUE;
-
- group_list = group_list->next;
- }
-
- return FALSE;
-}
-
-static gboolean
-context_has_h_entry (GOptionContext *context)
-{
- gsize i;
- GList *list;
-
- if (context->main_group)
- {
- for (i = 0; i < context->main_group->n_entries; i++)
- {
- if (context->main_group->entries[i].short_name == 'h')
- return TRUE;
- }
- }
-
- for (list = context->groups; list != NULL; list = g_list_next (list))
- {
- GOptionGroup *group;
-
- group = (GOptionGroup*)list->data;
- for (i = 0; i < group->n_entries; i++)
- {
- if (group->entries[i].short_name == 'h')
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- * g_option_context_get_help:
- * @context: a #GOptionContext
- * @main_help: if %TRUE, only include the main group
- * @group: the #GOptionGroup to create help for, or %NULL
- *
- * Returns a formatted, translated help text for the given context.
- * To obtain the text produced by <option>--help</option>, call
- * <literal>g_option_context_get_help (context, TRUE, NULL)</literal>.
- * To obtain the text produced by <option>--help-all</option>, call
- * <literal>g_option_context_get_help (context, FALSE, NULL)</literal>.
- * To obtain the help text for an option group, call
- * <literal>g_option_context_get_help (context, FALSE, group)</literal>.
- *
- * Returns: A newly allocated string containing the help text
- *
- * Since: 2.14
- */
-gchar *
-g_option_context_get_help (GOptionContext *context,
- gboolean main_help,
- GOptionGroup *group)
-{
- GList *list;
- gint max_length, len;
- gint i;
- GOptionEntry *entry;
- GHashTable *shadow_map;
- gboolean seen[256];
- const gchar *rest_description;
- GString *string;
- guchar token;
-
- string = g_string_sized_new (1024);
-
- rest_description = NULL;
- if (context->main_group)
- {
-
- for (i = 0; i < context->main_group->n_entries; i++)
- {
- entry = &context->main_group->entries[i];
- if (entry->long_name[0] == 0)
- {
- rest_description = TRANSLATE (context->main_group, entry->arg_description);
- break;
- }
- }
- }
-
- g_string_append_printf (string, "%s\n %s %s",
- _("Usage:"), g_get_prgname(), _("[OPTION...]"));
-
- if (rest_description)
- {
- g_string_append (string, " ");
- g_string_append (string, rest_description);
- }
-
- if (context->parameter_string)
- {
- g_string_append (string, " ");
- g_string_append (string, TRANSLATE (context, context->parameter_string));
- }
-
- g_string_append (string, "\n\n");
-
- if (context->summary)
- {
- g_string_append (string, TRANSLATE (context, context->summary));
- g_string_append (string, "\n\n");
- }
-
- memset (seen, 0, sizeof (gboolean) * 256);
- shadow_map = g_hash_table_new (g_str_hash, g_str_equal);
-
- if (context->main_group)
- {
- for (i = 0; i < context->main_group->n_entries; i++)
- {
- entry = &context->main_group->entries[i];
- g_hash_table_insert (shadow_map,
- (gpointer)entry->long_name,
- entry);
-
- if (seen[(guchar)entry->short_name])
- entry->short_name = 0;
- else
- seen[(guchar)entry->short_name] = TRUE;
- }
- }
-
- list = context->groups;
- while (list != NULL)
- {
- GOptionGroup *g = list->data;
- for (i = 0; i < g->n_entries; i++)
- {
- entry = &g->entries[i];
- if (g_hash_table_lookup (shadow_map, entry->long_name) &&
- !(entry->flags & G_OPTION_FLAG_NOALIAS))
- entry->long_name = g_strdup_printf ("%s-%s", g->name, entry->long_name);
- else
- g_hash_table_insert (shadow_map, (gpointer)entry->long_name, entry);
-
- if (seen[(guchar)entry->short_name] &&
- !(entry->flags & G_OPTION_FLAG_NOALIAS))
- entry->short_name = 0;
- else
- seen[(guchar)entry->short_name] = TRUE;
- }
- list = list->next;
- }
-
- g_hash_table_destroy (shadow_map);
-
- list = context->groups;
-
- max_length = _g_utf8_strwidth ("-?, --help", -1);
-
- if (list)
- {
- len = _g_utf8_strwidth ("--help-all", -1);
- max_length = MAX (max_length, len);
- }
-
- if (context->main_group)
- {
- len = calculate_max_length (context->main_group);
- max_length = MAX (max_length, len);
- }
-
- while (list != NULL)
- {
- GOptionGroup *g = list->data;
-
- /* First, we check the --help-<groupname> options */
- len = _g_utf8_strwidth ("--help-", -1) + _g_utf8_strwidth (g->name, -1);
- max_length = MAX (max_length, len);
-
- /* Then we go through the entries */
- len = calculate_max_length (g);
- max_length = MAX (max_length, len);
-
- list = list->next;
- }
-
- /* Add a bit of padding */
- max_length += 4;
-
- if (!group)
- {
- list = context->groups;
-
- token = context_has_h_entry (context) ? '?' : 'h';
-
- g_string_append_printf (string, "%s\n -%c, --%-*s %s\n",
- _("Help Options:"), token, max_length - 4, "help",
- _("Show help options"));
-
- /* We only want --help-all when there are groups */
- if (list)
- g_string_append_printf (string, " --%-*s %s\n",
- max_length, "help-all",
- _("Show all help options"));
-
- while (list)
- {
- GOptionGroup *g = list->data;
-
- if (group_has_visible_entries (context, g, FALSE))
- g_string_append_printf (string, " --help-%-*s %s\n",
- max_length - 5, g->name,
- TRANSLATE (g, g->help_description));
-
- list = list->next;
- }
-
- g_string_append (string, "\n");
- }
-
- if (group)
- {
- /* Print a certain group */
-
- if (group_has_visible_entries (context, group, FALSE))
- {
- g_string_append (string, TRANSLATE (group, group->description));
- g_string_append (string, "\n");
- for (i = 0; i < group->n_entries; i++)
- print_entry (group, max_length, &group->entries[i], string);
- g_string_append (string, "\n");
- }
- }
- else if (!main_help)
- {
- /* Print all groups */
-
- list = context->groups;
-
- while (list)
- {
- GOptionGroup *g = list->data;
-
- if (group_has_visible_entries (context, g, FALSE))
- {
- g_string_append (string, g->description);
- g_string_append (string, "\n");
- for (i = 0; i < g->n_entries; i++)
- if (!(g->entries[i].flags & G_OPTION_FLAG_IN_MAIN))
- print_entry (g, max_length, &g->entries[i], string);
-
- g_string_append (string, "\n");
- }
-
- list = list->next;
- }
- }
-
- /* Print application options if --help or --help-all has been specified */
- if ((main_help || !group) &&
- (group_has_visible_entries (context, context->main_group, TRUE) ||
- group_list_has_visible_entires (context, context->groups, TRUE)))
- {
- list = context->groups;
-
- g_string_append (string, _("Application Options:"));
- g_string_append (string, "\n");
- if (context->main_group)
- for (i = 0; i < context->main_group->n_entries; i++)
- print_entry (context->main_group, max_length,
- &context->main_group->entries[i], string);
-
- while (list != NULL)
- {
- GOptionGroup *g = list->data;
-
- /* Print main entries from other groups */
- for (i = 0; i < g->n_entries; i++)
- if (g->entries[i].flags & G_OPTION_FLAG_IN_MAIN)
- print_entry (g, max_length, &g->entries[i], string);
-
- list = list->next;
- }
-
- g_string_append (string, "\n");
- }
-
- if (context->description)
- {
- g_string_append (string, TRANSLATE (context, context->description));
- g_string_append (string, "\n");
- }
-
- return g_string_free (string, FALSE);
-}
-
-G_GNUC_NORETURN
-static void
-print_help (GOptionContext *context,
- gboolean main_help,
- GOptionGroup *group)
-{
- gchar *help;
-
- help = g_option_context_get_help (context, main_help, group);
- g_print ("%s", help);
- g_free (help);
-
- exit (0);
-}
-
-static gboolean
-parse_int (const gchar *arg_name,
- const gchar *arg,
- gint *result,
- GError **error)
-{
- gchar *end;
- glong tmp;
-
- errno = 0;
- tmp = strtol (arg, &end, 0);
-
- if (*arg == '\0' || *end != '\0')
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Cannot parse integer value '%s' for %s"),
- arg, arg_name);
- return FALSE;
- }
-
- *result = tmp;
- if (*result != tmp || errno == ERANGE)
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Integer value '%s' for %s out of range"),
- arg, arg_name);
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-static gboolean
-parse_double (const gchar *arg_name,
- const gchar *arg,
- gdouble *result,
- GError **error)
-{
- gchar *end;
- gdouble tmp;
-
- errno = 0;
- tmp = g_strtod (arg, &end);
-
- if (*arg == '\0' || *end != '\0')
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Cannot parse double value '%s' for %s"),
- arg, arg_name);
- return FALSE;
- }
- if (errno == ERANGE)
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Double value '%s' for %s out of range"),
- arg, arg_name);
- return FALSE;
- }
-
- *result = tmp;
-
- return TRUE;
-}
-
-
-static gboolean
-parse_int64 (const gchar *arg_name,
- const gchar *arg,
- gint64 *result,
- GError **error)
-{
- gchar *end;
- gint64 tmp;
-
- errno = 0;
- tmp = g_ascii_strtoll (arg, &end, 0);
-
- if (*arg == '\0' || *end != '\0')
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Cannot parse integer value '%s' for %s"),
- arg, arg_name);
- return FALSE;
- }
- if (errno == ERANGE)
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Integer value '%s' for %s out of range"),
- arg, arg_name);
- return FALSE;
- }
-
- *result = tmp;
-
- return TRUE;
-}
-
-
-static Change *
-get_change (GOptionContext *context,
- GOptionArg arg_type,
- gpointer arg_data)
-{
- GList *list;
- Change *change = NULL;
-
- for (list = context->changes; list != NULL; list = list->next)
- {
- change = list->data;
-
- if (change->arg_data == arg_data)
- goto found;
- }
-
- change = g_new0 (Change, 1);
- change->arg_type = arg_type;
- change->arg_data = arg_data;
-
- context->changes = g_list_prepend (context->changes, change);
-
- found:
-
- return change;
-}
-
-static void
-add_pending_null (GOptionContext *context,
- gchar **ptr,
- gchar *value)
-{
- PendingNull *n;
-
- n = g_new0 (PendingNull, 1);
- n->ptr = ptr;
- n->value = value;
-
- context->pending_nulls = g_list_prepend (context->pending_nulls, n);
-}
-
-static gboolean
-parse_arg (GOptionContext *context,
- GOptionGroup *group,
- GOptionEntry *entry,
- const gchar *value,
- const gchar *option_name,
- GError **error)
-
-{
- Change *change;
-
- g_assert (value || OPTIONAL_ARG (entry) || NO_ARG (entry));
-
- switch (entry->arg)
- {
- case G_OPTION_ARG_NONE:
- {
- change = get_change (context, G_OPTION_ARG_NONE,
- entry->arg_data);
-
- *(gboolean *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE);
- break;
- }
- case G_OPTION_ARG_STRING:
- {
- gchar *data;
-
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-
- if (!data)
- return FALSE;
-
- change = get_change (context, G_OPTION_ARG_STRING,
- entry->arg_data);
- g_free (change->allocated.str);
-
- change->prev.str = *(gchar **)entry->arg_data;
- change->allocated.str = data;
-
- *(gchar **)entry->arg_data = data;
- break;
- }
- case G_OPTION_ARG_STRING_ARRAY:
- {
- gchar *data;
-
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-
- if (!data)
- return FALSE;
-
- change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
- entry->arg_data);
-
- if (change->allocated.array.len == 0)
- {
- change->prev.array = *(gchar ***)entry->arg_data;
- change->allocated.array.data = g_new (gchar *, 2);
- }
- else
- change->allocated.array.data =
- g_renew (gchar *, change->allocated.array.data,
- change->allocated.array.len + 2);
-
- change->allocated.array.data[change->allocated.array.len] = data;
- change->allocated.array.data[change->allocated.array.len + 1] = NULL;
-
- change->allocated.array.len ++;
-
- *(gchar ***)entry->arg_data = change->allocated.array.data;
-
- break;
- }
-
- case G_OPTION_ARG_FILENAME:
- {
- gchar *data;
-
-#ifdef G_OS_WIN32
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-
- if (!data)
- return FALSE;
-#else
- data = g_strdup (value);
-#endif
- change = get_change (context, G_OPTION_ARG_FILENAME,
- entry->arg_data);
- g_free (change->allocated.str);
-
- change->prev.str = *(gchar **)entry->arg_data;
- change->allocated.str = data;
-
- *(gchar **)entry->arg_data = data;
- break;
- }
-
- case G_OPTION_ARG_FILENAME_ARRAY:
- {
- gchar *data;
-
-#ifdef G_OS_WIN32
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-
- if (!data)
- return FALSE;
-#else
- data = g_strdup (value);
-#endif
- change = get_change (context, G_OPTION_ARG_STRING_ARRAY,
- entry->arg_data);
-
- if (change->allocated.array.len == 0)
- {
- change->prev.array = *(gchar ***)entry->arg_data;
- change->allocated.array.data = g_new (gchar *, 2);
- }
- else
- change->allocated.array.data =
- g_renew (gchar *, change->allocated.array.data,
- change->allocated.array.len + 2);
-
- change->allocated.array.data[change->allocated.array.len] = data;
- change->allocated.array.data[change->allocated.array.len + 1] = NULL;
-
- change->allocated.array.len ++;
-
- *(gchar ***)entry->arg_data = change->allocated.array.data;
-
- break;
- }
-
- case G_OPTION_ARG_INT:
- {
- gint data;
-
- if (!parse_int (option_name, value,
- &data,
- error))
- return FALSE;
-
- change = get_change (context, G_OPTION_ARG_INT,
- entry->arg_data);
- change->prev.integer = *(gint *)entry->arg_data;
- *(gint *)entry->arg_data = data;
- break;
- }
- case G_OPTION_ARG_CALLBACK:
- {
- gchar *data;
- gboolean retval;
-
- if (!value && entry->flags & G_OPTION_FLAG_OPTIONAL_ARG)
- data = NULL;
- else if (entry->flags & G_OPTION_FLAG_NO_ARG)
- data = NULL;
- else if (entry->flags & G_OPTION_FLAG_FILENAME)
- {
-#ifdef G_OS_WIN32
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-#else
- data = g_strdup (value);
-#endif
- }
- else
- data = g_locale_to_utf8 (value, -1, NULL, NULL, error);
-
- if (!(entry->flags & (G_OPTION_FLAG_NO_ARG|G_OPTION_FLAG_OPTIONAL_ARG)) &&
- !data)
- return FALSE;
-
- retval = (* (GOptionArgFunc) entry->arg_data) (option_name, data, group->user_data, error);
-
- if (!retval && error != NULL && *error == NULL)
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
- _("Error parsing option %s"), option_name);
-
- g_free (data);
-
- return retval;
-
- break;
- }
- case G_OPTION_ARG_DOUBLE:
- {
- gdouble data;
-
- if (!parse_double (option_name, value,
- &data,
- error))
- {
- return FALSE;
- }
-
- change = get_change (context, G_OPTION_ARG_DOUBLE,
- entry->arg_data);
- change->prev.dbl = *(gdouble *)entry->arg_data;
- *(gdouble *)entry->arg_data = data;
- break;
- }
- case G_OPTION_ARG_INT64:
- {
- gint64 data;
-
- if (!parse_int64 (option_name, value,
- &data,
- error))
- {
- return FALSE;
- }
-
- change = get_change (context, G_OPTION_ARG_INT64,
- entry->arg_data);
- change->prev.int64 = *(gint64 *)entry->arg_data;
- *(gint64 *)entry->arg_data = data;
- break;
- }
- default:
- g_assert_not_reached ();
- }
-
- return TRUE;
-}
-
-static gboolean
-parse_short_option (GOptionContext *context,
- GOptionGroup *group,
- gint idx,
- gint *new_idx,
- gchar arg,
- gint *argc,
- gchar ***argv,
- GError **error,
- gboolean *parsed)
-{
- gint j;
-
- for (j = 0; j < group->n_entries; j++)
- {
- if (arg == group->entries[j].short_name)
- {
- gchar *option_name;
- gchar *value = NULL;
-
- option_name = g_strdup_printf ("-%c", group->entries[j].short_name);
-
- if (NO_ARG (&group->entries[j]))
- value = NULL;
- else
- {
- if (*new_idx > idx)
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
- _("Error parsing option %s"), option_name);
- g_free (option_name);
- return FALSE;
- }
-
- if (idx < *argc - 1)
- {
- if (!OPTIONAL_ARG (&group->entries[j]))
- {
- value = (*argv)[idx + 1];
- add_pending_null (context, &((*argv)[idx + 1]), NULL);
- *new_idx = idx + 1;
- }
- else
- {
- if ((*argv)[idx + 1][0] == '-')
- value = NULL;
- else
- {
- value = (*argv)[idx + 1];
- add_pending_null (context, &((*argv)[idx + 1]), NULL);
- *new_idx = idx + 1;
- }
- }
- }
- else if (idx >= *argc - 1 && OPTIONAL_ARG (&group->entries[j]))
- value = NULL;
- else
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Missing argument for %s"), option_name);
- g_free (option_name);
- return FALSE;
- }
- }
-
- if (!parse_arg (context, group, &group->entries[j],
- value, option_name, error))
- {
- g_free (option_name);
- return FALSE;
- }
-
- g_free (option_name);
- *parsed = TRUE;
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-parse_long_option (GOptionContext *context,
- GOptionGroup *group,
- gint *idx,
- gchar *arg,
- gboolean aliased,
- gint *argc,
- gchar ***argv,
- GError **error,
- gboolean *parsed)
-{
- gint j;
-
- for (j = 0; j < group->n_entries; j++)
- {
- if (*idx >= *argc)
- return TRUE;
-
- if (aliased && (group->entries[j].flags & G_OPTION_FLAG_NOALIAS))
- continue;
-
- if (NO_ARG (&group->entries[j]) &&
- strcmp (arg, group->entries[j].long_name) == 0)
- {
- gchar *option_name;
- gboolean retval;
-
- option_name = g_strconcat ("--", group->entries[j].long_name, NULL);
- retval = parse_arg (context, group, &group->entries[j],
- NULL, option_name, error);
- g_free (option_name);
-
- add_pending_null (context, &((*argv)[*idx]), NULL);
- *parsed = TRUE;
-
- return retval;
- }
- else
- {
- gint len = strlen (group->entries[j].long_name);
-
- if (strncmp (arg, group->entries[j].long_name, len) == 0 &&
- (arg[len] == '=' || arg[len] == 0))
- {
- gchar *value = NULL;
- gchar *option_name;
-
- add_pending_null (context, &((*argv)[*idx]), NULL);
- option_name = g_strconcat ("--", group->entries[j].long_name, NULL);
-
- if (arg[len] == '=')
- value = arg + len + 1;
- else if (*idx < *argc - 1)
- {
- if (!(group->entries[j].flags & G_OPTION_FLAG_OPTIONAL_ARG))
- {
- value = (*argv)[*idx + 1];
- add_pending_null (context, &((*argv)[*idx + 1]), NULL);
- (*idx)++;
- }
- else
- {
- if ((*argv)[*idx + 1][0] == '-')
- {
- gboolean retval;
- retval = parse_arg (context, group, &group->entries[j],
- NULL, option_name, error);
- *parsed = TRUE;
- g_free (option_name);
- return retval;
- }
- else
- {
- value = (*argv)[*idx + 1];
- add_pending_null (context, &((*argv)[*idx + 1]), NULL);
- (*idx)++;
- }
- }
- }
- else if (*idx >= *argc - 1 &&
- group->entries[j].flags & G_OPTION_FLAG_OPTIONAL_ARG)
- {
- gboolean retval;
- retval = parse_arg (context, group, &group->entries[j],
- NULL, option_name, error);
- *parsed = TRUE;
- g_free (option_name);
- return retval;
- }
- else
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
- _("Missing argument for %s"), option_name);
- g_free (option_name);
- return FALSE;
- }
-
- if (!parse_arg (context, group, &group->entries[j],
- value, option_name, error))
- {
- g_free (option_name);
- return FALSE;
- }
-
- g_free (option_name);
- *parsed = TRUE;
- }
- }
- }
-
- return TRUE;
-}
-
-static gboolean
-parse_remaining_arg (GOptionContext *context,
- GOptionGroup *group,
- gint *idx,
- gint *argc,
- gchar ***argv,
- GError **error,
- gboolean *parsed)
-{
- gint j;
-
- for (j = 0; j < group->n_entries; j++)
- {
- if (*idx >= *argc)
- return TRUE;
-
- if (group->entries[j].long_name[0])
- continue;
-
- g_return_val_if_fail (group->entries[j].arg == G_OPTION_ARG_CALLBACK ||
- group->entries[j].arg == G_OPTION_ARG_STRING_ARRAY ||
- group->entries[j].arg == G_OPTION_ARG_FILENAME_ARRAY, FALSE);
-
- add_pending_null (context, &((*argv)[*idx]), NULL);
-
- if (!parse_arg (context, group, &group->entries[j], (*argv)[*idx], "", error))
- return FALSE;
-
- *parsed = TRUE;
- return TRUE;
- }
-
- return TRUE;
-}
-
-static void
-free_changes_list (GOptionContext *context,
- gboolean revert)
-{
- GList *list;
-
- for (list = context->changes; list != NULL; list = list->next)
- {
- Change *change = list->data;
-
- if (revert)
- {
- switch (change->arg_type)
- {
- case G_OPTION_ARG_NONE:
- *(gboolean *)change->arg_data = change->prev.bool;
- break;
- case G_OPTION_ARG_INT:
- *(gint *)change->arg_data = change->prev.integer;
- break;
- case G_OPTION_ARG_STRING:
- case G_OPTION_ARG_FILENAME:
- g_free (change->allocated.str);
- *(gchar **)change->arg_data = change->prev.str;
- break;
- case G_OPTION_ARG_STRING_ARRAY:
- case G_OPTION_ARG_FILENAME_ARRAY:
- g_strfreev (change->allocated.array.data);
- *(gchar ***)change->arg_data = change->prev.array;
- break;
- case G_OPTION_ARG_DOUBLE:
- *(gdouble *)change->arg_data = change->prev.dbl;
- break;
- case G_OPTION_ARG_INT64:
- *(gint64 *)change->arg_data = change->prev.int64;
- break;
- default:
- g_assert_not_reached ();
- }
- }
-
- g_free (change);
- }
-
- g_list_free (context->changes);
- context->changes = NULL;
-}
-
-static void
-free_pending_nulls (GOptionContext *context,
- gboolean perform_nulls)
-{
- GList *list;
-
- for (list = context->pending_nulls; list != NULL; list = list->next)
- {
- PendingNull *n = list->data;
-
- if (perform_nulls)
- {
- if (n->value)
- {
- /* Copy back the short options */
- *(n->ptr)[0] = '-';
- strcpy (*n->ptr + 1, n->value);
- }
- else
- *n->ptr = NULL;
- }
-
- g_free (n->value);
- g_free (n);
- }
-
- g_list_free (context->pending_nulls);
- context->pending_nulls = NULL;
-}
-
-/**
- * g_option_context_parse:
- * @context: a #GOptionContext
- * @argc: a pointer to the number of command line arguments
- * @argv: a pointer to the array of command line arguments
- * @error: a return location for errors
- *
- * Parses the command line arguments, recognizing options
- * which have been added to @context. A side-effect of
- * calling this function is that g_set_prgname() will be
- * called.
- *
- * If the parsing is successful, any parsed arguments are
- * removed from the array and @argc and @argv are updated
- * accordingly. A '--' option is stripped from @argv
- * unless there are unparsed options before and after it,
- * or some of the options after it start with '-'. In case
- * of an error, @argc and @argv are left unmodified.
- *
- * If automatic <option>--help</option> support is enabled
- * (see g_option_context_set_help_enabled()), and the
- * @argv array contains one of the recognized help options,
- * this function will produce help output to stdout and
- * call <literal>exit (0)</literal>.
- *
- * Note that function depends on the
- * <link linkend="setlocale">current locale</link> for
- * automatic character set conversion of string and filename
- * arguments.
- *
- * Return value: %TRUE if the parsing was successful,
- * %FALSE if an error occurred
- *
- * Since: 2.6
- **/
-gboolean
-g_option_context_parse (GOptionContext *context,
- gint *argc,
- gchar ***argv,
- GError **error)
-{
- gint i, j, k;
- GList *list;
-
- /* Set program name */
- if (!g_get_prgname())
- {
- if (argc && argv && *argc)
- {
- gchar *prgname;
-
- prgname = g_path_get_basename ((*argv)[0]);
- g_set_prgname (prgname);
- g_free (prgname);
- }
- else
- g_set_prgname ("<unknown>");
- }
-
- /* Call pre-parse hooks */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (group->pre_parse_func)
- {
- if (!(* group->pre_parse_func) (context, group,
- group->user_data, error))
- goto fail;
- }
-
- list = list->next;
- }
-
- if (context->main_group && context->main_group->pre_parse_func)
- {
- if (!(* context->main_group->pre_parse_func) (context, context->main_group,
- context->main_group->user_data, error))
- goto fail;
- }
-
- if (argc && argv)
- {
- gboolean stop_parsing = FALSE;
- gboolean has_unknown = FALSE;
- gint separator_pos = 0;
-
- for (i = 1; i < *argc; i++)
- {
- gchar *arg, *dash;
- gboolean parsed = FALSE;
-
- if ((*argv)[i][0] == '-' && (*argv)[i][1] != '\0' && !stop_parsing)
- {
- if ((*argv)[i][1] == '-')
- {
- /* -- option */
-
- arg = (*argv)[i] + 2;
-
- /* '--' terminates list of arguments */
- if (*arg == 0)
- {
- separator_pos = i;
- stop_parsing = TRUE;
- continue;
- }
-
- /* Handle help options */
- if (context->help_enabled)
- {
- if (strcmp (arg, "help") == 0)
- print_help (context, TRUE, NULL);
- else if (strcmp (arg, "help-all") == 0)
- print_help (context, FALSE, NULL);
- else if (strncmp (arg, "help-", 5) == 0)
- {
- list = context->groups;
-
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (strcmp (arg + 5, group->name) == 0)
- print_help (context, FALSE, group);
-
- list = list->next;
- }
- }
- }
-
- if (context->main_group &&
- !parse_long_option (context, context->main_group, &i, arg,
- FALSE, argc, argv, error, &parsed))
- goto fail;
-
- if (parsed)
- continue;
-
- /* Try the groups */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (!parse_long_option (context, group, &i, arg,
- FALSE, argc, argv, error, &parsed))
- goto fail;
-
- if (parsed)
- break;
-
- list = list->next;
- }
-
- if (parsed)
- continue;
-
- /* Now look for --<group>-<option> */
- dash = strchr (arg, '-');
- if (dash)
- {
- /* Try the groups */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (strncmp (group->name, arg, dash - arg) == 0)
- {
- if (!parse_long_option (context, group, &i, dash + 1,
- TRUE, argc, argv, error, &parsed))
- goto fail;
-
- if (parsed)
- break;
- }
-
- list = list->next;
- }
- }
-
- if (context->ignore_unknown)
- continue;
- }
- else
- { /* short option */
- gint new_i = i, arg_length;
- gboolean *nulled_out = NULL;
- gboolean has_h_entry = context_has_h_entry (context);
- arg = (*argv)[i] + 1;
- arg_length = strlen (arg);
- nulled_out = g_newa (gboolean, arg_length);
- memset (nulled_out, 0, arg_length * sizeof (gboolean));
- for (j = 0; j < arg_length; j++)
- {
- if (context->help_enabled && (arg[j] == '?' ||
- (arg[j] == 'h' && !has_h_entry)))
- print_help (context, TRUE, NULL);
- parsed = FALSE;
- if (context->main_group &&
- !parse_short_option (context, context->main_group,
- i, &new_i, arg[j],
- argc, argv, error, &parsed))
- goto fail;
- if (!parsed)
- {
- /* Try the groups */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
- if (!parse_short_option (context, group, i, &new_i, arg[j],
- argc, argv, error, &parsed))
- goto fail;
- if (parsed)
- break;
- list = list->next;
- }
- }
-
- if (context->ignore_unknown && parsed)
- nulled_out[j] = TRUE;
- else if (context->ignore_unknown)
- continue;
- else if (!parsed)
- break;
- /* !context->ignore_unknown && parsed */
- }
- if (context->ignore_unknown)
- {
- gchar *new_arg = NULL;
- gint arg_index = 0;
- for (j = 0; j < arg_length; j++)
- {
- if (!nulled_out[j])
- {
- if (!new_arg)
- new_arg = g_malloc (arg_length + 1);
- new_arg[arg_index++] = arg[j];
- }
- }
- if (new_arg)
- new_arg[arg_index] = '\0';
- add_pending_null (context, &((*argv)[i]), new_arg);
- }
- else if (parsed)
- {
- add_pending_null (context, &((*argv)[i]), NULL);
- i = new_i;
- }
- }
-
- if (!parsed)
- has_unknown = TRUE;
-
- if (!parsed && !context->ignore_unknown)
- {
- g_set_error (error,
- G_OPTION_ERROR, G_OPTION_ERROR_UNKNOWN_OPTION,
- _("Unknown option %s"), (*argv)[i]);
- goto fail;
- }
- }
- else
- {
- /* Collect remaining args */
- if (context->main_group &&
- !parse_remaining_arg (context, context->main_group, &i,
- argc, argv, error, &parsed))
- goto fail;
-
- if (!parsed && (has_unknown || (*argv)[i][0] == '-'))
- separator_pos = 0;
- }
- }
-
- if (separator_pos > 0)
- add_pending_null (context, &((*argv)[separator_pos]), NULL);
-
- }
-
- /* Call post-parse hooks */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (group->post_parse_func)
- {
- if (!(* group->post_parse_func) (context, group,
- group->user_data, error))
- goto fail;
- }
-
- list = list->next;
- }
-
- if (context->main_group && context->main_group->post_parse_func)
- {
- if (!(* context->main_group->post_parse_func) (context, context->main_group,
- context->main_group->user_data, error))
- goto fail;
- }
-
- if (argc && argv)
- {
- free_pending_nulls (context, TRUE);
-
- for (i = 1; i < *argc; i++)
- {
- for (k = i; k < *argc; k++)
- if ((*argv)[k] != NULL)
- break;
-
- if (k > i)
- {
- k -= i;
- for (j = i + k; j < *argc; j++)
- {
- (*argv)[j-k] = (*argv)[j];
- (*argv)[j] = NULL;
- }
- *argc -= k;
- }
- }
- }
-
- return TRUE;
-
- fail:
-
- /* Call error hooks */
- list = context->groups;
- while (list)
- {
- GOptionGroup *group = list->data;
-
- if (group->error_func)
- (* group->error_func) (context, group,
- group->user_data, error);
-
- list = list->next;
- }
-
- if (context->main_group && context->main_group->error_func)
- (* context->main_group->error_func) (context, context->main_group,
- context->main_group->user_data, error);
-
- free_changes_list (context, TRUE);
- free_pending_nulls (context, FALSE);
-
- return FALSE;
-}
-
-/**
- * g_option_group_new:
- * @name: the name for the option group, this is used to provide
- * help for the options in this group with <option>--help-</option>@name
- * @description: a description for this group to be shown in
- * <option>--help</option>. This string is translated using the translation
- * domain or translation function of the group
- * @help_description: a description for the <option>--help-</option>@name option.
- * This string is translated using the translation domain or translation function
- * of the group
- * @user_data: user data that will be passed to the pre- and post-parse hooks,
- * the error hook and to callbacks of %G_OPTION_ARG_CALLBACK options, or %NULL
- * @destroy: a function that will be called to free @user_data, or %NULL
- *
- * Creates a new #GOptionGroup.
- *
- * Return value: a newly created option group. It should be added
- * to a #GOptionContext or freed with g_option_group_free().
- *
- * Since: 2.6
- **/
-GOptionGroup *
-g_option_group_new (const gchar *name,
- const gchar *description,
- const gchar *help_description,
- gpointer user_data,
- GDestroyNotify destroy)
-
-{
- GOptionGroup *group;
-
- group = g_new0 (GOptionGroup, 1);
- group->name = g_strdup (name);
- group->description = g_strdup (description);
- group->help_description = g_strdup (help_description);
- group->user_data = user_data;
- group->destroy_notify = destroy;
-
- return group;
-}
-
-
-/**
- * g_option_group_free:
- * @group: a #GOptionGroup
- *
- * Frees a #GOptionGroup. Note that you must <emphasis>not</emphasis>
- * free groups which have been added to a #GOptionContext.
- *
- * Since: 2.6
- **/
-void
-g_option_group_free (GOptionGroup *group)
-{
- g_return_if_fail (group != NULL);
-
- g_free (group->name);
- g_free (group->description);
- g_free (group->help_description);
-
- g_free (group->entries);
-
- if (group->destroy_notify)
- (* group->destroy_notify) (group->user_data);
-
- if (group->translate_notify)
- (* group->translate_notify) (group->translate_data);
-
- g_free (group);
-}
-
-
-/**
- * g_option_group_add_entries:
- * @group: a #GOptionGroup
- * @entries: a %NULL-terminated array of #GOptionEntry<!-- -->s
- *
- * Adds the options specified in @entries to @group.
- *
- * Since: 2.6
- **/
-void
-g_option_group_add_entries (GOptionGroup *group,
- const GOptionEntry *entries)
-{
- gint i, n_entries;
-
- g_return_if_fail (entries != NULL);
-
- for (n_entries = 0; entries[n_entries].long_name != NULL; n_entries++) ;
-
- group->entries = g_renew (GOptionEntry, group->entries, group->n_entries + n_entries);
-
- memcpy (group->entries + group->n_entries, entries, sizeof (GOptionEntry) * n_entries);
-
- for (i = group->n_entries; i < group->n_entries + n_entries; i++)
- {
- gchar c = group->entries[i].short_name;
-
- if (c)
- {
- if (c == '-' || !g_ascii_isprint (c))
- {
- g_warning (G_STRLOC": ignoring invalid short option '%c' (%d)", c, c);
- group->entries[i].short_name = 0;
- }
- }
- }
-
- group->n_entries += n_entries;
-}
-
-/**
- * g_option_group_set_parse_hooks:
- * @group: a #GOptionGroup
- * @pre_parse_func: a function to call before parsing, or %NULL
- * @post_parse_func: a function to call after parsing, or %NULL
- *
- * Associates two functions with @group which will be called
- * from g_option_context_parse() before the first option is parsed
- * and after the last option has been parsed, respectively.
- *
- * Note that the user data to be passed to @pre_parse_func and
- * @post_parse_func can be specified when constructing the group
- * with g_option_group_new().
- *
- * Since: 2.6
- **/
-void
-g_option_group_set_parse_hooks (GOptionGroup *group,
- GOptionParseFunc pre_parse_func,
- GOptionParseFunc post_parse_func)
-{
- g_return_if_fail (group != NULL);
-
- group->pre_parse_func = pre_parse_func;
- group->post_parse_func = post_parse_func;
-}
-
-/**
- * g_option_group_set_error_hook:
- * @group: a #GOptionGroup
- * @error_func: a function to call when an error occurs
- *
- * Associates a function with @group which will be called
- * from g_option_context_parse() when an error occurs.
- *
- * Note that the user data to be passed to @error_func can be
- * specified when constructing the group with g_option_group_new().
- *
- * Since: 2.6
- **/
-void
-g_option_group_set_error_hook (GOptionGroup *group,
- GOptionErrorFunc error_func)
-{
- g_return_if_fail (group != NULL);
-
- group->error_func = error_func;
-}
-
-
-/**
- * g_option_group_set_translate_func:
- * @group: a #GOptionGroup
- * @func: the #GTranslateFunc, or %NULL
- * @data: user data to pass to @func, or %NULL
- * @destroy_notify: a function which gets called to free @data, or %NULL
- *
- * Sets the function which is used to translate user-visible
- * strings, for <option>--help</option> output. Different
- * groups can use different #GTranslateFunc<!-- -->s. If @func
- * is %NULL, strings are not translated.
- *
- * If you are using gettext(), you only need to set the translation
- * domain, see g_option_group_set_translation_domain().
- *
- * Since: 2.6
- **/
-void
-g_option_group_set_translate_func (GOptionGroup *group,
- GTranslateFunc func,
- gpointer data,
- GDestroyNotify destroy_notify)
-{
- g_return_if_fail (group != NULL);
-
- if (group->translate_notify)
- group->translate_notify (group->translate_data);
-
- group->translate_func = func;
- group->translate_data = data;
- group->translate_notify = destroy_notify;
-}
-
-static const gchar *
-dgettext_swapped (const gchar *msgid,
- const gchar *domainname)
-{
- return g_dgettext (domainname, msgid);
-}
-
-/**
- * g_option_group_set_translation_domain:
- * @group: a #GOptionGroup
- * @domain: the domain to use
- *
- * A convenience function to use gettext() for translating
- * user-visible strings.
- *
- * Since: 2.6
- **/
-void
-g_option_group_set_translation_domain (GOptionGroup *group,
- const gchar *domain)
-{
- g_return_if_fail (group != NULL);
-
- g_option_group_set_translate_func (group,
- (GTranslateFunc)dgettext_swapped,
- g_strdup (domain),
- g_free);
-}
-
-/**
- * g_option_context_set_translate_func:
- * @context: a #GOptionContext
- * @func: the #GTranslateFunc, or %NULL
- * @data: user data to pass to @func, or %NULL
- * @destroy_notify: a function which gets called to free @data, or %NULL
- *
- * Sets the function which is used to translate the contexts
- * user-visible strings, for <option>--help</option> output.
- * If @func is %NULL, strings are not translated.
- *
- * Note that option groups have their own translation functions,
- * this function only affects the @parameter_string (see g_option_context_new()),
- * the summary (see g_option_context_set_summary()) and the description
- * (see g_option_context_set_description()).
- *
- * If you are using gettext(), you only need to set the translation
- * domain, see g_option_context_set_translation_domain().
- *
- * Since: 2.12
- **/
-void
-g_option_context_set_translate_func (GOptionContext *context,
- GTranslateFunc func,
- gpointer data,
- GDestroyNotify destroy_notify)
-{
- g_return_if_fail (context != NULL);
-
- if (context->translate_notify)
- context->translate_notify (context->translate_data);
-
- context->translate_func = func;
- context->translate_data = data;
- context->translate_notify = destroy_notify;
-}
-
-/**
- * g_option_context_set_translation_domain:
- * @context: a #GOptionContext
- * @domain: the domain to use
- *
- * A convenience function to use gettext() for translating
- * user-visible strings.
- *
- * Since: 2.12
- **/
-void
-g_option_context_set_translation_domain (GOptionContext *context,
- const gchar *domain)
-{
- g_return_if_fail (context != NULL);
-
- g_option_context_set_translate_func (context,
- (GTranslateFunc)dgettext_swapped,
- g_strdup (domain),
- g_free);
-}
-
-/**
- * g_option_context_set_summary:
- * @context: a #GOptionContext
- * @summary: a string to be shown in <option>--help</option> output
- * before the list of options, or %NULL
- *
- * Adds a string to be displayed in <option>--help</option> output
- * before the list of options. This is typically a summary of the
- * program functionality.
- *
- * Note that the summary is translated (see
- * g_option_context_set_translate_func() and
- * g_option_context_set_translation_domain()).
- *
- * Since: 2.12
- */
-void
-g_option_context_set_summary (GOptionContext *context,
- const gchar *summary)
-{
- g_return_if_fail (context != NULL);
-
- g_free (context->summary);
- context->summary = g_strdup (summary);
-}
-
-
-/**
- * g_option_context_get_summary:
- * @context: a #GOptionContext
- *
- * Returns the summary. See g_option_context_set_summary().
- *
- * Returns: the summary
- *
- * Since: 2.12
- */
-G_CONST_RETURN gchar *
-g_option_context_get_summary (GOptionContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
-
- return context->summary;
-}
-
-/**
- * g_option_context_set_description:
- * @context: a #GOptionContext
- * @description: a string to be shown in <option>--help</option> output
- * after the list of options, or %NULL
- *
- * Adds a string to be displayed in <option>--help</option> output
- * after the list of options. This text often includes a bug reporting
- * address.
- *
- * Note that the summary is translated (see
- * g_option_context_set_translate_func()).
- *
- * Since: 2.12
- */
-void
-g_option_context_set_description (GOptionContext *context,
- const gchar *description)
-{
- g_return_if_fail (context != NULL);
-
- g_free (context->description);
- context->description = g_strdup (description);
-}
-
-
-/**
- * g_option_context_get_description:
- * @context: a #GOptionContext
- *
- * Returns the description. See g_option_context_set_description().
- *
- * Returns: the description
- *
- * Since: 2.12
- */
-G_CONST_RETURN gchar *
-g_option_context_get_description (GOptionContext *context)
-{
- g_return_val_if_fail (context != NULL, NULL);
-
- return context->description;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997, 1999 Peter Mattis, Red Hat, Inc.
- *
- * 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 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 "config.h"
-
-#include <string.h>
-
-#include "gpattern.h"
-
-#include "gmacros.h"
-#include "gmessages.h"
-#include "gmem.h"
-#include "gunicode.h"
-#include "gutils.h"
-
-/**
- * SECTION: patterns
- * @title: Glob-style pattern matching
- * @short_description: matches strings against patterns containing '*'
- * (wildcard) and '?' (joker)
- *
- * The <function>g_pattern_match*</function> functions match a string
- * against a pattern containing '*' and '?' wildcards with similar
- * semantics as the standard glob() function: '*' matches an arbitrary,
- * possibly empty, string, '?' matches an arbitrary character.
- *
- * Note that in contrast to glob(), the '/' character
- * <emphasis>can</emphasis> be matched by the wildcards, there are no
- * '[...]' character ranges and '*' and '?' can
- * <emphasis>not</emphasis> be escaped to include them literally in a
- * pattern.
- *
- * When multiple strings must be matched against the same pattern, it
- * is better to compile the pattern to a #GPatternSpec using
- * g_pattern_spec_new() and use g_pattern_match_string() instead of
- * g_pattern_match_simple(). This avoids the overhead of repeated
- * pattern compilation.
- **/
-
-/**
- * GPatternSpec:
- *
- * A <structname>GPatternSpec</structname> is the 'compiled' form of a
- * pattern. This structure is opaque and its fields cannot be accessed
- * directly.
- **/
-
-/* keep enum and structure of gpattern.c and patterntest.c in sync */
-typedef enum
-{
- G_MATCH_ALL, /* "*A?A*" */
- G_MATCH_ALL_TAIL, /* "*A?AA" */
- G_MATCH_HEAD, /* "AAAA*" */
- G_MATCH_TAIL, /* "*AAAA" */
- G_MATCH_EXACT, /* "AAAAA" */
- G_MATCH_LAST
-} GMatchType;
-
-struct _GPatternSpec
-{
- GMatchType match_type;
- guint pattern_length;
- guint min_length;
- guint max_length;
- gchar *pattern;
-};
-
-
-/* --- functions --- */
-static inline gboolean
-g_pattern_ph_match (const gchar *match_pattern,
- const gchar *match_string,
- gboolean *wildcard_reached_p)
-{
- register const gchar *pattern, *string;
- register gchar ch;
-
- pattern = match_pattern;
- string = match_string;
-
- ch = *pattern;
- pattern++;
- while (ch)
- {
- switch (ch)
- {
- case '?':
- if (!*string)
- return FALSE;
- string = g_utf8_next_char (string);
- break;
-
- case '*':
- *wildcard_reached_p = TRUE;
- do
- {
- ch = *pattern;
- pattern++;
- if (ch == '?')
- {
- if (!*string)
- return FALSE;
- string = g_utf8_next_char (string);
- }
- }
- while (ch == '*' || ch == '?');
- if (!ch)
- return TRUE;
- do
- {
- gboolean next_wildcard_reached = FALSE;
- while (ch != *string)
- {
- if (!*string)
- return FALSE;
- string = g_utf8_next_char (string);
- }
- string++;
- if (g_pattern_ph_match (pattern, string, &next_wildcard_reached))
- return TRUE;
- if (next_wildcard_reached)
- /* the forthcoming pattern substring up to the next wildcard has
- * been matched, but a mismatch occoured for the rest of the
- * pattern, following the next wildcard.
- * there's no need to advance the current match position any
- * further if the rest pattern will not match.
- */
- return FALSE;
- }
- while (*string);
- break;
-
- default:
- if (ch == *string)
- string++;
- else
- return FALSE;
- break;
- }
-
- ch = *pattern;
- pattern++;
- }
-
- return *string == 0;
-}
-
-/**
- * g_pattern_match:
- * @pspec: a #GPatternSpec
- * @string_length: the length of @string (in bytes, i.e. strlen(),
- * <emphasis>not</emphasis> g_utf8_strlen())
- * @string: the UTF-8 encoded string to match
- * @string_reversed: the reverse of @string or %NULL
- * @Returns: %TRUE if @string matches @pspec
- *
- * Matches a string against a compiled pattern. Passing the correct
- * length of the string given is mandatory. The reversed string can be
- * omitted by passing %NULL, this is more efficient if the reversed
- * version of the string to be matched is not at hand, as
- * g_pattern_match() will only construct it if the compiled pattern
- * requires reverse matches.
- *
- * Note that, if the user code will (possibly) match a string against a
- * multitude of patterns containing wildcards, chances are high that
- * some patterns will require a reversed string. In this case, it's
- * more efficient to provide the reversed string to avoid multiple
- * constructions thereof in the various calls to g_pattern_match().
- *
- * Note also that the reverse of a UTF-8 encoded string can in general
- * <emphasis>not</emphasis> be obtained by g_strreverse(). This works
- * only if the string doesn't contain any multibyte characters. GLib
- * offers the g_utf8_strreverse() function to reverse UTF-8 encoded
- * strings.
- **/
-gboolean
-g_pattern_match (GPatternSpec *pspec,
- guint string_length,
- const gchar *string,
- const gchar *string_reversed)
-{
- g_return_val_if_fail (pspec != NULL, FALSE);
- g_return_val_if_fail (string != NULL, FALSE);
-
- if (string_length < pspec->min_length ||
- string_length > pspec->max_length)
- return FALSE;
-
- switch (pspec->match_type)
- {
- gboolean dummy;
- case G_MATCH_ALL:
- return g_pattern_ph_match (pspec->pattern, string, &dummy);
- case G_MATCH_ALL_TAIL:
- if (string_reversed)
- return g_pattern_ph_match (pspec->pattern, string_reversed, &dummy);
- else
- {
- gboolean result;
- gchar *tmp;
- tmp = g_utf8_strreverse (string, string_length);
- result = g_pattern_ph_match (pspec->pattern, tmp, &dummy);
- g_free (tmp);
- return result;
- }
- case G_MATCH_HEAD:
- if (pspec->pattern_length == string_length)
- return strcmp (pspec->pattern, string) == 0;
- else if (pspec->pattern_length)
- return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
- else
- return TRUE;
- case G_MATCH_TAIL:
- if (pspec->pattern_length)
- return strcmp (pspec->pattern, string + (string_length - pspec->pattern_length)) == 0;
- else
- return TRUE;
- case G_MATCH_EXACT:
- if (pspec->pattern_length != string_length)
- return FALSE;
- else
- return strcmp (pspec->pattern, string) == 0;
- default:
- g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
- return FALSE;
- }
-}
-
-/**
- * g_pattern_spec_new:
- * @pattern: a zero-terminated UTF-8 encoded string
- * @Returns: a newly-allocated #GPatternSpec
- *
- * Compiles a pattern to a #GPatternSpec.
- **/
-GPatternSpec*
-g_pattern_spec_new (const gchar *pattern)
-{
- GPatternSpec *pspec;
- gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
- gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
- gboolean follows_wildcard = FALSE;
- guint pending_jokers = 0;
- const gchar *s;
- gchar *d;
- guint i;
-
- g_return_val_if_fail (pattern != NULL, NULL);
-
- /* canonicalize pattern and collect necessary stats */
- pspec = g_new (GPatternSpec, 1);
- pspec->pattern_length = strlen (pattern);
- pspec->min_length = 0;
- pspec->max_length = 0;
- pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
- d = pspec->pattern;
- for (i = 0, s = pattern; *s != 0; s++)
- {
- switch (*s)
- {
- case '*':
- if (follows_wildcard) /* compress multiple wildcards */
- {
- pspec->pattern_length--;
- continue;
- }
- follows_wildcard = TRUE;
- if (hw_pos < 0)
- hw_pos = i;
- tw_pos = i;
- break;
- case '?':
- pending_jokers++;
- pspec->min_length++;
- pspec->max_length += 4; /* maximum UTF-8 character length */
- continue;
- default:
- for (; pending_jokers; pending_jokers--, i++) {
- *d++ = '?';
- if (hj_pos < 0)
- hj_pos = i;
- tj_pos = i;
- }
- follows_wildcard = FALSE;
- pspec->min_length++;
- pspec->max_length++;
- break;
- }
- *d++ = *s;
- i++;
- }
- for (; pending_jokers; pending_jokers--) {
- *d++ = '?';
- if (hj_pos < 0)
- hj_pos = i;
- tj_pos = i;
- }
- *d++ = 0;
- seen_joker = hj_pos >= 0;
- seen_wildcard = hw_pos >= 0;
- more_wildcards = seen_wildcard && hw_pos != tw_pos;
- if (seen_wildcard)
- pspec->max_length = G_MAXUINT;
-
- /* special case sole head/tail wildcard or exact matches */
- if (!seen_joker && !more_wildcards)
- {
- if (pspec->pattern[0] == '*')
- {
- pspec->match_type = G_MATCH_TAIL;
- memmove (pspec->pattern, pspec->pattern + 1, --pspec->pattern_length);
- pspec->pattern[pspec->pattern_length] = 0;
- return pspec;
- }
- if (pspec->pattern_length > 0 &&
- pspec->pattern[pspec->pattern_length - 1] == '*')
- {
- pspec->match_type = G_MATCH_HEAD;
- pspec->pattern[--pspec->pattern_length] = 0;
- return pspec;
- }
- if (!seen_wildcard)
- {
- pspec->match_type = G_MATCH_EXACT;
- return pspec;
- }
- }
-
- /* now just need to distinguish between head or tail match start */
- tw_pos = pspec->pattern_length - 1 - tw_pos; /* last pos to tail distance */
- tj_pos = pspec->pattern_length - 1 - tj_pos; /* last pos to tail distance */
- if (seen_wildcard)
- pspec->match_type = tw_pos > hw_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
- else /* seen_joker */
- pspec->match_type = tj_pos > hj_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
- if (pspec->match_type == G_MATCH_ALL_TAIL) {
- gchar *tmp = pspec->pattern;
- pspec->pattern = g_utf8_strreverse (pspec->pattern, pspec->pattern_length);
- g_free (tmp);
- }
- return pspec;
-}
-
-/**
- * g_pattern_spec_free:
- * @pspec: a #GPatternSpec
- *
- * Frees the memory allocated for the #GPatternSpec.
- **/
-void
-g_pattern_spec_free (GPatternSpec *pspec)
-{
- g_return_if_fail (pspec != NULL);
-
- g_free (pspec->pattern);
- g_free (pspec);
-}
-
-/**
- * g_pattern_spec_equal:
- * @pspec1: a #GPatternSpec
- * @pspec2: another #GPatternSpec
- * @Returns: Whether the compiled patterns are equal
- *
- * Compares two compiled pattern specs and returns whether they will
- * match the same set of strings.
- **/
-gboolean
-g_pattern_spec_equal (GPatternSpec *pspec1,
- GPatternSpec *pspec2)
-{
- g_return_val_if_fail (pspec1 != NULL, FALSE);
- g_return_val_if_fail (pspec2 != NULL, FALSE);
-
- return (pspec1->pattern_length == pspec2->pattern_length &&
- pspec1->match_type == pspec2->match_type &&
- strcmp (pspec1->pattern, pspec2->pattern) == 0);
-}
-
-/**
- * g_pattern_match_string:
- * @pspec: a #GPatternSpec
- * @string: the UTF-8 encoded string to match
- * @Returns: %TRUE if @string matches @pspec
- *
- * Matches a string against a compiled pattern. If the string is to be
- * matched against more than one pattern, consider using
- * g_pattern_match() instead while supplying the reversed string.
- **/
-gboolean
-g_pattern_match_string (GPatternSpec *pspec,
- const gchar *string)
-{
- g_return_val_if_fail (pspec != NULL, FALSE);
- g_return_val_if_fail (string != NULL, FALSE);
-
- return g_pattern_match (pspec, strlen (string), string, NULL);
-}
-
-/**
- * g_pattern_match_simple:
- * @pattern: the UTF-8 encoded pattern
- * @string: the UTF-8 encoded string to match
- * @Returns: %TRUE if @string matches @pspec
- *
- * Matches a string against a pattern given as a string. If this
- * function is to be called in a loop, it's more efficient to compile
- * the pattern once with g_pattern_spec_new() and call
- * g_pattern_match_string() repeatedly.
- **/
-gboolean
-g_pattern_match_simple (const gchar *pattern,
- const gchar *string)
-{
- GPatternSpec *pspec;
- gboolean ergo;
-
- g_return_val_if_fail (pattern != NULL, FALSE);
- g_return_val_if_fail (string != NULL, FALSE);
-
- pspec = g_pattern_spec_new (pattern);
- ergo = g_pattern_match (pspec, strlen (string), string, NULL);
- g_pattern_spec_free (pspec);
-
- return ergo;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gpoll.c: poll(2) abstraction
- * Copyright 1998 Owen Taylor
- * Copyright 2008 Red Hat, Inc.
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-#include "glibconfig.h"
-#include "giochannel.h"
-
-/* Uncomment the next line (and the corresponding line in gmain.c) to
- * enable debugging printouts if the environment variable
- * G_MAIN_POLL_DEBUG is set to some value.
- */
-/* #define G_MAIN_POLL_DEBUG */
-
-#ifdef _WIN32
-/* Always enable debugging printout on Windows, as it is more often
- * needed there...
- */
-#define G_MAIN_POLL_DEBUG
-#endif
-
-#include <sys/types.h>
-#include <time.h>
-#include <stdlib.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif /* HAVE_SYS_TIME_H */
-#ifdef GLIB_HAVE_SYS_POLL_H
-# include <sys/poll.h>
-# undef events /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
-# undef revents /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
-
-/* The poll() emulation on OS/X doesn't handle fds=NULL, nfds=0,
- * so we prefer our own poll emulation.
- */
-#if defined(_POLL_EMUL_H_) || defined(BROKEN_POLL)
-#undef HAVE_POLL
-#endif
-
-#endif /* GLIB_HAVE_SYS_POLL_H */
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#include <errno.h>
-
-#ifdef G_OS_WIN32
-#define STRICT
-#include <windows.h>
-#endif /* G_OS_WIN32 */
-
-#include "gpoll.h"
-
-#ifdef G_OS_WIN32
-#include "gprintf.h"
-#endif
-
-#ifdef G_MAIN_POLL_DEBUG
-extern gboolean _g_main_poll_debug;
-#endif
-
-#ifdef HAVE_POLL
-/* SunOS has poll, but doesn't provide a prototype. */
-# if defined (sun) && !defined (__SVR4)
-extern gint poll (struct pollfd *fds, guint nfsd, gint timeout);
-# endif /* !sun */
-
-/**
- * g_poll:
- * @fds: file descriptors to poll
- * @nfds: the number of file descriptors in @fds
- * @timeout: amount of time to wait, in milliseconds, or -1 to wait forever
- *
- * Polls @fds, as with the poll() system call, but portably. (On
- * systems that don't have poll(), it is emulated using select().)
- * This is used internally by #GMainContext, but it can be called
- * directly if you need to block until a file descriptor is ready, but
- * don't want to run the full main loop.
- *
- * Each element of @fds is a #GPollFD describing a single file
- * descriptor to poll. The %fd field indicates the file descriptor,
- * and the %events field indicates the events to poll for. On return,
- * the %revents fields will be filled with the events that actually
- * occurred.
- *
- * On POSIX systems, the file descriptors in @fds can be any sort of
- * file descriptor, but the situation is much more complicated on
- * Windows. If you need to use g_poll() in code that has to run on
- * Windows, the easiest solution is to construct all of your
- * #GPollFD<!-- -->s with g_io_channel_win32_make_pollfd().
- *
- * Return value: the number of entries in @fds whose %revents fields
- * were filled in, or 0 if the operation timed out, or -1 on error or
- * if the call was interrupted.
- *
- * Since: 2.20
- **/
-gint
-g_poll (GPollFD *fds,
- guint nfds,
- gint timeout)
-{
- return poll ((struct pollfd *)fds, nfds, timeout);
-}
-
-#else /* !HAVE_POLL */
-
-#ifdef G_OS_WIN32
-
-static int
-poll_rest (gboolean poll_msgs,
- HANDLE *handles,
- gint nhandles,
- GPollFD *fds,
- guint nfds,
- gint timeout)
-{
- DWORD ready;
- GPollFD *f;
- int recursed_result;
-
- if (poll_msgs)
- {
- /* Wait for either messages or handles
- * -> Use MsgWaitForMultipleObjectsEx
- */
- if (_g_main_poll_debug)
- g_print (" MsgWaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout);
-
- ready = MsgWaitForMultipleObjectsEx (nhandles, handles, timeout,
- QS_ALLINPUT, MWMO_ALERTABLE);
-
- if (ready == WAIT_FAILED)
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
- g_warning ("MsgWaitForMultipleObjectsEx failed: %s", emsg);
- g_free (emsg);
- }
- }
- else if (nhandles == 0)
- {
- /* No handles to wait for, just the timeout */
- if (timeout == INFINITE)
- ready = WAIT_FAILED;
- else
- {
- SleepEx (timeout, TRUE);
- ready = WAIT_TIMEOUT;
- }
- }
- else
- {
- /* Wait for just handles
- * -> Use WaitForMultipleObjectsEx
- */
- if (_g_main_poll_debug)
- g_print (" WaitForMultipleObjectsEx(%d, %d)\n", nhandles, timeout);
-
- ready = WaitForMultipleObjectsEx (nhandles, handles, FALSE, timeout, TRUE);
- if (ready == WAIT_FAILED)
- {
- gchar *emsg = g_win32_error_message (GetLastError ());
- g_warning ("WaitForMultipleObjectsEx failed: %s", emsg);
- g_free (emsg);
- }
- }
-
- if (_g_main_poll_debug)
- g_print (" wait returns %ld%s\n",
- ready,
- (ready == WAIT_FAILED ? " (WAIT_FAILED)" :
- (ready == WAIT_TIMEOUT ? " (WAIT_TIMEOUT)" :
- (poll_msgs && ready == WAIT_OBJECT_0 + nhandles ? " (msg)" : ""))));
-
- if (ready == WAIT_FAILED)
- return -1;
- else if (ready == WAIT_TIMEOUT ||
- ready == WAIT_IO_COMPLETION)
- return 0;
- else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles)
- {
- for (f = fds; f < &fds[nfds]; ++f)
- if (f->fd == G_WIN32_MSG_HANDLE && f->events & G_IO_IN)
- f->revents |= G_IO_IN;
-
- /* If we have a timeout, or no handles to poll, be satisfied
- * with just noticing we have messages waiting.
- */
- if (timeout != 0 || nhandles == 0)
- return 1;
-
- /* If no timeout and handles to poll, recurse to poll them,
- * too.
- */
- recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0);
- return (recursed_result == -1) ? -1 : 1 + recursed_result;
- }
- else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
- {
- for (f = fds; f < &fds[nfds]; ++f)
- {
- if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0])
- {
- f->revents = f->events;
- if (_g_main_poll_debug)
- g_print (" got event %p\n", (HANDLE) f->fd);
- }
- }
-
- /* If no timeout and polling several handles, recurse to poll
- * the rest of them.
- */
- if (timeout == 0 && nhandles > 1)
- {
- /* Remove the handle that fired */
- int i;
- if (ready < nhandles - 1)
- for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++)
- handles[i-1] = handles[i];
- nhandles--;
- recursed_result = poll_rest (FALSE, handles, nhandles, fds, nfds, 0);
- return (recursed_result == -1) ? -1 : 1 + recursed_result;
- }
- return 1;
- }
-
- return 0;
-}
-
-gint
-g_poll (GPollFD *fds,
- guint nfds,
- gint timeout)
-{
- HANDLE handles[MAXIMUM_WAIT_OBJECTS];
- gboolean poll_msgs = FALSE;
- GPollFD *f;
- gint nhandles = 0;
- int retval;
-
- if (_g_main_poll_debug)
- g_print ("g_poll: waiting for");
-
- for (f = fds; f < &fds[nfds]; ++f)
- if (f->fd == G_WIN32_MSG_HANDLE && (f->events & G_IO_IN))
- {
- if (_g_main_poll_debug && !poll_msgs)
- g_print (" MSG");
- poll_msgs = TRUE;
- }
- else if (f->fd > 0)
- {
- /* Don't add the same handle several times into the array, as
- * docs say that is not allowed, even if it actually does seem
- * to work.
- */
- gint i;
-
- for (i = 0; i < nhandles; i++)
- if (handles[i] == (HANDLE) f->fd)
- break;
-
- if (i == nhandles)
- {
- if (nhandles == MAXIMUM_WAIT_OBJECTS)
- {
- g_warning ("Too many handles to wait for!\n");
- break;
- }
- else
- {
- if (_g_main_poll_debug)
- g_print (" %p", (HANDLE) f->fd);
- handles[nhandles++] = (HANDLE) f->fd;
- }
- }
- }
-
- if (_g_main_poll_debug)
- g_print ("\n");
-
- for (f = fds; f < &fds[nfds]; ++f)
- f->revents = 0;
-
- if (timeout == -1)
- timeout = INFINITE;
-
- /* Polling for several things? */
- if (nhandles > 1 || (nhandles > 0 && poll_msgs))
- {
- /* First check if one or several of them are immediately
- * available
- */
- retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, 0);
-
- /* If not, and we have a significant timeout, poll again with
- * timeout then. Note that this will return indication for only
- * one event, or only for messages. We ignore timeouts less than
- * ten milliseconds as they are mostly pointless on Windows, the
- * MsgWaitForMultipleObjectsEx() call will timeout right away
- * anyway.
- */
- if (retval == 0 && (timeout == INFINITE || timeout >= 10))
- retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout);
- }
- else
- {
- /* Just polling for one thing, so no need to check first if
- * available immediately
- */
- retval = poll_rest (poll_msgs, handles, nhandles, fds, nfds, timeout);
- }
-
- if (retval == -1)
- for (f = fds; f < &fds[nfds]; ++f)
- f->revents = 0;
-
- return retval;
-}
-
-#else /* !G_OS_WIN32 */
-
-/* The following implementation of poll() comes from the GNU C Library.
- * Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
- */
-
-#include <string.h> /* for bzero on BSD systems */
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-
-#ifdef G_OS_BEOS
-#undef NO_FD_SET
-#endif /* G_OS_BEOS */
-
-#ifndef NO_FD_SET
-# define SELECT_MASK fd_set
-#else /* !NO_FD_SET */
-# ifndef _AIX
-typedef long fd_mask;
-# endif /* _AIX */
-# ifdef _IBMR2
-# define SELECT_MASK void
-# else /* !_IBMR2 */
-# define SELECT_MASK int
-# endif /* !_IBMR2 */
-#endif /* !NO_FD_SET */
-
-gint
-g_poll (GPollFD *fds,
- guint nfds,
- gint timeout)
-{
- struct timeval tv;
- SELECT_MASK rset, wset, xset;
- GPollFD *f;
- int ready;
- int maxfd = 0;
-
- FD_ZERO (&rset);
- FD_ZERO (&wset);
- FD_ZERO (&xset);
-
- for (f = fds; f < &fds[nfds]; ++f)
- if (f->fd >= 0)
- {
- if (f->events & G_IO_IN)
- FD_SET (f->fd, &rset);
- if (f->events & G_IO_OUT)
- FD_SET (f->fd, &wset);
- if (f->events & G_IO_PRI)
- FD_SET (f->fd, &xset);
- if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI)))
- maxfd = f->fd;
- }
-
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
-
- ready = select (maxfd + 1, &rset, &wset, &xset,
- timeout == -1 ? NULL : &tv);
- if (ready > 0)
- for (f = fds; f < &fds[nfds]; ++f)
- {
- f->revents = 0;
- if (f->fd >= 0)
- {
- if (FD_ISSET (f->fd, &rset))
- f->revents |= G_IO_IN;
- if (FD_ISSET (f->fd, &wset))
- f->revents |= G_IO_OUT;
- if (FD_ISSET (f->fd, &xset))
- f->revents |= G_IO_PRI;
- }
- }
-
- return ready;
-}
-
-#endif /* !G_OS_WIN32 */
-
-#endif /* !HAVE_POLL */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gprimes.h"
-
-
-static const guint g_primes[] =
-{
- 11,
- 19,
- 37,
- 73,
- 109,
- 163,
- 251,
- 367,
- 557,
- 823,
- 1237,
- 1861,
- 2777,
- 4177,
- 6247,
- 9371,
- 14057,
- 21089,
- 31627,
- 47431,
- 71143,
- 106721,
- 160073,
- 240101,
- 360163,
- 540217,
- 810343,
- 1215497,
- 1823231,
- 2734867,
- 4102283,
- 6153409,
- 9230113,
- 13845163,
-};
-
-static const guint g_nprimes = sizeof (g_primes) / sizeof (g_primes[0]);
-
-guint
-g_spaced_primes_closest (guint num)
-{
- gint i;
-
- for (i = 0; i < g_nprimes; i++)
- if (g_primes[i] > num)
- return g_primes[i];
-
- return g_primes[g_nprimes - 1];
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997, 2002 Peter Mattis, Red Hat, Inc.
- *
- * 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 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 "config.h"
-
-#ifndef _WIN32
-#define _GNU_SOURCE /* For vasprintf */
-#endif
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "gprintf.h"
-#include "gprintfint.h"
-
-
-/**
- * g_printf:
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @Varargs: the arguments to insert in the output.
- *
- * An implementation of the standard printf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_printf (gchar const *format,
- ...)
-{
- va_list args;
- gint retval;
-
- va_start (args, format);
- retval = g_vprintf (format, args);
- va_end (args);
-
- return retval;
-}
-
-/**
- * g_fprintf:
- * @file: the stream to write to.
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @Varargs: the arguments to insert in the output.
- *
- * An implementation of the standard fprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_fprintf (FILE *file,
- gchar const *format,
- ...)
-{
- va_list args;
- gint retval;
-
- va_start (args, format);
- retval = g_vfprintf (file, format, args);
- va_end (args);
-
- return retval;
-}
-
-/**
- * g_sprintf:
- * @string: A pointer to a memory buffer to contain the resulting string. It
- * is up to the caller to ensure that the allocated buffer is large
- * enough to hold the formatted result
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @Varargs: the arguments to insert in the output.
- *
- * An implementation of the standard sprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Note that it is usually better to use g_snprintf(), to avoid the
- * risk of buffer overflow.
- *
- * See also g_strdup_printf().
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_sprintf (gchar *string,
- gchar const *format,
- ...)
-{
- va_list args;
- gint retval;
-
- va_start (args, format);
- retval = g_vsprintf (string, format, args);
- va_end (args);
-
- return retval;
-}
-
-/**
- * g_snprintf:
- * @string: the buffer to hold the output.
- * @n: the maximum number of bytes to produce (including the
- * terminating nul character).
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @Varargs: the arguments to insert in the output.
- *
- * A safer form of the standard sprintf() function. The output is guaranteed
- * to not exceed @n characters (including the terminating nul character), so
- * it is easy to ensure that a buffer overflow cannot occur.
- *
- * See also g_strdup_printf().
- *
- * In versions of GLib prior to 1.2.3, this function may return -1 if the
- * output was truncated, and the truncated string may not be nul-terminated.
- * In versions prior to 1.3.12, this function returns the length of the output
- * string.
- *
- * The return value of g_snprintf() conforms to the snprintf()
- * function as standardized in ISO C99. Note that this is different from
- * traditional snprintf(), which returns the length of the output string.
- *
- * The format string may contain positional parameters, as specified in
- * the Single Unix Specification.
- *
- * Returns: the number of bytes which would be produced if the buffer
- * was large enough.
- **/
-gint
-g_snprintf (gchar *string,
- gulong n,
- gchar const *format,
- ...)
-{
- va_list args;
- gint retval;
-
- va_start (args, format);
- retval = g_vsnprintf (string, n, format, args);
- va_end (args);
-
- return retval;
-}
-
-/**
- * g_vprintf:
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @args: the list of arguments to insert in the output.
- *
- * An implementation of the standard vprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_vprintf (gchar const *format,
- va_list args)
-{
- g_return_val_if_fail (format != NULL, -1);
-
- return _g_vprintf (format, args);
-}
-
-/**
- * g_vfprintf:
- * @file: the stream to write to.
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @args: the list of arguments to insert in the output.
- *
- * An implementation of the standard fprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_vfprintf (FILE *file,
- gchar const *format,
- va_list args)
-{
- g_return_val_if_fail (format != NULL, -1);
-
- return _g_vfprintf (file, format, args);
-}
-
-/**
- * g_vsprintf:
- * @string: the buffer to hold the output.
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @args: the list of arguments to insert in the output.
- *
- * An implementation of the standard vsprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.2
- **/
-gint
-g_vsprintf (gchar *string,
- gchar const *format,
- va_list args)
-{
- g_return_val_if_fail (string != NULL, -1);
- g_return_val_if_fail (format != NULL, -1);
-
- return _g_vsprintf (string, format, args);
-}
-
-/**
- * g_vsnprintf:
- * @string: the buffer to hold the output.
- * @n: the maximum number of bytes to produce (including the
- * terminating nul character).
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @args: the list of arguments to insert in the output.
- *
- * A safer form of the standard vsprintf() function. The output is guaranteed
- * to not exceed @n characters (including the terminating nul character), so
- * it is easy to ensure that a buffer overflow cannot occur.
- *
- * See also g_strdup_vprintf().
- *
- * In versions of GLib prior to 1.2.3, this function may return -1 if the
- * output was truncated, and the truncated string may not be nul-terminated.
- * In versions prior to 1.3.12, this function returns the length of the output
- * string.
- *
- * The return value of g_vsnprintf() conforms to the vsnprintf() function
- * as standardized in ISO C99. Note that this is different from traditional
- * vsnprintf(), which returns the length of the output string.
- *
- * The format string may contain positional parameters, as specified in
- * the Single Unix Specification.
- *
- * Returns: the number of bytes which would be produced if the buffer
- * was large enough.
- */
-gint
-g_vsnprintf (gchar *string,
- gulong n,
- gchar const *format,
- va_list args)
-{
- g_return_val_if_fail (n == 0 || string != NULL, -1);
- g_return_val_if_fail (format != NULL, -1);
-
- return _g_vsnprintf (string, n, format, args);
-}
-
-/**
- * g_vasprintf:
- * @string: the return location for the newly-allocated string.
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>.
- * @args: the list of arguments to insert in the output.
- *
- * An implementation of the GNU vasprintf() function which supports
- * positional parameters, as specified in the Single Unix Specification.
- * This function is similar to g_vsprintf(), except that it allocates a
- * string to hold the output, instead of putting the output in a buffer
- * you allocate in advance.
- *
- * Returns: the number of bytes printed.
- *
- * Since: 2.4
- **/
-gint
-g_vasprintf (gchar **string,
- gchar const *format,
- va_list args)
-{
- gint len;
- g_return_val_if_fail (string != NULL, -1);
-
-#if !defined(HAVE_GOOD_PRINTF)
-
- len = _g_gnulib_vasprintf (string, format, args);
- if (len < 0)
- *string = NULL;
-
-#elif defined (HAVE_VASPRINTF)
-
- len = vasprintf (string, format, args);
- if (len < 0)
- *string = NULL;
- else if (!g_mem_is_system_malloc ())
- {
- /* vasprintf returns malloc-allocated memory */
- gchar *string1 = g_strndup (*string, len);
- free (*string);
- *string = string1;
- }
-
-#else
-
- {
- va_list args2;
-
- G_VA_COPY (args2, args);
-
- *string = g_new (gchar, g_printf_string_upper_bound (format, args));
-
- len = _g_vsprintf (*string, format, args2);
- va_end (args2);
- }
-#endif
-
- return len;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1991, 1992, 1996, 1997,1999,2004 Free Software Foundation, Inc.
- * Copyright (C) 2000 Eazel, Inc.
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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 was originally part of the GNU C Library, and was modified to allow
- * user data to be passed in to the sorting function.
- *
- * Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
- * Modified by Maciej Stachowiak (mjs@eazel.com)
- *
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with GLib
- * at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-#include "config.h"
-
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "gqsort.h"
-
-#include "gtestutils.h"
-
-/* Byte-wise swap two items of size SIZE. */
-#define SWAP(a, b, size) \
- do \
- { \
- register size_t __size = (size); \
- register char *__a = (a), *__b = (b); \
- do \
- { \
- char __tmp = *__a; \
- *__a++ = *__b; \
- *__b++ = __tmp; \
- } while (--__size > 0); \
- } while (0)
-
-/* Discontinue quicksort algorithm when partition gets below this size.
- This particular magic number was chosen to work best on a Sun 4/260. */
-#define MAX_THRESH 4
-
-/* Stack node declarations used to store unfulfilled partition obligations. */
-typedef struct
- {
- char *lo;
- char *hi;
- } stack_node;
-
-/* The next 4 #defines implement a very fast in-line stack abstraction. */
-/* The stack needs log (total_elements) entries (we could even subtract
- log(MAX_THRESH)). Since total_elements has type size_t, we get as
- upper bound for log (total_elements):
- bits per byte (CHAR_BIT) * sizeof(size_t). */
-#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
-#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
-#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
-#define STACK_NOT_EMPTY (stack < top)
-
-
-/* Order size using quicksort. This implementation incorporates
- four optimizations discussed in Sedgewick:
-
- 1. Non-recursive, using an explicit stack of pointer that store the
- next array partition to sort. To save time, this maximum amount
- of space required to store an array of SIZE_MAX is allocated on the
- stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
- only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
- Pretty cheap, actually.
-
- 2. Chose the pivot element using a median-of-three decision tree.
- This reduces the probability of selecting a bad pivot value and
- eliminates certain extraneous comparisons.
-
- 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
- insertion sort to order the MAX_THRESH items within each partition.
- This is a big win, since insertion sort is faster for small, mostly
- sorted array segments.
-
- 4. The larger of the two sub-partitions is always pushed onto the
- stack first, with the algorithm then concentrating on the
- smaller partition. This *guarantees* no more than log (total_elems)
- stack size is needed (actually O(1) in this case)! */
-
-/**
- * g_qsort_with_data:
- * @pbase: start of array to sort
- * @total_elems: elements in the array
- * @size: size of each element
- * @compare_func: function to compare elements
- * @user_data: data to pass to @compare_func
- *
- * This is just like the standard C qsort() function, but
- * the comparison routine accepts a user data argument.
- *
- **/
-void
-g_qsort_with_data (gconstpointer pbase,
- gint total_elems,
- gsize size,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- register char *base_ptr = (char *) pbase;
-
- const size_t max_thresh = MAX_THRESH * size;
-
- g_return_if_fail (total_elems >= 0);
- g_return_if_fail (pbase != NULL || total_elems == 0);
- g_return_if_fail (compare_func != NULL);
-
- if (total_elems == 0)
- /* Avoid lossage with unsigned arithmetic below. */
- return;
-
- if (total_elems > MAX_THRESH)
- {
- char *lo = base_ptr;
- char *hi = &lo[size * (total_elems - 1)];
- stack_node stack[STACK_SIZE];
- stack_node *top = stack;
-
- PUSH (NULL, NULL);
-
- while (STACK_NOT_EMPTY)
- {
- char *left_ptr;
- char *right_ptr;
-
- /* Select median value from among LO, MID, and HI. Rearrange
- LO and HI so the three values are sorted. This lowers the
- probability of picking a pathological pivot value and
- skips a comparison for both the LEFT_PTR and RIGHT_PTR in
- the while loops. */
-
- char *mid = lo + size * ((hi - lo) / size >> 1);
-
- if ((*compare_func) ((void *) mid, (void *) lo, user_data) < 0)
- SWAP (mid, lo, size);
- if ((*compare_func) ((void *) hi, (void *) mid, user_data) < 0)
- SWAP (mid, hi, size);
- else
- goto jump_over;
- if ((*compare_func) ((void *) mid, (void *) lo, user_data) < 0)
- SWAP (mid, lo, size);
- jump_over:;
-
- left_ptr = lo + size;
- right_ptr = hi - size;
-
- /* Here's the famous ``collapse the walls'' section of quicksort.
- Gotta like those tight inner loops! They are the main reason
- that this algorithm runs much faster than others. */
- do
- {
- while ((*compare_func) ((void *) left_ptr, (void *) mid, user_data) < 0)
- left_ptr += size;
-
- while ((*compare_func) ((void *) mid, (void *) right_ptr, user_data) < 0)
- right_ptr -= size;
-
- if (left_ptr < right_ptr)
- {
- SWAP (left_ptr, right_ptr, size);
- if (mid == left_ptr)
- mid = right_ptr;
- else if (mid == right_ptr)
- mid = left_ptr;
- left_ptr += size;
- right_ptr -= size;
- }
- else if (left_ptr == right_ptr)
- {
- left_ptr += size;
- right_ptr -= size;
- break;
- }
- }
- while (left_ptr <= right_ptr);
-
- /* Set up pointers for next iteration. First determine whether
- left and right partitions are below the threshold size. If so,
- ignore one or both. Otherwise, push the larger partition's
- bounds on the stack and continue sorting the smaller one. */
-
- if ((size_t) (right_ptr - lo) <= max_thresh)
- {
- if ((size_t) (hi - left_ptr) <= max_thresh)
- /* Ignore both small partitions. */
- POP (lo, hi);
- else
- /* Ignore small left partition. */
- lo = left_ptr;
- }
- else if ((size_t) (hi - left_ptr) <= max_thresh)
- /* Ignore small right partition. */
- hi = right_ptr;
- else if ((right_ptr - lo) > (hi - left_ptr))
- {
- /* Push larger left partition indices. */
- PUSH (lo, right_ptr);
- lo = left_ptr;
- }
- else
- {
- /* Push larger right partition indices. */
- PUSH (left_ptr, hi);
- hi = right_ptr;
- }
- }
- }
-
- /* Once the BASE_PTR array is partially sorted by quicksort the rest
- is completely sorted using insertion sort, since this is efficient
- for partitions below MAX_THRESH size. BASE_PTR points to the beginning
- of the array to sort, and END_PTR points at the very last element in
- the array (*not* one beyond it!). */
-
-#define min(x, y) ((x) < (y) ? (x) : (y))
-
- {
- char *const end_ptr = &base_ptr[size * (total_elems - 1)];
- char *tmp_ptr = base_ptr;
- char *thresh = min(end_ptr, base_ptr + max_thresh);
- register char *run_ptr;
-
- /* Find smallest element in first threshold and place it at the
- array's beginning. This is the smallest array element,
- and the operation speeds up insertion sort's inner loop. */
-
- for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
- if ((*compare_func) ((void *) run_ptr, (void *) tmp_ptr, user_data) < 0)
- tmp_ptr = run_ptr;
-
- if (tmp_ptr != base_ptr)
- SWAP (tmp_ptr, base_ptr, size);
-
- /* Insertion sort, running from left-hand-side up to right-hand-side. */
-
- run_ptr = base_ptr + size;
- while ((run_ptr += size) <= end_ptr)
- {
- tmp_ptr = run_ptr - size;
- while ((*compare_func) ((void *) run_ptr, (void *) tmp_ptr, user_data) < 0)
- tmp_ptr -= size;
-
- tmp_ptr += size;
- if (tmp_ptr != run_ptr)
- {
- char *trav;
-
- trav = run_ptr + size;
- while (--trav >= run_ptr)
- {
- char c = *trav;
- char *hi, *lo;
-
- for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
- *hi = *lo;
- *hi = c;
- }
- }
- }
- }
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GQueue: Double ended queue implementation, piggy backed on GList.
- * Copyright (C) 1998 Tim Janik
- *
- * 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 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.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gqueue.h"
-
-#include "gtestutils.h"
-
-/**
- * g_queue_new:
- *
- * Creates a new #GQueue.
- *
- * Returns: a new #GQueue.
- **/
-GQueue*
-g_queue_new (void)
-{
- return g_slice_new0 (GQueue);
-}
-
-/**
- * g_queue_free:
- * @queue: a #GQueue.
- *
- * Frees the memory allocated for the #GQueue. Only call this function if
- * @queue was created with g_queue_new(). If queue elements contain
- * dynamically-allocated memory, they should be freed first.
- **/
-void
-g_queue_free (GQueue *queue)
-{
- g_return_if_fail (queue != NULL);
-
- g_list_free (queue->head);
- g_slice_free (GQueue, queue);
-}
-
-/**
- * g_queue_init:
- * @queue: an uninitialized #GQueue
- *
- * A statically-allocated #GQueue must be initialized with this function
- * before it can be used. Alternatively you can initialize it with
- * #G_QUEUE_INIT. It is not necessary to initialize queues created with
- * g_queue_new().
- *
- * Since: 2.14
- **/
-void
-g_queue_init (GQueue *queue)
-{
- g_return_if_fail (queue != NULL);
-
- queue->head = queue->tail = NULL;
- queue->length = 0;
-}
-
-/**
- * g_queue_clear:
- * @queue: a #GQueue
- *
- * Removes all the elements in @queue. If queue elements contain
- * dynamically-allocated memory, they should be freed first.
- *
- * Since: 2.14
- */
-void
-g_queue_clear (GQueue *queue)
-{
- g_return_if_fail (queue != NULL);
-
- g_list_free (queue->head);
- g_queue_init (queue);
-}
-
-/**
- * g_queue_is_empty:
- * @queue: a #GQueue.
- *
- * Returns %TRUE if the queue is empty.
- *
- * Returns: %TRUE if the queue is empty.
- **/
-gboolean
-g_queue_is_empty (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, TRUE);
-
- return queue->head == NULL;
-}
-
-/**
- * g_queue_get_length:
- * @queue: a #GQueue
- *
- * Returns the number of items in @queue.
- *
- * Return value: The number of items in @queue.
- *
- * Since: 2.4
- **/
-guint
-g_queue_get_length (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, 0);
-
- return queue->length;
-}
-
-/**
- * g_queue_reverse:
- * @queue: a #GQueue
- *
- * Reverses the order of the items in @queue.
- *
- * Since: 2.4
- **/
-void
-g_queue_reverse (GQueue *queue)
-{
- g_return_if_fail (queue != NULL);
-
- queue->tail = queue->head;
- queue->head = g_list_reverse (queue->head);
-}
-
-/**
- * g_queue_copy:
- * @queue: a #GQueue
- *
- * Copies a @queue. Note that is a shallow copy. If the elements in the
- * queue consist of pointers to data, the pointers are copied, but the
- * actual data is not.
- *
- * Return value: A copy of @queue
- *
- * Since: 2.4
- **/
-GQueue *
-g_queue_copy (GQueue *queue)
-{
- GQueue *result;
- GList *list;
-
- g_return_val_if_fail (queue != NULL, NULL);
-
- result = g_queue_new ();
-
- for (list = queue->head; list != NULL; list = list->next)
- g_queue_push_tail (result, list->data);
-
- return result;
-}
-
-/**
- * g_queue_foreach:
- * @queue: a #GQueue
- * @func: the function to call for each element's data
- * @user_data: user data to pass to @func
- *
- * Calls @func for each element in the queue passing @user_data to the
- * function.
- *
- * Since: 2.4
- **/
-void
-g_queue_foreach (GQueue *queue,
- GFunc func,
- gpointer user_data)
-{
- GList *list;
-
- g_return_if_fail (queue != NULL);
- g_return_if_fail (func != NULL);
-
- list = queue->head;
- while (list)
- {
- GList *next = list->next;
- func (list->data, user_data);
- list = next;
- }
-}
-
-/**
- * g_queue_find:
- * @queue: a #GQueue
- * @data: data to find
- *
- * Finds the first link in @queue which contains @data.
- *
- * Return value: The first link in @queue which contains @data.
- *
- * Since: 2.4
- **/
-GList *
-g_queue_find (GQueue *queue,
- gconstpointer data)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- return g_list_find (queue->head, data);
-}
-
-/**
- * g_queue_find_custom:
- * @queue: a #GQueue
- * @data: user data passed to @func
- * @func: a #GCompareFunc to call for each element. It should return 0
- * when the desired element is found
- *
- * Finds an element in a #GQueue, using a supplied function to find the
- * desired element. It iterates over the queue, calling the given function
- * which should return 0 when the desired element is found. The function
- * takes two gconstpointer arguments, the #GQueue element's data as the
- * first argument and the given user data as the second argument.
- *
- * Return value: The found link, or %NULL if it wasn't found
- *
- * Since: 2.4
- **/
-GList *
-g_queue_find_custom (GQueue *queue,
- gconstpointer data,
- GCompareFunc func)
-{
- g_return_val_if_fail (queue != NULL, NULL);
- g_return_val_if_fail (func != NULL, NULL);
-
- return g_list_find_custom (queue->head, data, func);
-}
-
-/**
- * g_queue_sort:
- * @queue: a #GQueue
- * @compare_func: the #GCompareDataFunc used to sort @queue. This function
- * is passed two elements of the queue and should return 0 if they are
- * equal, a negative value if the first comes before the second, and
- * a positive value if the second comes before the first.
- * @user_data: user data passed to @compare_func
- *
- * Sorts @queue using @compare_func.
- *
- * Since: 2.4
- **/
-void
-g_queue_sort (GQueue *queue,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (compare_func != NULL);
-
- queue->head = g_list_sort_with_data (queue->head, compare_func, user_data);
- queue->tail = g_list_last (queue->head);
-}
-
-/**
- * g_queue_push_head:
- * @queue: a #GQueue.
- * @data: the data for the new element.
- *
- * Adds a new element at the head of the queue.
- **/
-void
-g_queue_push_head (GQueue *queue,
- gpointer data)
-{
- g_return_if_fail (queue != NULL);
-
- queue->head = g_list_prepend (queue->head, data);
- if (!queue->tail)
- queue->tail = queue->head;
- queue->length++;
-}
-
-/**
- * g_queue_push_nth:
- * @queue: a #GQueue
- * @data: the data for the new element
- * @n: the position to insert the new element. If @n is negative or
- * larger than the number of elements in the @queue, the element is
- * added to the end of the queue.
- *
- * Inserts a new element into @queue at the given position
- *
- * Since: 2.4
- **/
-void
-g_queue_push_nth (GQueue *queue,
- gpointer data,
- gint n)
-{
- g_return_if_fail (queue != NULL);
-
- if (n < 0 || n >= queue->length)
- {
- g_queue_push_tail (queue, data);
- return;
- }
-
- g_queue_insert_before (queue, g_queue_peek_nth_link (queue, n), data);
-}
-
-/**
- * g_queue_push_head_link:
- * @queue: a #GQueue.
- * @link_: a single #GList element, <emphasis>not</emphasis> a list with
- * more than one element.
- *
- * Adds a new element at the head of the queue.
- **/
-void
-g_queue_push_head_link (GQueue *queue,
- GList *link)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (link != NULL);
- g_return_if_fail (link->prev == NULL);
- g_return_if_fail (link->next == NULL);
-
- link->next = queue->head;
- if (queue->head)
- queue->head->prev = link;
- else
- queue->tail = link;
- queue->head = link;
- queue->length++;
-}
-
-/**
- * g_queue_push_tail:
- * @queue: a #GQueue.
- * @data: the data for the new element.
- *
- * Adds a new element at the tail of the queue.
- **/
-void
-g_queue_push_tail (GQueue *queue,
- gpointer data)
-{
- g_return_if_fail (queue != NULL);
-
- queue->tail = g_list_append (queue->tail, data);
- if (queue->tail->next)
- queue->tail = queue->tail->next;
- else
- queue->head = queue->tail;
- queue->length++;
-}
-
-/**
- * g_queue_push_tail_link:
- * @queue: a #GQueue.
- * @link_: a single #GList element, <emphasis>not</emphasis> a list with
- * more than one element.
- *
- * Adds a new element at the tail of the queue.
- **/
-void
-g_queue_push_tail_link (GQueue *queue,
- GList *link)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (link != NULL);
- g_return_if_fail (link->prev == NULL);
- g_return_if_fail (link->next == NULL);
-
- link->prev = queue->tail;
- if (queue->tail)
- queue->tail->next = link;
- else
- queue->head = link;
- queue->tail = link;
- queue->length++;
-}
-
-/**
- * g_queue_push_nth_link:
- * @queue: a #GQueue
- * @n: the position to insert the link. If this is negative or larger than
- * the number of elements in @queue, the link is added to the end of
- * @queue.
- * @link_: the link to add to @queue
- *
- * Inserts @link into @queue at the given position.
- *
- * Since: 2.4
- **/
-void
-g_queue_push_nth_link (GQueue *queue,
- gint n,
- GList *link_)
-{
- GList *next;
- GList *prev;
-
- g_return_if_fail (queue != NULL);
- g_return_if_fail (link_ != NULL);
-
- if (n < 0 || n >= queue->length)
- {
- g_queue_push_tail_link (queue, link_);
- return;
- }
-
- g_assert (queue->head);
- g_assert (queue->tail);
-
- next = g_queue_peek_nth_link (queue, n);
- prev = next->prev;
-
- if (prev)
- prev->next = link_;
- next->prev = link_;
-
- link_->next = next;
- link_->prev = prev;
-
- if (queue->head->prev)
- queue->head = queue->head->prev;
-
- if (queue->tail->next)
- queue->tail = queue->tail->next;
-
- queue->length++;
-}
-
-/**
- * g_queue_pop_head:
- * @queue: a #GQueue.
- *
- * Removes the first element of the queue.
- *
- * Returns: the data of the first element in the queue, or %NULL if the queue
- * is empty.
- **/
-gpointer
-g_queue_pop_head (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (queue->head)
- {
- GList *node = queue->head;
- gpointer data = node->data;
-
- queue->head = node->next;
- if (queue->head)
- queue->head->prev = NULL;
- else
- queue->tail = NULL;
- g_list_free_1 (node);
- queue->length--;
-
- return data;
- }
-
- return NULL;
-}
-
-/**
- * g_queue_pop_head_link:
- * @queue: a #GQueue.
- *
- * Removes the first element of the queue.
- *
- * Returns: the #GList element at the head of the queue, or %NULL if the queue
- * is empty.
- **/
-GList*
-g_queue_pop_head_link (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (queue->head)
- {
- GList *node = queue->head;
-
- queue->head = node->next;
- if (queue->head)
- {
- queue->head->prev = NULL;
- node->next = NULL;
- }
- else
- queue->tail = NULL;
- queue->length--;
-
- return node;
- }
-
- return NULL;
-}
-
-/**
- * g_queue_peek_head_link:
- * @queue: a #GQueue
- *
- * Returns the first link in @queue
- *
- * Return value: the first link in @queue, or %NULL if @queue is empty
- *
- * Since: 2.4
- **/
-GList*
-g_queue_peek_head_link (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- return queue->head;
-}
-
-/**
- * g_queue_peek_tail_link:
- * @queue: a #GQueue
- *
- * Returns the last link @queue.
- *
- * Return value: the last link in @queue, or %NULL if @queue is empty
- *
- * Since: 2.4
- **/
-GList*
-g_queue_peek_tail_link (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- return queue->tail;
-}
-
-/**
- * g_queue_pop_tail:
- * @queue: a #GQueue.
- *
- * Removes the last element of the queue.
- *
- * Returns: the data of the last element in the queue, or %NULL if the queue
- * is empty.
- **/
-gpointer
-g_queue_pop_tail (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (queue->tail)
- {
- GList *node = queue->tail;
- gpointer data = node->data;
-
- queue->tail = node->prev;
- if (queue->tail)
- queue->tail->next = NULL;
- else
- queue->head = NULL;
- queue->length--;
- g_list_free_1 (node);
-
- return data;
- }
-
- return NULL;
-}
-
-/**
- * g_queue_pop_nth:
- * @queue: a #GQueue
- * @n: the position of the element.
- *
- * Removes the @n'th element of @queue.
- *
- * Return value: the element's data, or %NULL if @n is off the end of @queue.
- *
- * Since: 2.4
- **/
-gpointer
-g_queue_pop_nth (GQueue *queue,
- guint n)
-{
- GList *nth_link;
- gpointer result;
-
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (n >= queue->length)
- return NULL;
-
- nth_link = g_queue_peek_nth_link (queue, n);
- result = nth_link->data;
-
- g_queue_delete_link (queue, nth_link);
-
- return result;
-}
-
-/**
- * g_queue_pop_tail_link:
- * @queue: a #GQueue.
- *
- * Removes the last element of the queue.
- *
- * Returns: the #GList element at the tail of the queue, or %NULL if the queue
- * is empty.
- **/
-GList*
-g_queue_pop_tail_link (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (queue->tail)
- {
- GList *node = queue->tail;
-
- queue->tail = node->prev;
- if (queue->tail)
- {
- queue->tail->next = NULL;
- node->prev = NULL;
- }
- else
- queue->head = NULL;
- queue->length--;
-
- return node;
- }
-
- return NULL;
-}
-
-/**
- * g_queue_pop_nth_link:
- * @queue: a #GQueue
- * @n: the link's position
- *
- * Removes and returns the link at the given position.
- *
- * Return value: The @n'th link, or %NULL if @n is off the end of @queue.
- *
- * Since: 2.4
- **/
-GList*
-g_queue_pop_nth_link (GQueue *queue,
- guint n)
-{
- GList *link;
-
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (n >= queue->length)
- return NULL;
-
- link = g_queue_peek_nth_link (queue, n);
- g_queue_unlink (queue, link);
-
- return link;
-}
-
-/**
- * g_queue_peek_nth_link:
- * @queue: a #GQueue
- * @n: the position of the link
- *
- * Returns the link at the given position
- *
- * Return value: The link at the @n'th position, or %NULL if @n is off the
- * end of the list
- *
- * Since: 2.4
- **/
-GList *
-g_queue_peek_nth_link (GQueue *queue,
- guint n)
-{
- GList *link;
- gint i;
-
- g_return_val_if_fail (queue != NULL, NULL);
-
- if (n >= queue->length)
- return NULL;
-
- if (n > queue->length / 2)
- {
- n = queue->length - n - 1;
-
- link = queue->tail;
- for (i = 0; i < n; ++i)
- link = link->prev;
- }
- else
- {
- link = queue->head;
- for (i = 0; i < n; ++i)
- link = link->next;
- }
-
- return link;
-}
-
-/**
- * g_queue_link_index:
- * @queue: a #Gqueue
- * @link_: A #GList link
- *
- * Returns the position of @link_ in @queue.
- *
- * Return value: The position of @link_, or -1 if the link is
- * not part of @queue
- *
- * Since: 2.4
- **/
-gint
-g_queue_link_index (GQueue *queue,
- GList *link_)
-{
- g_return_val_if_fail (queue != NULL, -1);
-
- return g_list_position (queue->head, link_);
-}
-
-/**
- * g_queue_unlink
- * @queue: a #GQueue
- * @link_: a #GList link that <emphasis>must</emphasis> be part of @queue
- *
- * Unlinks @link_ so that it will no longer be part of @queue. The link is
- * not freed.
- *
- * @link_ must be part of @queue,
- *
- * Since: 2.4
- **/
-void
-g_queue_unlink (GQueue *queue,
- GList *link_)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (link_ != NULL);
-
- if (link_ == queue->tail)
- queue->tail = queue->tail->prev;
-
- queue->head = g_list_remove_link (queue->head, link_);
- queue->length--;
-}
-
-/**
- * g_queue_delete_link:
- * @queue: a #GQueue
- * @link_: a #GList link that <emphasis>must</emphasis> be part of @queue
- *
- * Removes @link_ from @queue and frees it.
- *
- * @link_ must be part of @queue.
- *
- * Since: 2.4
- **/
-void
-g_queue_delete_link (GQueue *queue,
- GList *link_)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (link_ != NULL);
-
- g_queue_unlink (queue, link_);
- g_list_free (link_);
-}
-
-/**
- * g_queue_peek_head:
- * @queue: a #GQueue.
- *
- * Returns the first element of the queue.
- *
- * Returns: the data of the first element in the queue, or %NULL if the queue
- * is empty.
- **/
-gpointer
-g_queue_peek_head (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- return queue->head ? queue->head->data : NULL;
-}
-
-/**
- * g_queue_peek_tail:
- * @queue: a #GQueue.
- *
- * Returns the last element of the queue.
- *
- * Returns: the data of the last element in the queue, or %NULL if the queue
- * is empty.
- **/
-gpointer
-g_queue_peek_tail (GQueue *queue)
-{
- g_return_val_if_fail (queue != NULL, NULL);
-
- return queue->tail ? queue->tail->data : NULL;
-}
-
-/**
- * g_queue_peek_nth:
- * @queue: a #GQueue
- * @n: the position of the element.
- *
- * Returns the @n'th element of @queue.
- *
- * Return value: The data for the @n'th element of @queue, or %NULL if @n is
- * off the end of @queue.
- *
- * Since: 2.4
- **/
-gpointer
-g_queue_peek_nth (GQueue *queue,
- guint n)
-{
- GList *link;
-
- g_return_val_if_fail (queue != NULL, NULL);
-
- link = g_queue_peek_nth_link (queue, n);
-
- if (link)
- return link->data;
-
- return NULL;
-}
-
-/**
- * g_queue_index:
- * @queue: a #GQueue
- * @data: the data to find.
- *
- * Returns the position of the first element in @queue which contains @data.
- *
- * Return value: The position of the first element in @queue which contains @data, or -1 if no element in @queue contains @data.
- *
- * Since: 2.4
- **/
-gint
-g_queue_index (GQueue *queue,
- gconstpointer data)
-{
- g_return_val_if_fail (queue != NULL, -1);
-
- return g_list_index (queue->head, data);
-}
-
-/**
- * g_queue_remove:
- * @queue: a #GQueue
- * @data: data to remove.
- *
- * Removes the first element in @queue that contains @data.
- *
- * Since: 2.4
- **/
-void
-g_queue_remove (GQueue *queue,
- gconstpointer data)
-{
- GList *link;
-
- g_return_if_fail (queue != NULL);
-
- link = g_list_find (queue->head, data);
-
- if (link)
- g_queue_delete_link (queue, link);
-}
-
-/**
- * g_queue_remove_all:
- * @queue: a #GQueue
- * @data: data to remove
- *
- * Remove all elemeents in @queue which contains @data.
- *
- * Since: 2.4
- **/
-void
-g_queue_remove_all (GQueue *queue,
- gconstpointer data)
-{
- GList *list;
-
- g_return_if_fail (queue != NULL);
-
- list = queue->head;
- while (list)
- {
- GList *next = list->next;
-
- if (list->data == data)
- g_queue_delete_link (queue, list);
-
- list = next;
- }
-}
-
-/**
- * g_queue_insert_before:
- * @queue: a #GQueue
- * @sibling: a #GList link that <emphasis>must</emphasis> be part of @queue
- * @data: the data to insert
- *
- * Inserts @data into @queue before @sibling.
- *
- * @sibling must be part of @queue.
- *
- * Since: 2.4
- **/
-void
-g_queue_insert_before (GQueue *queue,
- GList *sibling,
- gpointer data)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (sibling != NULL);
-
- queue->head = g_list_insert_before (queue->head, sibling, data);
- queue->length++;
-}
-
-/**
- * g_queue_insert_after:
- * @queue: a #GQueue
- * @sibling: a #GList link that <emphasis>must</emphasis> be part of @queue
- * @data: the data to insert
- *
- * Inserts @data into @queue after @sibling
- *
- * @sibling must be part of @queue
- *
- * Since: 2.4
- **/
-void
-g_queue_insert_after (GQueue *queue,
- GList *sibling,
- gpointer data)
-{
- g_return_if_fail (queue != NULL);
- g_return_if_fail (sibling != NULL);
-
- if (sibling == queue->tail)
- g_queue_push_tail (queue, data);
- else
- g_queue_insert_before (queue, sibling->next, data);
-}
-
-/**
- * g_queue_insert_sorted:
- * @queue: a #GQueue
- * @data: the data to insert
- * @func: the #GCompareDataFunc used to compare elements in the queue. It is
- * called with two elements of the @queue and @user_data. It should
- * return 0 if the elements are equal, a negative value if the first
- * element comes before the second, and a positive value if the second
- * element comes before the first.
- * @user_data: user data passed to @func.
- *
- * Inserts @data into @queue using @func to determine the new position.
- *
- * Since: 2.4
- **/
-void
-g_queue_insert_sorted (GQueue *queue,
- gpointer data,
- GCompareDataFunc func,
- gpointer user_data)
-{
- GList *list;
-
- g_return_if_fail (queue != NULL);
-
- list = queue->head;
- while (list && func (list->data, data, user_data) < 0)
- list = list->next;
-
- if (list)
- g_queue_insert_before (queue, list, data);
- else
- g_queue_push_tail (queue, data);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/* Originally developed and coded by Makoto Matsumoto and Takuji
- * Nishimura. Please mail <matumoto@math.keio.ac.jp>, if you're using
- * code from this file in your own programs or libraries.
- * Further information on the Mersenne Twister can be found at
- * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
- * This code was adapted to glib by Sebastian Wilhelmi.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <math.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "grand.h"
-
-#include "gmain.h"
-#include "gmem.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gthreadprivate.h"
-
-#ifdef G_OS_WIN32
-#include <process.h> /* For getpid() */
-#endif
-
-/**
- * SECTION: random_numbers
- * @title: Random Numbers
- * @short_description: pseudo-random number generator
- *
- * The following functions allow you to use a portable, fast and good
- * pseudo-random number generator (PRNG). It uses the Mersenne Twister
- * PRNG, which was originally developed by Makoto Matsumoto and Takuji
- * Nishimura. Further information can be found at
- * <ulink url="http://www.math.keio.ac.jp/~matumoto/emt.html">
- * www.math.keio.ac.jp/~matumoto/emt.html</ulink>.
- *
- * If you just need a random number, you simply call the
- * <function>g_random_*</function> functions, which will create a
- * globally used #GRand and use the according
- * <function>g_rand_*</function> functions internally. Whenever you
- * need a stream of reproducible random numbers, you better create a
- * #GRand yourself and use the <function>g_rand_*</function> functions
- * directly, which will also be slightly faster. Initializing a #GRand
- * with a certain seed will produce exactly the same series of random
- * numbers on all platforms. This can thus be used as a seed for e.g.
- * games.
- *
- * The <function>g_rand*_range</function> functions will return high
- * quality equally distributed random numbers, whereas for example the
- * <literal>(g_random_int()%max)</literal> approach often
- * doesn't yield equally distributed numbers.
- *
- * GLib changed the seeding algorithm for the pseudo-random number
- * generator Mersenne Twister, as used by
- * <structname>GRand</structname> and <structname>GRandom</structname>.
- * This was necessary, because some seeds would yield very bad
- * pseudo-random streams. Also the pseudo-random integers generated by
- * <function>g_rand*_int_range()</function> will have a slightly better
- * equal distribution with the new version of GLib.
- *
- * The original seeding and generation algorithms, as found in GLib
- * 2.0.x, can be used instead of the new ones by setting the
- * environment variable <envar>G_RANDOM_VERSION</envar> to the value of
- * '2.0'. Use the GLib-2.0 algorithms only if you have sequences of
- * numbers generated with Glib-2.0 that you need to reproduce exactly.
- **/
-
-/**
- * GRand:
- *
- * The #GRand struct is an opaque data structure. It should only be
- * accessed through the <function>g_rand_*</function> functions.
- **/
-
-G_LOCK_DEFINE_STATIC (global_random);
-static GRand* global_random = NULL;
-
-/* Period parameters */
-#define N 624
-#define M 397
-#define MATRIX_A 0x9908b0df /* constant vector a */
-#define UPPER_MASK 0x80000000 /* most significant w-r bits */
-#define LOWER_MASK 0x7fffffff /* least significant r bits */
-
-/* Tempering parameters */
-#define TEMPERING_MASK_B 0x9d2c5680
-#define TEMPERING_MASK_C 0xefc60000
-#define TEMPERING_SHIFT_U(y) (y >> 11)
-#define TEMPERING_SHIFT_S(y) (y << 7)
-#define TEMPERING_SHIFT_T(y) (y << 15)
-#define TEMPERING_SHIFT_L(y) (y >> 18)
-
-static guint
-get_random_version (void)
-{
- static gboolean initialized = FALSE;
- static guint random_version;
-
- if (!initialized)
- {
- const gchar *version_string = g_getenv ("G_RANDOM_VERSION");
- if (!version_string || version_string[0] == '\000' ||
- strcmp (version_string, "2.2") == 0)
- random_version = 22;
- else if (strcmp (version_string, "2.0") == 0)
- random_version = 20;
- else
- {
- g_warning ("Unknown G_RANDOM_VERSION \"%s\". Using version 2.2.",
- version_string);
- random_version = 22;
- }
- initialized = TRUE;
- }
-
- return random_version;
-}
-
-/* This is called from g_thread_init(). It's used to
- * initialize some static data in a threadsafe way.
- */
-void
-_g_rand_thread_init (void)
-{
- (void)get_random_version ();
-}
-
-struct _GRand
-{
- guint32 mt[N]; /* the array for the state vector */
- guint mti;
-};
-
-/**
- * g_rand_new_with_seed:
- * @seed: a value to initialize the random number generator.
- *
- * Creates a new random number generator initialized with @seed.
- *
- * Return value: the new #GRand.
- **/
-GRand*
-g_rand_new_with_seed (guint32 seed)
-{
- GRand *rand = g_new0 (GRand, 1);
- g_rand_set_seed (rand, seed);
- return rand;
-}
-
-/**
- * g_rand_new_with_seed_array:
- * @seed: an array of seeds to initialize the random number generator.
- * @seed_length: an array of seeds to initialize the random number generator.
- *
- * Creates a new random number generator initialized with @seed.
- *
- * Return value: the new #GRand.
- *
- * Since: 2.4
- **/
-GRand*
-g_rand_new_with_seed_array (const guint32 *seed, guint seed_length)
-{
- GRand *rand = g_new0 (GRand, 1);
- g_rand_set_seed_array (rand, seed, seed_length);
- return rand;
-}
-
-/**
- * g_rand_new:
- *
- * Creates a new random number generator initialized with a seed taken
- * either from <filename>/dev/urandom</filename> (if existing) or from
- * the current time (as a fallback).
- *
- * Return value: the new #GRand.
- **/
-GRand*
-g_rand_new (void)
-{
- guint32 seed[4];
- GTimeVal now;
-#ifdef G_OS_UNIX
- static gboolean dev_urandom_exists = TRUE;
-
- if (dev_urandom_exists)
- {
- FILE* dev_urandom;
-
- do
- {
- errno = 0;
- dev_urandom = fopen("/dev/urandom", "rb");
- }
- while G_UNLIKELY (errno == EINTR);
-
- if (dev_urandom)
- {
- int r;
-
- setvbuf (dev_urandom, NULL, _IONBF, 0);
- do
- {
- errno = 0;
- r = fread (seed, sizeof (seed), 1, dev_urandom);
- }
- while G_UNLIKELY (errno == EINTR);
-
- if (r != 1)
- dev_urandom_exists = FALSE;
-
- fclose (dev_urandom);
- }
- else
- dev_urandom_exists = FALSE;
- }
-#else
- static gboolean dev_urandom_exists = FALSE;
-#endif
-
- if (!dev_urandom_exists)
- {
- g_get_current_time (&now);
- seed[0] = now.tv_sec;
- seed[1] = now.tv_usec;
- seed[2] = getpid ();
-#ifdef G_OS_UNIX
- seed[3] = getppid ();
-#else
- seed[3] = 0;
-#endif
- }
-
- return g_rand_new_with_seed_array (seed, 4);
-}
-
-/**
- * g_rand_free:
- * @rand_: a #GRand.
- *
- * Frees the memory allocated for the #GRand.
- **/
-void
-g_rand_free (GRand* rand)
-{
- g_return_if_fail (rand != NULL);
-
- g_free (rand);
-}
-
-/**
- * g_rand_copy:
- * @rand_: a #GRand.
- *
- * Copies a #GRand into a new one with the same exact state as before.
- * This way you can take a snapshot of the random number generator for
- * replaying later.
- *
- * Return value: the new #GRand.
- *
- * Since: 2.4
- **/
-GRand *
-g_rand_copy (GRand* rand)
-{
- GRand* new_rand;
-
- g_return_val_if_fail (rand != NULL, NULL);
-
- new_rand = g_new0 (GRand, 1);
- memcpy (new_rand, rand, sizeof (GRand));
-
- return new_rand;
-}
-
-/**
- * g_rand_set_seed:
- * @rand_: a #GRand.
- * @seed: a value to reinitialize the random number generator.
- *
- * Sets the seed for the random number generator #GRand to @seed.
- **/
-void
-g_rand_set_seed (GRand* rand, guint32 seed)
-{
- g_return_if_fail (rand != NULL);
-
- switch (get_random_version ())
- {
- case 20:
- /* setting initial seeds to mt[N] using */
- /* the generator Line 25 of Table 1 in */
- /* [KNUTH 1981, The Art of Computer Programming */
- /* Vol. 2 (2nd Ed.), pp102] */
-
- if (seed == 0) /* This would make the PRNG procude only zeros */
- seed = 0x6b842128; /* Just set it to another number */
-
- rand->mt[0]= seed;
- for (rand->mti=1; rand->mti<N; rand->mti++)
- rand->mt[rand->mti] = (69069 * rand->mt[rand->mti-1]);
-
- break;
- case 22:
- /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
- /* In the previous version (see above), MSBs of the */
- /* seed affect only MSBs of the array mt[]. */
-
- rand->mt[0]= seed;
- for (rand->mti=1; rand->mti<N; rand->mti++)
- rand->mt[rand->mti] = 1812433253UL *
- (rand->mt[rand->mti-1] ^ (rand->mt[rand->mti-1] >> 30)) + rand->mti;
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-/**
- * g_rand_set_seed_array:
- * @rand_: a #GRand.
- * @seed: array to initialize with
- * @seed_length: length of array
- *
- * Initializes the random number generator by an array of
- * longs. Array can be of arbitrary size, though only the
- * first 624 values are taken. This function is useful
- * if you have many low entropy seeds, or if you require more then
- * 32bits of actual entropy for your application.
- *
- * Since: 2.4
- **/
-void
-g_rand_set_seed_array (GRand* rand, const guint32 *seed, guint seed_length)
-{
- int i, j, k;
-
- g_return_if_fail (rand != NULL);
- g_return_if_fail (seed_length >= 1);
-
- g_rand_set_seed (rand, 19650218UL);
-
- i=1; j=0;
- k = (N>seed_length ? N : seed_length);
- for (; k; k--)
- {
- rand->mt[i] = (rand->mt[i] ^
- ((rand->mt[i-1] ^ (rand->mt[i-1] >> 30)) * 1664525UL))
- + seed[j] + j; /* non linear */
- rand->mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
- i++; j++;
- if (i>=N)
- {
- rand->mt[0] = rand->mt[N-1];
- i=1;
- }
- if (j>=seed_length)
- j=0;
- }
- for (k=N-1; k; k--)
- {
- rand->mt[i] = (rand->mt[i] ^
- ((rand->mt[i-1] ^ (rand->mt[i-1] >> 30)) * 1566083941UL))
- - i; /* non linear */
- rand->mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
- i++;
- if (i>=N)
- {
- rand->mt[0] = rand->mt[N-1];
- i=1;
- }
- }
-
- rand->mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
-}
-
-/**
- * g_rand_boolean:
- * @rand_: a #GRand.
- * @Returns: a random #gboolean.
- *
- * Returns a random #gboolean from @rand_. This corresponds to a
- * unbiased coin toss.
- **/
-/**
- * g_rand_int:
- * @rand_: a #GRand.
- *
- * Returns the next random #guint32 from @rand_ equally distributed over
- * the range [0..2^32-1].
- *
- * Return value: A random number.
- **/
-guint32
-g_rand_int (GRand* rand)
-{
- guint32 y;
- static const guint32 mag01[2]={0x0, MATRIX_A};
- /* mag01[x] = x * MATRIX_A for x=0,1 */
-
- g_return_val_if_fail (rand != NULL, 0);
-
- if (rand->mti >= N) { /* generate N words at one time */
- int kk;
-
- for (kk=0;kk<N-M;kk++) {
- y = (rand->mt[kk]&UPPER_MASK)|(rand->mt[kk+1]&LOWER_MASK);
- rand->mt[kk] = rand->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
- }
- for (;kk<N-1;kk++) {
- y = (rand->mt[kk]&UPPER_MASK)|(rand->mt[kk+1]&LOWER_MASK);
- rand->mt[kk] = rand->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
- }
- y = (rand->mt[N-1]&UPPER_MASK)|(rand->mt[0]&LOWER_MASK);
- rand->mt[N-1] = rand->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
-
- rand->mti = 0;
- }
-
- y = rand->mt[rand->mti++];
- y ^= TEMPERING_SHIFT_U(y);
- y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
- y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
- y ^= TEMPERING_SHIFT_L(y);
-
- return y;
-}
-
-/* transform [0..2^32] -> [0..1] */
-#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10
-
-/**
- * g_rand_int_range:
- * @rand_: a #GRand.
- * @begin: lower closed bound of the interval.
- * @end: upper open bound of the interval.
- *
- * Returns the next random #gint32 from @rand_ equally distributed over
- * the range [@begin..@end-1].
- *
- * Return value: A random number.
- **/
-gint32
-g_rand_int_range (GRand* rand, gint32 begin, gint32 end)
-{
- guint32 dist = end - begin;
- guint32 random;
-
- g_return_val_if_fail (rand != NULL, begin);
- g_return_val_if_fail (end > begin, begin);
-
- switch (get_random_version ())
- {
- case 20:
- if (dist <= 0x10000L) /* 2^16 */
- {
- /* This method, which only calls g_rand_int once is only good
- * for (end - begin) <= 2^16, because we only have 32 bits set
- * from the one call to g_rand_int (). */
-
- /* we are using (trans + trans * trans), because g_rand_int only
- * covers [0..2^32-1] and thus g_rand_int * trans only covers
- * [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
- */
-
- gdouble double_rand = g_rand_int (rand) *
- (G_RAND_DOUBLE_TRANSFORM +
- G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
-
- random = (gint32) (double_rand * dist);
- }
- else
- {
- /* Now we use g_rand_double_range (), which will set 52 bits for
- us, so that it is safe to round and still get a decent
- distribution */
- random = (gint32) g_rand_double_range (rand, 0, dist);
- }
- break;
- case 22:
- if (dist == 0)
- random = 0;
- else
- {
- /* maxvalue is set to the predecessor of the greatest
- * multiple of dist less or equal 2^32. */
- guint32 maxvalue;
- if (dist <= 0x80000000u) /* 2^31 */
- {
- /* maxvalue = 2^32 - 1 - (2^32 % dist) */
- guint32 leftover = (0x80000000u % dist) * 2;
- if (leftover >= dist) leftover -= dist;
- maxvalue = 0xffffffffu - leftover;
- }
- else
- maxvalue = dist - 1;
-
- do
- random = g_rand_int (rand);
- while (random > maxvalue);
-
- random %= dist;
- }
- break;
- default:
- random = 0; /* Quiet GCC */
- g_assert_not_reached ();
- }
-
- return begin + random;
-}
-
-/**
- * g_rand_double:
- * @rand_: a #GRand.
- *
- * Returns the next random #gdouble from @rand_ equally distributed over
- * the range [0..1).
- *
- * Return value: A random number.
- **/
-gdouble
-g_rand_double (GRand* rand)
-{
- /* We set all 52 bits after the point for this, not only the first
- 32. Thats why we need two calls to g_rand_int */
- gdouble retval = g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM;
- retval = (retval + g_rand_int (rand)) * G_RAND_DOUBLE_TRANSFORM;
-
- /* The following might happen due to very bad rounding luck, but
- * actually this should be more than rare, we just try again then */
- if (retval >= 1.0)
- return g_rand_double (rand);
-
- return retval;
-}
-
-/**
- * g_rand_double_range:
- * @rand_: a #GRand.
- * @begin: lower closed bound of the interval.
- * @end: upper open bound of the interval.
- *
- * Returns the next random #gdouble from @rand_ equally distributed over
- * the range [@begin..@end).
- *
- * Return value: A random number.
- **/
-gdouble
-g_rand_double_range (GRand* rand, gdouble begin, gdouble end)
-{
- return g_rand_double (rand) * (end - begin) + begin;
-}
-
-/**
- * g_random_boolean:
- * @Returns: a random #gboolean.
- *
- * Returns a random #gboolean. This corresponds to a unbiased coin toss.
- **/
-/**
- * g_random_int:
- *
- * Return a random #guint32 equally distributed over the range
- * [0..2^32-1].
- *
- * Return value: A random number.
- **/
-guint32
-g_random_int (void)
-{
- guint32 result;
- G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_int (global_random);
- G_UNLOCK (global_random);
- return result;
-}
-
-/**
- * g_random_int_range:
- * @begin: lower closed bound of the interval.
- * @end: upper open bound of the interval.
- *
- * Returns a random #gint32 equally distributed over the range
- * [@begin..@end-1].
- *
- * Return value: A random number.
- **/
-gint32
-g_random_int_range (gint32 begin, gint32 end)
-{
- gint32 result;
- G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_int_range (global_random, begin, end);
- G_UNLOCK (global_random);
- return result;
-}
-
-/**
- * g_random_double:
- *
- * Returns a random #gdouble equally distributed over the range [0..1).
- *
- * Return value: A random number.
- **/
-gdouble
-g_random_double (void)
-{
- double result;
- G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_double (global_random);
- G_UNLOCK (global_random);
- return result;
-}
-
-/**
- * g_random_double_range:
- * @begin: lower closed bound of the interval.
- * @end: upper open bound of the interval.
- *
- * Returns a random #gdouble equally distributed over the range [@begin..@end).
- *
- * Return value: A random number.
- **/
-gdouble
-g_random_double_range (gdouble begin, gdouble end)
-{
- double result;
- G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_double_range (global_random, begin, end);
- G_UNLOCK (global_random);
- return result;
-}
-
-/**
- * g_random_set_seed:
- * @seed: a value to reinitialize the global random number generator.
- *
- * Sets the seed for the global random number generator, which is used
- * by the <function>g_random_*</function> functions, to @seed.
- **/
-void
-g_random_set_seed (guint32 seed)
-{
- G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new_with_seed (seed);
- else
- g_rand_set_seed (global_random, seed);
- G_UNLOCK (global_random);
-}
+++ /dev/null
-/* GRegex -- regular expression API wrapper around PCRE.
- *
- * Copyright (C) 1999, 2000 Scott Wimer
- * Copyright (C) 2004, Matthias Clasen <mclasen@redhat.com>
- * Copyright (C) 2005 - 2007, Marco Barisione <marco@barisione.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#ifdef USE_SYSTEM_PCRE
-#include <pcre.h>
-#else
-#include "pcre/pcre.h"
-#endif
-
-#include "gtypes.h"
-#include "gregex.h"
-#include "glibintl.h"
-#include "glist.h"
-#include "gmessages.h"
-#include "gstrfuncs.h"
-#include "gatomic.h"
-
-/**
- * SECTION:gregex
- * @title: Perl-compatible regular expressions
- * @short_description: matches strings against regular expressions
- * @see_also: <xref linkend="glib-regex-syntax"/>
- *
- * The <function>g_regex_*()</function> functions implement regular
- * expression pattern matching using syntax and semantics similar to
- * Perl regular expression.
- *
- * Some functions accept a @start_position argument, setting it differs
- * from just passing over a shortened string and setting #G_REGEX_MATCH_NOTBOL
- * in the case of a pattern that begins with any kind of lookbehind assertion.
- * For example, consider the pattern "\Biss\B" which finds occurrences of "iss"
- * in the middle of words. ("\B" matches only if the current position in the
- * subject is not a word boundary.) When applied to the string "Mississipi"
- * from the fourth byte, namely "issipi", it does not match, because "\B" is
- * always false at the start of the subject, which is deemed to be a word
- * boundary. However, if the entire string is passed , but with
- * @start_position set to 4, it finds the second occurrence of "iss" because
- * it is able to look behind the starting point to discover that it is
- * preceded by a letter.
- *
- * Note that, unless you set the #G_REGEX_RAW flag, all the strings passed
- * to these functions must be encoded in UTF-8. The lengths and the positions
- * inside the strings are in bytes and not in characters, so, for instance,
- * "\xc3\xa0" (i.e. "à") is two bytes long but it is treated as a
- * single character. If you set #G_REGEX_RAW the strings can be non-valid
- * UTF-8 strings and a byte is treated as a character, so "\xc3\xa0" is two
- * bytes and two characters long.
- *
- * When matching a pattern, "\n" matches only against a "\n" character in
- * the string, and "\r" matches only a "\r" character. To match any newline
- * sequence use "\R". This particular group matches either the two-character
- * sequence CR + LF ("\r\n"), or one of the single characters LF (linefeed,
- * U+000A, "\n"), VT vertical tab, U+000B, "\v"), FF (formfeed, U+000C, "\f"),
- * CR (carriage return, U+000D, "\r"), NEL (next line, U+0085), LS (line
- * separator, U+2028), or PS (paragraph separator, U+2029).
- *
- * The behaviour of the dot, circumflex, and dollar metacharacters are
- * affected by newline characters, the default is to recognize any newline
- * character (the same characters recognized by "\R"). This can be changed
- * with #G_REGEX_NEWLINE_CR, #G_REGEX_NEWLINE_LF and #G_REGEX_NEWLINE_CRLF
- * compile options, and with #G_REGEX_MATCH_NEWLINE_ANY,
- * #G_REGEX_MATCH_NEWLINE_CR, #G_REGEX_MATCH_NEWLINE_LF and
- * #G_REGEX_MATCH_NEWLINE_CRLF match options. These settings are also
- * relevant when compiling a pattern if #G_REGEX_EXTENDED is set, and an
- * unescaped "#" outside a character class is encountered. This indicates
- * a comment that lasts until after the next newline.
- *
- * Creating and manipulating the same #GRegex structure from different
- * threads is not a problem as #GRegex does not modify its internal
- * state between creation and destruction, on the other hand #GMatchInfo
- * is not threadsafe.
- *
- * The regular expressions low-level functionalities are obtained through
- * the excellent <ulink url="http://www.pcre.org/">PCRE</ulink> library
- * written by Philip Hazel.
- */
-
-/* Mask of all the possible values for GRegexCompileFlags. */
-#define G_REGEX_COMPILE_MASK (G_REGEX_CASELESS | \
- G_REGEX_MULTILINE | \
- G_REGEX_DOTALL | \
- G_REGEX_EXTENDED | \
- G_REGEX_ANCHORED | \
- G_REGEX_DOLLAR_ENDONLY | \
- G_REGEX_UNGREEDY | \
- G_REGEX_RAW | \
- G_REGEX_NO_AUTO_CAPTURE | \
- G_REGEX_OPTIMIZE | \
- G_REGEX_DUPNAMES | \
- G_REGEX_NEWLINE_CR | \
- G_REGEX_NEWLINE_LF | \
- G_REGEX_NEWLINE_CRLF)
-
-/* Mask of all the possible values for GRegexMatchFlags. */
-#define G_REGEX_MATCH_MASK (G_REGEX_MATCH_ANCHORED | \
- G_REGEX_MATCH_NOTBOL | \
- G_REGEX_MATCH_NOTEOL | \
- G_REGEX_MATCH_NOTEMPTY | \
- G_REGEX_MATCH_PARTIAL | \
- G_REGEX_MATCH_NEWLINE_CR | \
- G_REGEX_MATCH_NEWLINE_LF | \
- G_REGEX_MATCH_NEWLINE_CRLF | \
- G_REGEX_MATCH_NEWLINE_ANY)
-
-/* if the string is in UTF-8 use g_utf8_ functions, else use
- * use just +/- 1. */
-#define NEXT_CHAR(re, s) (((re)->compile_opts & PCRE_UTF8) ? \
- g_utf8_next_char (s) : \
- ((s) + 1))
-#define PREV_CHAR(re, s) (((re)->compile_opts & PCRE_UTF8) ? \
- g_utf8_prev_char (s) : \
- ((s) - 1))
-
-struct _GMatchInfo
-{
- GRegex *regex; /* the regex */
- GRegexMatchFlags match_opts; /* options used at match time on the regex */
- gint matches; /* number of matching sub patterns */
- gint pos; /* position in the string where last match left off */
- gint *offsets; /* array of offsets paired 0,1 ; 2,3 ; 3,4 etc */
- gint n_offsets; /* number of offsets */
- gint *workspace; /* workspace for pcre_dfa_exec() */
- gint n_workspace; /* number of workspace elements */
- const gchar *string; /* string passed to the match function */
- gssize string_len; /* length of string */
-};
-
-struct _GRegex
-{
- volatile gint ref_count; /* the ref count for the immutable part */
- gchar *pattern; /* the pattern */
- pcre *pcre_re; /* compiled form of the pattern */
- GRegexCompileFlags compile_opts; /* options used at compile time on the pattern */
- GRegexMatchFlags match_opts; /* options used at match time on the regex */
- pcre_extra *extra; /* data stored when G_REGEX_OPTIMIZE is used */
-};
-
-/* TRUE if ret is an error code, FALSE otherwise. */
-#define IS_PCRE_ERROR(ret) ((ret) < PCRE_ERROR_NOMATCH && (ret) != PCRE_ERROR_PARTIAL)
-
-typedef struct _InterpolationData InterpolationData;
-static gboolean interpolation_list_needs_match (GList *list);
-static gboolean interpolate_replacement (const GMatchInfo *match_info,
- GString *result,
- gpointer data);
-static GList *split_replacement (const gchar *replacement,
- GError **error);
-static void free_interpolation_data (InterpolationData *data);
-
-
-static const gchar *
-match_error (gint errcode)
-{
- switch (errcode)
- {
- case PCRE_ERROR_NOMATCH:
- /* not an error */
- break;
- case PCRE_ERROR_NULL:
- /* NULL argument, this should not happen in GRegex */
- g_warning ("A NULL argument was passed to PCRE");
- break;
- case PCRE_ERROR_BADOPTION:
- return "bad options";
- case PCRE_ERROR_BADMAGIC:
- return _("corrupted object");
- case PCRE_ERROR_UNKNOWN_OPCODE:
- return N_("internal error or corrupted object");
- case PCRE_ERROR_NOMEMORY:
- return _("out of memory");
- case PCRE_ERROR_NOSUBSTRING:
- /* not used by pcre_exec() */
- break;
- case PCRE_ERROR_MATCHLIMIT:
- return _("backtracking limit reached");
- case PCRE_ERROR_CALLOUT:
- /* callouts are not implemented */
- break;
- case PCRE_ERROR_BADUTF8:
- case PCRE_ERROR_BADUTF8_OFFSET:
- /* we do not check if strings are valid */
- break;
- case PCRE_ERROR_PARTIAL:
- /* not an error */
- break;
- case PCRE_ERROR_BADPARTIAL:
- return _("the pattern contains items not supported for partial matching");
- case PCRE_ERROR_INTERNAL:
- return _("internal error");
- case PCRE_ERROR_BADCOUNT:
- /* negative ovecsize, this should not happen in GRegex */
- g_warning ("A negative ovecsize was passed to PCRE");
- break;
- case PCRE_ERROR_DFA_UITEM:
- return _("the pattern contains items not supported for partial matching");
- case PCRE_ERROR_DFA_UCOND:
- return _("back references as conditions are not supported for partial matching");
- case PCRE_ERROR_DFA_UMLIMIT:
- /* the match_field field is not used in GRegex */
- break;
- case PCRE_ERROR_DFA_WSSIZE:
- /* handled expanding the workspace */
- break;
- case PCRE_ERROR_DFA_RECURSE:
- case PCRE_ERROR_RECURSIONLIMIT:
- return _("recursion limit reached");
- case PCRE_ERROR_NULLWSLIMIT:
- return _("workspace limit for empty substrings reached");
- case PCRE_ERROR_BADNEWLINE:
- return _("invalid combination of newline flags");
- default:
- break;
- }
- return _("unknown error");
-}
-
-static void
-translate_compile_error (gint *errcode, const gchar **errmsg)
-{
- /* Compile errors are created adding 100 to the error code returned
- * by PCRE.
- * If errcode is known we put the translatable error message in
- * erromsg. If errcode is unknown we put the generic
- * G_REGEX_ERROR_COMPILE error code in errcode and keep the
- * untranslated error message returned by PCRE.
- * Note that there can be more PCRE errors with the same GRegexError
- * and that some PCRE errors are useless for us.
- */
- *errcode += 100;
-
- switch (*errcode)
- {
- case G_REGEX_ERROR_STRAY_BACKSLASH:
- *errmsg = _("\\ at end of pattern");
- break;
- case G_REGEX_ERROR_MISSING_CONTROL_CHAR:
- *errmsg = _("\\c at end of pattern");
- break;
- case G_REGEX_ERROR_UNRECOGNIZED_ESCAPE:
- *errmsg = _("unrecognized character follows \\");
- break;
- case 137:
- /* A number of Perl escapes are not handled by PCRE.
- * Therefore it explicitly raises ERR37.
- */
- *errcode = G_REGEX_ERROR_UNRECOGNIZED_ESCAPE;
- *errmsg = _("case-changing escapes (\\l, \\L, \\u, \\U) are not allowed here");
- break;
- case G_REGEX_ERROR_QUANTIFIERS_OUT_OF_ORDER:
- *errmsg = _("numbers out of order in {} quantifier");
- break;
- case G_REGEX_ERROR_QUANTIFIER_TOO_BIG:
- *errmsg = _("number too big in {} quantifier");
- break;
- case G_REGEX_ERROR_UNTERMINATED_CHARACTER_CLASS:
- *errmsg = _("missing terminating ] for character class");
- break;
- case G_REGEX_ERROR_INVALID_ESCAPE_IN_CHARACTER_CLASS:
- *errmsg = _("invalid escape sequence in character class");
- break;
- case G_REGEX_ERROR_RANGE_OUT_OF_ORDER:
- *errmsg = _("range out of order in character class");
- break;
- case G_REGEX_ERROR_NOTHING_TO_REPEAT:
- *errmsg = _("nothing to repeat");
- break;
- case G_REGEX_ERROR_UNRECOGNIZED_CHARACTER:
- *errmsg = _("unrecognized character after (?");
- break;
- case 124:
- *errcode = G_REGEX_ERROR_UNRECOGNIZED_CHARACTER;
- *errmsg = _("unrecognized character after (?<");
- break;
- case 141:
- *errcode = G_REGEX_ERROR_UNRECOGNIZED_CHARACTER;
- *errmsg = _("unrecognized character after (?P");
- break;
- case G_REGEX_ERROR_POSIX_NAMED_CLASS_OUTSIDE_CLASS:
- *errmsg = _("POSIX named classes are supported only within a class");
- break;
- case G_REGEX_ERROR_UNMATCHED_PARENTHESIS:
- *errmsg = _("missing terminating )");
- break;
- case 122:
- *errcode = G_REGEX_ERROR_UNMATCHED_PARENTHESIS;
- *errmsg = _(") without opening (");
- break;
- case 129:
- *errcode = G_REGEX_ERROR_UNMATCHED_PARENTHESIS;
- /* translators: '(?R' and '(?[+-]digits' are both meant as (groups of)
- * sequences here, '(?-54' would be an example for the second group.
- */
- *errmsg = _("(?R or (?[+-]digits must be followed by )");
- break;
- case G_REGEX_ERROR_INEXISTENT_SUBPATTERN_REFERENCE:
- *errmsg = _("reference to non-existent subpattern");
- break;
- case G_REGEX_ERROR_UNTERMINATED_COMMENT:
- *errmsg = _("missing ) after comment");
- break;
- case G_REGEX_ERROR_EXPRESSION_TOO_LARGE:
- *errmsg = _("regular expression too large");
- break;
- case G_REGEX_ERROR_MEMORY_ERROR:
- *errmsg = _("failed to get memory");
- break;
- case G_REGEX_ERROR_VARIABLE_LENGTH_LOOKBEHIND:
- *errmsg = _("lookbehind assertion is not fixed length");
- break;
- case G_REGEX_ERROR_MALFORMED_CONDITION:
- *errmsg = _("malformed number or name after (?(");
- break;
- case G_REGEX_ERROR_TOO_MANY_CONDITIONAL_BRANCHES:
- *errmsg = _("conditional group contains more than two branches");
- break;
- case G_REGEX_ERROR_ASSERTION_EXPECTED:
- *errmsg = _("assertion expected after (?(");
- break;
- case G_REGEX_ERROR_UNKNOWN_POSIX_CLASS_NAME:
- *errmsg = _("unknown POSIX class name");
- break;
- case G_REGEX_ERROR_POSIX_COLLATING_ELEMENTS_NOT_SUPPORTED:
- *errmsg = _("POSIX collating elements are not supported");
- break;
- case G_REGEX_ERROR_HEX_CODE_TOO_LARGE:
- *errmsg = _("character value in \\x{...} sequence is too large");
- break;
- case G_REGEX_ERROR_INVALID_CONDITION:
- *errmsg = _("invalid condition (?(0)");
- break;
- case G_REGEX_ERROR_SINGLE_BYTE_MATCH_IN_LOOKBEHIND:
- *errmsg = _("\\C not allowed in lookbehind assertion");
- break;
- case G_REGEX_ERROR_INFINITE_LOOP:
- *errmsg = _("recursive call could loop indefinitely");
- break;
- case G_REGEX_ERROR_MISSING_SUBPATTERN_NAME_TERMINATOR:
- *errmsg = _("missing terminator in subpattern name");
- break;
- case G_REGEX_ERROR_DUPLICATE_SUBPATTERN_NAME:
- *errmsg = _("two named subpatterns have the same name");
- break;
- case G_REGEX_ERROR_MALFORMED_PROPERTY:
- *errmsg = _("malformed \\P or \\p sequence");
- break;
- case G_REGEX_ERROR_UNKNOWN_PROPERTY:
- *errmsg = _("unknown property name after \\P or \\p");
- break;
- case G_REGEX_ERROR_SUBPATTERN_NAME_TOO_LONG:
- *errmsg = _("subpattern name is too long (maximum 32 characters)");
- break;
- case G_REGEX_ERROR_TOO_MANY_SUBPATTERNS:
- *errmsg = _("too many named subpatterns (maximum 10,000)");
- break;
- case G_REGEX_ERROR_INVALID_OCTAL_VALUE:
- *errmsg = _("octal value is greater than \\377");
- break;
- case G_REGEX_ERROR_TOO_MANY_BRANCHES_IN_DEFINE:
- *errmsg = _("DEFINE group contains more than one branch");
- break;
- case G_REGEX_ERROR_DEFINE_REPETION:
- *errmsg = _("repeating a DEFINE group is not allowed");
- break;
- case G_REGEX_ERROR_INCONSISTENT_NEWLINE_OPTIONS:
- *errmsg = _("inconsistent NEWLINE options");
- break;
- case G_REGEX_ERROR_MISSING_BACK_REFERENCE:
- *errmsg = _("\\g is not followed by a braced name or an optionally "
- "braced non-zero number");
- break;
- case 11:
- *errcode = G_REGEX_ERROR_INTERNAL;
- *errmsg = _("unexpected repeat");
- break;
- case 23:
- *errcode = G_REGEX_ERROR_INTERNAL;
- *errmsg = _("code overflow");
- break;
- case 52:
- *errcode = G_REGEX_ERROR_INTERNAL;
- *errmsg = _("overran compiling workspace");
- break;
- case 53:
- *errcode = G_REGEX_ERROR_INTERNAL;
- *errmsg = _("previously-checked referenced subpattern not found");
- break;
- case 16:
- /* This should not happen as we never pass a NULL erroffset */
- g_warning ("erroffset passed as NULL");
- *errcode = G_REGEX_ERROR_COMPILE;
- break;
- case 17:
- /* This should not happen as we check options before passing them
- * to pcre_compile2() */
- g_warning ("unknown option bit(s) set");
- *errcode = G_REGEX_ERROR_COMPILE;
- break;
- case 32:
- case 44:
- case 45:
- /* These errors should not happen as we are using an UTF8-enabled PCRE
- * and we do not check if strings are valid */
- g_warning ("%s", *errmsg);
- *errcode = G_REGEX_ERROR_COMPILE;
- break;
- default:
- *errcode = G_REGEX_ERROR_COMPILE;
- }
-}
-
-/* GMatchInfo */
-
-static GMatchInfo *
-match_info_new (const GRegex *regex,
- const gchar *string,
- gint string_len,
- gint start_position,
- gint match_options,
- gboolean is_dfa)
-{
- GMatchInfo *match_info;
-
- if (string_len < 0)
- string_len = strlen (string);
-
- match_info = g_new0 (GMatchInfo, 1);
- match_info->regex = g_regex_ref ((GRegex *)regex);
- match_info->string = string;
- match_info->string_len = string_len;
- match_info->matches = PCRE_ERROR_NOMATCH;
- match_info->pos = start_position;
- match_info->match_opts = match_options;
-
- if (is_dfa)
- {
- /* These values should be enough for most cases, if they are not
- * enough g_regex_match_all_full() will expand them. */
- match_info->n_offsets = 24;
- match_info->n_workspace = 100;
- match_info->workspace = g_new (gint, match_info->n_workspace);
- }
- else
- {
- gint capture_count;
- pcre_fullinfo (regex->pcre_re, regex->extra,
- PCRE_INFO_CAPTURECOUNT, &capture_count);
- match_info->n_offsets = (capture_count + 1) * 3;
- }
-
- match_info->offsets = g_new0 (gint, match_info->n_offsets);
- /* Set an invalid position for the previous match. */
- match_info->offsets[0] = -1;
- match_info->offsets[1] = -1;
-
- return match_info;
-}
-
-/**
- * g_match_info_get_regex:
- * @match_info: a #GMatchInfo
- *
- * Returns #GRegex object used in @match_info. It belongs to Glib
- * and must not be freed. Use g_regex_ref() if you need to keep it
- * after you free @match_info object.
- *
- * Returns: #GRegex object used in @match_info
- *
- * Since: 2.14
- */
-GRegex *
-g_match_info_get_regex (const GMatchInfo *match_info)
-{
- g_return_val_if_fail (match_info != NULL, NULL);
- return match_info->regex;
-}
-
-/**
- * g_match_info_get_string:
- * @match_info: a #GMatchInfo
- *
- * Returns the string searched with @match_info. This is the
- * string passed to g_regex_match() or g_regex_replace() so
- * you may not free it before calling this function.
- *
- * Returns: the string searched with @match_info
- *
- * Since: 2.14
- */
-const gchar *
-g_match_info_get_string (const GMatchInfo *match_info)
-{
- g_return_val_if_fail (match_info != NULL, NULL);
- return match_info->string;
-}
-
-/**
- * g_match_info_free:
- * @match_info: a #GMatchInfo
- *
- * Frees all the memory associated with the #GMatchInfo structure.
- *
- * Since: 2.14
- */
-void
-g_match_info_free (GMatchInfo *match_info)
-{
- if (match_info)
- {
- g_regex_unref (match_info->regex);
- g_free (match_info->offsets);
- g_free (match_info->workspace);
- g_free (match_info);
- }
-}
-
-/**
- * g_match_info_next:
- * @match_info: a #GMatchInfo structure
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Scans for the next match using the same parameters of the previous
- * call to g_regex_match_full() or g_regex_match() that returned
- * @match_info.
- *
- * The match is done on the string passed to the match function, so you
- * cannot free it before calling this function.
- *
- * Returns: %TRUE is the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_match_info_next (GMatchInfo *match_info,
- GError **error)
-{
- gint prev_match_start;
- gint prev_match_end;
-
- g_return_val_if_fail (match_info != NULL, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail (match_info->pos >= 0, FALSE);
-
- prev_match_start = match_info->offsets[0];
- prev_match_end = match_info->offsets[1];
-
- match_info->matches = pcre_exec (match_info->regex->pcre_re,
- match_info->regex->extra,
- match_info->string,
- match_info->string_len,
- match_info->pos,
- match_info->regex->match_opts | match_info->match_opts,
- match_info->offsets,
- match_info->n_offsets);
- if (IS_PCRE_ERROR (match_info->matches))
- {
- g_set_error (error, G_REGEX_ERROR, G_REGEX_ERROR_MATCH,
- _("Error while matching regular expression %s: %s"),
- match_info->regex->pattern, match_error (match_info->matches));
- return FALSE;
- }
-
- /* avoid infinite loops if the pattern is an empty string or something
- * equivalent */
- if (match_info->pos == match_info->offsets[1])
- {
- if (match_info->pos > match_info->string_len)
- {
- /* we have reached the end of the string */
- match_info->pos = -1;
- match_info->matches = PCRE_ERROR_NOMATCH;
- return FALSE;
- }
-
- match_info->pos = NEXT_CHAR (match_info->regex,
- &match_info->string[match_info->pos]) -
- match_info->string;
- }
- else
- {
- match_info->pos = match_info->offsets[1];
- }
-
- /* it's possible to get two identical matches when we are matching
- * empty strings, for instance if the pattern is "(?=[A-Z0-9])" and
- * the string is "RegExTest" we have:
- * - search at position 0: match from 0 to 0
- * - search at position 1: match from 3 to 3
- * - search at position 3: match from 3 to 3 (duplicate)
- * - search at position 4: match from 5 to 5
- * - search at position 5: match from 5 to 5 (duplicate)
- * - search at position 6: no match -> stop
- * so we have to ignore the duplicates.
- * see bug #515944: http://bugzilla.gnome.org/show_bug.cgi?id=515944 */
- if (match_info->matches >= 0 &&
- prev_match_start == match_info->offsets[0] &&
- prev_match_end == match_info->offsets[1])
- {
- /* ignore this match and search the next one */
- return g_match_info_next (match_info, error);
- }
-
- return match_info->matches >= 0;
-}
-
-/**
- * g_match_info_matches:
- * @match_info: a #GMatchInfo structure
- *
- * Returns whether the previous match operation succeeded.
- *
- * Returns: %TRUE if the previous match operation succeeded,
- * %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_match_info_matches (const GMatchInfo *match_info)
-{
- g_return_val_if_fail (match_info != NULL, FALSE);
-
- return match_info->matches >= 0;
-}
-
-/**
- * g_match_info_get_match_count:
- * @match_info: a #GMatchInfo structure
- *
- * Retrieves the number of matched substrings (including substring 0,
- * that is the whole matched text), so 1 is returned if the pattern
- * has no substrings in it and 0 is returned if the match failed.
- *
- * If the last match was obtained using the DFA algorithm, that is
- * using g_regex_match_all() or g_regex_match_all_full(), the retrieved
- * count is not that of the number of capturing parentheses but that of
- * the number of matched substrings.
- *
- * Returns: Number of matched substrings, or -1 if an error occurred
- *
- * Since: 2.14
- */
-gint
-g_match_info_get_match_count (const GMatchInfo *match_info)
-{
- g_return_val_if_fail (match_info, -1);
-
- if (match_info->matches == PCRE_ERROR_NOMATCH)
- /* no match */
- return 0;
- else if (match_info->matches < PCRE_ERROR_NOMATCH)
- /* error */
- return -1;
- else
- /* match */
- return match_info->matches;
-}
-
-/**
- * g_match_info_is_partial_match:
- * @match_info: a #GMatchInfo structure
- *
- * Usually if the string passed to g_regex_match*() matches as far as
- * it goes, but is too short to match the entire pattern, %FALSE is
- * returned. There are circumstances where it might be helpful to
- * distinguish this case from other cases in which there is no match.
- *
- * Consider, for example, an application where a human is required to
- * type in data for a field with specific formatting requirements. An
- * example might be a date in the form ddmmmyy, defined by the pattern
- * "^\d?\d(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)\d\d$".
- * If the application sees the user’s keystrokes one by one, and can
- * check that what has been typed so far is potentially valid, it is
- * able to raise an error as soon as a mistake is made.
- *
- * GRegex supports the concept of partial matching by means of the
- * #G_REGEX_MATCH_PARTIAL flag. When this is set the return code for
- * g_regex_match() or g_regex_match_full() is, as usual, %TRUE
- * for a complete match, %FALSE otherwise. But, when these functions
- * return %FALSE, you can check if the match was partial calling
- * g_match_info_is_partial_match().
- *
- * When using partial matching you cannot use g_match_info_fetch*().
- *
- * Because of the way certain internal optimizations are implemented
- * the partial matching algorithm cannot be used with all patterns.
- * So repeated single characters such as "a{2,4}" and repeated single
- * meta-sequences such as "\d+" are not permitted if the maximum number
- * of occurrences is greater than one. Optional items such as "\d?"
- * (where the maximum is one) are permitted. Quantifiers with any values
- * are permitted after parentheses, so the invalid examples above can be
- * coded thus "(a){2,4}" and "(\d)+". If #G_REGEX_MATCH_PARTIAL is set
- * for a pattern that does not conform to the restrictions, matching
- * functions return an error.
- *
- * Returns: %TRUE if the match was partial, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_match_info_is_partial_match (const GMatchInfo *match_info)
-{
- g_return_val_if_fail (match_info != NULL, FALSE);
-
- return match_info->matches == PCRE_ERROR_PARTIAL;
-}
-
-/**
- * g_match_info_expand_references:
- * @match_info: a #GMatchInfo or %NULL
- * @string_to_expand: the string to expand
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Returns a new string containing the text in @string_to_expand with
- * references and escape sequences expanded. References refer to the last
- * match done with @string against @regex and have the same syntax used by
- * g_regex_replace().
- *
- * The @string_to_expand must be UTF-8 encoded even if #G_REGEX_RAW was
- * passed to g_regex_new().
- *
- * The backreferences are extracted from the string passed to the match
- * function, so you cannot call this function after freeing the string.
- *
- * @match_info may be %NULL in which case @string_to_expand must not
- * contain references. For instance "foo\n" does not refer to an actual
- * pattern and '\n' merely will be replaced with \n character,
- * while to expand "\0" (whole match) one needs the result of a match.
- * Use g_regex_check_replacement() to find out whether @string_to_expand
- * contains references.
- *
- * Returns: (allow-none): the expanded string, or %NULL if an error occurred
- *
- * Since: 2.14
- */
-gchar *
-g_match_info_expand_references (const GMatchInfo *match_info,
- const gchar *string_to_expand,
- GError **error)
-{
- GString *result;
- GList *list;
- GError *tmp_error = NULL;
-
- g_return_val_if_fail (string_to_expand != NULL, NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
- list = split_replacement (string_to_expand, &tmp_error);
- if (tmp_error != NULL)
- {
- g_propagate_error (error, tmp_error);
- return NULL;
- }
-
- if (!match_info && interpolation_list_needs_match (list))
- {
- g_critical ("String '%s' contains references to the match, can't "
- "expand references without GMatchInfo object",
- string_to_expand);
- return NULL;
- }
-
- result = g_string_sized_new (strlen (string_to_expand));
- interpolate_replacement (match_info, result, list);
-
- g_list_foreach (list, (GFunc)free_interpolation_data, NULL);
- g_list_free (list);
-
- return g_string_free (result, FALSE);
-}
-
-/**
- * g_match_info_fetch:
- * @match_info: #GMatchInfo structure
- * @match_num: number of the sub expression
- *
- * Retrieves the text matching the @match_num<!-- -->'th capturing
- * parentheses. 0 is the full text of the match, 1 is the first paren
- * set, 2 the second, and so on.
- *
- * If @match_num is a valid sub pattern but it didn't match anything
- * (e.g. sub pattern 1, matching "b" against "(a)?b") then an empty
- * string is returned.
- *
- * If the match was obtained using the DFA algorithm, that is using
- * g_regex_match_all() or g_regex_match_all_full(), the retrieved
- * string is not that of a set of parentheses but that of a matched
- * substring. Substrings are matched in reverse order of length, so
- * 0 is the longest match.
- *
- * The string is fetched from the string passed to the match function,
- * so you cannot call this function after freeing the string.
- *
- * Returns: (allow-none): The matched substring, or %NULL if an error
- * occurred. You have to free the string yourself
- *
- * Since: 2.14
- */
-gchar *
-g_match_info_fetch (const GMatchInfo *match_info,
- gint match_num)
-{
- /* we cannot use pcre_get_substring() because it allocates the
- * string using pcre_malloc(). */
- gchar *match = NULL;
- gint start, end;
-
- g_return_val_if_fail (match_info != NULL, NULL);
- g_return_val_if_fail (match_num >= 0, NULL);
-
- /* match_num does not exist or it didn't matched, i.e. matching "b"
- * against "(a)?b" then group 0 is empty. */
- if (!g_match_info_fetch_pos (match_info, match_num, &start, &end))
- match = NULL;
- else if (start == -1)
- match = g_strdup ("");
- else
- match = g_strndup (&match_info->string[start], end - start);
-
- return match;
-}
-
-/**
- * g_match_info_fetch_pos:
- * @match_info: #GMatchInfo structure
- * @match_num: number of the sub expression
- * @start_pos: (out) (allow-none): pointer to location where to store
- * the start position, or %NULL
- * @end_pos: (out) (allow-none): pointer to location where to store
- * the end position, or %NULL
- *
- * Retrieves the position in bytes of the @match_num<!-- -->'th capturing
- * parentheses. 0 is the full text of the match, 1 is the first
- * paren set, 2 the second, and so on.
- *
- * If @match_num is a valid sub pattern but it didn't match anything
- * (e.g. sub pattern 1, matching "b" against "(a)?b") then @start_pos
- * and @end_pos are set to -1 and %TRUE is returned.
- *
- * If the match was obtained using the DFA algorithm, that is using
- * g_regex_match_all() or g_regex_match_all_full(), the retrieved
- * position is not that of a set of parentheses but that of a matched
- * substring. Substrings are matched in reverse order of length, so
- * 0 is the longest match.
- *
- * Returns: %TRUE if the position was fetched, %FALSE otherwise. If
- * the position cannot be fetched, @start_pos and @end_pos are left
- * unchanged
- *
- * Since: 2.14
- */
-gboolean
-g_match_info_fetch_pos (const GMatchInfo *match_info,
- gint match_num,
- gint *start_pos,
- gint *end_pos)
-{
- g_return_val_if_fail (match_info != NULL, FALSE);
- g_return_val_if_fail (match_num >= 0, FALSE);
-
- /* make sure the sub expression number they're requesting is less than
- * the total number of sub expressions that were matched. */
- if (match_num >= match_info->matches)
- return FALSE;
-
- if (start_pos != NULL)
- *start_pos = match_info->offsets[2 * match_num];
-
- if (end_pos != NULL)
- *end_pos = match_info->offsets[2 * match_num + 1];
-
- return TRUE;
-}
-
-/*
- * Returns number of first matched subpattern with name @name.
- * There may be more than one in case when DUPNAMES is used,
- * and not all subpatterns with that name match;
- * pcre_get_stringnumber() does not work in that case.
- */
-static gint
-get_matched_substring_number (const GMatchInfo *match_info,
- const gchar *name)
-{
- gint entrysize;
- gchar *first, *last;
- guchar *entry;
-
- if (!(match_info->regex->compile_opts & G_REGEX_DUPNAMES))
- return pcre_get_stringnumber (match_info->regex->pcre_re, name);
-
- /* This code is copied from pcre_get.c: get_first_set() */
- entrysize = pcre_get_stringtable_entries (match_info->regex->pcre_re,
- name,
- &first,
- &last);
-
- if (entrysize <= 0)
- return entrysize;
-
- for (entry = (guchar*) first; entry <= (guchar*) last; entry += entrysize)
- {
- gint n = (entry[0] << 8) + entry[1];
- if (match_info->offsets[n*2] >= 0)
- return n;
- }
-
- return (first[0] << 8) + first[1];
-}
-
-/**
- * g_match_info_fetch_named:
- * @match_info: #GMatchInfo structure
- * @name: name of the subexpression
- *
- * Retrieves the text matching the capturing parentheses named @name.
- *
- * If @name is a valid sub pattern name but it didn't match anything
- * (e.g. sub pattern "X", matching "b" against "(?P<X>a)?b")
- * then an empty string is returned.
- *
- * The string is fetched from the string passed to the match function,
- * so you cannot call this function after freeing the string.
- *
- * Returns: (allow-none): The matched substring, or %NULL if an error
- * occurred. You have to free the string yourself
- *
- * Since: 2.14
- */
-gchar *
-g_match_info_fetch_named (const GMatchInfo *match_info,
- const gchar *name)
-{
- /* we cannot use pcre_get_named_substring() because it allocates the
- * string using pcre_malloc(). */
- gint num;
-
- g_return_val_if_fail (match_info != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- num = get_matched_substring_number (match_info, name);
- if (num < 0)
- return NULL;
- else
- return g_match_info_fetch (match_info, num);
-}
-
-/**
- * g_match_info_fetch_named_pos:
- * @match_info: #GMatchInfo structure
- * @name: name of the subexpression
- * @start_pos: (out) (allow-none): pointer to location where to store
- * the start position, or %NULL
- * @end_pos: (out) (allow-none): pointer to location where to store
- * the end position, or %NULL
- *
- * Retrieves the position in bytes of the capturing parentheses named @name.
- *
- * If @name is a valid sub pattern name but it didn't match anything
- * (e.g. sub pattern "X", matching "b" against "(?P<X>a)?b")
- * then @start_pos and @end_pos are set to -1 and %TRUE is returned.
- *
- * Returns: %TRUE if the position was fetched, %FALSE otherwise.
- * If the position cannot be fetched, @start_pos and @end_pos
- * are left unchanged.
- *
- * Since: 2.14
- */
-gboolean
-g_match_info_fetch_named_pos (const GMatchInfo *match_info,
- const gchar *name,
- gint *start_pos,
- gint *end_pos)
-{
- gint num;
-
- g_return_val_if_fail (match_info != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- num = get_matched_substring_number (match_info, name);
- if (num < 0)
- return FALSE;
-
- return g_match_info_fetch_pos (match_info, num, start_pos, end_pos);
-}
-
-/**
- * g_match_info_fetch_all:
- * @match_info: a #GMatchInfo structure
- *
- * Bundles up pointers to each of the matching substrings from a match
- * and stores them in an array of gchar pointers. The first element in
- * the returned array is the match number 0, i.e. the entire matched
- * text.
- *
- * If a sub pattern didn't match anything (e.g. sub pattern 1, matching
- * "b" against "(a)?b") then an empty string is inserted.
- *
- * If the last match was obtained using the DFA algorithm, that is using
- * g_regex_match_all() or g_regex_match_all_full(), the retrieved
- * strings are not that matched by sets of parentheses but that of the
- * matched substring. Substrings are matched in reverse order of length,
- * so the first one is the longest match.
- *
- * The strings are fetched from the string passed to the match function,
- * so you cannot call this function after freeing the string.
- *
- * Returns: (allow-none): a %NULL-terminated array of gchar * pointers.
- * It must be freed using g_strfreev(). If the previous match failed
- * %NULL is returned
- *
- * Since: 2.14
- */
-gchar **
-g_match_info_fetch_all (const GMatchInfo *match_info)
-{
- /* we cannot use pcre_get_substring_list() because the returned value
- * isn't suitable for g_strfreev(). */
- gchar **result;
- gint i;
-
- g_return_val_if_fail (match_info != NULL, NULL);
-
- if (match_info->matches < 0)
- return NULL;
-
- result = g_new (gchar *, match_info->matches + 1);
- for (i = 0; i < match_info->matches; i++)
- result[i] = g_match_info_fetch (match_info, i);
- result[i] = NULL;
-
- return result;
-}
-
-
-/* GRegex */
-
-GQuark
-g_regex_error_quark (void)
-{
- static GQuark error_quark = 0;
-
- if (error_quark == 0)
- error_quark = g_quark_from_static_string ("g-regex-error-quark");
-
- return error_quark;
-}
-
-/**
- * g_regex_ref:
- * @regex: a #GRegex
- *
- * Increases reference count of @regex by 1.
- *
- * Returns: @regex
- *
- * Since: 2.14
- */
-GRegex *
-g_regex_ref (GRegex *regex)
-{
- g_return_val_if_fail (regex != NULL, NULL);
- g_atomic_int_inc (®ex->ref_count);
- return regex;
-}
-
-/**
- * g_regex_unref:
- * @regex: a #GRegex
- *
- * Decreases reference count of @regex by 1. When reference count drops
- * to zero, it frees all the memory associated with the regex structure.
- *
- * Since: 2.14
- */
-void
-g_regex_unref (GRegex *regex)
-{
- g_return_if_fail (regex != NULL);
-
- if (g_atomic_int_exchange_and_add (®ex->ref_count, -1) - 1 == 0)
- {
- g_free (regex->pattern);
- if (regex->pcre_re != NULL)
- pcre_free (regex->pcre_re);
- if (regex->extra != NULL)
- pcre_free (regex->extra);
- g_free (regex);
- }
-}
-
-/**
- * g_regex_new:
- * @pattern: the regular expression
- * @compile_options: compile options for the regular expression, or 0
- * @match_options: match options for the regular expression, or 0
- * @error: return location for a #GError
- *
- * Compiles the regular expression to an internal form, and does
- * the initial setup of the #GRegex structure.
- *
- * Returns: a #GRegex structure. Call g_regex_unref() when you
- * are done with it
- *
- * Since: 2.14
- */
-GRegex *
-g_regex_new (const gchar *pattern,
- GRegexCompileFlags compile_options,
- GRegexMatchFlags match_options,
- GError **error)
-{
- GRegex *regex;
- pcre *re;
- const gchar *errmsg;
- gint erroffset;
- gint errcode;
- gboolean optimize = FALSE;
- static gboolean initialized = FALSE;
- unsigned long int pcre_compile_options;
-
- g_return_val_if_fail (pattern != NULL, NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- g_return_val_if_fail ((compile_options & ~G_REGEX_COMPILE_MASK) == 0, NULL);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, NULL);
-
- if (!initialized)
- {
- gint support;
- const gchar *msg;
-
- pcre_config (PCRE_CONFIG_UTF8, &support);
- if (!support)
- {
- msg = N_("PCRE library is compiled without UTF8 support");
- g_critical ("%s", msg);
- g_set_error_literal (error, G_REGEX_ERROR, G_REGEX_ERROR_COMPILE, gettext (msg));
- return NULL;
- }
-
- pcre_config (PCRE_CONFIG_UNICODE_PROPERTIES, &support);
- if (!support)
- {
- msg = N_("PCRE library is compiled without UTF8 properties support");
- g_critical ("%s", msg);
- g_set_error_literal (error, G_REGEX_ERROR, G_REGEX_ERROR_COMPILE, gettext (msg));
- return NULL;
- }
-
- initialized = TRUE;
- }
-
- /* G_REGEX_OPTIMIZE has the same numeric value of PCRE_NO_UTF8_CHECK,
- * as we do not need to wrap PCRE_NO_UTF8_CHECK. */
- if (compile_options & G_REGEX_OPTIMIZE)
- optimize = TRUE;
-
- /* In GRegex the string are, by default, UTF-8 encoded. PCRE
- * instead uses UTF-8 only if required with PCRE_UTF8. */
- if (compile_options & G_REGEX_RAW)
- {
- /* disable utf-8 */
- compile_options &= ~G_REGEX_RAW;
- }
- else
- {
- /* enable utf-8 */
- compile_options |= PCRE_UTF8 | PCRE_NO_UTF8_CHECK;
- match_options |= PCRE_NO_UTF8_CHECK;
- }
-
- /* PCRE_NEWLINE_ANY is the default for the internal PCRE but
- * not for the system one. */
- if (!(compile_options & G_REGEX_NEWLINE_CR) &&
- !(compile_options & G_REGEX_NEWLINE_LF))
- {
- compile_options |= PCRE_NEWLINE_ANY;
- }
-
- /* compile the pattern */
- re = pcre_compile2 (pattern, compile_options, &errcode,
- &errmsg, &erroffset, NULL);
-
- /* if the compilation failed, set the error member and return
- * immediately */
- if (re == NULL)
- {
- GError *tmp_error;
-
- /* Translate the PCRE error code to GRegexError and use a translated
- * error message if possible */
- translate_compile_error (&errcode, &errmsg);
-
- /* PCRE uses byte offsets but we want to show character offsets */
- erroffset = g_utf8_pointer_to_offset (pattern, &pattern[erroffset]);
-
- tmp_error = g_error_new (G_REGEX_ERROR, errcode,
- _("Error while compiling regular "
- "expression %s at char %d: %s"),
- pattern, erroffset, errmsg);
- g_propagate_error (error, tmp_error);
-
- return NULL;
- }
-
- /* For options set at the beginning of the pattern, pcre puts them into
- * compile options, e.g. "(?i)foo" will make the pcre structure store
- * PCRE_CASELESS even though it wasn't explicitly given for compilation. */
- pcre_fullinfo (re, NULL, PCRE_INFO_OPTIONS, &pcre_compile_options);
- compile_options = pcre_compile_options;
-
- if (!(compile_options & G_REGEX_DUPNAMES))
- {
- gboolean jchanged = FALSE;
- pcre_fullinfo (re, NULL, PCRE_INFO_JCHANGED, &jchanged);
- if (jchanged)
- compile_options |= G_REGEX_DUPNAMES;
- }
-
- regex = g_new0 (GRegex, 1);
- regex->ref_count = 1;
- regex->pattern = g_strdup (pattern);
- regex->pcre_re = re;
- regex->compile_opts = compile_options;
- regex->match_opts = match_options;
-
- if (optimize)
- {
- regex->extra = pcre_study (regex->pcre_re, 0, &errmsg);
- if (errmsg != NULL)
- {
- GError *tmp_error = g_error_new (G_REGEX_ERROR,
- G_REGEX_ERROR_OPTIMIZE,
- _("Error while optimizing "
- "regular expression %s: %s"),
- regex->pattern,
- errmsg);
- g_propagate_error (error, tmp_error);
-
- g_regex_unref (regex);
- return NULL;
- }
- }
-
- return regex;
-}
-
-/**
- * g_regex_get_pattern:
- * @regex: a #GRegex structure
- *
- * Gets the pattern string associated with @regex, i.e. a copy of
- * the string passed to g_regex_new().
- *
- * Returns: the pattern of @regex
- *
- * Since: 2.14
- */
-const gchar *
-g_regex_get_pattern (const GRegex *regex)
-{
- g_return_val_if_fail (regex != NULL, NULL);
-
- return regex->pattern;
-}
-
-/**
- * g_regex_get_max_backref:
- * @regex: a #GRegex
- *
- * Returns the number of the highest back reference
- * in the pattern, or 0 if the pattern does not contain
- * back references.
- *
- * Returns: the number of the highest back reference
- *
- * Since: 2.14
- */
-gint
-g_regex_get_max_backref (const GRegex *regex)
-{
- gint value;
-
- pcre_fullinfo (regex->pcre_re, regex->extra,
- PCRE_INFO_BACKREFMAX, &value);
-
- return value;
-}
-
-/**
- * g_regex_get_capture_count:
- * @regex: a #GRegex
- *
- * Returns the number of capturing subpatterns in the pattern.
- *
- * Returns: the number of capturing subpatterns
- *
- * Since: 2.14
- */
-gint
-g_regex_get_capture_count (const GRegex *regex)
-{
- gint value;
-
- pcre_fullinfo (regex->pcre_re, regex->extra,
- PCRE_INFO_CAPTURECOUNT, &value);
-
- return value;
-}
-
-/**
- * g_regex_get_compile_flags:
- * @regex: a #GRegex
- *
- * Returns the compile options that @regex was created with.
- *
- * Returns: flags from #GRegexCompileFlags
- *
- * Since: 2.26
- */
-GRegexCompileFlags
-g_regex_get_compile_flags (const GRegex *regex)
-{
- g_return_val_if_fail (regex != NULL, 0);
-
- return regex->compile_opts;
-}
-
-/**
- * g_regex_get_match_flags:
- * @regex: a #GRegex
- *
- * Returns the match options that @regex was created with.
- *
- * Returns: flags from #GRegexMatchFlags
- *
- * Since: 2.26
- */
-GRegexMatchFlags
-g_regex_get_match_flags (const GRegex *regex)
-{
- g_return_val_if_fail (regex != NULL, 0);
-
- return regex->match_opts;
-}
-
-/**
- * g_regex_match_simple:
- * @pattern: the regular expression
- * @string: the string to scan for matches
- * @compile_options: compile options for the regular expression, or 0
- * @match_options: match options, or 0
- *
- * Scans for a match in @string for @pattern.
- *
- * This function is equivalent to g_regex_match() but it does not
- * require to compile the pattern with g_regex_new(), avoiding some
- * lines of code when you need just to do a match without extracting
- * substrings, capture counts, and so on.
- *
- * If this function is to be called on the same @pattern more than
- * once, it's more efficient to compile the pattern once with
- * g_regex_new() and then use g_regex_match().
- *
- * Returns: %TRUE if the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_regex_match_simple (const gchar *pattern,
- const gchar *string,
- GRegexCompileFlags compile_options,
- GRegexMatchFlags match_options)
-{
- GRegex *regex;
- gboolean result;
-
- regex = g_regex_new (pattern, compile_options, 0, NULL);
- if (!regex)
- return FALSE;
- result = g_regex_match_full (regex, string, -1, 0, match_options, NULL, NULL);
- g_regex_unref (regex);
- return result;
-}
-
-/**
- * g_regex_match:
- * @regex: a #GRegex structure from g_regex_new()
- * @string: the string to scan for matches
- * @match_options: match options
- * @match_info: (out) (allow-none): pointer to location where to store
- * the #GMatchInfo, or %NULL if you do not need it
- *
- * Scans for a match in string for the pattern in @regex.
- * The @match_options are combined with the match options specified
- * when the @regex structure was created, letting you have more
- * flexibility in reusing #GRegex structures.
- *
- * A #GMatchInfo structure, used to get information on the match,
- * is stored in @match_info if not %NULL. Note that if @match_info
- * is not %NULL then it is created even if the function returns %FALSE,
- * i.e. you must free it regardless if regular expression actually matched.
- *
- * To retrieve all the non-overlapping matches of the pattern in
- * string you can use g_match_info_next().
- *
- * |[
- * static void
- * print_uppercase_words (const gchar *string)
- * {
- * /* Print all uppercase-only words. */
- * GRegex *regex;
- * GMatchInfo *match_info;
- *
- * regex = g_regex_new ("[A-Z]+", 0, 0, NULL);
- * g_regex_match (regex, string, 0, &match_info);
- * while (g_match_info_matches (match_info))
- * {
- * gchar *word = g_match_info_fetch (match_info, 0);
- * g_print ("Found: %s\n", word);
- * g_free (word);
- * g_match_info_next (match_info, NULL);
- * }
- * g_match_info_free (match_info);
- * g_regex_unref (regex);
- * }
- * ]|
- *
- * @string is not copied and is used in #GMatchInfo internally. If
- * you use any #GMatchInfo method (except g_match_info_free()) after
- * freeing or modifying @string then the behaviour is undefined.
- *
- * Returns: %TRUE is the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_regex_match (const GRegex *regex,
- const gchar *string,
- GRegexMatchFlags match_options,
- GMatchInfo **match_info)
-{
- return g_regex_match_full (regex, string, -1, 0, match_options,
- match_info, NULL);
-}
-
-/**
- * g_regex_match_full:
- * @regex: a #GRegex structure from g_regex_new()
- * @string: (array length=string_len): the string to scan for matches
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @match_options: match options
- * @match_info: (out) (allow-none): pointer to location where to store
- * the #GMatchInfo, or %NULL if you do not need it
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Scans for a match in string for the pattern in @regex.
- * The @match_options are combined with the match options specified
- * when the @regex structure was created, letting you have more
- * flexibility in reusing #GRegex structures.
- *
- * Setting @start_position differs from just passing over a shortened
- * string and setting #G_REGEX_MATCH_NOTBOL in the case of a pattern
- * that begins with any kind of lookbehind assertion, such as "\b".
- *
- * A #GMatchInfo structure, used to get information on the match, is
- * stored in @match_info if not %NULL. Note that if @match_info is
- * not %NULL then it is created even if the function returns %FALSE,
- * i.e. you must free it regardless if regular expression actually
- * matched.
- *
- * @string is not copied and is used in #GMatchInfo internally. If
- * you use any #GMatchInfo method (except g_match_info_free()) after
- * freeing or modifying @string then the behaviour is undefined.
- *
- * To retrieve all the non-overlapping matches of the pattern in
- * string you can use g_match_info_next().
- *
- * |[
- * static void
- * print_uppercase_words (const gchar *string)
- * {
- * /* Print all uppercase-only words. */
- * GRegex *regex;
- * GMatchInfo *match_info;
- * GError *error = NULL;
- *
- * regex = g_regex_new ("[A-Z]+", 0, 0, NULL);
- * g_regex_match_full (regex, string, -1, 0, 0, &match_info, &error);
- * while (g_match_info_matches (match_info))
- * {
- * gchar *word = g_match_info_fetch (match_info, 0);
- * g_print ("Found: %s\n", word);
- * g_free (word);
- * g_match_info_next (match_info, &error);
- * }
- * g_match_info_free (match_info);
- * g_regex_unref (regex);
- * if (error != NULL)
- * {
- * g_printerr ("Error while matching: %s\n", error->message);
- * g_error_free (error);
- * }
- * }
- * ]|
- *
- * Returns: %TRUE is the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_regex_match_full (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- GRegexMatchFlags match_options,
- GMatchInfo **match_info,
- GError **error)
-{
- GMatchInfo *info;
- gboolean match_ok;
-
- g_return_val_if_fail (regex != NULL, FALSE);
- g_return_val_if_fail (string != NULL, FALSE);
- g_return_val_if_fail (start_position >= 0, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, FALSE);
-
- info = match_info_new (regex, string, string_len, start_position,
- match_options, FALSE);
- match_ok = g_match_info_next (info, error);
- if (match_info != NULL)
- *match_info = info;
- else
- g_match_info_free (info);
-
- return match_ok;
-}
-
-/**
- * g_regex_match_all:
- * @regex: a #GRegex structure from g_regex_new()
- * @string: the string to scan for matches
- * @match_options: match options
- * @match_info: (out) (allow-none): pointer to location where to store
- * the #GMatchInfo, or %NULL if you do not need it
- *
- * Using the standard algorithm for regular expression matching only
- * the longest match in the string is retrieved. This function uses
- * a different algorithm so it can retrieve all the possible matches.
- * For more documentation see g_regex_match_all_full().
- *
- * A #GMatchInfo structure, used to get information on the match, is
- * stored in @match_info if not %NULL. Note that if @match_info is
- * not %NULL then it is created even if the function returns %FALSE,
- * i.e. you must free it regardless if regular expression actually
- * matched.
- *
- * @string is not copied and is used in #GMatchInfo internally. If
- * you use any #GMatchInfo method (except g_match_info_free()) after
- * freeing or modifying @string then the behaviour is undefined.
- *
- * Returns: %TRUE is the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_regex_match_all (const GRegex *regex,
- const gchar *string,
- GRegexMatchFlags match_options,
- GMatchInfo **match_info)
-{
- return g_regex_match_all_full (regex, string, -1, 0, match_options,
- match_info, NULL);
-}
-
-/**
- * g_regex_match_all_full:
- * @regex: a #GRegex structure from g_regex_new()
- * @string: (array length=string_len): the string to scan for matches
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @match_options: match options
- * @match_info: (out) (allow-none): pointer to location where to store
- * the #GMatchInfo, or %NULL if you do not need it
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Using the standard algorithm for regular expression matching only
- * the longest match in the string is retrieved, it is not possibile
- * to obtain all the available matches. For instance matching
- * "<a> <b> <c>" against the pattern "<.*>"
- * you get "<a> <b> <c>".
- *
- * This function uses a different algorithm (called DFA, i.e. deterministic
- * finite automaton), so it can retrieve all the possible matches, all
- * starting at the same point in the string. For instance matching
- * "<a> <b> <c>" against the pattern "<.*>"
- * you would obtain three matches: "<a> <b> <c>",
- * "<a> <b>" and "<a>".
- *
- * The number of matched strings is retrieved using
- * g_match_info_get_match_count(). To obtain the matched strings and
- * their position you can use, respectively, g_match_info_fetch() and
- * g_match_info_fetch_pos(). Note that the strings are returned in
- * reverse order of length; that is, the longest matching string is
- * given first.
- *
- * Note that the DFA algorithm is slower than the standard one and it
- * is not able to capture substrings, so backreferences do not work.
- *
- * Setting @start_position differs from just passing over a shortened
- * string and setting #G_REGEX_MATCH_NOTBOL in the case of a pattern
- * that begins with any kind of lookbehind assertion, such as "\b".
- *
- * A #GMatchInfo structure, used to get information on the match, is
- * stored in @match_info if not %NULL. Note that if @match_info is
- * not %NULL then it is created even if the function returns %FALSE,
- * i.e. you must free it regardless if regular expression actually
- * matched.
- *
- * @string is not copied and is used in #GMatchInfo internally. If
- * you use any #GMatchInfo method (except g_match_info_free()) after
- * freeing or modifying @string then the behaviour is undefined.
- *
- * Returns: %TRUE is the string matched, %FALSE otherwise
- *
- * Since: 2.14
- */
-gboolean
-g_regex_match_all_full (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- GRegexMatchFlags match_options,
- GMatchInfo **match_info,
- GError **error)
-{
- GMatchInfo *info;
- gboolean done;
-
- g_return_val_if_fail (regex != NULL, FALSE);
- g_return_val_if_fail (string != NULL, FALSE);
- g_return_val_if_fail (start_position >= 0, FALSE);
- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, FALSE);
-
- info = match_info_new (regex, string, string_len, start_position,
- match_options, TRUE);
-
- done = FALSE;
- while (!done)
- {
- done = TRUE;
- info->matches = pcre_dfa_exec (regex->pcre_re, regex->extra,
- info->string, info->string_len,
- info->pos,
- regex->match_opts | match_options,
- info->offsets, info->n_offsets,
- info->workspace, info->n_workspace);
- if (info->matches == PCRE_ERROR_DFA_WSSIZE)
- {
- /* info->workspace is too small. */
- info->n_workspace *= 2;
- info->workspace = g_realloc (info->workspace,
- info->n_workspace * sizeof (gint));
- done = FALSE;
- }
- else if (info->matches == 0)
- {
- /* info->offsets is too small. */
- info->n_offsets *= 2;
- info->offsets = g_realloc (info->offsets,
- info->n_offsets * sizeof (gint));
- done = FALSE;
- }
- else if (IS_PCRE_ERROR (info->matches))
- {
- g_set_error (error, G_REGEX_ERROR, G_REGEX_ERROR_MATCH,
- _("Error while matching regular expression %s: %s"),
- regex->pattern, match_error (info->matches));
- }
- }
-
- /* set info->pos to -1 so that a call to g_match_info_next() fails. */
- info->pos = -1;
-
- if (match_info != NULL)
- *match_info = info;
- else
- g_match_info_free (info);
-
- return info->matches >= 0;
-}
-
-/**
- * g_regex_get_string_number:
- * @regex: #GRegex structure
- * @name: name of the subexpression
- *
- * Retrieves the number of the subexpression named @name.
- *
- * Returns: The number of the subexpression or -1 if @name
- * does not exists
- *
- * Since: 2.14
- */
-gint
-g_regex_get_string_number (const GRegex *regex,
- const gchar *name)
-{
- gint num;
-
- g_return_val_if_fail (regex != NULL, -1);
- g_return_val_if_fail (name != NULL, -1);
-
- num = pcre_get_stringnumber (regex->pcre_re, name);
- if (num == PCRE_ERROR_NOSUBSTRING)
- num = -1;
-
- return num;
-}
-
-/**
- * g_regex_split_simple:
- * @pattern: the regular expression
- * @string: the string to scan for matches
- * @compile_options: compile options for the regular expression, or 0
- * @match_options: match options, or 0
- *
- * Breaks the string on the pattern, and returns an array of
- * the tokens. If the pattern contains capturing parentheses,
- * then the text for each of the substrings will also be returned.
- * If the pattern does not match anywhere in the string, then the
- * whole string is returned as the first token.
- *
- * This function is equivalent to g_regex_split() but it does
- * not require to compile the pattern with g_regex_new(), avoiding
- * some lines of code when you need just to do a split without
- * extracting substrings, capture counts, and so on.
- *
- * If this function is to be called on the same @pattern more than
- * once, it's more efficient to compile the pattern once with
- * g_regex_new() and then use g_regex_split().
- *
- * As a special case, the result of splitting the empty string ""
- * is an empty vector, not a vector containing a single string.
- * The reason for this special case is that being able to represent
- * a empty vector is typically more useful than consistent handling
- * of empty elements. If you do need to represent empty elements,
- * you'll need to check for the empty string before calling this
- * function.
- *
- * A pattern that can match empty strings splits @string into
- * separate characters wherever it matches the empty string between
- * characters. For example splitting "ab c" using as a separator
- * "\s*", you will get "a", "b" and "c".
- *
- * Returns: a %NULL-terminated array of strings. Free it using g_strfreev()
- *
- * Since: 2.14
- **/
-gchar **
-g_regex_split_simple (const gchar *pattern,
- const gchar *string,
- GRegexCompileFlags compile_options,
- GRegexMatchFlags match_options)
-{
- GRegex *regex;
- gchar **result;
-
- regex = g_regex_new (pattern, compile_options, 0, NULL);
- if (!regex)
- return NULL;
- result = g_regex_split_full (regex, string, -1, 0, match_options, 0, NULL);
- g_regex_unref (regex);
- return result;
-}
-
-/**
- * g_regex_split:
- * @regex: a #GRegex structure
- * @string: the string to split with the pattern
- * @match_options: match time option flags
- *
- * Breaks the string on the pattern, and returns an array of the tokens.
- * If the pattern contains capturing parentheses, then the text for each
- * of the substrings will also be returned. If the pattern does not match
- * anywhere in the string, then the whole string is returned as the first
- * token.
- *
- * As a special case, the result of splitting the empty string "" is an
- * empty vector, not a vector containing a single string. The reason for
- * this special case is that being able to represent a empty vector is
- * typically more useful than consistent handling of empty elements. If
- * you do need to represent empty elements, you'll need to check for the
- * empty string before calling this function.
- *
- * A pattern that can match empty strings splits @string into separate
- * characters wherever it matches the empty string between characters.
- * For example splitting "ab c" using as a separator "\s*", you will get
- * "a", "b" and "c".
- *
- * Returns: a %NULL-terminated gchar ** array. Free it using g_strfreev()
- *
- * Since: 2.14
- **/
-gchar **
-g_regex_split (const GRegex *regex,
- const gchar *string,
- GRegexMatchFlags match_options)
-{
- return g_regex_split_full (regex, string, -1, 0,
- match_options, 0, NULL);
-}
-
-/**
- * g_regex_split_full:
- * @regex: a #GRegex structure
- * @string: (array length=string_len): the string to split with the pattern
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @match_options: match time option flags
- * @max_tokens: the maximum number of tokens to split @string into.
- * If this is less than 1, the string is split completely
- * @error: return location for a #GError
- *
- * Breaks the string on the pattern, and returns an array of the tokens.
- * If the pattern contains capturing parentheses, then the text for each
- * of the substrings will also be returned. If the pattern does not match
- * anywhere in the string, then the whole string is returned as the first
- * token.
- *
- * As a special case, the result of splitting the empty string "" is an
- * empty vector, not a vector containing a single string. The reason for
- * this special case is that being able to represent a empty vector is
- * typically more useful than consistent handling of empty elements. If
- * you do need to represent empty elements, you'll need to check for the
- * empty string before calling this function.
- *
- * A pattern that can match empty strings splits @string into separate
- * characters wherever it matches the empty string between characters.
- * For example splitting "ab c" using as a separator "\s*", you will get
- * "a", "b" and "c".
- *
- * Setting @start_position differs from just passing over a shortened
- * string and setting #G_REGEX_MATCH_NOTBOL in the case of a pattern
- * that begins with any kind of lookbehind assertion, such as "\b".
- *
- * Returns: a %NULL-terminated gchar ** array. Free it using g_strfreev()
- *
- * Since: 2.14
- **/
-gchar **
-g_regex_split_full (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- GRegexMatchFlags match_options,
- gint max_tokens,
- GError **error)
-{
- GError *tmp_error = NULL;
- GMatchInfo *match_info;
- GList *list, *last;
- gint i;
- gint token_count;
- gboolean match_ok;
- /* position of the last separator. */
- gint last_separator_end;
- /* was the last match 0 bytes long? */
- gboolean last_match_is_empty;
- /* the returned array of char **s */
- gchar **string_list;
-
- g_return_val_if_fail (regex != NULL, NULL);
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (start_position >= 0, NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, NULL);
-
- if (max_tokens <= 0)
- max_tokens = G_MAXINT;
-
- if (string_len < 0)
- string_len = strlen (string);
-
- /* zero-length string */
- if (string_len - start_position == 0)
- return g_new0 (gchar *, 1);
-
- if (max_tokens == 1)
- {
- string_list = g_new0 (gchar *, 2);
- string_list[0] = g_strndup (&string[start_position],
- string_len - start_position);
- return string_list;
- }
-
- list = NULL;
- token_count = 0;
- last_separator_end = start_position;
- last_match_is_empty = FALSE;
-
- match_ok = g_regex_match_full (regex, string, string_len, start_position,
- match_options, &match_info, &tmp_error);
- while (tmp_error == NULL)
- {
- if (match_ok)
- {
- last_match_is_empty =
- (match_info->offsets[0] == match_info->offsets[1]);
-
- /* we need to skip empty separators at the same position of the end
- * of another separator. e.g. the string is "a b" and the separator
- * is " *", so from 1 to 2 we have a match and at position 2 we have
- * an empty match. */
- if (last_separator_end != match_info->offsets[1])
- {
- gchar *token;
- gint match_count;
-
- token = g_strndup (string + last_separator_end,
- match_info->offsets[0] - last_separator_end);
- list = g_list_prepend (list, token);
- token_count++;
-
- /* if there were substrings, these need to be added to
- * the list. */
- match_count = g_match_info_get_match_count (match_info);
- if (match_count > 1)
- {
- for (i = 1; i < match_count; i++)
- list = g_list_prepend (list, g_match_info_fetch (match_info, i));
- }
- }
- }
- else
- {
- /* if there was no match, copy to end of string. */
- if (!last_match_is_empty)
- {
- gchar *token = g_strndup (string + last_separator_end,
- match_info->string_len - last_separator_end);
- list = g_list_prepend (list, token);
- }
- /* no more tokens, end the loop. */
- break;
- }
-
- /* -1 to leave room for the last part. */
- if (token_count >= max_tokens - 1)
- {
- /* we have reached the maximum number of tokens, so we copy
- * the remaining part of the string. */
- if (last_match_is_empty)
- {
- /* the last match was empty, so we have moved one char
- * after the real position to avoid empty matches at the
- * same position. */
- match_info->pos = PREV_CHAR (regex, &string[match_info->pos]) - string;
- }
- /* the if is needed in the case we have terminated the available
- * tokens, but we are at the end of the string, so there are no
- * characters left to copy. */
- if (string_len > match_info->pos)
- {
- gchar *token = g_strndup (string + match_info->pos,
- string_len - match_info->pos);
- list = g_list_prepend (list, token);
- }
- /* end the loop. */
- break;
- }
-
- last_separator_end = match_info->pos;
- if (last_match_is_empty)
- /* if the last match was empty, g_match_info_next() has moved
- * forward to avoid infinite loops, but we still need to copy that
- * character. */
- last_separator_end = PREV_CHAR (regex, &string[last_separator_end]) - string;
-
- match_ok = g_match_info_next (match_info, &tmp_error);
- }
- g_match_info_free (match_info);
- if (tmp_error != NULL)
- {
- g_propagate_error (error, tmp_error);
- g_list_foreach (list, (GFunc)g_free, NULL);
- g_list_free (list);
- match_info->pos = -1;
- return NULL;
- }
-
- string_list = g_new (gchar *, g_list_length (list) + 1);
- i = 0;
- for (last = g_list_last (list); last; last = g_list_previous (last))
- string_list[i++] = last->data;
- string_list[i] = NULL;
- g_list_free (list);
-
- return string_list;
-}
-
-enum
-{
- REPL_TYPE_STRING,
- REPL_TYPE_CHARACTER,
- REPL_TYPE_SYMBOLIC_REFERENCE,
- REPL_TYPE_NUMERIC_REFERENCE,
- REPL_TYPE_CHANGE_CASE
-};
-
-typedef enum
-{
- CHANGE_CASE_NONE = 1 << 0,
- CHANGE_CASE_UPPER = 1 << 1,
- CHANGE_CASE_LOWER = 1 << 2,
- CHANGE_CASE_UPPER_SINGLE = 1 << 3,
- CHANGE_CASE_LOWER_SINGLE = 1 << 4,
- CHANGE_CASE_SINGLE_MASK = CHANGE_CASE_UPPER_SINGLE | CHANGE_CASE_LOWER_SINGLE,
- CHANGE_CASE_LOWER_MASK = CHANGE_CASE_LOWER | CHANGE_CASE_LOWER_SINGLE,
- CHANGE_CASE_UPPER_MASK = CHANGE_CASE_UPPER | CHANGE_CASE_UPPER_SINGLE
-} ChangeCase;
-
-struct _InterpolationData
-{
- gchar *text;
- gint type;
- gint num;
- gchar c;
- ChangeCase change_case;
-};
-
-static void
-free_interpolation_data (InterpolationData *data)
-{
- g_free (data->text);
- g_free (data);
-}
-
-static const gchar *
-expand_escape (const gchar *replacement,
- const gchar *p,
- InterpolationData *data,
- GError **error)
-{
- const gchar *q, *r;
- gint x, d, h, i;
- const gchar *error_detail;
- gint base = 0;
- GError *tmp_error = NULL;
-
- p++;
- switch (*p)
- {
- case 't':
- p++;
- data->c = '\t';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'n':
- p++;
- data->c = '\n';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'v':
- p++;
- data->c = '\v';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'r':
- p++;
- data->c = '\r';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'f':
- p++;
- data->c = '\f';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'a':
- p++;
- data->c = '\a';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'b':
- p++;
- data->c = '\b';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case '\\':
- p++;
- data->c = '\\';
- data->type = REPL_TYPE_CHARACTER;
- break;
- case 'x':
- p++;
- x = 0;
- if (*p == '{')
- {
- p++;
- do
- {
- h = g_ascii_xdigit_value (*p);
- if (h < 0)
- {
- error_detail = _("hexadecimal digit or '}' expected");
- goto error;
- }
- x = x * 16 + h;
- p++;
- }
- while (*p != '}');
- p++;
- }
- else
- {
- for (i = 0; i < 2; i++)
- {
- h = g_ascii_xdigit_value (*p);
- if (h < 0)
- {
- error_detail = _("hexadecimal digit expected");
- goto error;
- }
- x = x * 16 + h;
- p++;
- }
- }
- data->type = REPL_TYPE_STRING;
- data->text = g_new0 (gchar, 8);
- g_unichar_to_utf8 (x, data->text);
- break;
- case 'l':
- p++;
- data->type = REPL_TYPE_CHANGE_CASE;
- data->change_case = CHANGE_CASE_LOWER_SINGLE;
- break;
- case 'u':
- p++;
- data->type = REPL_TYPE_CHANGE_CASE;
- data->change_case = CHANGE_CASE_UPPER_SINGLE;
- break;
- case 'L':
- p++;
- data->type = REPL_TYPE_CHANGE_CASE;
- data->change_case = CHANGE_CASE_LOWER;
- break;
- case 'U':
- p++;
- data->type = REPL_TYPE_CHANGE_CASE;
- data->change_case = CHANGE_CASE_UPPER;
- break;
- case 'E':
- p++;
- data->type = REPL_TYPE_CHANGE_CASE;
- data->change_case = CHANGE_CASE_NONE;
- break;
- case 'g':
- p++;
- if (*p != '<')
- {
- error_detail = _("missing '<' in symbolic reference");
- goto error;
- }
- q = p + 1;
- do
- {
- p++;
- if (!*p)
- {
- error_detail = _("unfinished symbolic reference");
- goto error;
- }
- }
- while (*p != '>');
- if (p - q == 0)
- {
- error_detail = _("zero-length symbolic reference");
- goto error;
- }
- if (g_ascii_isdigit (*q))
- {
- x = 0;
- do
- {
- h = g_ascii_digit_value (*q);
- if (h < 0)
- {
- error_detail = _("digit expected");
- p = q;
- goto error;
- }
- x = x * 10 + h;
- q++;
- }
- while (q != p);
- data->num = x;
- data->type = REPL_TYPE_NUMERIC_REFERENCE;
- }
- else
- {
- r = q;
- do
- {
- if (!g_ascii_isalnum (*r))
- {
- error_detail = _("illegal symbolic reference");
- p = r;
- goto error;
- }
- r++;
- }
- while (r != p);
- data->text = g_strndup (q, p - q);
- data->type = REPL_TYPE_SYMBOLIC_REFERENCE;
- }
- p++;
- break;
- case '0':
- /* if \0 is followed by a number is an octal number representing a
- * character, else it is a numeric reference. */
- if (g_ascii_digit_value (*g_utf8_next_char (p)) >= 0)
- {
- base = 8;
- p = g_utf8_next_char (p);
- }
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- x = 0;
- d = 0;
- for (i = 0; i < 3; i++)
- {
- h = g_ascii_digit_value (*p);
- if (h < 0)
- break;
- if (h > 7)
- {
- if (base == 8)
- break;
- else
- base = 10;
- }
- if (i == 2 && base == 10)
- break;
- x = x * 8 + h;
- d = d * 10 + h;
- p++;
- }
- if (base == 8 || i == 3)
- {
- data->type = REPL_TYPE_STRING;
- data->text = g_new0 (gchar, 8);
- g_unichar_to_utf8 (x, data->text);
- }
- else
- {
- data->type = REPL_TYPE_NUMERIC_REFERENCE;
- data->num = d;
- }
- break;
- case 0:
- error_detail = _("stray final '\\'");
- goto error;
- break;
- default:
- error_detail = _("unknown escape sequence");
- goto error;
- }
-
- return p;
-
- error:
- /* G_GSSIZE_FORMAT doesn't work with gettext, so we use %lu */
- tmp_error = g_error_new (G_REGEX_ERROR,
- G_REGEX_ERROR_REPLACE,
- _("Error while parsing replacement "
- "text \"%s\" at char %lu: %s"),
- replacement,
- (gulong)(p - replacement),
- error_detail);
- g_propagate_error (error, tmp_error);
-
- return NULL;
-}
-
-static GList *
-split_replacement (const gchar *replacement,
- GError **error)
-{
- GList *list = NULL;
- InterpolationData *data;
- const gchar *p, *start;
-
- start = p = replacement;
- while (*p)
- {
- if (*p == '\\')
- {
- data = g_new0 (InterpolationData, 1);
- start = p = expand_escape (replacement, p, data, error);
- if (p == NULL)
- {
- g_list_foreach (list, (GFunc)free_interpolation_data, NULL);
- g_list_free (list);
- free_interpolation_data (data);
-
- return NULL;
- }
- list = g_list_prepend (list, data);
- }
- else
- {
- p++;
- if (*p == '\\' || *p == '\0')
- {
- if (p - start > 0)
- {
- data = g_new0 (InterpolationData, 1);
- data->text = g_strndup (start, p - start);
- data->type = REPL_TYPE_STRING;
- list = g_list_prepend (list, data);
- }
- }
- }
- }
-
- return g_list_reverse (list);
-}
-
-/* Change the case of c based on change_case. */
-#define CHANGE_CASE(c, change_case) \
- (((change_case) & CHANGE_CASE_LOWER_MASK) ? \
- g_unichar_tolower (c) : \
- g_unichar_toupper (c))
-
-static void
-string_append (GString *string,
- const gchar *text,
- ChangeCase *change_case)
-{
- gunichar c;
-
- if (text[0] == '\0')
- return;
-
- if (*change_case == CHANGE_CASE_NONE)
- {
- g_string_append (string, text);
- }
- else if (*change_case & CHANGE_CASE_SINGLE_MASK)
- {
- c = g_utf8_get_char (text);
- g_string_append_unichar (string, CHANGE_CASE (c, *change_case));
- g_string_append (string, g_utf8_next_char (text));
- *change_case = CHANGE_CASE_NONE;
- }
- else
- {
- while (*text != '\0')
- {
- c = g_utf8_get_char (text);
- g_string_append_unichar (string, CHANGE_CASE (c, *change_case));
- text = g_utf8_next_char (text);
- }
- }
-}
-
-static gboolean
-interpolate_replacement (const GMatchInfo *match_info,
- GString *result,
- gpointer data)
-{
- GList *list;
- InterpolationData *idata;
- gchar *match;
- ChangeCase change_case = CHANGE_CASE_NONE;
-
- for (list = data; list; list = list->next)
- {
- idata = list->data;
- switch (idata->type)
- {
- case REPL_TYPE_STRING:
- string_append (result, idata->text, &change_case);
- break;
- case REPL_TYPE_CHARACTER:
- g_string_append_c (result, CHANGE_CASE (idata->c, change_case));
- if (change_case & CHANGE_CASE_SINGLE_MASK)
- change_case = CHANGE_CASE_NONE;
- break;
- case REPL_TYPE_NUMERIC_REFERENCE:
- match = g_match_info_fetch (match_info, idata->num);
- if (match)
- {
- string_append (result, match, &change_case);
- g_free (match);
- }
- break;
- case REPL_TYPE_SYMBOLIC_REFERENCE:
- match = g_match_info_fetch_named (match_info, idata->text);
- if (match)
- {
- string_append (result, match, &change_case);
- g_free (match);
- }
- break;
- case REPL_TYPE_CHANGE_CASE:
- change_case = idata->change_case;
- break;
- }
- }
-
- return FALSE;
-}
-
-/* whether actual match_info is needed for replacement, i.e.
- * whether there are references
- */
-static gboolean
-interpolation_list_needs_match (GList *list)
-{
- while (list != NULL)
- {
- InterpolationData *data = list->data;
-
- if (data->type == REPL_TYPE_SYMBOLIC_REFERENCE ||
- data->type == REPL_TYPE_NUMERIC_REFERENCE)
- {
- return TRUE;
- }
-
- list = list->next;
- }
-
- return FALSE;
-}
-
-/**
- * g_regex_replace:
- * @regex: a #GRegex structure
- * @string: (array length=string_len): the string to perform matches against
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @replacement: text to replace each match with
- * @match_options: options for the match
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Replaces all occurrences of the pattern in @regex with the
- * replacement text. Backreferences of the form '\number' or
- * '\g<number>' in the replacement text are interpolated by the
- * number-th captured subexpression of the match, '\g<name>' refers
- * to the captured subexpression with the given name. '\0' refers to the
- * complete match, but '\0' followed by a number is the octal representation
- * of a character. To include a literal '\' in the replacement, write '\\'.
- * There are also escapes that changes the case of the following text:
- *
- * <variablelist>
- * <varlistentry><term>\l</term>
- * <listitem>
- * <para>Convert to lower case the next character</para>
- * </listitem>
- * </varlistentry>
- * <varlistentry><term>\u</term>
- * <listitem>
- * <para>Convert to upper case the next character</para>
- * </listitem>
- * </varlistentry>
- * <varlistentry><term>\L</term>
- * <listitem>
- * <para>Convert to lower case till \E</para>
- * </listitem>
- * </varlistentry>
- * <varlistentry><term>\U</term>
- * <listitem>
- * <para>Convert to upper case till \E</para>
- * </listitem>
- * </varlistentry>
- * <varlistentry><term>\E</term>
- * <listitem>
- * <para>End case modification</para>
- * </listitem>
- * </varlistentry>
- * </variablelist>
- *
- * If you do not need to use backreferences use g_regex_replace_literal().
- *
- * The @replacement string must be UTF-8 encoded even if #G_REGEX_RAW was
- * passed to g_regex_new(). If you want to use not UTF-8 encoded stings
- * you can use g_regex_replace_literal().
- *
- * Setting @start_position differs from just passing over a shortened
- * string and setting #G_REGEX_MATCH_NOTBOL in the case of a pattern that
- * begins with any kind of lookbehind assertion, such as "\b".
- *
- * Returns: a newly allocated string containing the replacements
- *
- * Since: 2.14
- */
-gchar *
-g_regex_replace (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- const gchar *replacement,
- GRegexMatchFlags match_options,
- GError **error)
-{
- gchar *result;
- GList *list;
- GError *tmp_error = NULL;
-
- g_return_val_if_fail (regex != NULL, NULL);
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (start_position >= 0, NULL);
- g_return_val_if_fail (replacement != NULL, NULL);
- g_return_val_if_fail (error == NULL || *error == NULL, NULL);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, NULL);
-
- list = split_replacement (replacement, &tmp_error);
- if (tmp_error != NULL)
- {
- g_propagate_error (error, tmp_error);
- return NULL;
- }
-
- result = g_regex_replace_eval (regex,
- string, string_len, start_position,
- match_options,
- interpolate_replacement,
- (gpointer)list,
- &tmp_error);
- if (tmp_error != NULL)
- g_propagate_error (error, tmp_error);
-
- g_list_foreach (list, (GFunc)free_interpolation_data, NULL);
- g_list_free (list);
-
- return result;
-}
-
-static gboolean
-literal_replacement (const GMatchInfo *match_info,
- GString *result,
- gpointer data)
-{
- g_string_append (result, data);
- return FALSE;
-}
-
-/**
- * g_regex_replace_literal:
- * @regex: a #GRegex structure
- * @string: (array length=string_len): the string to perform matches against
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @replacement: text to replace each match with
- * @match_options: options for the match
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Replaces all occurrences of the pattern in @regex with the
- * replacement text. @replacement is replaced literally, to
- * include backreferences use g_regex_replace().
- *
- * Setting @start_position differs from just passing over a
- * shortened string and setting #G_REGEX_MATCH_NOTBOL in the
- * case of a pattern that begins with any kind of lookbehind
- * assertion, such as "\b".
- *
- * Returns: a newly allocated string containing the replacements
- *
- * Since: 2.14
- */
-gchar *
-g_regex_replace_literal (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- const gchar *replacement,
- GRegexMatchFlags match_options,
- GError **error)
-{
- g_return_val_if_fail (replacement != NULL, NULL);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, NULL);
-
- return g_regex_replace_eval (regex,
- string, string_len, start_position,
- match_options,
- literal_replacement,
- (gpointer)replacement,
- error);
-}
-
-/**
- * g_regex_replace_eval:
- * @regex: a #GRegex structure from g_regex_new()
- * @string: (array length=string_len): string to perform matches against
- * @string_len: the length of @string, or -1 if @string is nul-terminated
- * @start_position: starting index of the string to match
- * @match_options: options for the match
- * @eval: a function to call for each match
- * @user_data: user data to pass to the function
- * @error: location to store the error occuring, or %NULL to ignore errors
- *
- * Replaces occurrences of the pattern in regex with the output of
- * @eval for that occurrence.
- *
- * Setting @start_position differs from just passing over a shortened
- * string and setting #G_REGEX_MATCH_NOTBOL in the case of a pattern
- * that begins with any kind of lookbehind assertion, such as "\b".
- *
- * The following example uses g_regex_replace_eval() to replace multiple
- * strings at once:
- * |[
- * static gboolean
- * eval_cb (const GMatchInfo *info,
- * GString *res,
- * gpointer data)
- * {
- * gchar *match;
- * gchar *r;
- *
- * match = g_match_info_fetch (info, 0);
- * r = g_hash_table_lookup ((GHashTable *)data, match);
- * g_string_append (res, r);
- * g_free (match);
- *
- * return FALSE;
- * }
- *
- * /* ... */
- *
- * GRegex *reg;
- * GHashTable *h;
- * gchar *res;
- *
- * h = g_hash_table_new (g_str_hash, g_str_equal);
- *
- * g_hash_table_insert (h, "1", "ONE");
- * g_hash_table_insert (h, "2", "TWO");
- * g_hash_table_insert (h, "3", "THREE");
- * g_hash_table_insert (h, "4", "FOUR");
- *
- * reg = g_regex_new ("1|2|3|4", 0, 0, NULL);
- * res = g_regex_replace_eval (reg, text, -1, 0, 0, eval_cb, h, NULL);
- * g_hash_table_destroy (h);
- *
- * /* ... */
- * ]|
- *
- * Returns: a newly allocated string containing the replacements
- *
- * Since: 2.14
- */
-gchar *
-g_regex_replace_eval (const GRegex *regex,
- const gchar *string,
- gssize string_len,
- gint start_position,
- GRegexMatchFlags match_options,
- GRegexEvalCallback eval,
- gpointer user_data,
- GError **error)
-{
- GMatchInfo *match_info;
- GString *result;
- gint str_pos = 0;
- gboolean done = FALSE;
- GError *tmp_error = NULL;
-
- g_return_val_if_fail (regex != NULL, NULL);
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (start_position >= 0, NULL);
- g_return_val_if_fail (eval != NULL, NULL);
- g_return_val_if_fail ((match_options & ~G_REGEX_MATCH_MASK) == 0, NULL);
-
- if (string_len < 0)
- string_len = strlen (string);
-
- result = g_string_sized_new (string_len);
-
- /* run down the string making matches. */
- g_regex_match_full (regex, string, string_len, start_position,
- match_options, &match_info, &tmp_error);
- while (!done && g_match_info_matches (match_info))
- {
- g_string_append_len (result,
- string + str_pos,
- match_info->offsets[0] - str_pos);
- done = (*eval) (match_info, result, user_data);
- str_pos = match_info->offsets[1];
- g_match_info_next (match_info, &tmp_error);
- }
- g_match_info_free (match_info);
- if (tmp_error != NULL)
- {
- g_propagate_error (error, tmp_error);
- g_string_free (result, TRUE);
- return NULL;
- }
-
- g_string_append_len (result, string + str_pos, string_len - str_pos);
- return g_string_free (result, FALSE);
-}
-
-/**
- * g_regex_check_replacement:
- * @replacement: the replacement string
- * @has_references: (out) (allow-none): location to store information about
- * references in @replacement or %NULL
- * @error: location to store error
- *
- * Checks whether @replacement is a valid replacement string
- * (see g_regex_replace()), i.e. that all escape sequences in
- * it are valid.
- *
- * If @has_references is not %NULL then @replacement is checked
- * for pattern references. For instance, replacement text 'foo\n'
- * does not contain references and may be evaluated without information
- * about actual match, but '\0\1' (whole match followed by first
- * subpattern) requires valid #GMatchInfo object.
- *
- * Returns: whether @replacement is a valid replacement string
- *
- * Since: 2.14
- */
-gboolean
-g_regex_check_replacement (const gchar *replacement,
- gboolean *has_references,
- GError **error)
-{
- GList *list;
- GError *tmp = NULL;
-
- list = split_replacement (replacement, &tmp);
-
- if (tmp)
- {
- g_propagate_error (error, tmp);
- return FALSE;
- }
-
- if (has_references)
- *has_references = interpolation_list_needs_match (list);
-
- g_list_foreach (list, (GFunc) free_interpolation_data, NULL);
- g_list_free (list);
-
- return TRUE;
-}
-
-/**
- * g_regex_escape_string:
- * @string: (array length=length): the string to escape
- * @length: the length of @string, or -1 if @string is nul-terminated
- *
- * Escapes the special characters used for regular expressions
- * in @string, for instance "a.b*c" becomes "a\.b\*c". This
- * function is useful to dynamically generate regular expressions.
- *
- * @string can contain nul characters that are replaced with "\0",
- * in this case remember to specify the correct length of @string
- * in @length.
- *
- * Returns: a newly-allocated escaped string
- *
- * Since: 2.14
- */
-gchar *
-g_regex_escape_string (const gchar *string,
- gint length)
-{
- GString *escaped;
- const char *p, *piece_start, *end;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- if (length < 0)
- length = strlen (string);
-
- end = string + length;
- p = piece_start = string;
- escaped = g_string_sized_new (length + 1);
-
- while (p < end)
- {
- switch (*p)
- {
- case '\0':
- case '\\':
- case '|':
- case '(':
- case ')':
- case '[':
- case ']':
- case '{':
- case '}':
- case '^':
- case '$':
- case '*':
- case '+':
- case '?':
- case '.':
- if (p != piece_start)
- /* copy the previous piece. */
- g_string_append_len (escaped, piece_start, p - piece_start);
- g_string_append_c (escaped, '\\');
- if (*p == '\0')
- g_string_append_c (escaped, '0');
- else
- g_string_append_c (escaped, *p);
- piece_start = ++p;
- break;
- default:
- p = g_utf8_next_char (p);
- break;
- }
- }
-
- if (piece_start < end)
- g_string_append_len (escaped, piece_start, end - piece_start);
-
- return g_string_free (escaped, FALSE);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <stdarg.h>
-#include <string.h>
-
-#include "ghash.h"
-#include "gmessages.h"
-#include "gtestutils.h"
-#include "gstring.h"
-
-#undef G_DISABLE_DEPRECATED
-
-#include "grel.h"
-
-/**
- * SECTION: relations
- * @title: Relations and Tuples
- * @short_description: tables of data which can be indexed on any
- * number of fields
- *
- * A #GRelation is a table of data which can be indexed on any number
- * of fields, rather like simple database tables. A #GRelation contains
- * a number of records, called tuples. Each record contains a number of
- * fields. Records are not ordered, so it is not possible to find the
- * record at a particular index.
- *
- * Note that #GRelation tables are currently limited to 2 fields.
- *
- * To create a GRelation, use g_relation_new().
- *
- * To specify which fields should be indexed, use g_relation_index().
- * Note that this must be called before any tuples are added to the
- * #GRelation.
- *
- * To add records to a #GRelation use g_relation_insert().
- *
- * To determine if a given record appears in a #GRelation, use
- * g_relation_exists(). Note that fields are compared directly, so
- * pointers must point to the exact same position (i.e. different
- * copies of the same string will not match.)
- *
- * To count the number of records which have a particular value in a
- * given field, use g_relation_count().
- *
- * To get all the records which have a particular value in a given
- * field, use g_relation_select(). To access fields of the resulting
- * records, use g_tuples_index(). To free the resulting records use
- * g_tuples_destroy().
- *
- * To delete all records which have a particular value in a given
- * field, use g_relation_delete().
- *
- * To destroy the #GRelation, use g_relation_destroy().
- *
- * To help debug #GRelation objects, use g_relation_print().
- *
- * GRelation has been marked as deprecated, since this API has never
- * been fully implemented, is not very actively maintained and rarely
- * used.
- **/
-
-typedef struct _GRealTuples GRealTuples;
-
-/**
- * GRelation:
- *
- * The #GRelation struct is an opaque data structure to represent a
- * <link linkend="glib-Relations-and-Tuples">Relation</link>. It should
- * only be accessed via the following functions.
- **/
-struct _GRelation
-{
- gint fields;
- gint current_field;
-
- GHashTable *all_tuples;
- GHashTable **hashed_tuple_tables;
-
- gint count;
-};
-
-/**
- * GTuples:
- * @len: the number of records that matched.
- *
- * The #GTuples struct is used to return records (or tuples) from the
- * #GRelation by g_relation_select(). It only contains one public
- * member - the number of records that matched. To access the matched
- * records, you must use g_tuples_index().
- **/
-struct _GRealTuples
-{
- gint len;
- gint width;
- gpointer *data;
-};
-
-static gboolean
-tuple_equal_2 (gconstpointer v_a,
- gconstpointer v_b)
-{
- gpointer* a = (gpointer*) v_a;
- gpointer* b = (gpointer*) v_b;
-
- return a[0] == b[0] && a[1] == b[1];
-}
-
-static guint
-tuple_hash_2 (gconstpointer v_a)
-{
-#if GLIB_SIZEOF_VOID_P > GLIB_SIZEOF_LONG
- /* In practise this snippet has been written for 64-bit Windows
- * where ints are 32 bits, pointers 64 bits. More exotic platforms
- * need more tweaks.
- */
- guint* a = (guint*) v_a;
-
- return (a[0] ^ a[1] ^ a[2] ^ a[3]);
-#else
- gpointer* a = (gpointer*) v_a;
-
- return (gulong)a[0] ^ (gulong)a[1];
-#endif
-}
-
-static GHashFunc
-tuple_hash (gint fields)
-{
- switch (fields)
- {
- case 2:
- return tuple_hash_2;
- default:
- g_error ("no tuple hash for %d", fields);
- }
-
- return NULL;
-}
-
-static GEqualFunc
-tuple_equal (gint fields)
-{
- switch (fields)
- {
- case 2:
- return tuple_equal_2;
- default:
- g_error ("no tuple equal for %d", fields);
- }
-
- return NULL;
-}
-
-/**
- * g_relation_new:
- * @fields: the number of fields.
- * @Returns: a new #GRelation.
- *
- * Creates a new #GRelation with the given number of fields. Note that
- * currently the number of fields must be 2.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-GRelation*
-g_relation_new (gint fields)
-{
- GRelation* rel = g_new0 (GRelation, 1);
-
- rel->fields = fields;
- rel->all_tuples = g_hash_table_new (tuple_hash (fields), tuple_equal (fields));
- rel->hashed_tuple_tables = g_new0 (GHashTable*, fields);
-
- return rel;
-}
-
-static void
-relation_delete_value_tuple (gpointer tuple_key,
- gpointer tuple_value,
- gpointer user_data)
-{
- GRelation *relation = user_data;
- gpointer *tuple = tuple_value;
- g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
-}
-
-static void
-g_relation_free_array (gpointer key, gpointer value, gpointer user_data)
-{
- g_hash_table_destroy ((GHashTable*) value);
-}
-
-/**
- * g_relation_destroy:
- * @relation: a #GRelation.
- *
- * Destroys the #GRelation, freeing all memory allocated. However, it
- * does not free memory allocated for the tuple data, so you should
- * free that first if appropriate.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_relation_destroy (GRelation *relation)
-{
- gint i;
-
- if (relation)
- {
- for (i = 0; i < relation->fields; i += 1)
- {
- if (relation->hashed_tuple_tables[i])
- {
- g_hash_table_foreach (relation->hashed_tuple_tables[i], g_relation_free_array, NULL);
- g_hash_table_destroy (relation->hashed_tuple_tables[i]);
- }
- }
-
- g_hash_table_foreach (relation->all_tuples, relation_delete_value_tuple, relation);
- g_hash_table_destroy (relation->all_tuples);
-
- g_free (relation->hashed_tuple_tables);
- g_free (relation);
- }
-}
-
-/**
- * g_relation_index:
- * @relation: a #GRelation.
- * @field: the field to index, counting from 0.
- * @hash_func: a function to produce a hash value from the field data.
- * @key_equal_func: a function to compare two values of the given field.
- *
- * Creates an index on the given field. Note that this must be called
- * before any records are added to the #GRelation.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_relation_index (GRelation *relation,
- gint field,
- GHashFunc hash_func,
- GEqualFunc key_equal_func)
-{
- g_return_if_fail (relation != NULL);
-
- g_return_if_fail (relation->count == 0 && relation->hashed_tuple_tables[field] == NULL);
-
- relation->hashed_tuple_tables[field] = g_hash_table_new (hash_func, key_equal_func);
-}
-
-/**
- * g_relation_insert:
- * @relation: a #GRelation.
- * @Varargs: the fields of the record to add. These must match the
- * number of fields in the #GRelation, and of type #gpointer
- * or #gconstpointer.
- *
- * Inserts a record into a #GRelation.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_relation_insert (GRelation *relation,
- ...)
-{
- gpointer* tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
- va_list args;
- gint i;
-
- va_start (args, relation);
-
- for (i = 0; i < relation->fields; i += 1)
- tuple[i] = va_arg (args, gpointer);
-
- va_end (args);
-
- g_hash_table_insert (relation->all_tuples, tuple, tuple);
-
- relation->count += 1;
-
- for (i = 0; i < relation->fields; i += 1)
- {
- GHashTable *table;
- gpointer key;
- GHashTable *per_key_table;
-
- table = relation->hashed_tuple_tables[i];
-
- if (table == NULL)
- continue;
-
- key = tuple[i];
- per_key_table = g_hash_table_lookup (table, key);
-
- if (per_key_table == NULL)
- {
- per_key_table = g_hash_table_new (tuple_hash (relation->fields), tuple_equal (relation->fields));
- g_hash_table_insert (table, key, per_key_table);
- }
-
- g_hash_table_insert (per_key_table, tuple, tuple);
- }
-}
-
-static void
-g_relation_delete_tuple (gpointer tuple_key,
- gpointer tuple_value,
- gpointer user_data)
-{
- gpointer *tuple = (gpointer*) tuple_value;
- GRelation *relation = (GRelation *) user_data;
- gint j;
-
- g_assert (tuple_key == tuple_value);
-
- for (j = 0; j < relation->fields; j += 1)
- {
- GHashTable *one_table = relation->hashed_tuple_tables[j];
- gpointer one_key;
- GHashTable *per_key_table;
-
- if (one_table == NULL)
- continue;
-
- if (j == relation->current_field)
- /* can't delete from the table we're foreaching in */
- continue;
-
- one_key = tuple[j];
-
- per_key_table = g_hash_table_lookup (one_table, one_key);
-
- g_hash_table_remove (per_key_table, tuple);
- }
-
- if (g_hash_table_remove (relation->all_tuples, tuple))
- g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
-
- relation->count -= 1;
-}
-
-/**
- * g_relation_delete:
- * @relation: a #GRelation.
- * @key: the value to compare with.
- * @field: the field of each record to match.
- * @Returns: the number of records deleted.
- *
- * Deletes any records from a #GRelation that have the given key value
- * in the given field.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-gint
-g_relation_delete (GRelation *relation,
- gconstpointer key,
- gint field)
-{
- GHashTable *table;
- GHashTable *key_table;
- gint count;
-
- g_return_val_if_fail (relation != NULL, 0);
-
- table = relation->hashed_tuple_tables[field];
- count = relation->count;
-
- g_return_val_if_fail (table != NULL, 0);
-
- key_table = g_hash_table_lookup (table, key);
-
- if (!key_table)
- return 0;
-
- relation->current_field = field;
-
- g_hash_table_foreach (key_table, g_relation_delete_tuple, relation);
-
- g_hash_table_remove (table, key);
-
- g_hash_table_destroy (key_table);
-
- /* @@@ FIXME: Remove empty hash tables. */
-
- return count - relation->count;
-}
-
-static void
-g_relation_select_tuple (gpointer tuple_key,
- gpointer tuple_value,
- gpointer user_data)
-{
- gpointer *tuple = (gpointer*) tuple_value;
- GRealTuples *tuples = (GRealTuples*) user_data;
- gint stride = sizeof (gpointer) * tuples->width;
-
- g_assert (tuple_key == tuple_value);
-
- memcpy (tuples->data + (tuples->len * tuples->width),
- tuple,
- stride);
-
- tuples->len += 1;
-}
-
-/**
- * g_relation_select:
- * @relation: a #GRelation.
- * @key: the value to compare with.
- * @field: the field of each record to match.
- * @Returns: the records (tuples) that matched.
- *
- * Returns all of the tuples which have the given key in the given
- * field. Use g_tuples_index() to access the returned records. The
- * returned records should be freed with g_tuples_destroy().
- *
- * Deprecated: 2.26: Rarely used API
- **/
-GTuples*
-g_relation_select (GRelation *relation,
- gconstpointer key,
- gint field)
-{
- GHashTable *table;
- GHashTable *key_table;
- GRealTuples *tuples;
- gint count;
-
- g_return_val_if_fail (relation != NULL, NULL);
-
- table = relation->hashed_tuple_tables[field];
-
- g_return_val_if_fail (table != NULL, NULL);
-
- tuples = g_new0 (GRealTuples, 1);
- key_table = g_hash_table_lookup (table, key);
-
- if (!key_table)
- return (GTuples*)tuples;
-
- count = g_relation_count (relation, key, field);
-
- tuples->data = g_malloc (sizeof (gpointer) * relation->fields * count);
- tuples->width = relation->fields;
-
- g_hash_table_foreach (key_table, g_relation_select_tuple, tuples);
-
- g_assert (count == tuples->len);
-
- return (GTuples*)tuples;
-}
-
-/**
- * g_relation_count:
- * @relation: a #GRelation.
- * @key: the value to compare with.
- * @field: the field of each record to match.
- * @Returns: the number of matches.
- *
- * Returns the number of tuples in a #GRelation that have the given
- * value in the given field.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-gint
-g_relation_count (GRelation *relation,
- gconstpointer key,
- gint field)
-{
- GHashTable *table;
- GHashTable *key_table;
-
- g_return_val_if_fail (relation != NULL, 0);
-
- table = relation->hashed_tuple_tables[field];
-
- g_return_val_if_fail (table != NULL, 0);
-
- key_table = g_hash_table_lookup (table, key);
-
- if (!key_table)
- return 0;
-
- return g_hash_table_size (key_table);
-}
-
-/**
- * g_relation_exists:
- * @relation: a #GRelation.
- * @Varargs: the fields of the record to compare. The number must match
- * the number of fields in the #GRelation.
- * @Returns: %TRUE if a record matches.
- *
- * Returns %TRUE if a record with the given values exists in a
- * #GRelation. Note that the values are compared directly, so that, for
- * example, two copies of the same string will not match.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-gboolean
-g_relation_exists (GRelation *relation, ...)
-{
- gpointer *tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
- va_list args;
- gint i;
- gboolean result;
-
- va_start(args, relation);
-
- for (i = 0; i < relation->fields; i += 1)
- tuple[i] = va_arg(args, gpointer);
-
- va_end(args);
-
- result = g_hash_table_lookup (relation->all_tuples, tuple) != NULL;
-
- g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
-
- return result;
-}
-
-/**
- * g_tuples_destroy:
- * @tuples: the tuple data to free.
- *
- * Frees the records which were returned by g_relation_select(). This
- * should always be called after g_relation_select() when you are
- * finished with the records. The records are not removed from the
- * #GRelation.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_tuples_destroy (GTuples *tuples0)
-{
- GRealTuples *tuples = (GRealTuples*) tuples0;
-
- if (tuples)
- {
- g_free (tuples->data);
- g_free (tuples);
- }
-}
-
-/**
- * g_tuples_index:
- * @tuples: the tuple data, returned by g_relation_select().
- * @index_: the index of the record.
- * @field: the field to return.
- * @Returns: the field of the record.
- *
- * Gets a field from the records returned by g_relation_select(). It
- * returns the given field of the record at the given index. The
- * returned value should not be changed.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-gpointer
-g_tuples_index (GTuples *tuples0,
- gint index,
- gint field)
-{
- GRealTuples *tuples = (GRealTuples*) tuples0;
-
- g_return_val_if_fail (tuples0 != NULL, NULL);
- g_return_val_if_fail (field < tuples->width, NULL);
-
- return tuples->data[index * tuples->width + field];
-}
-
-/* Print
- */
-
-static void
-g_relation_print_one (gpointer tuple_key,
- gpointer tuple_value,
- gpointer user_data)
-{
- gint i;
- GString *gstring;
- GRelation* rel = (GRelation*) user_data;
- gpointer* tuples = (gpointer*) tuple_value;
-
- gstring = g_string_new ("[");
-
- for (i = 0; i < rel->fields; i += 1)
- {
- g_string_append_printf (gstring, "%p", tuples[i]);
-
- if (i < (rel->fields - 1))
- g_string_append (gstring, ",");
- }
-
- g_string_append (gstring, "]");
- g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%s", gstring->str);
- g_string_free (gstring, TRUE);
-}
-
-static void
-g_relation_print_index (gpointer tuple_key,
- gpointer tuple_value,
- gpointer user_data)
-{
- GRelation* rel = (GRelation*) user_data;
- GHashTable* table = (GHashTable*) tuple_value;
-
- g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** key %p", tuple_key);
-
- g_hash_table_foreach (table,
- g_relation_print_one,
- rel);
-}
-
-/**
- * g_relation_print:
- * @relation: a #GRelation.
- *
- * Outputs information about all records in a #GRelation, as well as
- * the indexes. It is for debugging.
- *
- * Deprecated: 2.26: Rarely used API
- **/
-void
-g_relation_print (GRelation *relation)
-{
- gint i;
-
- g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** all tuples (%d)", relation->count);
-
- g_hash_table_foreach (relation->all_tuples,
- g_relation_print_one,
- relation);
-
- for (i = 0; i < relation->fields; i += 1)
- {
- if (relation->hashed_tuple_tables[i] == NULL)
- continue;
-
- g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** index %d", i);
-
- g_hash_table_foreach (relation->hashed_tuple_tables[i],
- g_relation_print_index,
- relation);
- }
-
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GScanner: Flexible lexical scanner for general purpose.
- * Copyright (C) 1997, 1998 Tim Janik
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef G_OS_WIN32
-#include <io.h> /* For _read() */
-#endif
-
-#include "gscanner.h"
-
-#include "gprintfint.h"
-#include "gstrfuncs.h"
-#include "gstring.h"
-#include "gtestutils.h"
-
-/* --- defines --- */
-#define to_lower(c) ( \
- (guchar) ( \
- ( (((guchar)(c))>='A' && ((guchar)(c))<='Z') * ('a'-'A') ) | \
- ( (((guchar)(c))>=192 && ((guchar)(c))<=214) * (224-192) ) | \
- ( (((guchar)(c))>=216 && ((guchar)(c))<=222) * (248-216) ) | \
- ((guchar)(c)) \
- ) \
-)
-#define READ_BUFFER_SIZE (4000)
-
-
-/* --- typedefs --- */
-typedef struct _GScannerKey GScannerKey;
-
-struct _GScannerKey
-{
- guint scope_id;
- gchar *symbol;
- gpointer value;
-};
-
-
-/* --- variables --- */
-static const GScannerConfig g_scanner_config_template =
-{
- (
- " \t\r\n"
- ) /* cset_skip_characters */,
- (
- G_CSET_a_2_z
- "_"
- G_CSET_A_2_Z
- ) /* cset_identifier_first */,
- (
- G_CSET_a_2_z
- "_"
- G_CSET_A_2_Z
- G_CSET_DIGITS
- G_CSET_LATINS
- G_CSET_LATINC
- ) /* cset_identifier_nth */,
- ( "#\n" ) /* cpair_comment_single */,
-
- FALSE /* case_sensitive */,
-
- TRUE /* skip_comment_multi */,
- TRUE /* skip_comment_single */,
- TRUE /* scan_comment_multi */,
- TRUE /* scan_identifier */,
- FALSE /* scan_identifier_1char */,
- FALSE /* scan_identifier_NULL */,
- TRUE /* scan_symbols */,
- FALSE /* scan_binary */,
- TRUE /* scan_octal */,
- TRUE /* scan_float */,
- TRUE /* scan_hex */,
- FALSE /* scan_hex_dollar */,
- TRUE /* scan_string_sq */,
- TRUE /* scan_string_dq */,
- TRUE /* numbers_2_int */,
- FALSE /* int_2_float */,
- FALSE /* identifier_2_string */,
- TRUE /* char_2_token */,
- FALSE /* symbol_2_token */,
- FALSE /* scope_0_fallback */,
- FALSE /* store_int64 */,
-};
-
-
-/* --- prototypes --- */
-static inline
-GScannerKey* g_scanner_lookup_internal (GScanner *scanner,
- guint scope_id,
- const gchar *symbol);
-static gboolean g_scanner_key_equal (gconstpointer v1,
- gconstpointer v2);
-static guint g_scanner_key_hash (gconstpointer v);
-static void g_scanner_get_token_ll (GScanner *scanner,
- GTokenType *token_p,
- GTokenValue *value_p,
- guint *line_p,
- guint *position_p);
-static void g_scanner_get_token_i (GScanner *scanner,
- GTokenType *token_p,
- GTokenValue *value_p,
- guint *line_p,
- guint *position_p);
-
-static guchar g_scanner_peek_next_char (GScanner *scanner);
-static guchar g_scanner_get_char (GScanner *scanner,
- guint *line_p,
- guint *position_p);
-static void g_scanner_msg_handler (GScanner *scanner,
- gchar *message,
- gboolean is_error);
-
-
-/* --- functions --- */
-static inline gint
-g_scanner_char_2_num (guchar c,
- guchar base)
-{
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (c >= 'A' && c <= 'Z')
- c -= 'A' - 10;
- else if (c >= 'a' && c <= 'z')
- c -= 'a' - 10;
- else
- return -1;
-
- if (c < base)
- return c;
-
- return -1;
-}
-
-GScanner*
-g_scanner_new (const GScannerConfig *config_templ)
-{
- GScanner *scanner;
-
- if (!config_templ)
- config_templ = &g_scanner_config_template;
-
- scanner = g_new0 (GScanner, 1);
-
- scanner->user_data = NULL;
- scanner->max_parse_errors = 1;
- scanner->parse_errors = 0;
- scanner->input_name = NULL;
- g_datalist_init (&scanner->qdata);
-
- scanner->config = g_new0 (GScannerConfig, 1);
-
- scanner->config->case_sensitive = config_templ->case_sensitive;
- scanner->config->cset_skip_characters = config_templ->cset_skip_characters;
- if (!scanner->config->cset_skip_characters)
- scanner->config->cset_skip_characters = "";
- scanner->config->cset_identifier_first = config_templ->cset_identifier_first;
- scanner->config->cset_identifier_nth = config_templ->cset_identifier_nth;
- scanner->config->cpair_comment_single = config_templ->cpair_comment_single;
- scanner->config->skip_comment_multi = config_templ->skip_comment_multi;
- scanner->config->skip_comment_single = config_templ->skip_comment_single;
- scanner->config->scan_comment_multi = config_templ->scan_comment_multi;
- scanner->config->scan_identifier = config_templ->scan_identifier;
- scanner->config->scan_identifier_1char = config_templ->scan_identifier_1char;
- scanner->config->scan_identifier_NULL = config_templ->scan_identifier_NULL;
- scanner->config->scan_symbols = config_templ->scan_symbols;
- scanner->config->scan_binary = config_templ->scan_binary;
- scanner->config->scan_octal = config_templ->scan_octal;
- scanner->config->scan_float = config_templ->scan_float;
- scanner->config->scan_hex = config_templ->scan_hex;
- scanner->config->scan_hex_dollar = config_templ->scan_hex_dollar;
- scanner->config->scan_string_sq = config_templ->scan_string_sq;
- scanner->config->scan_string_dq = config_templ->scan_string_dq;
- scanner->config->numbers_2_int = config_templ->numbers_2_int;
- scanner->config->int_2_float = config_templ->int_2_float;
- scanner->config->identifier_2_string = config_templ->identifier_2_string;
- scanner->config->char_2_token = config_templ->char_2_token;
- scanner->config->symbol_2_token = config_templ->symbol_2_token;
- scanner->config->scope_0_fallback = config_templ->scope_0_fallback;
- scanner->config->store_int64 = config_templ->store_int64;
-
- scanner->token = G_TOKEN_NONE;
- scanner->value.v_int64 = 0;
- scanner->line = 1;
- scanner->position = 0;
-
- scanner->next_token = G_TOKEN_NONE;
- scanner->next_value.v_int64 = 0;
- scanner->next_line = 1;
- scanner->next_position = 0;
-
- scanner->symbol_table = g_hash_table_new (g_scanner_key_hash, g_scanner_key_equal);
- scanner->input_fd = -1;
- scanner->text = NULL;
- scanner->text_end = NULL;
- scanner->buffer = NULL;
- scanner->scope_id = 0;
-
- scanner->msg_handler = g_scanner_msg_handler;
-
- return scanner;
-}
-
-static inline void
-g_scanner_free_value (GTokenType *token_p,
- GTokenValue *value_p)
-{
- switch (*token_p)
- {
- case G_TOKEN_STRING:
- case G_TOKEN_IDENTIFIER:
- case G_TOKEN_IDENTIFIER_NULL:
- case G_TOKEN_COMMENT_SINGLE:
- case G_TOKEN_COMMENT_MULTI:
- g_free (value_p->v_string);
- break;
-
- default:
- break;
- }
-
- *token_p = G_TOKEN_NONE;
-}
-
-static void
-g_scanner_destroy_symbol_table_entry (gpointer _key,
- gpointer _value,
- gpointer _data)
-{
- GScannerKey *key = _key;
-
- g_free (key->symbol);
- g_free (key);
-}
-
-void
-g_scanner_destroy (GScanner *scanner)
-{
- g_return_if_fail (scanner != NULL);
-
- g_datalist_clear (&scanner->qdata);
- g_hash_table_foreach (scanner->symbol_table,
- g_scanner_destroy_symbol_table_entry, NULL);
- g_hash_table_destroy (scanner->symbol_table);
- g_scanner_free_value (&scanner->token, &scanner->value);
- g_scanner_free_value (&scanner->next_token, &scanner->next_value);
- g_free (scanner->config);
- g_free (scanner->buffer);
- g_free (scanner);
-}
-
-static void
-g_scanner_msg_handler (GScanner *scanner,
- gchar *message,
- gboolean is_error)
-{
- g_return_if_fail (scanner != NULL);
-
- _g_fprintf (stderr, "%s:%d: ",
- scanner->input_name ? scanner->input_name : "<memory>",
- scanner->line);
- if (is_error)
- _g_fprintf (stderr, "error: ");
- _g_fprintf (stderr, "%s\n", message);
-}
-
-void
-g_scanner_error (GScanner *scanner,
- const gchar *format,
- ...)
-{
- g_return_if_fail (scanner != NULL);
- g_return_if_fail (format != NULL);
-
- scanner->parse_errors++;
-
- if (scanner->msg_handler)
- {
- va_list args;
- gchar *string;
-
- va_start (args, format);
- string = g_strdup_vprintf (format, args);
- va_end (args);
-
- scanner->msg_handler (scanner, string, TRUE);
-
- g_free (string);
- }
-}
-
-void
-g_scanner_warn (GScanner *scanner,
- const gchar *format,
- ...)
-{
- g_return_if_fail (scanner != NULL);
- g_return_if_fail (format != NULL);
-
- if (scanner->msg_handler)
- {
- va_list args;
- gchar *string;
-
- va_start (args, format);
- string = g_strdup_vprintf (format, args);
- va_end (args);
-
- scanner->msg_handler (scanner, string, FALSE);
-
- g_free (string);
- }
-}
-
-static gboolean
-g_scanner_key_equal (gconstpointer v1,
- gconstpointer v2)
-{
- const GScannerKey *key1 = v1;
- const GScannerKey *key2 = v2;
-
- return (key1->scope_id == key2->scope_id) && (strcmp (key1->symbol, key2->symbol) == 0);
-}
-
-static guint
-g_scanner_key_hash (gconstpointer v)
-{
- const GScannerKey *key = v;
- gchar *c;
- guint h;
-
- h = key->scope_id;
- for (c = key->symbol; *c; c++)
- h = (h << 5) - h + *c;
-
- return h;
-}
-
-static inline GScannerKey*
-g_scanner_lookup_internal (GScanner *scanner,
- guint scope_id,
- const gchar *symbol)
-{
- GScannerKey *key_p;
- GScannerKey key;
-
- key.scope_id = scope_id;
-
- if (!scanner->config->case_sensitive)
- {
- gchar *d;
- const gchar *c;
-
- key.symbol = g_new (gchar, strlen (symbol) + 1);
- for (d = key.symbol, c = symbol; *c; c++, d++)
- *d = to_lower (*c);
- *d = 0;
- key_p = g_hash_table_lookup (scanner->symbol_table, &key);
- g_free (key.symbol);
- }
- else
- {
- key.symbol = (gchar*) symbol;
- key_p = g_hash_table_lookup (scanner->symbol_table, &key);
- }
-
- return key_p;
-}
-
-void
-g_scanner_scope_add_symbol (GScanner *scanner,
- guint scope_id,
- const gchar *symbol,
- gpointer value)
-{
- GScannerKey *key;
-
- g_return_if_fail (scanner != NULL);
- g_return_if_fail (symbol != NULL);
-
- key = g_scanner_lookup_internal (scanner, scope_id, symbol);
-
- if (!key)
- {
- key = g_new (GScannerKey, 1);
- key->scope_id = scope_id;
- key->symbol = g_strdup (symbol);
- key->value = value;
- if (!scanner->config->case_sensitive)
- {
- gchar *c;
-
- c = key->symbol;
- while (*c != 0)
- {
- *c = to_lower (*c);
- c++;
- }
- }
- g_hash_table_insert (scanner->symbol_table, key, key);
- }
- else
- key->value = value;
-}
-
-void
-g_scanner_scope_remove_symbol (GScanner *scanner,
- guint scope_id,
- const gchar *symbol)
-{
- GScannerKey *key;
-
- g_return_if_fail (scanner != NULL);
- g_return_if_fail (symbol != NULL);
-
- key = g_scanner_lookup_internal (scanner, scope_id, symbol);
-
- if (key)
- {
- g_hash_table_remove (scanner->symbol_table, key);
- g_free (key->symbol);
- g_free (key);
- }
-}
-
-gpointer
-g_scanner_lookup_symbol (GScanner *scanner,
- const gchar *symbol)
-{
- GScannerKey *key;
- guint scope_id;
-
- g_return_val_if_fail (scanner != NULL, NULL);
-
- if (!symbol)
- return NULL;
-
- scope_id = scanner->scope_id;
- key = g_scanner_lookup_internal (scanner, scope_id, symbol);
- if (!key && scope_id && scanner->config->scope_0_fallback)
- key = g_scanner_lookup_internal (scanner, 0, symbol);
-
- if (key)
- return key->value;
- else
- return NULL;
-}
-
-gpointer
-g_scanner_scope_lookup_symbol (GScanner *scanner,
- guint scope_id,
- const gchar *symbol)
-{
- GScannerKey *key;
-
- g_return_val_if_fail (scanner != NULL, NULL);
-
- if (!symbol)
- return NULL;
-
- key = g_scanner_lookup_internal (scanner, scope_id, symbol);
-
- if (key)
- return key->value;
- else
- return NULL;
-}
-
-guint
-g_scanner_set_scope (GScanner *scanner,
- guint scope_id)
-{
- guint old_scope_id;
-
- g_return_val_if_fail (scanner != NULL, 0);
-
- old_scope_id = scanner->scope_id;
- scanner->scope_id = scope_id;
-
- return old_scope_id;
-}
-
-static void
-g_scanner_foreach_internal (gpointer _key,
- gpointer _value,
- gpointer _user_data)
-{
- GScannerKey *key;
- gpointer *d;
- GHFunc func;
- gpointer user_data;
- guint *scope_id;
-
- d = _user_data;
- func = (GHFunc) d[0];
- user_data = d[1];
- scope_id = d[2];
- key = _value;
-
- if (key->scope_id == *scope_id)
- func (key->symbol, key->value, user_data);
-}
-
-void
-g_scanner_scope_foreach_symbol (GScanner *scanner,
- guint scope_id,
- GHFunc func,
- gpointer user_data)
-{
- gpointer d[3];
-
- g_return_if_fail (scanner != NULL);
-
- d[0] = (gpointer) func;
- d[1] = user_data;
- d[2] = &scope_id;
-
- g_hash_table_foreach (scanner->symbol_table, g_scanner_foreach_internal, d);
-}
-
-GTokenType
-g_scanner_peek_next_token (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
-
- if (scanner->next_token == G_TOKEN_NONE)
- {
- scanner->next_line = scanner->line;
- scanner->next_position = scanner->position;
- g_scanner_get_token_i (scanner,
- &scanner->next_token,
- &scanner->next_value,
- &scanner->next_line,
- &scanner->next_position);
- }
-
- return scanner->next_token;
-}
-
-GTokenType
-g_scanner_get_next_token (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
-
- if (scanner->next_token != G_TOKEN_NONE)
- {
- g_scanner_free_value (&scanner->token, &scanner->value);
-
- scanner->token = scanner->next_token;
- scanner->value = scanner->next_value;
- scanner->line = scanner->next_line;
- scanner->position = scanner->next_position;
- scanner->next_token = G_TOKEN_NONE;
- }
- else
- g_scanner_get_token_i (scanner,
- &scanner->token,
- &scanner->value,
- &scanner->line,
- &scanner->position);
-
- return scanner->token;
-}
-
-GTokenType
-g_scanner_cur_token (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, G_TOKEN_EOF);
-
- return scanner->token;
-}
-
-GTokenValue
-g_scanner_cur_value (GScanner *scanner)
-{
- GTokenValue v;
-
- v.v_int64 = 0;
-
- g_return_val_if_fail (scanner != NULL, v);
-
- /* MSC isn't capable of handling return scanner->value; ? */
-
- v = scanner->value;
-
- return v;
-}
-
-guint
-g_scanner_cur_line (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, 0);
-
- return scanner->line;
-}
-
-guint
-g_scanner_cur_position (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, 0);
-
- return scanner->position;
-}
-
-gboolean
-g_scanner_eof (GScanner *scanner)
-{
- g_return_val_if_fail (scanner != NULL, TRUE);
-
- return scanner->token == G_TOKEN_EOF || scanner->token == G_TOKEN_ERROR;
-}
-
-void
-g_scanner_input_file (GScanner *scanner,
- gint input_fd)
-{
- g_return_if_fail (scanner != NULL);
- g_return_if_fail (input_fd >= 0);
-
- if (scanner->input_fd >= 0)
- g_scanner_sync_file_offset (scanner);
-
- scanner->token = G_TOKEN_NONE;
- scanner->value.v_int64 = 0;
- scanner->line = 1;
- scanner->position = 0;
- scanner->next_token = G_TOKEN_NONE;
-
- scanner->input_fd = input_fd;
- scanner->text = NULL;
- scanner->text_end = NULL;
-
- if (!scanner->buffer)
- scanner->buffer = g_new (gchar, READ_BUFFER_SIZE + 1);
-}
-
-void
-g_scanner_input_text (GScanner *scanner,
- const gchar *text,
- guint text_len)
-{
- g_return_if_fail (scanner != NULL);
- if (text_len)
- g_return_if_fail (text != NULL);
- else
- text = NULL;
-
- if (scanner->input_fd >= 0)
- g_scanner_sync_file_offset (scanner);
-
- scanner->token = G_TOKEN_NONE;
- scanner->value.v_int64 = 0;
- scanner->line = 1;
- scanner->position = 0;
- scanner->next_token = G_TOKEN_NONE;
-
- scanner->input_fd = -1;
- scanner->text = text;
- scanner->text_end = text + text_len;
-
- if (scanner->buffer)
- {
- g_free (scanner->buffer);
- scanner->buffer = NULL;
- }
-}
-
-static guchar
-g_scanner_peek_next_char (GScanner *scanner)
-{
- if (scanner->text < scanner->text_end)
- {
- return *scanner->text;
- }
- else if (scanner->input_fd >= 0)
- {
- gint count;
- gchar *buffer;
-
- buffer = scanner->buffer;
- do
- {
- count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
- }
- while (count == -1 && (errno == EINTR || errno == EAGAIN));
-
- if (count < 1)
- {
- scanner->input_fd = -1;
-
- return 0;
- }
- else
- {
- scanner->text = buffer;
- scanner->text_end = buffer + count;
-
- return *buffer;
- }
- }
- else
- return 0;
-}
-
-void
-g_scanner_sync_file_offset (GScanner *scanner)
-{
- g_return_if_fail (scanner != NULL);
-
- /* for file input, rewind the filedescriptor to the current
- * buffer position and blow the file read ahead buffer. useful
- * for third party uses of our file descriptor, which hooks
- * onto the current scanning position.
- */
-
- if (scanner->input_fd >= 0 && scanner->text_end > scanner->text)
- {
- gint buffered;
-
- buffered = scanner->text_end - scanner->text;
- if (lseek (scanner->input_fd, - buffered, SEEK_CUR) >= 0)
- {
- /* we succeeded, blow our buffer's contents now */
- scanner->text = NULL;
- scanner->text_end = NULL;
- }
- else
- errno = 0;
- }
-}
-
-static guchar
-g_scanner_get_char (GScanner *scanner,
- guint *line_p,
- guint *position_p)
-{
- guchar fchar;
-
- if (scanner->text < scanner->text_end)
- fchar = *(scanner->text++);
- else if (scanner->input_fd >= 0)
- {
- gint count;
- gchar *buffer;
-
- buffer = scanner->buffer;
- do
- {
- count = read (scanner->input_fd, buffer, READ_BUFFER_SIZE);
- }
- while (count == -1 && (errno == EINTR || errno == EAGAIN));
-
- if (count < 1)
- {
- scanner->input_fd = -1;
- fchar = 0;
- }
- else
- {
- scanner->text = buffer + 1;
- scanner->text_end = buffer + count;
- fchar = *buffer;
- if (!fchar)
- {
- g_scanner_sync_file_offset (scanner);
- scanner->text_end = scanner->text;
- scanner->input_fd = -1;
- }
- }
- }
- else
- fchar = 0;
-
- if (fchar == '\n')
- {
- (*position_p) = 0;
- (*line_p)++;
- }
- else if (fchar)
- {
- (*position_p)++;
- }
-
- return fchar;
-}
-
-void
-g_scanner_unexp_token (GScanner *scanner,
- GTokenType expected_token,
- const gchar *identifier_spec,
- const gchar *symbol_spec,
- const gchar *symbol_name,
- const gchar *message,
- gint is_error)
-{
- gchar *token_string;
- guint token_string_len;
- gchar *expected_string;
- guint expected_string_len;
- gchar *message_prefix;
- gboolean print_unexp;
- void (*msg_handler) (GScanner*, const gchar*, ...);
-
- g_return_if_fail (scanner != NULL);
-
- if (is_error)
- msg_handler = g_scanner_error;
- else
- msg_handler = g_scanner_warn;
-
- if (!identifier_spec)
- identifier_spec = "identifier";
- if (!symbol_spec)
- symbol_spec = "symbol";
-
- token_string_len = 56;
- token_string = g_new (gchar, token_string_len + 1);
- expected_string_len = 64;
- expected_string = g_new (gchar, expected_string_len + 1);
- print_unexp = TRUE;
-
- switch (scanner->token)
- {
- case G_TOKEN_EOF:
- _g_snprintf (token_string, token_string_len, "end of file");
- break;
-
- default:
- if (scanner->token >= 1 && scanner->token <= 255)
- {
- if ((scanner->token >= ' ' && scanner->token <= '~') ||
- strchr (scanner->config->cset_identifier_first, scanner->token) ||
- strchr (scanner->config->cset_identifier_nth, scanner->token))
- _g_snprintf (token_string, token_string_len, "character `%c'", scanner->token);
- else
- _g_snprintf (token_string, token_string_len, "character `\\%o'", scanner->token);
- break;
- }
- else if (!scanner->config->symbol_2_token)
- {
- _g_snprintf (token_string, token_string_len, "(unknown) token <%d>", scanner->token);
- break;
- }
- /* fall through */
- case G_TOKEN_SYMBOL:
- if (expected_token == G_TOKEN_SYMBOL ||
- (scanner->config->symbol_2_token &&
- expected_token > G_TOKEN_LAST))
- print_unexp = FALSE;
- if (symbol_name)
- _g_snprintf (token_string,
- token_string_len,
- "%s%s `%s'",
- print_unexp ? "" : "invalid ",
- symbol_spec,
- symbol_name);
- else
- _g_snprintf (token_string,
- token_string_len,
- "%s%s",
- print_unexp ? "" : "invalid ",
- symbol_spec);
- break;
-
- case G_TOKEN_ERROR:
- print_unexp = FALSE;
- expected_token = G_TOKEN_NONE;
- switch (scanner->value.v_error)
- {
- case G_ERR_UNEXP_EOF:
- _g_snprintf (token_string, token_string_len, "scanner: unexpected end of file");
- break;
-
- case G_ERR_UNEXP_EOF_IN_STRING:
- _g_snprintf (token_string, token_string_len, "scanner: unterminated string constant");
- break;
-
- case G_ERR_UNEXP_EOF_IN_COMMENT:
- _g_snprintf (token_string, token_string_len, "scanner: unterminated comment");
- break;
-
- case G_ERR_NON_DIGIT_IN_CONST:
- _g_snprintf (token_string, token_string_len, "scanner: non digit in constant");
- break;
-
- case G_ERR_FLOAT_RADIX:
- _g_snprintf (token_string, token_string_len, "scanner: invalid radix for floating constant");
- break;
-
- case G_ERR_FLOAT_MALFORMED:
- _g_snprintf (token_string, token_string_len, "scanner: malformed floating constant");
- break;
-
- case G_ERR_DIGIT_RADIX:
- _g_snprintf (token_string, token_string_len, "scanner: digit is beyond radix");
- break;
-
- case G_ERR_UNKNOWN:
- default:
- _g_snprintf (token_string, token_string_len, "scanner: unknown error");
- break;
- }
- break;
-
- case G_TOKEN_CHAR:
- _g_snprintf (token_string, token_string_len, "character `%c'", scanner->value.v_char);
- break;
-
- case G_TOKEN_IDENTIFIER:
- case G_TOKEN_IDENTIFIER_NULL:
- if (expected_token == G_TOKEN_IDENTIFIER ||
- expected_token == G_TOKEN_IDENTIFIER_NULL)
- print_unexp = FALSE;
- _g_snprintf (token_string,
- token_string_len,
- "%s%s `%s'",
- print_unexp ? "" : "invalid ",
- identifier_spec,
- scanner->token == G_TOKEN_IDENTIFIER ? scanner->value.v_string : "null");
- break;
-
- case G_TOKEN_BINARY:
- case G_TOKEN_OCTAL:
- case G_TOKEN_INT:
- case G_TOKEN_HEX:
- if (scanner->config->store_int64)
- _g_snprintf (token_string, token_string_len, "number `%" G_GUINT64_FORMAT "'", scanner->value.v_int64);
- else
- _g_snprintf (token_string, token_string_len, "number `%lu'", scanner->value.v_int);
- break;
-
- case G_TOKEN_FLOAT:
- _g_snprintf (token_string, token_string_len, "number `%.3f'", scanner->value.v_float);
- break;
-
- case G_TOKEN_STRING:
- if (expected_token == G_TOKEN_STRING)
- print_unexp = FALSE;
- _g_snprintf (token_string,
- token_string_len,
- "%s%sstring constant \"%s\"",
- print_unexp ? "" : "invalid ",
- scanner->value.v_string[0] == 0 ? "empty " : "",
- scanner->value.v_string);
- token_string[token_string_len - 2] = '"';
- token_string[token_string_len - 1] = 0;
- break;
-
- case G_TOKEN_COMMENT_SINGLE:
- case G_TOKEN_COMMENT_MULTI:
- _g_snprintf (token_string, token_string_len, "comment");
- break;
-
- case G_TOKEN_NONE:
- /* somehow the user's parsing code is screwed, there isn't much
- * we can do about it.
- * Note, a common case to trigger this is
- * g_scanner_peek_next_token(); g_scanner_unexp_token();
- * without an intermediate g_scanner_get_next_token().
- */
- g_assert_not_reached ();
- break;
- }
-
-
- switch (expected_token)
- {
- gboolean need_valid;
- gchar *tstring;
- case G_TOKEN_EOF:
- _g_snprintf (expected_string, expected_string_len, "end of file");
- break;
- default:
- if (expected_token >= 1 && expected_token <= 255)
- {
- if ((expected_token >= ' ' && expected_token <= '~') ||
- strchr (scanner->config->cset_identifier_first, expected_token) ||
- strchr (scanner->config->cset_identifier_nth, expected_token))
- _g_snprintf (expected_string, expected_string_len, "character `%c'", expected_token);
- else
- _g_snprintf (expected_string, expected_string_len, "character `\\%o'", expected_token);
- break;
- }
- else if (!scanner->config->symbol_2_token)
- {
- _g_snprintf (expected_string, expected_string_len, "(unknown) token <%d>", expected_token);
- break;
- }
- /* fall through */
- case G_TOKEN_SYMBOL:
- need_valid = (scanner->token == G_TOKEN_SYMBOL ||
- (scanner->config->symbol_2_token &&
- scanner->token > G_TOKEN_LAST));
- _g_snprintf (expected_string,
- expected_string_len,
- "%s%s",
- need_valid ? "valid " : "",
- symbol_spec);
- /* FIXME: should we attempt to lookup the symbol_name for symbol_2_token? */
- break;
- case G_TOKEN_CHAR:
- _g_snprintf (expected_string, expected_string_len, "%scharacter",
- scanner->token == G_TOKEN_CHAR ? "valid " : "");
- break;
- case G_TOKEN_BINARY:
- tstring = "binary";
- _g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_OCTAL:
- tstring = "octal";
- _g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_INT:
- tstring = "integer";
- _g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_HEX:
- tstring = "hexadecimal";
- _g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_FLOAT:
- tstring = "float";
- _g_snprintf (expected_string, expected_string_len, "%snumber (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_STRING:
- _g_snprintf (expected_string,
- expected_string_len,
- "%sstring constant",
- scanner->token == G_TOKEN_STRING ? "valid " : "");
- break;
- case G_TOKEN_IDENTIFIER:
- case G_TOKEN_IDENTIFIER_NULL:
- need_valid = (scanner->token == G_TOKEN_IDENTIFIER_NULL ||
- scanner->token == G_TOKEN_IDENTIFIER);
- _g_snprintf (expected_string,
- expected_string_len,
- "%s%s",
- need_valid ? "valid " : "",
- identifier_spec);
- break;
- case G_TOKEN_COMMENT_SINGLE:
- tstring = "single-line";
- _g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_COMMENT_MULTI:
- tstring = "multi-line";
- _g_snprintf (expected_string, expected_string_len, "%scomment (%s)",
- scanner->token == expected_token ? "valid " : "", tstring);
- break;
- case G_TOKEN_NONE:
- case G_TOKEN_ERROR:
- /* this is handled upon printout */
- break;
- }
-
- if (message && message[0] != 0)
- message_prefix = " - ";
- else
- {
- message_prefix = "";
- message = "";
- }
- if (expected_token == G_TOKEN_ERROR)
- {
- msg_handler (scanner,
- "failure around %s%s%s",
- token_string,
- message_prefix,
- message);
- }
- else if (expected_token == G_TOKEN_NONE)
- {
- if (print_unexp)
- msg_handler (scanner,
- "unexpected %s%s%s",
- token_string,
- message_prefix,
- message);
- else
- msg_handler (scanner,
- "%s%s%s",
- token_string,
- message_prefix,
- message);
- }
- else
- {
- if (print_unexp)
- msg_handler (scanner,
- "unexpected %s, expected %s%s%s",
- token_string,
- expected_string,
- message_prefix,
- message);
- else
- msg_handler (scanner,
- "%s, expected %s%s%s",
- token_string,
- expected_string,
- message_prefix,
- message);
- }
-
- g_free (token_string);
- g_free (expected_string);
-}
-
-static void
-g_scanner_get_token_i (GScanner *scanner,
- GTokenType *token_p,
- GTokenValue *value_p,
- guint *line_p,
- guint *position_p)
-{
- do
- {
- g_scanner_free_value (token_p, value_p);
- g_scanner_get_token_ll (scanner, token_p, value_p, line_p, position_p);
- }
- while (((*token_p > 0 && *token_p < 256) &&
- strchr (scanner->config->cset_skip_characters, *token_p)) ||
- (*token_p == G_TOKEN_CHAR &&
- strchr (scanner->config->cset_skip_characters, value_p->v_char)) ||
- (*token_p == G_TOKEN_COMMENT_MULTI &&
- scanner->config->skip_comment_multi) ||
- (*token_p == G_TOKEN_COMMENT_SINGLE &&
- scanner->config->skip_comment_single));
-
- switch (*token_p)
- {
- case G_TOKEN_IDENTIFIER:
- if (scanner->config->identifier_2_string)
- *token_p = G_TOKEN_STRING;
- break;
-
- case G_TOKEN_SYMBOL:
- if (scanner->config->symbol_2_token)
- *token_p = (GTokenType) value_p->v_symbol;
- break;
-
- case G_TOKEN_BINARY:
- case G_TOKEN_OCTAL:
- case G_TOKEN_HEX:
- if (scanner->config->numbers_2_int)
- *token_p = G_TOKEN_INT;
- break;
-
- default:
- break;
- }
-
- if (*token_p == G_TOKEN_INT &&
- scanner->config->int_2_float)
- {
- *token_p = G_TOKEN_FLOAT;
- if (scanner->config->store_int64)
- {
-#ifdef _MSC_VER
- /* work around error C2520, see gvaluetransform.c */
- value_p->v_float = (__int64)value_p->v_int64;
-#else
- value_p->v_float = value_p->v_int64;
-#endif
- }
- else
- value_p->v_float = value_p->v_int;
- }
-
- errno = 0;
-}
-
-static void
-g_scanner_get_token_ll (GScanner *scanner,
- GTokenType *token_p,
- GTokenValue *value_p,
- guint *line_p,
- guint *position_p)
-{
- GScannerConfig *config;
- GTokenType token;
- gboolean in_comment_multi;
- gboolean in_comment_single;
- gboolean in_string_sq;
- gboolean in_string_dq;
- GString *gstring;
- GTokenValue value;
- guchar ch;
-
- config = scanner->config;
- (*value_p).v_int64 = 0;
-
- if ((scanner->text >= scanner->text_end && scanner->input_fd < 0) ||
- scanner->token == G_TOKEN_EOF)
- {
- *token_p = G_TOKEN_EOF;
- return;
- }
-
- in_comment_multi = FALSE;
- in_comment_single = FALSE;
- in_string_sq = FALSE;
- in_string_dq = FALSE;
- gstring = NULL;
-
- do /* while (ch != 0) */
- {
- gboolean dotted_float = FALSE;
-
- ch = g_scanner_get_char (scanner, line_p, position_p);
-
- value.v_int64 = 0;
- token = G_TOKEN_NONE;
-
- /* this is *evil*, but needed ;(
- * we first check for identifier first character, because it
- * might interfere with other key chars like slashes or numbers
- */
- if (config->scan_identifier &&
- ch && strchr (config->cset_identifier_first, ch))
- goto identifier_precedence;
-
- switch (ch)
- {
- case 0:
- token = G_TOKEN_EOF;
- (*position_p)++;
- /* ch = 0; */
- break;
-
- case '/':
- if (!config->scan_comment_multi ||
- g_scanner_peek_next_char (scanner) != '*')
- goto default_case;
- g_scanner_get_char (scanner, line_p, position_p);
- token = G_TOKEN_COMMENT_MULTI;
- in_comment_multi = TRUE;
- gstring = g_string_new (NULL);
- while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
- {
- if (ch == '*' && g_scanner_peek_next_char (scanner) == '/')
- {
- g_scanner_get_char (scanner, line_p, position_p);
- in_comment_multi = FALSE;
- break;
- }
- else
- gstring = g_string_append_c (gstring, ch);
- }
- ch = 0;
- break;
-
- case '\'':
- if (!config->scan_string_sq)
- goto default_case;
- token = G_TOKEN_STRING;
- in_string_sq = TRUE;
- gstring = g_string_new (NULL);
- while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
- {
- if (ch == '\'')
- {
- in_string_sq = FALSE;
- break;
- }
- else
- gstring = g_string_append_c (gstring, ch);
- }
- ch = 0;
- break;
-
- case '"':
- if (!config->scan_string_dq)
- goto default_case;
- token = G_TOKEN_STRING;
- in_string_dq = TRUE;
- gstring = g_string_new (NULL);
- while ((ch = g_scanner_get_char (scanner, line_p, position_p)) != 0)
- {
- if (ch == '"')
- {
- in_string_dq = FALSE;
- break;
- }
- else
- {
- if (ch == '\\')
- {
- ch = g_scanner_get_char (scanner, line_p, position_p);
- switch (ch)
- {
- guint i;
- guint fchar;
-
- case 0:
- break;
-
- case '\\':
- gstring = g_string_append_c (gstring, '\\');
- break;
-
- case 'n':
- gstring = g_string_append_c (gstring, '\n');
- break;
-
- case 't':
- gstring = g_string_append_c (gstring, '\t');
- break;
-
- case 'r':
- gstring = g_string_append_c (gstring, '\r');
- break;
-
- case 'b':
- gstring = g_string_append_c (gstring, '\b');
- break;
-
- case 'f':
- gstring = g_string_append_c (gstring, '\f');
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- i = ch - '0';
- fchar = g_scanner_peek_next_char (scanner);
- if (fchar >= '0' && fchar <= '7')
- {
- ch = g_scanner_get_char (scanner, line_p, position_p);
- i = i * 8 + ch - '0';
- fchar = g_scanner_peek_next_char (scanner);
- if (fchar >= '0' && fchar <= '7')
- {
- ch = g_scanner_get_char (scanner, line_p, position_p);
- i = i * 8 + ch - '0';
- }
- }
- gstring = g_string_append_c (gstring, i);
- break;
-
- default:
- gstring = g_string_append_c (gstring, ch);
- break;
- }
- }
- else
- gstring = g_string_append_c (gstring, ch);
- }
- }
- ch = 0;
- break;
-
- case '.':
- if (!config->scan_float)
- goto default_case;
- token = G_TOKEN_FLOAT;
- dotted_float = TRUE;
- ch = g_scanner_get_char (scanner, line_p, position_p);
- goto number_parsing;
-
- case '$':
- if (!config->scan_hex_dollar)
- goto default_case;
- token = G_TOKEN_HEX;
- ch = g_scanner_get_char (scanner, line_p, position_p);
- goto number_parsing;
-
- case '0':
- if (config->scan_octal)
- token = G_TOKEN_OCTAL;
- else
- token = G_TOKEN_INT;
- ch = g_scanner_peek_next_char (scanner);
- if (config->scan_hex && (ch == 'x' || ch == 'X'))
- {
- token = G_TOKEN_HEX;
- g_scanner_get_char (scanner, line_p, position_p);
- ch = g_scanner_get_char (scanner, line_p, position_p);
- if (ch == 0)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_UNEXP_EOF;
- (*position_p)++;
- break;
- }
- if (g_scanner_char_2_num (ch, 16) < 0)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_DIGIT_RADIX;
- ch = 0;
- break;
- }
- }
- else if (config->scan_binary && (ch == 'b' || ch == 'B'))
- {
- token = G_TOKEN_BINARY;
- g_scanner_get_char (scanner, line_p, position_p);
- ch = g_scanner_get_char (scanner, line_p, position_p);
- if (ch == 0)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_UNEXP_EOF;
- (*position_p)++;
- break;
- }
- if (g_scanner_char_2_num (ch, 10) < 0)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_NON_DIGIT_IN_CONST;
- ch = 0;
- break;
- }
- }
- else
- ch = '0';
- /* fall through */
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- number_parsing:
- {
- gboolean in_number = TRUE;
- gchar *endptr;
-
- if (token == G_TOKEN_NONE)
- token = G_TOKEN_INT;
-
- gstring = g_string_new (dotted_float ? "0." : "");
- gstring = g_string_append_c (gstring, ch);
-
- do /* while (in_number) */
- {
- gboolean is_E;
-
- is_E = token == G_TOKEN_FLOAT && (ch == 'e' || ch == 'E');
-
- ch = g_scanner_peek_next_char (scanner);
-
- if (g_scanner_char_2_num (ch, 36) >= 0 ||
- (config->scan_float && ch == '.') ||
- (is_E && (ch == '+' || ch == '-')))
- {
- ch = g_scanner_get_char (scanner, line_p, position_p);
-
- switch (ch)
- {
- case '.':
- if (token != G_TOKEN_INT && token != G_TOKEN_OCTAL)
- {
- value.v_error = token == G_TOKEN_FLOAT ? G_ERR_FLOAT_MALFORMED : G_ERR_FLOAT_RADIX;
- token = G_TOKEN_ERROR;
- in_number = FALSE;
- }
- else
- {
- token = G_TOKEN_FLOAT;
- gstring = g_string_append_c (gstring, ch);
- }
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- gstring = g_string_append_c (gstring, ch);
- break;
-
- case '-':
- case '+':
- if (token != G_TOKEN_FLOAT)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_NON_DIGIT_IN_CONST;
- in_number = FALSE;
- }
- else
- gstring = g_string_append_c (gstring, ch);
- break;
-
- case 'e':
- case 'E':
- if ((token != G_TOKEN_HEX && !config->scan_float) ||
- (token != G_TOKEN_HEX &&
- token != G_TOKEN_OCTAL &&
- token != G_TOKEN_FLOAT &&
- token != G_TOKEN_INT))
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_NON_DIGIT_IN_CONST;
- in_number = FALSE;
- }
- else
- {
- if (token != G_TOKEN_HEX)
- token = G_TOKEN_FLOAT;
- gstring = g_string_append_c (gstring, ch);
- }
- break;
-
- default:
- if (token != G_TOKEN_HEX)
- {
- token = G_TOKEN_ERROR;
- value.v_error = G_ERR_NON_DIGIT_IN_CONST;
- in_number = FALSE;
- }
- else
- gstring = g_string_append_c (gstring, ch);
- break;
- }
- }
- else
- in_number = FALSE;
- }
- while (in_number);
-
- endptr = NULL;
- if (token == G_TOKEN_FLOAT)
- value.v_float = g_strtod (gstring->str, &endptr);
- else
- {
- guint64 ui64 = 0;
- switch (token)
- {
- case G_TOKEN_BINARY:
- ui64 = g_ascii_strtoull (gstring->str, &endptr, 2);
- break;
- case G_TOKEN_OCTAL:
- ui64 = g_ascii_strtoull (gstring->str, &endptr, 8);
- break;
- case G_TOKEN_INT:
- ui64 = g_ascii_strtoull (gstring->str, &endptr, 10);
- break;
- case G_TOKEN_HEX:
- ui64 = g_ascii_strtoull (gstring->str, &endptr, 16);
- break;
- default: ;
- }
- if (scanner->config->store_int64)
- value.v_int64 = ui64;
- else
- value.v_int = ui64;
- }
- if (endptr && *endptr)
- {
- token = G_TOKEN_ERROR;
- if (*endptr == 'e' || *endptr == 'E')
- value.v_error = G_ERR_NON_DIGIT_IN_CONST;
- else
- value.v_error = G_ERR_DIGIT_RADIX;
- }
- g_string_free (gstring, TRUE);
- gstring = NULL;
- ch = 0;
- } /* number_parsing:... */
- break;
-
- default:
- default_case:
- {
- if (config->cpair_comment_single &&
- ch == config->cpair_comment_single[0])
- {
- token = G_TOKEN_COMMENT_SINGLE;
- in_comment_single = TRUE;
- gstring = g_string_new (NULL);
- ch = g_scanner_get_char (scanner, line_p, position_p);
- while (ch != 0)
- {
- if (ch == config->cpair_comment_single[1])
- {
- in_comment_single = FALSE;
- ch = 0;
- break;
- }
-
- gstring = g_string_append_c (gstring, ch);
- ch = g_scanner_get_char (scanner, line_p, position_p);
- }
- /* ignore a missing newline at EOF for single line comments */
- if (in_comment_single &&
- config->cpair_comment_single[1] == '\n')
- in_comment_single = FALSE;
- }
- else if (config->scan_identifier && ch &&
- strchr (config->cset_identifier_first, ch))
- {
- identifier_precedence:
-
- if (config->cset_identifier_nth && ch &&
- strchr (config->cset_identifier_nth,
- g_scanner_peek_next_char (scanner)))
- {
- token = G_TOKEN_IDENTIFIER;
- gstring = g_string_new (NULL);
- gstring = g_string_append_c (gstring, ch);
- do
- {
- ch = g_scanner_get_char (scanner, line_p, position_p);
- gstring = g_string_append_c (gstring, ch);
- ch = g_scanner_peek_next_char (scanner);
- }
- while (ch && strchr (config->cset_identifier_nth, ch));
- ch = 0;
- }
- else if (config->scan_identifier_1char)
- {
- token = G_TOKEN_IDENTIFIER;
- value.v_identifier = g_new0 (gchar, 2);
- value.v_identifier[0] = ch;
- ch = 0;
- }
- }
- if (ch)
- {
- if (config->char_2_token)
- token = ch;
- else
- {
- token = G_TOKEN_CHAR;
- value.v_char = ch;
- }
- ch = 0;
- }
- } /* default_case:... */
- break;
- }
- g_assert (ch == 0 && token != G_TOKEN_NONE); /* paranoid */
- }
- while (ch != 0);
-
- if (in_comment_multi || in_comment_single ||
- in_string_sq || in_string_dq)
- {
- token = G_TOKEN_ERROR;
- if (gstring)
- {
- g_string_free (gstring, TRUE);
- gstring = NULL;
- }
- (*position_p)++;
- if (in_comment_multi || in_comment_single)
- value.v_error = G_ERR_UNEXP_EOF_IN_COMMENT;
- else /* (in_string_sq || in_string_dq) */
- value.v_error = G_ERR_UNEXP_EOF_IN_STRING;
- }
-
- if (gstring)
- {
- value.v_string = g_string_free (gstring, FALSE);
- gstring = NULL;
- }
-
- if (token == G_TOKEN_IDENTIFIER)
- {
- if (config->scan_symbols)
- {
- GScannerKey *key;
- guint scope_id;
-
- scope_id = scanner->scope_id;
- key = g_scanner_lookup_internal (scanner, scope_id, value.v_identifier);
- if (!key && scope_id && scanner->config->scope_0_fallback)
- key = g_scanner_lookup_internal (scanner, 0, value.v_identifier);
-
- if (key)
- {
- g_free (value.v_identifier);
- token = G_TOKEN_SYMBOL;
- value.v_symbol = key->value;
- }
- }
-
- if (token == G_TOKEN_IDENTIFIER &&
- config->scan_identifier_NULL &&
- strlen (value.v_identifier) == 4)
- {
- gchar *null_upper = "NULL";
- gchar *null_lower = "null";
-
- if (scanner->config->case_sensitive)
- {
- if (value.v_identifier[0] == null_upper[0] &&
- value.v_identifier[1] == null_upper[1] &&
- value.v_identifier[2] == null_upper[2] &&
- value.v_identifier[3] == null_upper[3])
- token = G_TOKEN_IDENTIFIER_NULL;
- }
- else
- {
- if ((value.v_identifier[0] == null_upper[0] ||
- value.v_identifier[0] == null_lower[0]) &&
- (value.v_identifier[1] == null_upper[1] ||
- value.v_identifier[1] == null_lower[1]) &&
- (value.v_identifier[2] == null_upper[2] ||
- value.v_identifier[2] == null_lower[2]) &&
- (value.v_identifier[3] == null_upper[3] ||
- value.v_identifier[3] == null_lower[3]))
- token = G_TOKEN_IDENTIFIER_NULL;
- }
- }
- }
-
- *token_p = token;
- *value_p = value;
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
- * Soeren Sandmann (sandmann@daimi.au.dk)
- *
- * 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 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 "config.h"
-
-#include "gsequence.h"
-
-#include "gmem.h"
-#include "gtestutils.h"
-/**
- * SECTION: sequence
- * @title: Sequences
- * @short_description: scalable lists
- *
- * The #GSequence data structure has the API of a list, but is
- * implemented internally with a balanced binary tree. This means that
- * it is possible to maintain a sorted list of n elements in time O(n
- * log n). The data contained in each element can be either integer
- * values, by using of the <link
- * linkend="glib-Type-Conversion-Macros">Type Conversion Macros</link>,
- * or simply pointers to any type of data.
- *
- * A #GSequence is accessed through <firstterm>iterators</firstterm>,
- * represented by a #GSequenceIter. An iterator represents a position
- * between two elements of the sequence. For example, the
- * <firstterm>begin</firstterm> iterator represents the gap immediately
- * before the first element of the sequence, and the
- * <firstterm>end</firstterm> iterator represents the gap immediately
- * after the last element. In an empty sequence, the begin and end
- * iterators are the same.
- *
- * Some methods on #GSequence operate on ranges of items. For example
- * g_sequence_foreach_range() will call a user-specified function on
- * each element with the given range. The range is delimited by the
- * gaps represented by the passed-in iterators, so if you pass in the
- * begin and end iterators, the range in question is the entire
- * sequence.
- *
- * The function g_sequence_get() is used with an iterator to access the
- * element immediately following the gap that the iterator represents.
- * The iterator is said to <firstterm>point</firstterm> to that element.
- *
- * Iterators are stable across most operations on a #GSequence. For
- * example an iterator pointing to some element of a sequence will
- * continue to point to that element even after the sequence is sorted.
- * Even moving an element to another sequence using for example
- * g_sequence_move_range() will not invalidate the iterators pointing
- * to it. The only operation that will invalidate an iterator is when
- * the element it points to is removed from any sequence.
- **/
-
-/**
- * GSequenceIter:
- *
- * The #GSequenceIter struct is an opaque data type representing an
- * iterator pointing into a #GSequence.
- **/
-
-/**
- * GSequenceIterCompareFunc:
- * @a: a #GSequenceIter
- * @b: a #GSequenceIter
- * @data: user data
- * @Returns: zero if the iterators are equal, a negative value if @a
- * comes before @b, and a positive value if @b comes before
- * @a.
- *
- * A #GSequenceIterCompareFunc is a function used to compare iterators.
- * It must return zero if the iterators compare equal, a negative value
- * if @a comes before @b, and a positive value if @b comes before @a.
- **/
-
-typedef struct _GSequenceNode GSequenceNode;
-
-/**
- * GSequence:
- *
- * The #GSequence struct is an opaque data type representing a
- * <link linkend="glib-Sequences">Sequence</link> data type.
- **/
-struct _GSequence
-{
- GSequenceNode * end_node;
- GDestroyNotify data_destroy_notify;
- gboolean access_prohibited;
-
- /* The 'real_sequence' is used when temporary sequences are created
- * to hold nodes that are being rearranged. The 'real_sequence' of such
- * a temporary sequence points to the sequence that is actually being
- * manipulated. The only reason we need this is so that when the
- * sort/sort_changed/search_iter() functions call out to the application
- * g_sequence_iter_get_sequence() will return the correct sequence.
- */
- GSequence * real_sequence;
-};
-
-struct _GSequenceNode
-{
- gint n_nodes;
- GSequenceNode * parent;
- GSequenceNode * left;
- GSequenceNode * right;
- gpointer data; /* For the end node, this field points
- * to the sequence
- */
-};
-
-/*
- * Declaration of GSequenceNode methods
- */
-static GSequenceNode *node_new (gpointer data);
-static GSequenceNode *node_get_first (GSequenceNode *node);
-static GSequenceNode *node_get_last (GSequenceNode *node);
-static GSequenceNode *node_get_prev (GSequenceNode *node);
-static GSequenceNode *node_get_next (GSequenceNode *node);
-static gint node_get_pos (GSequenceNode *node);
-static GSequenceNode *node_get_by_pos (GSequenceNode *node,
- gint pos);
-static GSequenceNode *node_find_closest (GSequenceNode *haystack,
- GSequenceNode *needle,
- GSequenceNode *end,
- GSequenceIterCompareFunc cmp,
- gpointer user_data);
-static gint node_get_length (GSequenceNode *node);
-static void node_free (GSequenceNode *node,
- GSequence *seq);
-static void node_cut (GSequenceNode *split);
-static void node_insert_before (GSequenceNode *node,
- GSequenceNode *new);
-static void node_unlink (GSequenceNode *node);
-static void node_join (GSequenceNode *left,
- GSequenceNode *right);
-static void node_insert_sorted (GSequenceNode *node,
- GSequenceNode *new,
- GSequenceNode *end,
- GSequenceIterCompareFunc cmp_func,
- gpointer cmp_data);
-
-
-/*
- * Various helper functions
- */
-static void
-check_seq_access (GSequence *seq)
-{
- if (G_UNLIKELY (seq->access_prohibited))
- {
- g_warning ("Accessing a sequence while it is "
- "being sorted or searched is not allowed");
- }
-}
-
-static GSequence *
-get_sequence (GSequenceNode *node)
-{
- return (GSequence *)node_get_last (node)->data;
-}
-
-static void
-check_iter_access (GSequenceIter *iter)
-{
- check_seq_access (get_sequence (iter));
-}
-
-static gboolean
-is_end (GSequenceIter *iter)
-{
- GSequence *seq;
-
- if (iter->right)
- return FALSE;
-
- if (!iter->parent)
- return TRUE;
-
- if (iter->parent->right != iter)
- return FALSE;
-
- seq = get_sequence (iter);
-
- return seq->end_node == iter;
-}
-
-typedef struct
-{
- GCompareDataFunc cmp_func;
- gpointer cmp_data;
- GSequenceNode *end_node;
-} SortInfo;
-
-/* This function compares two iters using a normal compare
- * function and user_data passed in in a SortInfo struct
- */
-static gint
-iter_compare (GSequenceIter *node1,
- GSequenceIter *node2,
- gpointer data)
-{
- const SortInfo *info = data;
- gint retval;
-
- if (node1 == info->end_node)
- return 1;
-
- if (node2 == info->end_node)
- return -1;
-
- retval = info->cmp_func (node1->data, node2->data, info->cmp_data);
-
- return retval;
-}
-
-/*
- * Public API
- */
-
-/**
- * g_sequence_new:
- * @data_destroy: a #GDestroyNotify function, or %NULL
- *
- * Creates a new GSequence. The @data_destroy function, if non-%NULL will
- * be called on all items when the sequence is destroyed and on items that
- * are removed from the sequence.
- *
- * Return value: a new #GSequence
- *
- * Since: 2.14
- **/
-GSequence *
-g_sequence_new (GDestroyNotify data_destroy)
-{
- GSequence *seq = g_new (GSequence, 1);
- seq->data_destroy_notify = data_destroy;
-
- seq->end_node = node_new (seq);
-
- seq->access_prohibited = FALSE;
-
- seq->real_sequence = seq;
-
- return seq;
-}
-
-/**
- * g_sequence_free:
- * @seq: a #GSequence
- *
- * Frees the memory allocated for @seq. If @seq has a data destroy
- * function associated with it, that function is called on all items in
- * @seq.
- *
- * Since: 2.14
- **/
-void
-g_sequence_free (GSequence *seq)
-{
- g_return_if_fail (seq != NULL);
-
- check_seq_access (seq);
-
- node_free (seq->end_node, seq);
-
- g_free (seq);
-}
-
-/**
- * g_sequence_foreach_range:
- * @begin: a #GSequenceIter
- * @end: a #GSequenceIter
- * @func: a #GFunc
- * @user_data: user data passed to @func
- *
- * Calls @func for each item in the range (@begin, @end) passing
- * @user_data to the function.
- *
- * Since: 2.14
- **/
-void
-g_sequence_foreach_range (GSequenceIter *begin,
- GSequenceIter *end,
- GFunc func,
- gpointer user_data)
-{
- GSequence *seq;
- GSequenceIter *iter;
-
- g_return_if_fail (func != NULL);
- g_return_if_fail (begin != NULL);
- g_return_if_fail (end != NULL);
-
- seq = get_sequence (begin);
-
- seq->access_prohibited = TRUE;
-
- iter = begin;
- while (iter != end)
- {
- GSequenceIter *next = node_get_next (iter);
-
- func (iter->data, user_data);
-
- iter = next;
- }
-
- seq->access_prohibited = FALSE;
-}
-
-/**
- * g_sequence_foreach:
- * @seq: a #GSequence
- * @func: the function to call for each item in @seq
- * @user_data: user data passed to @func
- *
- * Calls @func for each item in the sequence passing @user_data
- * to the function.
- *
- * Since: 2.14
- **/
-void
-g_sequence_foreach (GSequence *seq,
- GFunc func,
- gpointer user_data)
-{
- GSequenceIter *begin, *end;
-
- check_seq_access (seq);
-
- begin = g_sequence_get_begin_iter (seq);
- end = g_sequence_get_end_iter (seq);
-
- g_sequence_foreach_range (begin, end, func, user_data);
-}
-
-/**
- * g_sequence_range_get_midpoint:
- * @begin: a #GSequenceIter
- * @end: a #GSequenceIter
- *
- * Finds an iterator somewhere in the range (@begin, @end). This
- * iterator will be close to the middle of the range, but is not
- * guaranteed to be <emphasis>exactly</emphasis> in the middle.
- *
- * The @begin and @end iterators must both point to the same sequence and
- * @begin must come before or be equal to @end in the sequence.
- *
- * Return value: A #GSequenceIter pointing somewhere in the
- * (@begin, @end) range.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_range_get_midpoint (GSequenceIter *begin,
- GSequenceIter *end)
-{
- int begin_pos, end_pos, mid_pos;
-
- g_return_val_if_fail (begin != NULL, NULL);
- g_return_val_if_fail (end != NULL, NULL);
- g_return_val_if_fail (get_sequence (begin) == get_sequence (end), NULL);
-
- begin_pos = node_get_pos (begin);
- end_pos = node_get_pos (end);
-
- g_return_val_if_fail (end_pos >= begin_pos, NULL);
-
- mid_pos = begin_pos + (end_pos - begin_pos) / 2;
-
- return node_get_by_pos (begin, mid_pos);
-}
-
-/**
- * g_sequence_iter_compare:
- * @a: a #GSequenceIter
- * @b: a #GSequenceIter
- *
- * Returns a negative number if @a comes before @b, 0 if they are equal,
- * and a positive number if @a comes after @b.
- *
- * The @a and @b iterators must point into the same sequence.
- *
- * Return value: A negative number if @a comes before @b, 0 if they are
- * equal, and a positive number if @a comes after @b.
- *
- * Since: 2.14
- **/
-gint
-g_sequence_iter_compare (GSequenceIter *a,
- GSequenceIter *b)
-{
- gint a_pos, b_pos;
-
- g_return_val_if_fail (a != NULL, 0);
- g_return_val_if_fail (b != NULL, 0);
- g_return_val_if_fail (get_sequence (a) == get_sequence (b), 0);
-
- check_iter_access (a);
- check_iter_access (b);
-
- a_pos = node_get_pos (a);
- b_pos = node_get_pos (b);
-
- if (a_pos == b_pos)
- return 0;
- else if (a_pos > b_pos)
- return 1;
- else
- return -1;
-}
-
-/**
- * g_sequence_append:
- * @seq: a #GSequencePointer
- * @data: the data for the new item
- *
- * Adds a new item to the end of @seq.
- *
- * Return value: an iterator pointing to the new item
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_append (GSequence *seq,
- gpointer data)
-{
- GSequenceNode *node;
-
- g_return_val_if_fail (seq != NULL, NULL);
-
- check_seq_access (seq);
-
- node = node_new (data);
- node_insert_before (seq->end_node, node);
-
- return node;
-}
-
-/**
- * g_sequence_prepend:
- * @seq: a #GSequence
- * @data: the data for the new item
- *
- * Adds a new item to the front of @seq
- *
- * Return value: an iterator pointing to the new item
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_prepend (GSequence *seq,
- gpointer data)
-{
- GSequenceNode *node, *first;
-
- g_return_val_if_fail (seq != NULL, NULL);
-
- check_seq_access (seq);
-
- node = node_new (data);
- first = node_get_first (seq->end_node);
-
- node_insert_before (first, node);
-
- return node;
-}
-
-/**
- * g_sequence_insert_before:
- * @iter: a #GSequenceIter
- * @data: the data for the new item
- *
- * Inserts a new item just before the item pointed to by @iter.
- *
- * Return value: an iterator pointing to the new item
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_insert_before (GSequenceIter *iter,
- gpointer data)
-{
- GSequenceNode *node;
-
- g_return_val_if_fail (iter != NULL, NULL);
-
- check_iter_access (iter);
-
- node = node_new (data);
-
- node_insert_before (iter, node);
-
- return node;
-}
-
-/**
- * g_sequence_remove:
- * @iter: a #GSequenceIter
- *
- * Removes the item pointed to by @iter. It is an error to pass the
- * end iterator to this function.
- *
- * If the sequnce has a data destroy function associated with it, this
- * function is called on the data for the removed item.
- *
- * Since: 2.14
- **/
-void
-g_sequence_remove (GSequenceIter *iter)
-{
- GSequence *seq;
-
- g_return_if_fail (iter != NULL);
- g_return_if_fail (!is_end (iter));
-
- check_iter_access (iter);
-
- seq = get_sequence (iter);
-
- node_unlink (iter);
- node_free (iter, seq);
-}
-
-/**
- * g_sequence_remove_range:
- * @begin: a #GSequenceIter
- * @end: a #GSequenceIter
- *
- * Removes all items in the (@begin, @end) range.
- *
- * If the sequence has a data destroy function associated with it, this
- * function is called on the data for the removed items.
- *
- * Since: 2.14
- **/
-void
-g_sequence_remove_range (GSequenceIter *begin,
- GSequenceIter *end)
-{
- g_return_if_fail (get_sequence (begin) == get_sequence (end));
-
- check_iter_access (begin);
- check_iter_access (end);
-
- g_sequence_move_range (NULL, begin, end);
-}
-
-/**
- * g_sequence_move_range:
- * @dest: a #GSequenceIter
- * @begin: a #GSequenceIter
- * @end: a #GSequenceIter
- *
- * Inserts the (@begin, @end) range at the destination pointed to by ptr.
- * The @begin and @end iters must point into the same sequence. It is
- * allowed for @dest to point to a different sequence than the one pointed
- * into by @begin and @end.
- *
- * If @dest is NULL, the range indicated by @begin and @end is
- * removed from the sequence. If @dest iter points to a place within
- * the (@begin, @end) range, the range does not move.
- *
- * Since: 2.14
- **/
-void
-g_sequence_move_range (GSequenceIter *dest,
- GSequenceIter *begin,
- GSequenceIter *end)
-{
- GSequence *src_seq;
- GSequenceNode *first;
-
- g_return_if_fail (begin != NULL);
- g_return_if_fail (end != NULL);
-
- check_iter_access (begin);
- check_iter_access (end);
- if (dest)
- check_iter_access (dest);
-
- src_seq = get_sequence (begin);
-
- g_return_if_fail (src_seq == get_sequence (end));
-
- /* Dest points to begin or end? */
- if (dest == begin || dest == end)
- return;
-
- /* begin comes after end? */
- if (g_sequence_iter_compare (begin, end) >= 0)
- return;
-
- /* dest points somewhere in the (begin, end) range? */
- if (dest && get_sequence (dest) == src_seq &&
- g_sequence_iter_compare (dest, begin) > 0 &&
- g_sequence_iter_compare (dest, end) < 0)
- {
- return;
- }
-
- src_seq = get_sequence (begin);
-
- first = node_get_first (begin);
-
- node_cut (begin);
-
- node_cut (end);
-
- if (first != begin)
- node_join (first, end);
-
- if (dest)
- {
- first = node_get_first (dest);
-
- node_cut (dest);
-
- node_join (begin, dest);
-
- if (dest != first)
- node_join (first, begin);
- }
- else
- {
- node_free (begin, src_seq);
- }
-}
-
-/**
- * g_sequence_sort:
- * @seq: a #GSequence
- * @cmp_func: the #GCompareDataFunc used to sort @seq. This function is
- * passed two items of @seq and should return 0 if they are equal,
- * a negative value if the first comes before the second, and a
- * positive value if the second comes before the first.
- * @cmp_data: user data passed to @cmp_func
- *
- * Sorts @seq using @cmp_func.
- *
- * Since: 2.14
- **/
-void
-g_sequence_sort (GSequence *seq,
- GCompareDataFunc cmp_func,
- gpointer cmp_data)
-{
- SortInfo info;
-
- info.cmp_func = cmp_func;
- info.cmp_data = cmp_data;
- info.end_node = seq->end_node;
-
- check_seq_access (seq);
-
- g_sequence_sort_iter (seq, iter_compare, &info);
-}
-
-/**
- * g_sequence_insert_sorted:
- * @seq: a #GSequence
- * @data: the data to insert
- * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
- * is called with two items of the @seq and @user_data. It should
- * return 0 if the items are equal, a negative value if the first
- * item comes before the second, and a positive value if the second
- * item comes before the first.
- * @cmp_data: user data passed to @cmp_func.
- *
- * Inserts @data into @sequence using @func to determine the new position.
- * The sequence must already be sorted according to @cmp_func; otherwise the
- * new position of @data is undefined.
- *
- * Return value: a #GSequenceIter pointing to the new item.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_insert_sorted (GSequence *seq,
- gpointer data,
- GCompareDataFunc cmp_func,
- gpointer cmp_data)
-{
- SortInfo info;
-
- g_return_val_if_fail (seq != NULL, NULL);
- g_return_val_if_fail (cmp_func != NULL, NULL);
-
- info.cmp_func = cmp_func;
- info.cmp_data = cmp_data;
- info.end_node = seq->end_node;
- check_seq_access (seq);
-
- return g_sequence_insert_sorted_iter (seq, data, iter_compare, &info);
-}
-
-/**
- * g_sequence_sort_changed:
- * @iter: A #GSequenceIter
- * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
- * is called with two items of the @seq and @user_data. It should
- * return 0 if the items are equal, a negative value if the first
- * item comes before the second, and a positive value if the second
- * item comes before the first.
- * @cmp_data: user data passed to @cmp_func.
- *
- * Moves the data pointed to a new position as indicated by @cmp_func. This
- * function should be called for items in a sequence already sorted according
- * to @cmp_func whenever some aspect of an item changes so that @cmp_func
- * may return different values for that item.
- *
- * Since: 2.14
- **/
-void
-g_sequence_sort_changed (GSequenceIter *iter,
- GCompareDataFunc cmp_func,
- gpointer cmp_data)
-{
- SortInfo info;
-
- g_return_if_fail (!is_end (iter));
-
- info.cmp_func = cmp_func;
- info.cmp_data = cmp_data;
- info.end_node = get_sequence (iter)->end_node;
- check_iter_access (iter);
-
- g_sequence_sort_changed_iter (iter, iter_compare, &info);
-}
-
-/**
- * g_sequence_search:
- * @seq: a #GSequence
- * @data: data for the new item
- * @cmp_func: the #GCompareDataFunc used to compare items in the sequence. It
- * is called with two items of the @seq and @user_data. It should
- * return 0 if the items are equal, a negative value if the first
- * item comes before the second, and a positive value if the second
- * item comes before the first.
- * @cmp_data: user data passed to @cmp_func.
- *
- * Returns an iterator pointing to the position where @data would
- * be inserted according to @cmp_func and @cmp_data.
- *
- * Return value: an #GSequenceIter pointing to the position where @data
- * would have been inserted according to @cmp_func and @cmp_data.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_search (GSequence *seq,
- gpointer data,
- GCompareDataFunc cmp_func,
- gpointer cmp_data)
-{
- SortInfo info;
-
- g_return_val_if_fail (seq != NULL, NULL);
-
- info.cmp_func = cmp_func;
- info.cmp_data = cmp_data;
- info.end_node = seq->end_node;
- check_seq_access (seq);
-
- return g_sequence_search_iter (seq, data, iter_compare, &info);
-}
-
-/**
- * g_sequence_sort_iter:
- * @seq: a #GSequence
- * @cmp_func: the #GSequenceItercompare used to compare iterators in the
- * sequence. It is called with two iterators pointing into @seq. It should
- * return 0 if the iterators are equal, a negative value if the first
- * iterator comes before the second, and a positive value if the second
- * iterator comes before the first.
- * @cmp_data: user data passed to @cmp_func
- *
- * Like g_sequence_sort(), but uses a #GSequenceIterCompareFunc instead
- * of a GCompareDataFunc as the compare function
- *
- * Since: 2.14
- **/
-void
-g_sequence_sort_iter (GSequence *seq,
- GSequenceIterCompareFunc cmp_func,
- gpointer cmp_data)
-{
- GSequence *tmp;
- GSequenceNode *begin, *end;
-
- g_return_if_fail (seq != NULL);
- g_return_if_fail (cmp_func != NULL);
-
- check_seq_access (seq);
-
- begin = g_sequence_get_begin_iter (seq);
- end = g_sequence_get_end_iter (seq);
-
- tmp = g_sequence_new (NULL);
- tmp->real_sequence = seq;
-
- g_sequence_move_range (g_sequence_get_begin_iter (tmp), begin, end);
-
- seq->access_prohibited = TRUE;
- tmp->access_prohibited = TRUE;
-
- while (g_sequence_get_length (tmp) > 0)
- {
- GSequenceNode *node = g_sequence_get_begin_iter (tmp);
-
- node_insert_sorted (seq->end_node, node, seq->end_node,
- cmp_func, cmp_data);
- }
-
- tmp->access_prohibited = FALSE;
- seq->access_prohibited = FALSE;
-
- g_sequence_free (tmp);
-}
-
-/**
- * g_sequence_sort_changed_iter:
- * @iter: a #GSequenceIter
- * @iter_cmp: the #GSequenceItercompare used to compare iterators in the
- * sequence. It is called with two iterators pointing into @seq. It should
- * return 0 if the iterators are equal, a negative value if the first
- * iterator comes before the second, and a positive value if the second
- * iterator comes before the first.
- * @cmp_data: user data passed to @cmp_func
- *
- * Like g_sequence_sort_changed(), but uses
- * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
- * the compare function.
- *
- * Since: 2.14
- **/
-void
-g_sequence_sort_changed_iter (GSequenceIter *iter,
- GSequenceIterCompareFunc iter_cmp,
- gpointer cmp_data)
-{
- GSequence *seq, *tmp_seq;
- GSequenceIter *next, *prev;
-
- g_return_if_fail (iter != NULL);
- g_return_if_fail (!is_end (iter));
- g_return_if_fail (iter_cmp != NULL);
- check_iter_access (iter);
-
- /* If one of the neighbours is equal to iter, then
- * don't move it. This ensures that sort_changed() is
- * a stable operation.
- */
-
- next = node_get_next (iter);
- prev = node_get_prev (iter);
-
- if (prev != iter && iter_cmp (prev, iter, cmp_data) == 0)
- return;
-
- if (!is_end (next) && iter_cmp (next, iter, cmp_data) == 0)
- return;
-
- seq = get_sequence (iter);
-
- seq->access_prohibited = TRUE;
-
- tmp_seq = g_sequence_new (NULL);
- tmp_seq->real_sequence = seq;
-
- node_unlink (iter);
- node_insert_before (tmp_seq->end_node, iter);
-
- node_insert_sorted (seq->end_node, iter, seq->end_node,
- iter_cmp, cmp_data);
-
- g_sequence_free (tmp_seq);
-
- seq->access_prohibited = FALSE;
-}
-
-/**
- * g_sequence_insert_sorted_iter:
- * @seq: a #GSequence
- * @data: data for the new item
- * @iter_cmp: the #GSequenceItercompare used to compare iterators in the
- * sequence. It is called with two iterators pointing into @seq. It should
- * return 0 if the iterators are equal, a negative value if the first
- * iterator comes before the second, and a positive value if the second
- * iterator comes before the first.
- * @cmp_data: user data passed to @cmp_func
- *
- * Like g_sequence_insert_sorted(), but uses
- * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
- * the compare function.
- *
- * Return value: a #GSequenceIter pointing to the new item
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_insert_sorted_iter (GSequence *seq,
- gpointer data,
- GSequenceIterCompareFunc iter_cmp,
- gpointer cmp_data)
-{
- GSequenceNode *new_node;
- GSequence *tmp_seq;
-
- g_return_val_if_fail (seq != NULL, NULL);
- g_return_val_if_fail (iter_cmp != NULL, NULL);
-
- check_seq_access (seq);
-
- seq->access_prohibited = TRUE;
-
- /* Create a new temporary sequence and put the new node into
- * that. The reason for this is that the user compare function
- * will be called with the new node, and if it dereferences,
- * "is_end" will be called on it. But that will crash if the
- * node is not actually in a sequence.
- *
- * node_insert_sorted() makes sure the node is unlinked before
- * it is inserted.
- *
- * The reason we need the "iter" versions at all is that that
- * is the only kind of compare functions GtkTreeView can use.
- */
- tmp_seq = g_sequence_new (NULL);
- tmp_seq->real_sequence = seq;
-
- new_node = g_sequence_append (tmp_seq, data);
-
- node_insert_sorted (seq->end_node, new_node,
- seq->end_node, iter_cmp, cmp_data);
-
- g_sequence_free (tmp_seq);
-
- seq->access_prohibited = FALSE;
-
- return new_node;
-}
-
-/**
- * g_sequence_search_iter:
- * @seq: a #GSequence
- * @data: data for the new item
- * @iter_cmp: the #GSequenceIterCompare function used to compare iterators
- * in the sequence. It is called with two iterators pointing into @seq.
- * It should return 0 if the iterators are equal, a negative value if the
- * first iterator comes before the second, and a positive value if the
- * second iterator comes before the first.
- * @cmp_data: user data passed to @iter_cmp
- *
- * Like g_sequence_search(), but uses
- * a #GSequenceIterCompareFunc instead of a #GCompareDataFunc as
- * the compare function.
- *
- * Return value: a #GSequenceIter pointing to the position in @seq
- * where @data would have been inserted according to @iter_cmp and @cmp_data.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_search_iter (GSequence *seq,
- gpointer data,
- GSequenceIterCompareFunc iter_cmp,
- gpointer cmp_data)
-{
- GSequenceNode *node;
- GSequenceNode *dummy;
- GSequence *tmp_seq;
-
- g_return_val_if_fail (seq != NULL, NULL);
-
- check_seq_access (seq);
-
- seq->access_prohibited = TRUE;
-
- tmp_seq = g_sequence_new (NULL);
- tmp_seq->real_sequence = seq;
-
- dummy = g_sequence_append (tmp_seq, data);
-
- node = node_find_closest (seq->end_node, dummy,
- seq->end_node, iter_cmp, cmp_data);
-
- g_sequence_free (tmp_seq);
-
- seq->access_prohibited = FALSE;
-
- return node;
-}
-
-/**
- * g_sequence_iter_get_sequence:
- * @iter: a #GSequenceIter
- *
- * Returns the #GSequence that @iter points into.
- *
- * Return value: the #GSequence that @iter points into.
- *
- * Since: 2.14
- **/
-GSequence *
-g_sequence_iter_get_sequence (GSequenceIter *iter)
-{
- GSequence *seq;
-
- g_return_val_if_fail (iter != NULL, NULL);
-
- seq = get_sequence (iter);
-
- /* For temporary sequences, this points to the sequence that
- * is actually being manipulated
- */
- return seq->real_sequence;
-}
-
-/**
- * g_sequence_get:
- * @iter: a #GSequenceIter
- *
- * Returns the data that @iter points to.
- *
- * Return value: the data that @iter points to
- *
- * Since: 2.14
- **/
-gpointer
-g_sequence_get (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, NULL);
- g_return_val_if_fail (!is_end (iter), NULL);
-
- return iter->data;
-}
-
-/**
- * g_sequence_set:
- * @iter: a #GSequenceIter
- * @data: new data for the item
- *
- * Changes the data for the item pointed to by @iter to be @data. If
- * the sequence has a data destroy function associated with it, that
- * function is called on the existing data that @iter pointed to.
- *
- * Since: 2.14
- **/
-void
-g_sequence_set (GSequenceIter *iter,
- gpointer data)
-{
- GSequence *seq;
-
- g_return_if_fail (iter != NULL);
- g_return_if_fail (!is_end (iter));
-
- seq = get_sequence (iter);
-
- /* If @data is identical to iter->data, it is destroyed
- * here. This will work right in case of ref-counted objects. Also
- * it is similar to what ghashtables do.
- *
- * For non-refcounted data it's a little less convenient, but
- * code relying on self-setting not destroying would be
- * pretty dubious anyway ...
- */
-
- if (seq->data_destroy_notify)
- seq->data_destroy_notify (iter->data);
-
- iter->data = data;
-}
-
-/**
- * g_sequence_get_length:
- * @seq: a #GSequence
- *
- * Returns the length of @seq
- *
- * Return value: the length of @seq
- *
- * Since: 2.14
- **/
-gint
-g_sequence_get_length (GSequence *seq)
-{
- return node_get_length (seq->end_node) - 1;
-}
-
-/**
- * g_sequence_get_end_iter:
- * @seq: a #GSequence
- *
- * Returns the end iterator for @seg
- *
- * Return value: the end iterator for @seq
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_get_end_iter (GSequence *seq)
-{
- g_return_val_if_fail (seq != NULL, NULL);
-
- return seq->end_node;
-}
-
-/**
- * g_sequence_get_begin_iter:
- * @seq: a #GSequence
- *
- * Returns the begin iterator for @seq.
- *
- * Return value: the begin iterator for @seq.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_get_begin_iter (GSequence *seq)
-{
- g_return_val_if_fail (seq != NULL, NULL);
-
- return node_get_first (seq->end_node);
-}
-
-static int
-clamp_position (GSequence *seq,
- int pos)
-{
- gint len = g_sequence_get_length (seq);
-
- if (pos > len || pos < 0)
- pos = len;
-
- return pos;
-}
-
-/*
- * if pos > number of items or -1, will return end pointer
- */
-/**
- * g_sequence_get_iter_at_pos:
- * @seq: a #GSequence
- * @pos: a position in @seq, or -1 for the end.
- *
- * Returns the iterator at position @pos. If @pos is negative or larger
- * than the number of items in @seq, the end iterator is returned.
- *
- * Return value: The #GSequenceIter at position @pos
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_get_iter_at_pos (GSequence *seq,
- gint pos)
-{
- g_return_val_if_fail (seq != NULL, NULL);
-
- pos = clamp_position (seq, pos);
-
- return node_get_by_pos (seq->end_node, pos);
-}
-
-/**
- * g_sequence_move:
- * @src: a #GSequenceIter pointing to the item to move
- * @dest: a #GSequenceIter pointing to the position to which
- * the item is moved.
- *
- * Moves the item pointed to by @src to the position indicated by @dest.
- * After calling this function @dest will point to the position immediately
- * after @src. It is allowed for @src and @dest to point into different
- * sequences.
- *
- * Since: 2.14
- **/
-void
-g_sequence_move (GSequenceIter *src,
- GSequenceIter *dest)
-{
- g_return_if_fail (src != NULL);
- g_return_if_fail (dest != NULL);
- g_return_if_fail (!is_end (src));
-
- if (src == dest)
- return;
-
- node_unlink (src);
- node_insert_before (dest, src);
-}
-
-/* GSequenceIter */
-
-/**
- * g_sequence_iter_is_end:
- * @iter: a #GSequenceIter
- *
- * Returns whether @iter is the end iterator
- *
- * Return value: Whether @iter is the end iterator.
- *
- * Since: 2.14
- **/
-gboolean
-g_sequence_iter_is_end (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- return is_end (iter);
-}
-
-/**
- * g_sequence_iter_is_begin:
- * @iter: a #GSequenceIter
- *
- * Returns whether @iter is the begin iterator
- *
- * Return value: whether @iter is the begin iterator
- *
- * Since: 2.14
- **/
-gboolean
-g_sequence_iter_is_begin (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, FALSE);
-
- return (node_get_prev (iter) == iter);
-}
-
-/**
- * g_sequence_iter_get_position:
- * @iter: a #GSequenceIter
- *
- * Returns the position of @iter
- *
- * Return value: the position of @iter
- *
- * Since: 2.14
- **/
-gint
-g_sequence_iter_get_position (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, -1);
-
- return node_get_pos (iter);
-}
-
-/**
- * g_sequence_iter_next:
- * @iter: a #GSequenceIter
- *
- * Returns an iterator pointing to the next position after @iter. If
- * @iter is the end iterator, the end iterator is returned.
- *
- * Return value: a #GSequenceIter pointing to the next position after @iter.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_iter_next (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, NULL);
-
- return node_get_next (iter);
-}
-
-/**
- * g_sequence_iter_prev:
- * @iter: a #GSequenceIter
- *
- * Returns an iterator pointing to the previous position before @iter. If
- * @iter is the begin iterator, the begin iterator is returned.
- *
- * Return value: a #GSequenceIter pointing to the previous position before
- * @iter.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_iter_prev (GSequenceIter *iter)
-{
- g_return_val_if_fail (iter != NULL, NULL);
-
- return node_get_prev (iter);
-}
-
-/**
- * g_sequence_iter_move:
- * @iter: a #GSequenceIter
- * @delta: A positive or negative number indicating how many positions away
- * from @iter the returned #GSequenceIter will be.
- *
- * Returns the #GSequenceIter which is @delta positions away from @iter.
- * If @iter is closer than -@delta positions to the beginning of the sequence,
- * the begin iterator is returned. If @iter is closer than @delta positions
- * to the end of the sequence, the end iterator is returned.
- *
- * Return value: a #GSequenceIter which is @delta positions away from @iter.
- *
- * Since: 2.14
- **/
-GSequenceIter *
-g_sequence_iter_move (GSequenceIter *iter,
- gint delta)
-{
- gint new_pos;
-
- g_return_val_if_fail (iter != NULL, NULL);
-
- new_pos = node_get_pos (iter) + delta;
-
- new_pos = clamp_position (get_sequence (iter), new_pos);
-
- return node_get_by_pos (iter, new_pos);
-}
-
-/**
- * g_sequence_swap:
- * @a: a #GSequenceIter
- * @b: a #GSequenceIter
- *
- * Swaps the items pointed to by @a and @b. It is allowed for @a and @b
- * to point into difference sequences.
- *
- * Since: 2.14
- **/
-void
-g_sequence_swap (GSequenceIter *a,
- GSequenceIter *b)
-{
- GSequenceNode *leftmost, *rightmost, *rightmost_next;
- int a_pos, b_pos;
-
- g_return_if_fail (!g_sequence_iter_is_end (a));
- g_return_if_fail (!g_sequence_iter_is_end (b));
-
- if (a == b)
- return;
-
- a_pos = g_sequence_iter_get_position (a);
- b_pos = g_sequence_iter_get_position (b);
-
- if (a_pos > b_pos)
- {
- leftmost = b;
- rightmost = a;
- }
- else
- {
- leftmost = a;
- rightmost = b;
- }
-
- rightmost_next = node_get_next (rightmost);
-
- /* The situation is now like this:
- *
- * ..., leftmost, ......., rightmost, rightmost_next, ...
- *
- */
- g_sequence_move (rightmost, leftmost);
- g_sequence_move (leftmost, rightmost_next);
-}
-
-/*
- * Implementation of a treap
- *
- *
- */
-static guint
-get_priority (GSequenceNode *node)
-{
- guint key = GPOINTER_TO_UINT (node);
-
- /* This hash function is based on one found on Thomas Wang's
- * web page at
- *
- * http://www.concentric.net/~Ttwang/tech/inthash.htm
- *
- */
- key = (key << 15) - key - 1;
- key = key ^ (key >> 12);
- key = key + (key << 2);
- key = key ^ (key >> 4);
- key = key + (key << 3) + (key << 11);
- key = key ^ (key >> 16);
-
- /* We rely on 0 being less than all other priorities */
- return key? key : 1;
-}
-
-static GSequenceNode *
-find_root (GSequenceNode *node)
-{
- while (node->parent)
- node = node->parent;
-
- return node;
-}
-
-static GSequenceNode *
-node_new (gpointer data)
-{
- GSequenceNode *node = g_slice_new0 (GSequenceNode);
-
- node->n_nodes = 1;
- node->data = data;
- node->left = NULL;
- node->right = NULL;
- node->parent = NULL;
-
- return node;
-}
-
-static GSequenceNode *
-node_get_first (GSequenceNode *node)
-{
- node = find_root (node);
-
- while (node->left)
- node = node->left;
-
- return node;
-}
-
-static GSequenceNode *
-node_get_last (GSequenceNode *node)
-{
- node = find_root (node);
-
- while (node->right)
- node = node->right;
-
- return node;
-}
-
-#define NODE_LEFT_CHILD(n) (((n)->parent) && ((n)->parent->left) == (n))
-#define NODE_RIGHT_CHILD(n) (((n)->parent) && ((n)->parent->right) == (n))
-
-static GSequenceNode *
-node_get_next (GSequenceNode *node)
-{
- GSequenceNode *n = node;
-
- if (n->right)
- {
- n = n->right;
- while (n->left)
- n = n->left;
- }
- else
- {
- while (NODE_RIGHT_CHILD (n))
- n = n->parent;
-
- if (n->parent)
- n = n->parent;
- else
- n = node;
- }
-
- return n;
-}
-
-static GSequenceNode *
-node_get_prev (GSequenceNode *node)
-{
- GSequenceNode *n = node;
-
- if (n->left)
- {
- n = n->left;
- while (n->right)
- n = n->right;
- }
- else
- {
- while (NODE_LEFT_CHILD (n))
- n = n->parent;
-
- if (n->parent)
- n = n->parent;
- else
- n = node;
- }
-
- return n;
-}
-
-#define N_NODES(n) ((n)? (n)->n_nodes : 0)
-
-static gint
-node_get_pos (GSequenceNode *node)
-{
- int n_smaller = 0;
-
- if (node->left)
- n_smaller = node->left->n_nodes;
-
- while (node)
- {
- if (NODE_RIGHT_CHILD (node))
- n_smaller += N_NODES (node->parent->left) + 1;
-
- node = node->parent;
- }
-
- return n_smaller;
-}
-
-static GSequenceNode *
-node_get_by_pos (GSequenceNode *node,
- gint pos)
-{
- int i;
-
- node = find_root (node);
-
- while ((i = N_NODES (node->left)) != pos)
- {
- if (i < pos)
- {
- node = node->right;
- pos -= (i + 1);
- }
- else
- {
- node = node->left;
- }
- }
-
- return node;
-}
-
-static GSequenceNode *
-node_find_closest (GSequenceNode *haystack,
- GSequenceNode *needle,
- GSequenceNode *end,
- GSequenceIterCompareFunc iter_cmp,
- gpointer cmp_data)
-{
- GSequenceNode *best;
- gint c;
-
- haystack = find_root (haystack);
-
- do
- {
- best = haystack;
-
- /* iter_cmp can't be passed the end node, since the function may
- * be user-supplied
- */
- if (haystack == end)
- c = 1;
- else
- c = iter_cmp (haystack, needle, cmp_data);
-
- /* In the following we don't break even if c == 0. Instaed we go on
- * searching along the 'bigger' nodes, so that we find the last one
- * that is equal to the needle.
- */
- if (c > 0)
- haystack = haystack->left;
- else
- haystack = haystack->right;
- }
- while (haystack != NULL);
-
- /* If the best node is smaller or equal to the data, then move one step
- * to the right to make sure the best one is strictly bigger than the data
- */
- if (best != end && c <= 0)
- best = node_get_next (best);
-
- return best;
-}
-
-static gint
-node_get_length (GSequenceNode *node)
-{
- node = find_root (node);
-
- return node->n_nodes;
-}
-
-static void
-real_node_free (GSequenceNode *node,
- GSequence *seq)
-{
- if (node)
- {
- real_node_free (node->left, seq);
- real_node_free (node->right, seq);
-
- if (seq && seq->data_destroy_notify && node != seq->end_node)
- seq->data_destroy_notify (node->data);
-
- g_slice_free (GSequenceNode, node);
- }
-}
-
-static void
-node_free (GSequenceNode *node,
- GSequence *seq)
-{
- node = find_root (node);
-
- real_node_free (node, seq);
-}
-
-static void
-node_update_fields (GSequenceNode *node)
-{
- int n_nodes = 1;
-
- n_nodes += N_NODES (node->left);
- n_nodes += N_NODES (node->right);
-
- node->n_nodes = n_nodes;
-}
-
-static void
-node_rotate (GSequenceNode *node)
-{
- GSequenceNode *tmp, *old;
-
- g_assert (node->parent);
- g_assert (node->parent != node);
-
- if (NODE_LEFT_CHILD (node))
- {
- /* rotate right */
- tmp = node->right;
-
- node->right = node->parent;
- node->parent = node->parent->parent;
- if (node->parent)
- {
- if (node->parent->left == node->right)
- node->parent->left = node;
- else
- node->parent->right = node;
- }
-
- g_assert (node->right);
-
- node->right->parent = node;
- node->right->left = tmp;
-
- if (node->right->left)
- node->right->left->parent = node->right;
-
- old = node->right;
- }
- else
- {
- /* rotate left */
- tmp = node->left;
-
- node->left = node->parent;
- node->parent = node->parent->parent;
- if (node->parent)
- {
- if (node->parent->right == node->left)
- node->parent->right = node;
- else
- node->parent->left = node;
- }
-
- g_assert (node->left);
-
- node->left->parent = node;
- node->left->right = tmp;
-
- if (node->left->right)
- node->left->right->parent = node->left;
-
- old = node->left;
- }
-
- node_update_fields (old);
- node_update_fields (node);
-}
-
-static void
-node_update_fields_deep (GSequenceNode *node)
-{
- if (node)
- {
- node_update_fields (node);
-
- node_update_fields_deep (node->parent);
- }
-}
-
-static void
-rotate_down (GSequenceNode *node,
- guint priority)
-{
- guint left, right;
-
- left = node->left ? get_priority (node->left) : 0;
- right = node->right ? get_priority (node->right) : 0;
-
- while (priority < left || priority < right)
- {
- if (left > right)
- node_rotate (node->left);
- else
- node_rotate (node->right);
-
- left = node->left ? get_priority (node->left) : 0;
- right = node->right ? get_priority (node->right) : 0;
- }
-}
-
-static void
-node_cut (GSequenceNode *node)
-{
- while (node->parent)
- node_rotate (node);
-
- if (node->left)
- node->left->parent = NULL;
-
- node->left = NULL;
- node_update_fields (node);
-
- rotate_down (node, get_priority (node));
-}
-
-static void
-node_join (GSequenceNode *left,
- GSequenceNode *right)
-{
- GSequenceNode *fake = node_new (NULL);
-
- fake->left = find_root (left);
- fake->right = find_root (right);
- fake->left->parent = fake;
- fake->right->parent = fake;
-
- node_update_fields (fake);
-
- node_unlink (fake);
-
- node_free (fake, NULL);
-}
-
-static void
-node_insert_before (GSequenceNode *node,
- GSequenceNode *new)
-{
- new->left = node->left;
- if (new->left)
- new->left->parent = new;
-
- new->parent = node;
- node->left = new;
-
- node_update_fields_deep (new);
-
- while (new->parent && get_priority (new) > get_priority (new->parent))
- node_rotate (new);
-
- rotate_down (new, get_priority (new));
-}
-
-static void
-node_unlink (GSequenceNode *node)
-{
- rotate_down (node, 0);
-
- if (NODE_RIGHT_CHILD (node))
- node->parent->right = NULL;
- else if (NODE_LEFT_CHILD (node))
- node->parent->left = NULL;
-
- if (node->parent)
- node_update_fields_deep (node->parent);
-
- node->parent = NULL;
-}
-
-static void
-node_insert_sorted (GSequenceNode *node,
- GSequenceNode *new,
- GSequenceNode *end,
- GSequenceIterCompareFunc iter_cmp,
- gpointer cmp_data)
-{
- GSequenceNode *closest;
-
- closest = node_find_closest (node, new, end, iter_cmp, cmp_data);
-
- node_unlink (new);
-
- node_insert_before (closest, new);
-}
+++ /dev/null
-/* gshell.c - Shell-related utilities
- *
- * Copyright 2000 Red Hat, Inc.
- * g_execvpe implementation based on GNU libc execvp:
- * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <string.h>
-
-#include "gshell.h"
-
-#include "gslist.h"
-#include "gstrfuncs.h"
-#include "gstring.h"
-#include "gtestutils.h"
-#include "glibintl.h"
-
-/**
- * SECTION: shell
- * @title: Shell-related Utilities
- * @short_description: shell-like commandline handling
- **/
-
-/**
- * G_SHELL_ERROR:
- *
- * Error domain for shell functions. Errors in this domain will be from
- * the #GShellError enumeration. See #GError for information on error
- * domains.
- **/
-
-/**
- * GShellError:
- * @G_SHELL_ERROR_BAD_QUOTING: Mismatched or otherwise mangled quoting.
- * @G_SHELL_ERROR_EMPTY_STRING: String to be parsed was empty.
- * @G_SHELL_ERROR_FAILED: Some other error.
- *
- * Error codes returned by shell functions.
- **/
-GQuark
-g_shell_error_quark (void)
-{
- return g_quark_from_static_string ("g-shell-error-quark");
-}
-
-/* Single quotes preserve the literal string exactly. escape
- * sequences are not allowed; not even \' - if you want a '
- * in the quoted text, you have to do something like 'foo'\''bar'
- *
- * Double quotes allow $ ` " \ and newline to be escaped with backslash.
- * Otherwise double quotes preserve things literally.
- */
-
-static gboolean
-unquote_string_inplace (gchar* str, gchar** end, GError** err)
-{
- gchar* dest;
- gchar* s;
- gchar quote_char;
-
- g_return_val_if_fail(end != NULL, FALSE);
- g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
- g_return_val_if_fail(str != NULL, FALSE);
-
- dest = s = str;
-
- quote_char = *s;
-
- if (!(*s == '"' || *s == '\''))
- {
- g_set_error_literal (err,
- G_SHELL_ERROR,
- G_SHELL_ERROR_BAD_QUOTING,
- _("Quoted text doesn't begin with a quotation mark"));
- *end = str;
- return FALSE;
- }
-
- /* Skip the initial quote mark */
- ++s;
-
- if (quote_char == '"')
- {
- while (*s)
- {
- g_assert(s > dest); /* loop invariant */
-
- switch (*s)
- {
- case '"':
- /* End of the string, return now */
- *dest = '\0';
- ++s;
- *end = s;
- return TRUE;
- break;
-
- case '\\':
- /* Possible escaped quote or \ */
- ++s;
- switch (*s)
- {
- case '"':
- case '\\':
- case '`':
- case '$':
- case '\n':
- *dest = *s;
- ++s;
- ++dest;
- break;
-
- default:
- /* not an escaped char */
- *dest = '\\';
- ++dest;
- /* ++s already done. */
- break;
- }
- break;
-
- default:
- *dest = *s;
- ++dest;
- ++s;
- break;
- }
-
- g_assert(s > dest); /* loop invariant */
- }
- }
- else
- {
- while (*s)
- {
- g_assert(s > dest); /* loop invariant */
-
- if (*s == '\'')
- {
- /* End of the string, return now */
- *dest = '\0';
- ++s;
- *end = s;
- return TRUE;
- }
- else
- {
- *dest = *s;
- ++dest;
- ++s;
- }
-
- g_assert(s > dest); /* loop invariant */
- }
- }
-
- /* If we reach here this means the close quote was never encountered */
-
- *dest = '\0';
-
- g_set_error_literal (err,
- G_SHELL_ERROR,
- G_SHELL_ERROR_BAD_QUOTING,
- _("Unmatched quotation mark in command line or other shell-quoted text"));
- *end = s;
- return FALSE;
-}
-
-/**
- * g_shell_quote:
- * @unquoted_string: a literal string
- *
- * Quotes a string so that the shell (/bin/sh) will interpret the
- * quoted string to mean @unquoted_string. If you pass a filename to
- * the shell, for example, you should first quote it with this
- * function. The return value must be freed with g_free(). The
- * quoting style used is undefined (single or double quotes may be
- * used).
- *
- * Return value: quoted string
- **/
-gchar*
-g_shell_quote (const gchar *unquoted_string)
-{
- /* We always use single quotes, because the algorithm is cheesier.
- * We could use double if we felt like it, that might be more
- * human-readable.
- */
-
- const gchar *p;
- GString *dest;
-
- g_return_val_if_fail (unquoted_string != NULL, NULL);
-
- dest = g_string_new ("'");
-
- p = unquoted_string;
-
- /* could speed this up a lot by appending chunks of text at a
- * time.
- */
- while (*p)
- {
- /* Replace literal ' with a close ', a \', and a open ' */
- if (*p == '\'')
- g_string_append (dest, "'\\''");
- else
- g_string_append_c (dest, *p);
-
- ++p;
- }
-
- /* close the quote */
- g_string_append_c (dest, '\'');
-
- return g_string_free (dest, FALSE);
-}
-
-/**
- * g_shell_unquote:
- * @quoted_string: shell-quoted string
- * @error: error return location or NULL
- *
- * Unquotes a string as the shell (/bin/sh) would. Only handles
- * quotes; if a string contains file globs, arithmetic operators,
- * variables, backticks, redirections, or other special-to-the-shell
- * features, the result will be different from the result a real shell
- * would produce (the variables, backticks, etc. will be passed
- * through literally instead of being expanded). This function is
- * guaranteed to succeed if applied to the result of
- * g_shell_quote(). If it fails, it returns %NULL and sets the
- * error. The @quoted_string need not actually contain quoted or
- * escaped text; g_shell_unquote() simply goes through the string and
- * unquotes/unescapes anything that the shell would. Both single and
- * double quotes are handled, as are escapes including escaped
- * newlines. The return value must be freed with g_free(). Possible
- * errors are in the #G_SHELL_ERROR domain.
- *
- * Shell quoting rules are a bit strange. Single quotes preserve the
- * literal string exactly. escape sequences are not allowed; not even
- * \' - if you want a ' in the quoted text, you have to do something
- * like 'foo'\''bar'. Double quotes allow $, `, ", \, and newline to
- * be escaped with backslash. Otherwise double quotes preserve things
- * literally.
- *
- * Return value: an unquoted string
- **/
-gchar*
-g_shell_unquote (const gchar *quoted_string,
- GError **error)
-{
- gchar *unquoted;
- gchar *end;
- gchar *start;
- GString *retval;
-
- g_return_val_if_fail (quoted_string != NULL, NULL);
-
- unquoted = g_strdup (quoted_string);
-
- start = unquoted;
- end = unquoted;
- retval = g_string_new (NULL);
-
- /* The loop allows cases such as
- * "foo"blah blah'bar'woo foo"baz"la la la\'\''foo'
- */
- while (*start)
- {
- /* Append all non-quoted chars, honoring backslash escape
- */
-
- while (*start && !(*start == '"' || *start == '\''))
- {
- if (*start == '\\')
- {
- /* all characters can get escaped by backslash,
- * except newline, which is removed if it follows
- * a backslash outside of quotes
- */
-
- ++start;
- if (*start)
- {
- if (*start != '\n')
- g_string_append_c (retval, *start);
- ++start;
- }
- }
- else
- {
- g_string_append_c (retval, *start);
- ++start;
- }
- }
-
- if (*start)
- {
- if (!unquote_string_inplace (start, &end, error))
- {
- goto error;
- }
- else
- {
- g_string_append (retval, start);
- start = end;
- }
- }
- }
-
- g_free (unquoted);
- return g_string_free (retval, FALSE);
-
- error:
- g_assert (error == NULL || *error != NULL);
-
- g_free (unquoted);
- g_string_free (retval, TRUE);
- return NULL;
-}
-
-/* g_parse_argv() does a semi-arbitrary weird subset of the way
- * the shell parses a command line. We don't do variable expansion,
- * don't understand that operators are tokens, don't do tilde expansion,
- * don't do command substitution, no arithmetic expansion, IFS gets ignored,
- * don't do filename globs, don't remove redirection stuff, etc.
- *
- * READ THE UNIX98 SPEC on "Shell Command Language" before changing
- * the behavior of this code.
- *
- * Steps to parsing the argv string:
- *
- * - tokenize the string (but since we ignore operators,
- * our tokenization may diverge from what the shell would do)
- * note that tokenization ignores the internals of a quoted
- * word and it always splits on spaces, not on IFS even
- * if we used IFS. We also ignore "end of input indicator"
- * (I guess this is control-D?)
- *
- * Tokenization steps, from UNIX98 with operator stuff removed,
- * are:
- *
- * 1) "If the current character is backslash, single-quote or
- * double-quote (\, ' or ") and it is not quoted, it will affect
- * quoting for subsequent characters up to the end of the quoted
- * text. The rules for quoting are as described in Quoting
- * . During token recognition no substitutions will be actually
- * performed, and the result token will contain exactly the
- * characters that appear in the input (except for newline
- * character joining), unmodified, including any embedded or
- * enclosing quotes or substitution operators, between the quote
- * mark and the end of the quoted text. The token will not be
- * delimited by the end of the quoted field."
- *
- * 2) "If the current character is an unquoted newline character,
- * the current token will be delimited."
- *
- * 3) "If the current character is an unquoted blank character, any
- * token containing the previous character is delimited and the
- * current character will be discarded."
- *
- * 4) "If the previous character was part of a word, the current
- * character will be appended to that word."
- *
- * 5) "If the current character is a "#", it and all subsequent
- * characters up to, but excluding, the next newline character
- * will be discarded as a comment. The newline character that
- * ends the line is not considered part of the comment. The
- * "#" starts a comment only when it is at the beginning of a
- * token. Since the search for the end-of-comment does not
- * consider an escaped newline character specially, a comment
- * cannot be continued to the next line."
- *
- * 6) "The current character will be used as the start of a new word."
- *
- *
- * - for each token (word), perform portions of word expansion, namely
- * field splitting (using default whitespace IFS) and quote
- * removal. Field splitting may increase the number of words.
- * Quote removal does not increase the number of words.
- *
- * "If the complete expansion appropriate for a word results in an
- * empty field, that empty field will be deleted from the list of
- * fields that form the completely expanded command, unless the
- * original word contained single-quote or double-quote characters."
- * - UNIX98 spec
- *
- *
- */
-
-static inline void
-ensure_token (GString **token)
-{
- if (*token == NULL)
- *token = g_string_new (NULL);
-}
-
-static void
-delimit_token (GString **token,
- GSList **retval)
-{
- if (*token == NULL)
- return;
-
- *retval = g_slist_prepend (*retval, g_string_free (*token, FALSE));
-
- *token = NULL;
-}
-
-static GSList*
-tokenize_command_line (const gchar *command_line,
- GError **error)
-{
- gchar current_quote;
- const gchar *p;
- GString *current_token = NULL;
- GSList *retval = NULL;
- gboolean quoted;
-
- current_quote = '\0';
- quoted = FALSE;
- p = command_line;
-
- while (*p)
- {
- if (current_quote == '\\')
- {
- if (*p == '\n')
- {
- /* we append nothing; backslash-newline become nothing */
- }
- else
- {
- /* we append the backslash and the current char,
- * to be interpreted later after tokenization
- */
- ensure_token (¤t_token);
- g_string_append_c (current_token, '\\');
- g_string_append_c (current_token, *p);
- }
-
- current_quote = '\0';
- }
- else if (current_quote == '#')
- {
- /* Discard up to and including next newline */
- while (*p && *p != '\n')
- ++p;
-
- current_quote = '\0';
-
- if (*p == '\0')
- break;
- }
- else if (current_quote)
- {
- if (*p == current_quote &&
- /* check that it isn't an escaped double quote */
- !(current_quote == '"' && quoted))
- {
- /* close the quote */
- current_quote = '\0';
- }
-
- /* Everything inside quotes, and the close quote,
- * gets appended literally.
- */
-
- ensure_token (¤t_token);
- g_string_append_c (current_token, *p);
- }
- else
- {
- switch (*p)
- {
- case '\n':
- delimit_token (¤t_token, &retval);
- break;
-
- case ' ':
- case '\t':
- /* If the current token contains the previous char, delimit
- * the current token. A nonzero length
- * token should always contain the previous char.
- */
- if (current_token &&
- current_token->len > 0)
- {
- delimit_token (¤t_token, &retval);
- }
-
- /* discard all unquoted blanks (don't add them to a token) */
- break;
-
-
- /* single/double quotes are appended to the token,
- * escapes are maybe appended next time through the loop,
- * comment chars are never appended.
- */
-
- case '\'':
- case '"':
- ensure_token (¤t_token);
- g_string_append_c (current_token, *p);
-
- /* FALL THRU */
-
- case '#':
- case '\\':
- current_quote = *p;
- break;
-
- default:
- /* Combines rules 4) and 6) - if we have a token, append to it,
- * otherwise create a new token.
- */
- ensure_token (¤t_token);
- g_string_append_c (current_token, *p);
- break;
- }
- }
-
- /* We need to count consecutive backslashes mod 2,
- * to detect escaped doublequotes.
- */
- if (*p != '\\')
- quoted = FALSE;
- else
- quoted = !quoted;
-
- ++p;
- }
-
- delimit_token (¤t_token, &retval);
-
- if (current_quote)
- {
- if (current_quote == '\\')
- g_set_error (error,
- G_SHELL_ERROR,
- G_SHELL_ERROR_BAD_QUOTING,
- _("Text ended just after a '\\' character."
- " (The text was '%s')"),
- command_line);
- else
- g_set_error (error,
- G_SHELL_ERROR,
- G_SHELL_ERROR_BAD_QUOTING,
- _("Text ended before matching quote was found for %c."
- " (The text was '%s')"),
- current_quote, command_line);
-
- goto error;
- }
-
- if (retval == NULL)
- {
- g_set_error_literal (error,
- G_SHELL_ERROR,
- G_SHELL_ERROR_EMPTY_STRING,
- _("Text was empty (or contained only whitespace)"));
-
- goto error;
- }
-
- /* we appended backward */
- retval = g_slist_reverse (retval);
-
- return retval;
-
- error:
- g_assert (error == NULL || *error != NULL);
-
- if (retval)
- {
- g_slist_foreach (retval, (GFunc)g_free, NULL);
- g_slist_free (retval);
- }
-
- return NULL;
-}
-
-/**
- * g_shell_parse_argv:
- * @command_line: command line to parse
- * @argcp: return location for number of args
- * @argvp: return location for array of args
- * @error: return location for error
- *
- * Parses a command line into an argument vector, in much the same way
- * the shell would, but without many of the expansions the shell would
- * perform (variable expansion, globs, operators, filename expansion,
- * etc. are not supported). The results are defined to be the same as
- * those you would get from a UNIX98 /bin/sh, as long as the input
- * contains none of the unsupported shell expansions. If the input
- * does contain such expansions, they are passed through
- * literally. Possible errors are those from the #G_SHELL_ERROR
- * domain. Free the returned vector with g_strfreev().
- *
- * Return value: %TRUE on success, %FALSE if error set
- **/
-gboolean
-g_shell_parse_argv (const gchar *command_line,
- gint *argcp,
- gchar ***argvp,
- GError **error)
-{
- /* Code based on poptParseArgvString() from libpopt */
- gint argc = 0;
- gchar **argv = NULL;
- GSList *tokens = NULL;
- gint i;
- GSList *tmp_list;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- tokens = tokenize_command_line (command_line, error);
- if (tokens == NULL)
- return FALSE;
-
- /* Because we can't have introduced any new blank space into the
- * tokens (we didn't do any new expansions), we don't need to
- * perform field splitting. If we were going to honor IFS or do any
- * expansions, we would have to do field splitting on each word
- * here. Also, if we were going to do any expansion we would need to
- * remove any zero-length words that didn't contain quotes
- * originally; but since there's no expansion we know all words have
- * nonzero length, unless they contain quotes.
- *
- * So, we simply remove quotes, and don't do any field splitting or
- * empty word removal, since we know there was no way to introduce
- * such things.
- */
-
- argc = g_slist_length (tokens);
- argv = g_new0 (gchar*, argc + 1);
- i = 0;
- tmp_list = tokens;
- while (tmp_list)
- {
- argv[i] = g_shell_unquote (tmp_list->data, error);
-
- /* Since we already checked that quotes matched up in the
- * tokenizer, this shouldn't be possible to reach I guess.
- */
- if (argv[i] == NULL)
- goto failed;
-
- tmp_list = g_slist_next (tmp_list);
- ++i;
- }
-
- g_slist_foreach (tokens, (GFunc)g_free, NULL);
- g_slist_free (tokens);
-
- if (argcp)
- *argcp = argc;
-
- if (argvp)
- *argvp = argv;
- else
- g_strfreev (argv);
-
- return TRUE;
-
- failed:
-
- g_assert (error == NULL || *error != NULL);
- g_strfreev (argv);
- g_slist_foreach (tokens, (GFunc) g_free, NULL);
- g_slist_free (tokens);
-
- return FALSE;
-}
+++ /dev/null
-/* GLIB sliced memory - fast concurrent memory chunk allocator
- * Copyright (C) 2005 Tim Janik
- *
- * 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 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.
- */
-/* MT safe */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#if defined HAVE_POSIX_MEMALIGN && defined POSIX_MEMALIGN_WITH_COMPLIANT_ALLOCS
-# define HAVE_COMPLIANT_POSIX_MEMALIGN 1
-#endif
-
-#ifdef HAVE_COMPLIANT_POSIX_MEMALIGN
-#define _XOPEN_SOURCE 600 /* posix_memalign() */
-#endif
-#include <stdlib.h> /* posix_memalign() */
-#include <string.h>
-#include <errno.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h> /* sysconf() */
-#endif
-#ifdef G_OS_WIN32
-#include <windows.h>
-#include <process.h>
-#endif
-
-#include <stdio.h> /* fputs/fprintf */
-
-#include "gslice.h"
-
-#include "gmain.h"
-#include "gmem.h" /* gslice.h */
-#include "gstrfuncs.h"
-#include "gutils.h"
-#include "gtestutils.h"
-#include "gthread.h"
-#include "gthreadprivate.h"
-#include "glib_trace.h"
-
-/* the GSlice allocator is split up into 4 layers, roughly modelled after the slab
- * allocator and magazine extensions as outlined in:
- * + [Bonwick94] Jeff Bonwick, The slab allocator: An object-caching kernel
- * memory allocator. USENIX 1994, http://citeseer.ist.psu.edu/bonwick94slab.html
- * + [Bonwick01] Bonwick and Jonathan Adams, Magazines and vmem: Extending the
- * slab allocator to many cpu's and arbitrary resources.
- * USENIX 2001, http://citeseer.ist.psu.edu/bonwick01magazines.html
- * the layers are:
- * - the thread magazines. for each (aligned) chunk size, a magazine (a list)
- * of recently freed and soon to be allocated chunks is maintained per thread.
- * this way, most alloc/free requests can be quickly satisfied from per-thread
- * free lists which only require one g_private_get() call to retrive the
- * thread handle.
- * - the magazine cache. allocating and freeing chunks to/from threads only
- * occours at magazine sizes from a global depot of magazines. the depot
- * maintaines a 15 second working set of allocated magazines, so full
- * magazines are not allocated and released too often.
- * the chunk size dependent magazine sizes automatically adapt (within limits,
- * see [3]) to lock contention to properly scale performance across a variety
- * of SMP systems.
- * - the slab allocator. this allocator allocates slabs (blocks of memory) close
- * to the system page size or multiples thereof which have to be page aligned.
- * the blocks are divided into smaller chunks which are used to satisfy
- * allocations from the upper layers. the space provided by the reminder of
- * the chunk size division is used for cache colorization (random distribution
- * of chunk addresses) to improve processor cache utilization. multiple slabs
- * with the same chunk size are kept in a partially sorted ring to allow O(1)
- * freeing and allocation of chunks (as long as the allocation of an entirely
- * new slab can be avoided).
- * - the page allocator. on most modern systems, posix_memalign(3) or
- * memalign(3) should be available, so this is used to allocate blocks with
- * system page size based alignments and sizes or multiples thereof.
- * if no memalign variant is provided, valloc() is used instead and
- * block sizes are limited to the system page size (no multiples thereof).
- * as a fallback, on system without even valloc(), a malloc(3)-based page
- * allocator with alloc-only behaviour is used.
- *
- * NOTES:
- * [1] some systems memalign(3) implementations may rely on boundary tagging for
- * the handed out memory chunks. to avoid excessive page-wise fragmentation,
- * we reserve 2 * sizeof (void*) per block size for the systems memalign(3),
- * specified in NATIVE_MALLOC_PADDING.
- * [2] using the slab allocator alone already provides for a fast and efficient
- * allocator, it doesn't properly scale beyond single-threaded uses though.
- * also, the slab allocator implements eager free(3)-ing, i.e. does not
- * provide any form of caching or working set maintenance. so if used alone,
- * it's vulnerable to trashing for sequences of balanced (alloc, free) pairs
- * at certain thresholds.
- * [3] magazine sizes are bound by an implementation specific minimum size and
- * a chunk size specific maximum to limit magazine storage sizes to roughly
- * 16KB.
- * [4] allocating ca. 8 chunks per block/page keeps a good balance between
- * external and internal fragmentation (<= 12.5%). [Bonwick94]
- */
-
-/* --- macros and constants --- */
-#define LARGEALIGNMENT (256)
-#define P2ALIGNMENT (2 * sizeof (gsize)) /* fits 2 pointers (assumed to be 2 * GLIB_SIZEOF_SIZE_T below) */
-#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))
-#define NATIVE_MALLOC_PADDING P2ALIGNMENT /* per-page padding left for native malloc(3) see [1] */
-#define SLAB_INFO_SIZE P2ALIGN (sizeof (SlabInfo) + NATIVE_MALLOC_PADDING)
-#define MAX_MAGAZINE_SIZE (256) /* see [3] and allocator_get_magazine_threshold() for this */
-#define MIN_MAGAZINE_SIZE (4)
-#define MAX_STAMP_COUNTER (7) /* distributes the load of gettimeofday() */
-#define MAX_SLAB_CHUNK_SIZE(al) (((al)->max_page_size - SLAB_INFO_SIZE) / 8) /* we want at last 8 chunks per page, see [4] */
-#define MAX_SLAB_INDEX(al) (SLAB_INDEX (al, MAX_SLAB_CHUNK_SIZE (al)) + 1)
-#define SLAB_INDEX(al, asize) ((asize) / P2ALIGNMENT - 1) /* asize must be P2ALIGNMENT aligned */
-#define SLAB_CHUNK_SIZE(al, ix) (((ix) + 1) * P2ALIGNMENT)
-#define SLAB_BPAGE_SIZE(al,csz) (8 * (csz) + SLAB_INFO_SIZE)
-
-/* optimized version of ALIGN (size, P2ALIGNMENT) */
-#if GLIB_SIZEOF_SIZE_T * 2 == 8 /* P2ALIGNMENT */
-#define P2ALIGN(size) (((size) + 0x7) & ~(gsize) 0x7)
-#elif GLIB_SIZEOF_SIZE_T * 2 == 16 /* P2ALIGNMENT */
-#define P2ALIGN(size) (((size) + 0xf) & ~(gsize) 0xf)
-#else
-#define P2ALIGN(size) ALIGN (size, P2ALIGNMENT)
-#endif
-
-/* special helpers to avoid gmessage.c dependency */
-static void mem_error (const char *format, ...) G_GNUC_PRINTF (1,2);
-#define mem_assert(cond) do { if (G_LIKELY (cond)) ; else mem_error ("assertion failed: %s", #cond); } while (0)
-
-/* --- structures --- */
-typedef struct _ChunkLink ChunkLink;
-typedef struct _SlabInfo SlabInfo;
-typedef struct _CachedMagazine CachedMagazine;
-struct _ChunkLink {
- ChunkLink *next;
- ChunkLink *data;
-};
-struct _SlabInfo {
- ChunkLink *chunks;
- guint n_allocated;
- SlabInfo *next, *prev;
-};
-typedef struct {
- ChunkLink *chunks;
- gsize count; /* approximative chunks list length */
-} Magazine;
-typedef struct {
- Magazine *magazine1; /* array of MAX_SLAB_INDEX (allocator) */
- Magazine *magazine2; /* array of MAX_SLAB_INDEX (allocator) */
-} ThreadMemory;
-typedef struct {
- gboolean always_malloc;
- gboolean bypass_magazines;
- gboolean debug_blocks;
- gsize working_set_msecs;
- guint color_increment;
-} SliceConfig;
-typedef struct {
- /* const after initialization */
- gsize min_page_size, max_page_size;
- SliceConfig config;
- gsize max_slab_chunk_size_for_magazine_cache;
- /* magazine cache */
- GMutex *magazine_mutex;
- ChunkLink **magazines; /* array of MAX_SLAB_INDEX (allocator) */
- guint *contention_counters; /* array of MAX_SLAB_INDEX (allocator) */
- gint mutex_counter;
- guint stamp_counter;
- guint last_stamp;
- /* slab allocator */
- GMutex *slab_mutex;
- SlabInfo **slab_stack; /* array of MAX_SLAB_INDEX (allocator) */
- guint color_accu;
-} Allocator;
-
-/* --- g-slice prototypes --- */
-static gpointer slab_allocator_alloc_chunk (gsize chunk_size);
-static void slab_allocator_free_chunk (gsize chunk_size,
- gpointer mem);
-static void private_thread_memory_cleanup (gpointer data);
-static gpointer allocator_memalign (gsize alignment,
- gsize memsize);
-static void allocator_memfree (gsize memsize,
- gpointer mem);
-static inline void magazine_cache_update_stamp (void);
-static inline gsize allocator_get_magazine_threshold (Allocator *allocator,
- guint ix);
-
-/* --- g-slice memory checker --- */
-static void smc_notify_alloc (void *pointer,
- size_t size);
-static int smc_notify_free (void *pointer,
- size_t size);
-
-/* --- variables --- */
-static GPrivate *private_thread_memory = NULL;
-static gsize sys_page_size = 0;
-static Allocator allocator[1] = { { 0, }, };
-static SliceConfig slice_config = {
- FALSE, /* always_malloc */
- FALSE, /* bypass_magazines */
- FALSE, /* debug_blocks */
- 15 * 1000, /* working_set_msecs */
- 1, /* color increment, alt: 0x7fffffff */
-};
-static GMutex *smc_tree_mutex = NULL; /* mutex for G_SLICE=debug-blocks */
-
-/* --- auxillary funcitons --- */
-void
-g_slice_set_config (GSliceConfig ckey,
- gint64 value)
-{
- g_return_if_fail (sys_page_size == 0);
- switch (ckey)
- {
- case G_SLICE_CONFIG_ALWAYS_MALLOC:
- slice_config.always_malloc = value != 0;
- break;
- case G_SLICE_CONFIG_BYPASS_MAGAZINES:
- slice_config.bypass_magazines = value != 0;
- break;
- case G_SLICE_CONFIG_WORKING_SET_MSECS:
- slice_config.working_set_msecs = value;
- break;
- case G_SLICE_CONFIG_COLOR_INCREMENT:
- slice_config.color_increment = value;
- default: ;
- }
-}
-
-gint64
-g_slice_get_config (GSliceConfig ckey)
-{
- switch (ckey)
- {
- case G_SLICE_CONFIG_ALWAYS_MALLOC:
- return slice_config.always_malloc;
- case G_SLICE_CONFIG_BYPASS_MAGAZINES:
- return slice_config.bypass_magazines;
- case G_SLICE_CONFIG_WORKING_SET_MSECS:
- return slice_config.working_set_msecs;
- case G_SLICE_CONFIG_CHUNK_SIZES:
- return MAX_SLAB_INDEX (allocator);
- case G_SLICE_CONFIG_COLOR_INCREMENT:
- return slice_config.color_increment;
- default:
- return 0;
- }
-}
-
-gint64*
-g_slice_get_config_state (GSliceConfig ckey,
- gint64 address,
- guint *n_values)
-{
- guint i = 0;
- g_return_val_if_fail (n_values != NULL, NULL);
- *n_values = 0;
- switch (ckey)
- {
- gint64 array[64];
- case G_SLICE_CONFIG_CONTENTION_COUNTER:
- array[i++] = SLAB_CHUNK_SIZE (allocator, address);
- array[i++] = allocator->contention_counters[address];
- array[i++] = allocator_get_magazine_threshold (allocator, address);
- *n_values = i;
- return g_memdup (array, sizeof (array[0]) * *n_values);
- default:
- return NULL;
- }
-}
-
-static void
-slice_config_init (SliceConfig *config)
-{
- /* don't use g_malloc/g_message here */
- gchar buffer[1024];
- const gchar *val = _g_getenv_nomalloc ("G_SLICE", buffer);
- const GDebugKey keys[] = {
- { "always-malloc", 1 << 0 },
- { "debug-blocks", 1 << 1 },
- };
- gint flags = !val ? 0 : g_parse_debug_string (val, keys, G_N_ELEMENTS (keys));
- *config = slice_config;
- if (flags & (1 << 0)) /* always-malloc */
- config->always_malloc = TRUE;
- if (flags & (1 << 1)) /* debug-blocks */
- config->debug_blocks = TRUE;
-}
-
-static void
-g_slice_init_nomessage (void)
-{
- /* we may not use g_error() or friends here */
- mem_assert (sys_page_size == 0);
- mem_assert (MIN_MAGAZINE_SIZE >= 4);
-
-#ifdef G_OS_WIN32
- {
- SYSTEM_INFO system_info;
- GetSystemInfo (&system_info);
- sys_page_size = system_info.dwPageSize;
- }
-#else
- sys_page_size = sysconf (_SC_PAGESIZE); /* = sysconf (_SC_PAGE_SIZE); = getpagesize(); */
-#endif
- mem_assert (sys_page_size >= 2 * LARGEALIGNMENT);
- mem_assert ((sys_page_size & (sys_page_size - 1)) == 0);
- slice_config_init (&allocator->config);
- allocator->min_page_size = sys_page_size;
-#if HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN
- /* allow allocation of pages up to 8KB (with 8KB alignment).
- * this is useful because many medium to large sized structures
- * fit less than 8 times (see [4]) into 4KB pages.
- * we allow very small page sizes here, to reduce wastage in
- * threads if only small allocations are required (this does
- * bear the risk of incresing allocation times and fragmentation
- * though).
- */
- allocator->min_page_size = MAX (allocator->min_page_size, 4096);
- allocator->max_page_size = MAX (allocator->min_page_size, 8192);
- allocator->min_page_size = MIN (allocator->min_page_size, 128);
-#else
- /* we can only align to system page size */
- allocator->max_page_size = sys_page_size;
-#endif
- if (allocator->config.always_malloc)
- {
- allocator->contention_counters = NULL;
- allocator->magazines = NULL;
- allocator->slab_stack = NULL;
- }
- else
- {
- allocator->contention_counters = g_new0 (guint, MAX_SLAB_INDEX (allocator));
- allocator->magazines = g_new0 (ChunkLink*, MAX_SLAB_INDEX (allocator));
- allocator->slab_stack = g_new0 (SlabInfo*, MAX_SLAB_INDEX (allocator));
- }
-
- allocator->magazine_mutex = NULL; /* _g_slice_thread_init_nomessage() */
- allocator->mutex_counter = 0;
- allocator->stamp_counter = MAX_STAMP_COUNTER; /* force initial update */
- allocator->last_stamp = 0;
- allocator->slab_mutex = NULL; /* _g_slice_thread_init_nomessage() */
- allocator->color_accu = 0;
- magazine_cache_update_stamp();
- /* values cached for performance reasons */
- allocator->max_slab_chunk_size_for_magazine_cache = MAX_SLAB_CHUNK_SIZE (allocator);
- if (allocator->config.always_malloc || allocator->config.bypass_magazines)
- allocator->max_slab_chunk_size_for_magazine_cache = 0; /* non-optimized cases */
- /* at this point, g_mem_gc_friendly() should be initialized, this
- * should have been accomplished by the above g_malloc/g_new calls
- */
-}
-
-static inline guint
-allocator_categorize (gsize aligned_chunk_size)
-{
- /* speed up the likely path */
- if (G_LIKELY (aligned_chunk_size && aligned_chunk_size <= allocator->max_slab_chunk_size_for_magazine_cache))
- return 1; /* use magazine cache */
-
- /* the above will fail (max_slab_chunk_size_for_magazine_cache == 0) if the
- * allocator is still uninitialized, or if we are not configured to use the
- * magazine cache.
- */
- if (!sys_page_size)
- g_slice_init_nomessage ();
- if (!allocator->config.always_malloc &&
- aligned_chunk_size &&
- aligned_chunk_size <= MAX_SLAB_CHUNK_SIZE (allocator))
- {
- if (allocator->config.bypass_magazines)
- return 2; /* use slab allocator, see [2] */
- return 1; /* use magazine cache */
- }
- return 0; /* use malloc() */
-}
-
-void
-_g_slice_thread_init_nomessage (void)
-{
- /* we may not use g_error() or friends here */
- if (!sys_page_size)
- g_slice_init_nomessage();
- else
- {
- /* g_slice_init_nomessage() has been called already, probably due
- * to a g_slice_alloc1() before g_thread_init().
- */
- }
- private_thread_memory = g_private_new (private_thread_memory_cleanup);
- allocator->magazine_mutex = g_mutex_new();
- allocator->slab_mutex = g_mutex_new();
- if (allocator->config.debug_blocks)
- smc_tree_mutex = g_mutex_new();
-}
-
-static inline void
-g_mutex_lock_a (GMutex *mutex,
- guint *contention_counter)
-{
- gboolean contention = FALSE;
- if (!g_mutex_trylock (mutex))
- {
- g_mutex_lock (mutex);
- contention = TRUE;
- }
- if (contention)
- {
- allocator->mutex_counter++;
- if (allocator->mutex_counter >= 1) /* quickly adapt to contention */
- {
- allocator->mutex_counter = 0;
- *contention_counter = MIN (*contention_counter + 1, MAX_MAGAZINE_SIZE);
- }
- }
- else /* !contention */
- {
- allocator->mutex_counter--;
- if (allocator->mutex_counter < -11) /* moderately recover magazine sizes */
- {
- allocator->mutex_counter = 0;
- *contention_counter = MAX (*contention_counter, 1) - 1;
- }
- }
-}
-
-static inline ThreadMemory*
-thread_memory_from_self (void)
-{
- ThreadMemory *tmem = g_private_get (private_thread_memory);
- if (G_UNLIKELY (!tmem))
- {
- static ThreadMemory *single_thread_memory = NULL; /* remember single-thread info for multi-threaded case */
- if (single_thread_memory && g_thread_supported ())
- {
- g_mutex_lock (allocator->slab_mutex);
- if (single_thread_memory)
- {
- /* GSlice has been used before g_thread_init(), and now
- * we are running threaded. to cope with it, use the saved
- * thread memory structure from when we weren't threaded.
- */
- tmem = single_thread_memory;
- single_thread_memory = NULL; /* slab_mutex protected when multi-threaded */
- }
- g_mutex_unlock (allocator->slab_mutex);
- }
- if (!tmem)
- {
- const guint n_magazines = MAX_SLAB_INDEX (allocator);
- tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines);
- tmem->magazine1 = (Magazine*) (tmem + 1);
- tmem->magazine2 = &tmem->magazine1[n_magazines];
- }
- /* g_private_get/g_private_set works in the single-threaded xor the multi-
- * threaded case. but not *across* g_thread_init(), after multi-thread
- * initialization it returns NULL for previously set single-thread data.
- */
- g_private_set (private_thread_memory, tmem);
- /* save single-thread thread memory structure, in case we need to
- * pick it up again after multi-thread initialization happened.
- */
- if (!single_thread_memory && !g_thread_supported ())
- single_thread_memory = tmem; /* no slab_mutex created yet */
- }
- return tmem;
-}
-
-static inline ChunkLink*
-magazine_chain_pop_head (ChunkLink **magazine_chunks)
-{
- /* magazine chains are linked via ChunkLink->next.
- * each ChunkLink->data of the toplevel chain may point to a subchain,
- * linked via ChunkLink->next. ChunkLink->data of the subchains just
- * contains uninitialized junk.
- */
- ChunkLink *chunk = (*magazine_chunks)->data;
- if (G_UNLIKELY (chunk))
- {
- /* allocating from freed list */
- (*magazine_chunks)->data = chunk->next;
- }
- else
- {
- chunk = *magazine_chunks;
- *magazine_chunks = chunk->next;
- }
- return chunk;
-}
-
-#if 0 /* useful for debugging */
-static guint
-magazine_count (ChunkLink *head)
-{
- guint count = 0;
- if (!head)
- return 0;
- while (head)
- {
- ChunkLink *child = head->data;
- count += 1;
- for (child = head->data; child; child = child->next)
- count += 1;
- head = head->next;
- }
- return count;
-}
-#endif
-
-static inline gsize
-allocator_get_magazine_threshold (Allocator *allocator,
- guint ix)
-{
- /* the magazine size calculated here has a lower bound of MIN_MAGAZINE_SIZE,
- * which is required by the implementation. also, for moderately sized chunks
- * (say >= 64 bytes), magazine sizes shouldn't be much smaller then the number
- * of chunks available per page/2 to avoid excessive traffic in the magazine
- * cache for small to medium sized structures.
- * the upper bound of the magazine size is effectively provided by
- * MAX_MAGAZINE_SIZE. for larger chunks, this number is scaled down so that
- * the content of a single magazine doesn't exceed ca. 16KB.
- */
- gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
- guint threshold = MAX (MIN_MAGAZINE_SIZE, allocator->max_page_size / MAX (5 * chunk_size, 5 * 32));
- guint contention_counter = allocator->contention_counters[ix];
- if (G_UNLIKELY (contention_counter)) /* single CPU bias */
- {
- /* adapt contention counter thresholds to chunk sizes */
- contention_counter = contention_counter * 64 / chunk_size;
- threshold = MAX (threshold, contention_counter);
- }
- return threshold;
-}
-
-/* --- magazine cache --- */
-static inline void
-magazine_cache_update_stamp (void)
-{
- if (allocator->stamp_counter >= MAX_STAMP_COUNTER)
- {
- GTimeVal tv;
- g_get_current_time (&tv);
- allocator->last_stamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* milli seconds */
- allocator->stamp_counter = 0;
- }
- else
- allocator->stamp_counter++;
-}
-
-static inline ChunkLink*
-magazine_chain_prepare_fields (ChunkLink *magazine_chunks)
-{
- ChunkLink *chunk1;
- ChunkLink *chunk2;
- ChunkLink *chunk3;
- ChunkLink *chunk4;
- /* checked upon initialization: mem_assert (MIN_MAGAZINE_SIZE >= 4); */
- /* ensure a magazine with at least 4 unused data pointers */
- chunk1 = magazine_chain_pop_head (&magazine_chunks);
- chunk2 = magazine_chain_pop_head (&magazine_chunks);
- chunk3 = magazine_chain_pop_head (&magazine_chunks);
- chunk4 = magazine_chain_pop_head (&magazine_chunks);
- chunk4->next = magazine_chunks;
- chunk3->next = chunk4;
- chunk2->next = chunk3;
- chunk1->next = chunk2;
- return chunk1;
-}
-
-/* access the first 3 fields of a specially prepared magazine chain */
-#define magazine_chain_prev(mc) ((mc)->data)
-#define magazine_chain_stamp(mc) ((mc)->next->data)
-#define magazine_chain_uint_stamp(mc) GPOINTER_TO_UINT ((mc)->next->data)
-#define magazine_chain_next(mc) ((mc)->next->next->data)
-#define magazine_chain_count(mc) ((mc)->next->next->next->data)
-
-static void
-magazine_cache_trim (Allocator *allocator,
- guint ix,
- guint stamp)
-{
- /* g_mutex_lock (allocator->mutex); done by caller */
- /* trim magazine cache from tail */
- ChunkLink *current = magazine_chain_prev (allocator->magazines[ix]);
- ChunkLink *trash = NULL;
- while (ABS (stamp - magazine_chain_uint_stamp (current)) >= allocator->config.working_set_msecs)
- {
- /* unlink */
- ChunkLink *prev = magazine_chain_prev (current);
- ChunkLink *next = magazine_chain_next (current);
- magazine_chain_next (prev) = next;
- magazine_chain_prev (next) = prev;
- /* clear special fields, put on trash stack */
- magazine_chain_next (current) = NULL;
- magazine_chain_count (current) = NULL;
- magazine_chain_stamp (current) = NULL;
- magazine_chain_prev (current) = trash;
- trash = current;
- /* fixup list head if required */
- if (current == allocator->magazines[ix])
- {
- allocator->magazines[ix] = NULL;
- break;
- }
- current = prev;
- }
- g_mutex_unlock (allocator->magazine_mutex);
- /* free trash */
- if (trash)
- {
- const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
- g_mutex_lock (allocator->slab_mutex);
- while (trash)
- {
- current = trash;
- trash = magazine_chain_prev (current);
- magazine_chain_prev (current) = NULL; /* clear special field */
- while (current)
- {
- ChunkLink *chunk = magazine_chain_pop_head (¤t);
- slab_allocator_free_chunk (chunk_size, chunk);
- }
- }
- g_mutex_unlock (allocator->slab_mutex);
- }
-}
-
-static void
-magazine_cache_push_magazine (guint ix,
- ChunkLink *magazine_chunks,
- gsize count) /* must be >= MIN_MAGAZINE_SIZE */
-{
- ChunkLink *current = magazine_chain_prepare_fields (magazine_chunks);
- ChunkLink *next, *prev;
- g_mutex_lock (allocator->magazine_mutex);
- /* add magazine at head */
- next = allocator->magazines[ix];
- if (next)
- prev = magazine_chain_prev (next);
- else
- next = prev = current;
- magazine_chain_next (prev) = current;
- magazine_chain_prev (next) = current;
- magazine_chain_prev (current) = prev;
- magazine_chain_next (current) = next;
- magazine_chain_count (current) = (gpointer) count;
- /* stamp magazine */
- magazine_cache_update_stamp();
- magazine_chain_stamp (current) = GUINT_TO_POINTER (allocator->last_stamp);
- allocator->magazines[ix] = current;
- /* free old magazines beyond a certain threshold */
- magazine_cache_trim (allocator, ix, allocator->last_stamp);
- /* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */
-}
-
-static ChunkLink*
-magazine_cache_pop_magazine (guint ix,
- gsize *countp)
-{
- g_mutex_lock_a (allocator->magazine_mutex, &allocator->contention_counters[ix]);
- if (!allocator->magazines[ix])
- {
- guint magazine_threshold = allocator_get_magazine_threshold (allocator, ix);
- gsize i, chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
- ChunkLink *chunk, *head;
- g_mutex_unlock (allocator->magazine_mutex);
- g_mutex_lock (allocator->slab_mutex);
- head = slab_allocator_alloc_chunk (chunk_size);
- head->data = NULL;
- chunk = head;
- for (i = 1; i < magazine_threshold; i++)
- {
- chunk->next = slab_allocator_alloc_chunk (chunk_size);
- chunk = chunk->next;
- chunk->data = NULL;
- }
- chunk->next = NULL;
- g_mutex_unlock (allocator->slab_mutex);
- *countp = i;
- return head;
- }
- else
- {
- ChunkLink *current = allocator->magazines[ix];
- ChunkLink *prev = magazine_chain_prev (current);
- ChunkLink *next = magazine_chain_next (current);
- /* unlink */
- magazine_chain_next (prev) = next;
- magazine_chain_prev (next) = prev;
- allocator->magazines[ix] = next == current ? NULL : next;
- g_mutex_unlock (allocator->magazine_mutex);
- /* clear special fields and hand out */
- *countp = (gsize) magazine_chain_count (current);
- magazine_chain_prev (current) = NULL;
- magazine_chain_next (current) = NULL;
- magazine_chain_count (current) = NULL;
- magazine_chain_stamp (current) = NULL;
- return current;
- }
-}
-
-/* --- thread magazines --- */
-static void
-private_thread_memory_cleanup (gpointer data)
-{
- ThreadMemory *tmem = data;
- const guint n_magazines = MAX_SLAB_INDEX (allocator);
- guint ix;
- for (ix = 0; ix < n_magazines; ix++)
- {
- Magazine *mags[2];
- guint j;
- mags[0] = &tmem->magazine1[ix];
- mags[1] = &tmem->magazine2[ix];
- for (j = 0; j < 2; j++)
- {
- Magazine *mag = mags[j];
- if (mag->count >= MIN_MAGAZINE_SIZE)
- magazine_cache_push_magazine (ix, mag->chunks, mag->count);
- else
- {
- const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
- g_mutex_lock (allocator->slab_mutex);
- while (mag->chunks)
- {
- ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
- slab_allocator_free_chunk (chunk_size, chunk);
- }
- g_mutex_unlock (allocator->slab_mutex);
- }
- }
- }
- g_free (tmem);
-}
-
-static void
-thread_memory_magazine1_reload (ThreadMemory *tmem,
- guint ix)
-{
- Magazine *mag = &tmem->magazine1[ix];
- mem_assert (mag->chunks == NULL); /* ensure that we may reset mag->count */
- mag->count = 0;
- mag->chunks = magazine_cache_pop_magazine (ix, &mag->count);
-}
-
-static void
-thread_memory_magazine2_unload (ThreadMemory *tmem,
- guint ix)
-{
- Magazine *mag = &tmem->magazine2[ix];
- magazine_cache_push_magazine (ix, mag->chunks, mag->count);
- mag->chunks = NULL;
- mag->count = 0;
-}
-
-static inline void
-thread_memory_swap_magazines (ThreadMemory *tmem,
- guint ix)
-{
- Magazine xmag = tmem->magazine1[ix];
- tmem->magazine1[ix] = tmem->magazine2[ix];
- tmem->magazine2[ix] = xmag;
-}
-
-static inline gboolean
-thread_memory_magazine1_is_empty (ThreadMemory *tmem,
- guint ix)
-{
- return tmem->magazine1[ix].chunks == NULL;
-}
-
-static inline gboolean
-thread_memory_magazine2_is_full (ThreadMemory *tmem,
- guint ix)
-{
- return tmem->magazine2[ix].count >= allocator_get_magazine_threshold (allocator, ix);
-}
-
-static inline gpointer
-thread_memory_magazine1_alloc (ThreadMemory *tmem,
- guint ix)
-{
- Magazine *mag = &tmem->magazine1[ix];
- ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
- if (G_LIKELY (mag->count > 0))
- mag->count--;
- return chunk;
-}
-
-static inline void
-thread_memory_magazine2_free (ThreadMemory *tmem,
- guint ix,
- gpointer mem)
-{
- Magazine *mag = &tmem->magazine2[ix];
- ChunkLink *chunk = mem;
- chunk->data = NULL;
- chunk->next = mag->chunks;
- mag->chunks = chunk;
- mag->count++;
-}
-
-/* --- API functions --- */
-gpointer
-g_slice_alloc (gsize mem_size)
-{
- gsize chunk_size;
- gpointer mem;
- guint acat;
- chunk_size = P2ALIGN (mem_size);
- acat = allocator_categorize (chunk_size);
- if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
- {
- ThreadMemory *tmem = thread_memory_from_self();
- guint ix = SLAB_INDEX (allocator, chunk_size);
- if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
- {
- thread_memory_swap_magazines (tmem, ix);
- if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
- thread_memory_magazine1_reload (tmem, ix);
- }
- mem = thread_memory_magazine1_alloc (tmem, ix);
- }
- else if (acat == 2) /* allocate through slab allocator */
- {
- g_mutex_lock (allocator->slab_mutex);
- mem = slab_allocator_alloc_chunk (chunk_size);
- g_mutex_unlock (allocator->slab_mutex);
- }
- else /* delegate to system malloc */
- mem = g_malloc (mem_size);
- if (G_UNLIKELY (allocator->config.debug_blocks))
- smc_notify_alloc (mem, mem_size);
-
- TRACE (GLIB_SLICE_ALLOC((void*)mem, mem_size));
-
- return mem;
-}
-
-gpointer
-g_slice_alloc0 (gsize mem_size)
-{
- gpointer mem = g_slice_alloc (mem_size);
- if (mem)
- memset (mem, 0, mem_size);
- return mem;
-}
-
-gpointer
-g_slice_copy (gsize mem_size,
- gconstpointer mem_block)
-{
- gpointer mem = g_slice_alloc (mem_size);
- if (mem)
- memcpy (mem, mem_block, mem_size);
- return mem;
-}
-
-void
-g_slice_free1 (gsize mem_size,
- gpointer mem_block)
-{
- gsize chunk_size = P2ALIGN (mem_size);
- guint acat = allocator_categorize (chunk_size);
- if (G_UNLIKELY (!mem_block))
- return;
- if (G_UNLIKELY (allocator->config.debug_blocks) &&
- !smc_notify_free (mem_block, mem_size))
- abort();
- if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
- {
- ThreadMemory *tmem = thread_memory_from_self();
- guint ix = SLAB_INDEX (allocator, chunk_size);
- if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
- {
- thread_memory_swap_magazines (tmem, ix);
- if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
- thread_memory_magazine2_unload (tmem, ix);
- }
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (mem_block, 0, chunk_size);
- thread_memory_magazine2_free (tmem, ix, mem_block);
- }
- else if (acat == 2) /* allocate through slab allocator */
- {
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (mem_block, 0, chunk_size);
- g_mutex_lock (allocator->slab_mutex);
- slab_allocator_free_chunk (chunk_size, mem_block);
- g_mutex_unlock (allocator->slab_mutex);
- }
- else /* delegate to system malloc */
- {
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (mem_block, 0, mem_size);
- g_free (mem_block);
- }
- TRACE (GLIB_SLICE_FREE((void*)mem_block, mem_size));
-}
-
-void
-g_slice_free_chain_with_offset (gsize mem_size,
- gpointer mem_chain,
- gsize next_offset)
-{
- gpointer slice = mem_chain;
- /* while the thread magazines and the magazine cache are implemented so that
- * they can easily be extended to allow for free lists containing more free
- * lists for the first level nodes, which would allow O(1) freeing in this
- * function, the benefit of such an extension is questionable, because:
- * - the magazine size counts will become mere lower bounds which confuses
- * the code adapting to lock contention;
- * - freeing a single node to the thread magazines is very fast, so this
- * O(list_length) operation is multiplied by a fairly small factor;
- * - memory usage histograms on larger applications seem to indicate that
- * the amount of released multi node lists is negligible in comparison
- * to single node releases.
- * - the major performance bottle neck, namely g_private_get() or
- * g_mutex_lock()/g_mutex_unlock() has already been moved out of the
- * inner loop for freeing chained slices.
- */
- gsize chunk_size = P2ALIGN (mem_size);
- guint acat = allocator_categorize (chunk_size);
- if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
- {
- ThreadMemory *tmem = thread_memory_from_self();
- guint ix = SLAB_INDEX (allocator, chunk_size);
- while (slice)
- {
- guint8 *current = slice;
- slice = *(gpointer*) (current + next_offset);
- if (G_UNLIKELY (allocator->config.debug_blocks) &&
- !smc_notify_free (current, mem_size))
- abort();
- if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
- {
- thread_memory_swap_magazines (tmem, ix);
- if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
- thread_memory_magazine2_unload (tmem, ix);
- }
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (current, 0, chunk_size);
- thread_memory_magazine2_free (tmem, ix, current);
- }
- }
- else if (acat == 2) /* allocate through slab allocator */
- {
- g_mutex_lock (allocator->slab_mutex);
- while (slice)
- {
- guint8 *current = slice;
- slice = *(gpointer*) (current + next_offset);
- if (G_UNLIKELY (allocator->config.debug_blocks) &&
- !smc_notify_free (current, mem_size))
- abort();
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (current, 0, chunk_size);
- slab_allocator_free_chunk (chunk_size, current);
- }
- g_mutex_unlock (allocator->slab_mutex);
- }
- else /* delegate to system malloc */
- while (slice)
- {
- guint8 *current = slice;
- slice = *(gpointer*) (current + next_offset);
- if (G_UNLIKELY (allocator->config.debug_blocks) &&
- !smc_notify_free (current, mem_size))
- abort();
- if (G_UNLIKELY (g_mem_gc_friendly))
- memset (current, 0, mem_size);
- g_free (current);
- }
-}
-
-/* --- single page allocator --- */
-static void
-allocator_slab_stack_push (Allocator *allocator,
- guint ix,
- SlabInfo *sinfo)
-{
- /* insert slab at slab ring head */
- if (!allocator->slab_stack[ix])
- {
- sinfo->next = sinfo;
- sinfo->prev = sinfo;
- }
- else
- {
- SlabInfo *next = allocator->slab_stack[ix], *prev = next->prev;
- next->prev = sinfo;
- prev->next = sinfo;
- sinfo->next = next;
- sinfo->prev = prev;
- }
- allocator->slab_stack[ix] = sinfo;
-}
-
-static gsize
-allocator_aligned_page_size (Allocator *allocator,
- gsize n_bytes)
-{
- gsize val = 1 << g_bit_storage (n_bytes - 1);
- val = MAX (val, allocator->min_page_size);
- return val;
-}
-
-static void
-allocator_add_slab (Allocator *allocator,
- guint ix,
- gsize chunk_size)
-{
- ChunkLink *chunk;
- SlabInfo *sinfo;
- gsize addr, padding, n_chunks, color = 0;
- gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator, chunk_size));
- /* allocate 1 page for the chunks and the slab */
- gpointer aligned_memory = allocator_memalign (page_size, page_size - NATIVE_MALLOC_PADDING);
- guint8 *mem = aligned_memory;
- guint i;
- if (!mem)
- {
- const gchar *syserr = "unknown error";
-#if HAVE_STRERROR
- syserr = strerror (errno);
-#endif
- mem_error ("failed to allocate %u bytes (alignment: %u): %s\n",
- (guint) (page_size - NATIVE_MALLOC_PADDING), (guint) page_size, syserr);
- }
- /* mask page adress */
- addr = ((gsize) mem / page_size) * page_size;
- /* assert alignment */
- mem_assert (aligned_memory == (gpointer) addr);
- /* basic slab info setup */
- sinfo = (SlabInfo*) (mem + page_size - SLAB_INFO_SIZE);
- sinfo->n_allocated = 0;
- sinfo->chunks = NULL;
- /* figure cache colorization */
- n_chunks = ((guint8*) sinfo - mem) / chunk_size;
- padding = ((guint8*) sinfo - mem) - n_chunks * chunk_size;
- if (padding)
- {
- color = (allocator->color_accu * P2ALIGNMENT) % padding;
- allocator->color_accu += allocator->config.color_increment;
- }
- /* add chunks to free list */
- chunk = (ChunkLink*) (mem + color);
- sinfo->chunks = chunk;
- for (i = 0; i < n_chunks - 1; i++)
- {
- chunk->next = (ChunkLink*) ((guint8*) chunk + chunk_size);
- chunk = chunk->next;
- }
- chunk->next = NULL; /* last chunk */
- /* add slab to slab ring */
- allocator_slab_stack_push (allocator, ix, sinfo);
-}
-
-static gpointer
-slab_allocator_alloc_chunk (gsize chunk_size)
-{
- ChunkLink *chunk;
- guint ix = SLAB_INDEX (allocator, chunk_size);
- /* ensure non-empty slab */
- if (!allocator->slab_stack[ix] || !allocator->slab_stack[ix]->chunks)
- allocator_add_slab (allocator, ix, chunk_size);
- /* allocate chunk */
- chunk = allocator->slab_stack[ix]->chunks;
- allocator->slab_stack[ix]->chunks = chunk->next;
- allocator->slab_stack[ix]->n_allocated++;
- /* rotate empty slabs */
- if (!allocator->slab_stack[ix]->chunks)
- allocator->slab_stack[ix] = allocator->slab_stack[ix]->next;
- return chunk;
-}
-
-static void
-slab_allocator_free_chunk (gsize chunk_size,
- gpointer mem)
-{
- ChunkLink *chunk;
- gboolean was_empty;
- guint ix = SLAB_INDEX (allocator, chunk_size);
- gsize page_size = allocator_aligned_page_size (allocator, SLAB_BPAGE_SIZE (allocator, chunk_size));
- gsize addr = ((gsize) mem / page_size) * page_size;
- /* mask page adress */
- guint8 *page = (guint8*) addr;
- SlabInfo *sinfo = (SlabInfo*) (page + page_size - SLAB_INFO_SIZE);
- /* assert valid chunk count */
- mem_assert (sinfo->n_allocated > 0);
- /* add chunk to free list */
- was_empty = sinfo->chunks == NULL;
- chunk = (ChunkLink*) mem;
- chunk->next = sinfo->chunks;
- sinfo->chunks = chunk;
- sinfo->n_allocated--;
- /* keep slab ring partially sorted, empty slabs at end */
- if (was_empty)
- {
- /* unlink slab */
- SlabInfo *next = sinfo->next, *prev = sinfo->prev;
- next->prev = prev;
- prev->next = next;
- if (allocator->slab_stack[ix] == sinfo)
- allocator->slab_stack[ix] = next == sinfo ? NULL : next;
- /* insert slab at head */
- allocator_slab_stack_push (allocator, ix, sinfo);
- }
- /* eagerly free complete unused slabs */
- if (!sinfo->n_allocated)
- {
- /* unlink slab */
- SlabInfo *next = sinfo->next, *prev = sinfo->prev;
- next->prev = prev;
- prev->next = next;
- if (allocator->slab_stack[ix] == sinfo)
- allocator->slab_stack[ix] = next == sinfo ? NULL : next;
- /* free slab */
- allocator_memfree (page_size, page);
- }
-}
-
-/* --- memalign implementation --- */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h> /* memalign() */
-#endif
-
-/* from config.h:
- * define HAVE_POSIX_MEMALIGN 1 // if free(posix_memalign(3)) works, <stdlib.h>
- * define HAVE_COMPLIANT_POSIX_MEMALIGN 1 // if free(posix_memalign(3)) works for sizes != 2^n, <stdlib.h>
- * define HAVE_MEMALIGN 1 // if free(memalign(3)) works, <malloc.h>
- * define HAVE_VALLOC 1 // if free(valloc(3)) works, <stdlib.h> or <malloc.h>
- * if none is provided, we implement malloc(3)-based alloc-only page alignment
- */
-
-#if !(HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC)
-static GTrashStack *compat_valloc_trash = NULL;
-#endif
-
-static gpointer
-allocator_memalign (gsize alignment,
- gsize memsize)
-{
- gpointer aligned_memory = NULL;
- gint err = ENOMEM;
-#if HAVE_COMPLIANT_POSIX_MEMALIGN
- err = posix_memalign (&aligned_memory, alignment, memsize);
-#elif HAVE_MEMALIGN
- errno = 0;
- aligned_memory = memalign (alignment, memsize);
- err = errno;
-#elif HAVE_VALLOC
- errno = 0;
- aligned_memory = valloc (memsize);
- err = errno;
-#else
- /* simplistic non-freeing page allocator */
- mem_assert (alignment == sys_page_size);
- mem_assert (memsize <= sys_page_size);
- if (!compat_valloc_trash)
- {
- const guint n_pages = 16;
- guint8 *mem = malloc (n_pages * sys_page_size);
- err = errno;
- if (mem)
- {
- gint i = n_pages;
- guint8 *amem = (guint8*) ALIGN ((gsize) mem, sys_page_size);
- if (amem != mem)
- i--; /* mem wasn't page aligned */
- while (--i >= 0)
- g_trash_stack_push (&compat_valloc_trash, amem + i * sys_page_size);
- }
- }
- aligned_memory = g_trash_stack_pop (&compat_valloc_trash);
-#endif
- if (!aligned_memory)
- errno = err;
- return aligned_memory;
-}
-
-static void
-allocator_memfree (gsize memsize,
- gpointer mem)
-{
-#if HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC
- free (mem);
-#else
- mem_assert (memsize <= sys_page_size);
- g_trash_stack_push (&compat_valloc_trash, mem);
-#endif
-}
-
-static void
-mem_error (const char *format,
- ...)
-{
- const char *pname;
- va_list args;
- /* at least, put out "MEMORY-ERROR", in case we segfault during the rest of the function */
- fputs ("\n***MEMORY-ERROR***: ", stderr);
- pname = g_get_prgname();
- fprintf (stderr, "%s[%ld]: GSlice: ", pname ? pname : "", (long)getpid());
- va_start (args, format);
- vfprintf (stderr, format, args);
- va_end (args);
- fputs ("\n", stderr);
- abort();
- _exit (1);
-}
-
-/* --- g-slice memory checker tree --- */
-typedef size_t SmcKType; /* key type */
-typedef size_t SmcVType; /* value type */
-typedef struct {
- SmcKType key;
- SmcVType value;
-} SmcEntry;
-static void smc_tree_insert (SmcKType key,
- SmcVType value);
-static gboolean smc_tree_lookup (SmcKType key,
- SmcVType *value_p);
-static gboolean smc_tree_remove (SmcKType key);
-
-
-/* --- g-slice memory checker implementation --- */
-static void
-smc_notify_alloc (void *pointer,
- size_t size)
-{
- size_t adress = (size_t) pointer;
- if (pointer)
- smc_tree_insert (adress, size);
-}
-
-#if 0
-static void
-smc_notify_ignore (void *pointer)
-{
- size_t adress = (size_t) pointer;
- if (pointer)
- smc_tree_remove (adress);
-}
-#endif
-
-static int
-smc_notify_free (void *pointer,
- size_t size)
-{
- size_t adress = (size_t) pointer;
- SmcVType real_size;
- gboolean found_one;
-
- if (!pointer)
- return 1; /* ignore */
- found_one = smc_tree_lookup (adress, &real_size);
- if (!found_one)
- {
- fprintf (stderr, "GSlice: MemChecker: attempt to release non-allocated block: %p size=%" G_GSIZE_FORMAT "\n", pointer, size);
- return 0;
- }
- if (real_size != size && (real_size || size))
- {
- fprintf (stderr, "GSlice: MemChecker: attempt to release block with invalid size: %p size=%" G_GSIZE_FORMAT " invalid-size=%" G_GSIZE_FORMAT "\n", pointer, real_size, size);
- return 0;
- }
- if (!smc_tree_remove (adress))
- {
- fprintf (stderr, "GSlice: MemChecker: attempt to release non-allocated block: %p size=%" G_GSIZE_FORMAT "\n", pointer, size);
- return 0;
- }
- return 1; /* all fine */
-}
-
-/* --- g-slice memory checker tree implementation --- */
-#define SMC_TRUNK_COUNT (4093 /* 16381 */) /* prime, to distribute trunk collisions (big, allocated just once) */
-#define SMC_BRANCH_COUNT (511) /* prime, to distribute branch collisions */
-#define SMC_TRUNK_EXTENT (SMC_BRANCH_COUNT * 2039) /* key adress space per trunk, should distribute uniformly across BRANCH_COUNT */
-#define SMC_TRUNK_HASH(k) ((k / SMC_TRUNK_EXTENT) % SMC_TRUNK_COUNT) /* generate new trunk hash per megabyte (roughly) */
-#define SMC_BRANCH_HASH(k) (k % SMC_BRANCH_COUNT)
-
-typedef struct {
- SmcEntry *entries;
- unsigned int n_entries;
-} SmcBranch;
-
-static SmcBranch **smc_tree_root = NULL;
-
-static void
-smc_tree_abort (int errval)
-{
- const char *syserr = "unknown error";
-#if HAVE_STRERROR
- syserr = strerror (errval);
-#endif
- mem_error ("MemChecker: failure in debugging tree: %s", syserr);
-}
-
-static inline SmcEntry*
-smc_tree_branch_grow_L (SmcBranch *branch,
- unsigned int index)
-{
- unsigned int old_size = branch->n_entries * sizeof (branch->entries[0]);
- unsigned int new_size = old_size + sizeof (branch->entries[0]);
- SmcEntry *entry;
- mem_assert (index <= branch->n_entries);
- branch->entries = (SmcEntry*) realloc (branch->entries, new_size);
- if (!branch->entries)
- smc_tree_abort (errno);
- entry = branch->entries + index;
- g_memmove (entry + 1, entry, (branch->n_entries - index) * sizeof (entry[0]));
- branch->n_entries += 1;
- return entry;
-}
-
-static inline SmcEntry*
-smc_tree_branch_lookup_nearest_L (SmcBranch *branch,
- SmcKType key)
-{
- unsigned int n_nodes = branch->n_entries, offs = 0;
- SmcEntry *check = branch->entries;
- int cmp = 0;
- while (offs < n_nodes)
- {
- unsigned int i = (offs + n_nodes) >> 1;
- check = branch->entries + i;
- cmp = key < check->key ? -1 : key != check->key;
- if (cmp == 0)
- return check; /* return exact match */
- else if (cmp < 0)
- n_nodes = i;
- else /* (cmp > 0) */
- offs = i + 1;
- }
- /* check points at last mismatch, cmp > 0 indicates greater key */
- return cmp > 0 ? check + 1 : check; /* return insertion position for inexact match */
-}
-
-static void
-smc_tree_insert (SmcKType key,
- SmcVType value)
-{
- unsigned int ix0, ix1;
- SmcEntry *entry;
-
- g_mutex_lock (smc_tree_mutex);
- ix0 = SMC_TRUNK_HASH (key);
- ix1 = SMC_BRANCH_HASH (key);
- if (!smc_tree_root)
- {
- smc_tree_root = calloc (SMC_TRUNK_COUNT, sizeof (smc_tree_root[0]));
- if (!smc_tree_root)
- smc_tree_abort (errno);
- }
- if (!smc_tree_root[ix0])
- {
- smc_tree_root[ix0] = calloc (SMC_BRANCH_COUNT, sizeof (smc_tree_root[0][0]));
- if (!smc_tree_root[ix0])
- smc_tree_abort (errno);
- }
- entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
- if (!entry || /* need create */
- entry >= smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries || /* need append */
- entry->key != key) /* need insert */
- entry = smc_tree_branch_grow_L (&smc_tree_root[ix0][ix1], entry - smc_tree_root[ix0][ix1].entries);
- entry->key = key;
- entry->value = value;
- g_mutex_unlock (smc_tree_mutex);
-}
-
-static gboolean
-smc_tree_lookup (SmcKType key,
- SmcVType *value_p)
-{
- SmcEntry *entry = NULL;
- unsigned int ix0 = SMC_TRUNK_HASH (key), ix1 = SMC_BRANCH_HASH (key);
- gboolean found_one = FALSE;
- *value_p = 0;
- g_mutex_lock (smc_tree_mutex);
- if (smc_tree_root && smc_tree_root[ix0])
- {
- entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
- if (entry &&
- entry < smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries &&
- entry->key == key)
- {
- found_one = TRUE;
- *value_p = entry->value;
- }
- }
- g_mutex_unlock (smc_tree_mutex);
- return found_one;
-}
-
-static gboolean
-smc_tree_remove (SmcKType key)
-{
- unsigned int ix0 = SMC_TRUNK_HASH (key), ix1 = SMC_BRANCH_HASH (key);
- gboolean found_one = FALSE;
- g_mutex_lock (smc_tree_mutex);
- if (smc_tree_root && smc_tree_root[ix0])
- {
- SmcEntry *entry = smc_tree_branch_lookup_nearest_L (&smc_tree_root[ix0][ix1], key);
- if (entry &&
- entry < smc_tree_root[ix0][ix1].entries + smc_tree_root[ix0][ix1].n_entries &&
- entry->key == key)
- {
- unsigned int i = entry - smc_tree_root[ix0][ix1].entries;
- smc_tree_root[ix0][ix1].n_entries -= 1;
- g_memmove (entry, entry + 1, (smc_tree_root[ix0][ix1].n_entries - i) * sizeof (entry[0]));
- if (!smc_tree_root[ix0][ix1].n_entries)
- {
- /* avoid useless pressure on the memory system */
- free (smc_tree_root[ix0][ix1].entries);
- smc_tree_root[ix0][ix1].entries = NULL;
- }
- found_one = TRUE;
- }
- }
- g_mutex_unlock (smc_tree_mutex);
- return found_one;
-}
-
-#ifdef G_ENABLE_DEBUG
-void
-g_slice_debug_tree_statistics (void)
-{
- g_mutex_lock (smc_tree_mutex);
- if (smc_tree_root)
- {
- unsigned int i, j, t = 0, o = 0, b = 0, su = 0, ex = 0, en = 4294967295u;
- double tf, bf;
- for (i = 0; i < SMC_TRUNK_COUNT; i++)
- if (smc_tree_root[i])
- {
- t++;
- for (j = 0; j < SMC_BRANCH_COUNT; j++)
- if (smc_tree_root[i][j].n_entries)
- {
- b++;
- su += smc_tree_root[i][j].n_entries;
- en = MIN (en, smc_tree_root[i][j].n_entries);
- ex = MAX (ex, smc_tree_root[i][j].n_entries);
- }
- else if (smc_tree_root[i][j].entries)
- o++; /* formerly used, now empty */
- }
- en = b ? en : 0;
- tf = MAX (t, 1.0); /* max(1) to be a valid divisor */
- bf = MAX (b, 1.0); /* max(1) to be a valid divisor */
- fprintf (stderr, "GSlice: MemChecker: %u trunks, %u branches, %u old branches\n", t, b, o);
- fprintf (stderr, "GSlice: MemChecker: %f branches per trunk, %.2f%% utilization\n",
- b / tf,
- 100.0 - (SMC_BRANCH_COUNT - b / tf) / (0.01 * SMC_BRANCH_COUNT));
- fprintf (stderr, "GSlice: MemChecker: %f entries per branch, %u minimum, %u maximum\n",
- su / bf, en, ex);
- }
- else
- fprintf (stderr, "GSlice: MemChecker: root=NULL\n");
- g_mutex_unlock (smc_tree_mutex);
-
- /* sample statistics (beast + GSLice + 24h scripted core & GUI activity):
- * PID %CPU %MEM VSZ RSS COMMAND
- * 8887 30.3 45.8 456068 414856 beast-0.7.1 empty.bse
- * $ cat /proc/8887/statm # total-program-size resident-set-size shared-pages text/code data/stack library dirty-pages
- * 114017 103714 2354 344 0 108676 0
- * $ cat /proc/8887/status
- * Name: beast-0.7.1
- * VmSize: 456068 kB
- * VmLck: 0 kB
- * VmRSS: 414856 kB
- * VmData: 434620 kB
- * VmStk: 84 kB
- * VmExe: 1376 kB
- * VmLib: 13036 kB
- * VmPTE: 456 kB
- * Threads: 3
- * (gdb) print g_slice_debug_tree_statistics ()
- * GSlice: MemChecker: 422 trunks, 213068 branches, 0 old branches
- * GSlice: MemChecker: 504.900474 branches per trunk, 98.81% utilization
- * GSlice: MemChecker: 4.965039 entries per branch, 1 minimum, 37 maximum
- */
-}
-#endif /* G_ENABLE_DEBUG */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gslist.h"
-#include "gtestutils.h"
-
-/**
- * SECTION: linked_lists_single
- * @title: Singly-Linked Lists
- * @short_description: linked lists containing integer values or
- * pointers to data, limited to iterating over the
- * list in one direction
- *
- * The #GSList structure and its associated functions provide a
- * standard singly-linked list data structure.
- *
- * Each element in the list contains a piece of data, together with a
- * pointer which links to the next element in the list. Using this
- * pointer it is possible to move through the list in one direction
- * only (unlike the <link
- * linkend="glib-Doubly-Linked-lists">Doubly-Linked Lists</link> which
- * allow movement in both directions).
- *
- * The data contained in each element can be either integer values, by
- * using one of the <link linkend="glib-Type-Conversion-Macros">Type
- * Conversion Macros</link>, or simply pointers to any type of data.
- *
- * List elements are allocated from the <link
- * linkend="glib-Memory-Slices">slice allocator</link>, which is more
- * efficient than allocating elements individually.
- *
- * Note that most of the #GSList functions expect to be passed a
- * pointer to the first element in the list. The functions which insert
- * elements return the new start of the list, which may have changed.
- *
- * There is no function to create a #GSList. %NULL is considered to be
- * the empty list so you simply set a #GSList* to %NULL.
- *
- * To add elements, use g_slist_append(), g_slist_prepend(),
- * g_slist_insert() and g_slist_insert_sorted().
- *
- * To remove elements, use g_slist_remove().
- *
- * To find elements in the list use g_slist_last(), g_slist_next(),
- * g_slist_nth(), g_slist_nth_data(), g_slist_find() and
- * g_slist_find_custom().
- *
- * To find the index of an element use g_slist_position() and
- * g_slist_index().
- *
- * To call a function for each element in the list use
- * g_slist_foreach().
- *
- * To free the entire list, use g_slist_free().
- **/
-
-/**
- * GSList:
- * @data: holds the element's data, which can be a pointer to any kind
- * of data, or any integer value using the <link
- * linkend="glib-Type-Conversion-Macros">Type Conversion
- * Macros</link>.
- * @next: contains the link to the next element in the list.
- *
- * The #GSList struct is used for each element in the singly-linked
- * list.
- **/
-
-/**
- * g_slist_next:
- * @slist: an element in a #GSList.
- * @Returns: the next element, or %NULL if there are no more elements.
- *
- * A convenience macro to get the next element in a #GSList.
- **/
-
-
-/**
- * g_slist_push_allocator:
- * @dummy: the #GAllocator to use when allocating #GSList elements.
- *
- * Sets the allocator to use to allocate #GSList elements. Use
- * g_slist_pop_allocator() to restore the previous allocator.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated: 2.10: It does nothing, since #GSList has been converted
- * to the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_slist_push_allocator (gpointer dummy) { /* present for binary compat only */ }
-
-/**
- * g_slist_pop_allocator:
- *
- * Restores the previous #GAllocator, used when allocating #GSList
- * elements.
- *
- * Note that this function is not available if GLib has been compiled
- * with <option>--disable-mem-pools</option>
- *
- * Deprecated: 2.10: It does nothing, since #GSList has been converted
- * to the <link linkend="glib-Memory-Slices">slice
- * allocator</link>
- **/
-void g_slist_pop_allocator (void) { /* present for binary compat only */ }
-
-#define _g_slist_alloc0() g_slice_new0 (GSList)
-#define _g_slist_alloc() g_slice_new (GSList)
-#define _g_slist_free1(slist) g_slice_free (GSList, slist)
-
-/**
- * g_slist_alloc:
- * @Returns: a pointer to the newly-allocated #GSList element.
- *
- * Allocates space for one #GSList element. It is called by the
- * g_slist_append(), g_slist_prepend(), g_slist_insert() and
- * g_slist_insert_sorted() functions and so is rarely used on its own.
- **/
-GSList*
-g_slist_alloc (void)
-{
- return _g_slist_alloc0 ();
-}
-
-/**
- * g_slist_free:
- * @list: a #GSList
- *
- * Frees all of the memory used by a #GSList.
- * The freed elements are returned to the slice allocator.
- */
-void
-g_slist_free (GSList *list)
-{
- g_slice_free_chain (GSList, list, next);
-}
-
-/**
- * g_slist_free_1:
- * @list: a #GSList element
- *
- * Frees one #GSList element.
- * It is usually used after g_slist_remove_link().
- */
-/**
- * g_slist_free1:
- *
- * A macro which does the same as g_slist_free_1().
- *
- * Since: 2.10
- **/
-void
-g_slist_free_1 (GSList *list)
-{
- _g_slist_free1 (list);
-}
-
-/**
- * g_slist_append:
- * @list: a #GSList
- * @data: the data for the new element
- *
- * Adds a new element on to the end of the list.
- *
- * <note><para>
- * The return value is the new start of the list, which may
- * have changed, so make sure you store the new value.
- * </para></note>
- *
- * <note><para>
- * Note that g_slist_append() has to traverse the entire list
- * to find the end, which is inefficient when adding multiple
- * elements. A common idiom to avoid the inefficiency is to prepend
- * the elements and reverse the list when all elements have been added.
- * </para></note>
- *
- * |[
- * /* Notice that these are initialized to the empty list. */
- * GSList *list = NULL, *number_list = NULL;
- *
- * /* This is a list of strings. */
- * list = g_slist_append (list, "first");
- * list = g_slist_append (list, "second");
- *
- * /* This is a list of integers. */
- * number_list = g_slist_append (number_list, GINT_TO_POINTER (27));
- * number_list = g_slist_append (number_list, GINT_TO_POINTER (14));
- * ]|
- *
- * Returns: the new start of the #GSList
- */
-GSList*
-g_slist_append (GSList *list,
- gpointer data)
-{
- GSList *new_list;
- GSList *last;
-
- new_list = _g_slist_alloc ();
- new_list->data = data;
- new_list->next = NULL;
-
- if (list)
- {
- last = g_slist_last (list);
- /* g_assert (last != NULL); */
- last->next = new_list;
-
- return list;
- }
- else
- return new_list;
-}
-
-/**
- * g_slist_prepend:
- * @list: a #GSList
- * @data: the data for the new element
- *
- * Adds a new element on to the start of the list.
- *
- * <note><para>
- * The return value is the new start of the list, which
- * may have changed, so make sure you store the new value.
- * </para></note>
- *
- * |[
- * /* Notice that it is initialized to the empty list. */
- * GSList *list = NULL;
- * list = g_slist_prepend (list, "last");
- * list = g_slist_prepend (list, "first");
- * ]|
- *
- * Returns: the new start of the #GSList
- */
-GSList*
-g_slist_prepend (GSList *list,
- gpointer data)
-{
- GSList *new_list;
-
- new_list = _g_slist_alloc ();
- new_list->data = data;
- new_list->next = list;
-
- return new_list;
-}
-
-/**
- * g_slist_insert:
- * @list: a #GSList
- * @data: the data for the new element
- * @position: the position to insert the element.
- * If this is negative, or is larger than the number
- * of elements in the list, the new element is added on
- * to the end of the list.
- *
- * Inserts a new element into the list at the given position.
- *
- * Returns: the new start of the #GSList
- */
-GSList*
-g_slist_insert (GSList *list,
- gpointer data,
- gint position)
-{
- GSList *prev_list;
- GSList *tmp_list;
- GSList *new_list;
-
- if (position < 0)
- return g_slist_append (list, data);
- else if (position == 0)
- return g_slist_prepend (list, data);
-
- new_list = _g_slist_alloc ();
- new_list->data = data;
-
- if (!list)
- {
- new_list->next = NULL;
- return new_list;
- }
-
- prev_list = NULL;
- tmp_list = list;
-
- while ((position-- > 0) && tmp_list)
- {
- prev_list = tmp_list;
- tmp_list = tmp_list->next;
- }
-
- if (prev_list)
- {
- new_list->next = prev_list->next;
- prev_list->next = new_list;
- }
- else
- {
- new_list->next = list;
- list = new_list;
- }
-
- return list;
-}
-
-/**
- * g_slist_insert_before:
- * @slist: a #GSList
- * @sibling: node to insert @data before
- * @data: data to put in the newly-inserted node
- *
- * Inserts a node before @sibling containing @data.
- *
- * Returns: the new head of the list.
- */
-GSList*
-g_slist_insert_before (GSList *slist,
- GSList *sibling,
- gpointer data)
-{
- if (!slist)
- {
- slist = _g_slist_alloc ();
- slist->data = data;
- slist->next = NULL;
- g_return_val_if_fail (sibling == NULL, slist);
- return slist;
- }
- else
- {
- GSList *node, *last = NULL;
-
- for (node = slist; node; last = node, node = last->next)
- if (node == sibling)
- break;
- if (!last)
- {
- node = _g_slist_alloc ();
- node->data = data;
- node->next = slist;
-
- return node;
- }
- else
- {
- node = _g_slist_alloc ();
- node->data = data;
- node->next = last->next;
- last->next = node;
-
- return slist;
- }
- }
-}
-
-/**
- * g_slist_concat:
- * @list1: a #GSList
- * @list2: the #GSList to add to the end of the first #GSList
- *
- * Adds the second #GSList onto the end of the first #GSList.
- * Note that the elements of the second #GSList are not copied.
- * They are used directly.
- *
- * Returns: the start of the new #GSList
- */
-GSList *
-g_slist_concat (GSList *list1, GSList *list2)
-{
- if (list2)
- {
- if (list1)
- g_slist_last (list1)->next = list2;
- else
- list1 = list2;
- }
-
- return list1;
-}
-
-/**
- * g_slist_remove:
- * @list: a #GSList
- * @data: the data of the element to remove
- *
- * Removes an element from a #GSList.
- * If two elements contain the same data, only the first is removed.
- * If none of the elements contain the data, the #GSList is unchanged.
- *
- * Returns: the new start of the #GSList
- */
-GSList*
-g_slist_remove (GSList *list,
- gconstpointer data)
-{
- GSList *tmp, *prev = NULL;
-
- tmp = list;
- while (tmp)
- {
- if (tmp->data == data)
- {
- if (prev)
- prev->next = tmp->next;
- else
- list = tmp->next;
-
- g_slist_free_1 (tmp);
- break;
- }
- prev = tmp;
- tmp = prev->next;
- }
-
- return list;
-}
-
-/**
- * g_slist_remove_all:
- * @list: a #GSList
- * @data: data to remove
- *
- * Removes all list nodes with data equal to @data.
- * Returns the new head of the list. Contrast with
- * g_slist_remove() which removes only the first node
- * matching the given data.
- *
- * Returns: new head of @list
- */
-GSList*
-g_slist_remove_all (GSList *list,
- gconstpointer data)
-{
- GSList *tmp, *prev = NULL;
-
- tmp = list;
- while (tmp)
- {
- if (tmp->data == data)
- {
- GSList *next = tmp->next;
-
- if (prev)
- prev->next = next;
- else
- list = next;
-
- g_slist_free_1 (tmp);
- tmp = next;
- }
- else
- {
- prev = tmp;
- tmp = prev->next;
- }
- }
-
- return list;
-}
-
-static inline GSList*
-_g_slist_remove_link (GSList *list,
- GSList *link)
-{
- GSList *tmp;
- GSList *prev;
-
- prev = NULL;
- tmp = list;
-
- while (tmp)
- {
- if (tmp == link)
- {
- if (prev)
- prev->next = tmp->next;
- if (list == tmp)
- list = list->next;
-
- tmp->next = NULL;
- break;
- }
-
- prev = tmp;
- tmp = tmp->next;
- }
-
- return list;
-}
-
-/**
- * g_slist_remove_link:
- * @list: a #GSList
- * @link_: an element in the #GSList
- *
- * Removes an element from a #GSList, without
- * freeing the element. The removed element's next
- * link is set to %NULL, so that it becomes a
- * self-contained list with one element.
- *
- * Returns: the new start of the #GSList, without the element
- */
-GSList*
-g_slist_remove_link (GSList *list,
- GSList *link_)
-{
- return _g_slist_remove_link (list, link_);
-}
-
-/**
- * g_slist_delete_link:
- * @list: a #GSList
- * @link_: node to delete
- *
- * Removes the node link_ from the list and frees it.
- * Compare this to g_slist_remove_link() which removes the node
- * without freeing it.
- *
- * Returns: the new head of @list
- */
-GSList*
-g_slist_delete_link (GSList *list,
- GSList *link_)
-{
- list = _g_slist_remove_link (list, link_);
- _g_slist_free1 (link_);
-
- return list;
-}
-
-/**
- * g_slist_copy:
- * @list: a #GSList
- *
- * Copies a #GSList.
- *
- * <note><para>
- * Note that this is a "shallow" copy. If the list elements
- * consist of pointers to data, the pointers are copied but
- * the actual data isn't.
- * </para></note>
- *
- * Returns: a copy of @list
- */
-GSList*
-g_slist_copy (GSList *list)
-{
- GSList *new_list = NULL;
-
- if (list)
- {
- GSList *last;
-
- new_list = _g_slist_alloc ();
- new_list->data = list->data;
- last = new_list;
- list = list->next;
- while (list)
- {
- last->next = _g_slist_alloc ();
- last = last->next;
- last->data = list->data;
- list = list->next;
- }
- last->next = NULL;
- }
-
- return new_list;
-}
-
-/**
- * g_slist_reverse:
- * @list: a #GSList
- *
- * Reverses a #GSList.
- *
- * Returns: the start of the reversed #GSList
- */
-GSList*
-g_slist_reverse (GSList *list)
-{
- GSList *prev = NULL;
-
- while (list)
- {
- GSList *next = list->next;
-
- list->next = prev;
-
- prev = list;
- list = next;
- }
-
- return prev;
-}
-
-/**
- * g_slist_nth:
- * @list: a #GSList
- * @n: the position of the element, counting from 0
- *
- * Gets the element at the given position in a #GSList.
- *
- * Returns: the element, or %NULL if the position is off
- * the end of the #GSList
- */
-GSList*
-g_slist_nth (GSList *list,
- guint n)
-{
- while (n-- > 0 && list)
- list = list->next;
-
- return list;
-}
-
-/**
- * g_slist_nth_data:
- * @list: a #GSList
- * @n: the position of the element
- *
- * Gets the data of the element at the given position.
- *
- * Returns: the element's data, or %NULL if the position
- * is off the end of the #GSList
- */
-gpointer
-g_slist_nth_data (GSList *list,
- guint n)
-{
- while (n-- > 0 && list)
- list = list->next;
-
- return list ? list->data : NULL;
-}
-
-/**
- * g_slist_find:
- * @list: a #GSList
- * @data: the element data to find
- *
- * Finds the element in a #GSList which
- * contains the given data.
- *
- * Returns: the found #GSList element,
- * or %NULL if it is not found
- */
-GSList*
-g_slist_find (GSList *list,
- gconstpointer data)
-{
- while (list)
- {
- if (list->data == data)
- break;
- list = list->next;
- }
-
- return list;
-}
-
-
-/**
- * g_slist_find_custom:
- * @list: a #GSList
- * @data: user data passed to the function
- * @func: the function to call for each element.
- * It should return 0 when the desired element is found
- *
- * Finds an element in a #GSList, using a supplied function to
- * find the desired element. It iterates over the list, calling
- * the given function which should return 0 when the desired
- * element is found. The function takes two #gconstpointer arguments,
- * the #GSList element's data as the first argument and the
- * given user data.
- *
- * Returns: the found #GSList element, or %NULL if it is not found
- */
-GSList*
-g_slist_find_custom (GSList *list,
- gconstpointer data,
- GCompareFunc func)
-{
- g_return_val_if_fail (func != NULL, list);
-
- while (list)
- {
- if (! func (list->data, data))
- return list;
- list = list->next;
- }
-
- return NULL;
-}
-
-/**
- * g_slist_position:
- * @list: a #GSList
- * @llink: an element in the #GSList
- *
- * Gets the position of the given element
- * in the #GSList (starting from 0).
- *
- * Returns: the position of the element in the #GSList,
- * or -1 if the element is not found
- */
-gint
-g_slist_position (GSList *list,
- GSList *llink)
-{
- gint i;
-
- i = 0;
- while (list)
- {
- if (list == llink)
- return i;
- i++;
- list = list->next;
- }
-
- return -1;
-}
-
-/**
- * g_slist_index:
- * @list: a #GSList
- * @data: the data to find
- *
- * Gets the position of the element containing
- * the given data (starting from 0).
- *
- * Returns: the index of the element containing the data,
- * or -1 if the data is not found
- */
-gint
-g_slist_index (GSList *list,
- gconstpointer data)
-{
- gint i;
-
- i = 0;
- while (list)
- {
- if (list->data == data)
- return i;
- i++;
- list = list->next;
- }
-
- return -1;
-}
-
-/**
- * g_slist_last:
- * @list: a #GSList
- *
- * Gets the last element in a #GSList.
- *
- * <note><para>
- * This function iterates over the whole list.
- * </para></note>
- *
- * Returns: the last element in the #GSList,
- * or %NULL if the #GSList has no elements
- */
-GSList*
-g_slist_last (GSList *list)
-{
- if (list)
- {
- while (list->next)
- list = list->next;
- }
-
- return list;
-}
-
-/**
- * g_slist_length:
- * @list: a #GSList
- *
- * Gets the number of elements in a #GSList.
- *
- * <note><para>
- * This function iterates over the whole list to
- * count its elements.
- * </para></note>
- *
- * Returns: the number of elements in the #GSList
- */
-guint
-g_slist_length (GSList *list)
-{
- guint length;
-
- length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
-
- return length;
-}
-
-/**
- * g_slist_foreach:
- * @list: a #GSList
- * @func: the function to call with each element's data
- * @user_data: user data to pass to the function
- *
- * Calls a function for each element of a #GSList.
- */
-void
-g_slist_foreach (GSList *list,
- GFunc func,
- gpointer user_data)
-{
- while (list)
- {
- GSList *next = list->next;
- (*func) (list->data, user_data);
- list = next;
- }
-}
-
-static GSList*
-g_slist_insert_sorted_real (GSList *list,
- gpointer data,
- GFunc func,
- gpointer user_data)
-{
- GSList *tmp_list = list;
- GSList *prev_list = NULL;
- GSList *new_list;
- gint cmp;
-
- g_return_val_if_fail (func != NULL, list);
-
- if (!list)
- {
- new_list = _g_slist_alloc ();
- new_list->data = data;
- new_list->next = NULL;
- return new_list;
- }
-
- cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data);
-
- while ((tmp_list->next) && (cmp > 0))
- {
- prev_list = tmp_list;
- tmp_list = tmp_list->next;
-
- cmp = ((GCompareDataFunc) func) (data, tmp_list->data, user_data);
- }
-
- new_list = _g_slist_alloc ();
- new_list->data = data;
-
- if ((!tmp_list->next) && (cmp > 0))
- {
- tmp_list->next = new_list;
- new_list->next = NULL;
- return list;
- }
-
- if (prev_list)
- {
- prev_list->next = new_list;
- new_list->next = tmp_list;
- return list;
- }
- else
- {
- new_list->next = list;
- return new_list;
- }
-}
-
-/**
- * g_slist_insert_sorted:
- * @list: a #GSList
- * @data: the data for the new element
- * @func: the function to compare elements in the list.
- * It should return a number > 0 if the first parameter
- * comes after the second parameter in the sort order.
- *
- * Inserts a new element into the list, using the given
- * comparison function to determine its position.
- *
- * Returns: the new start of the #GSList
- */
-GSList*
-g_slist_insert_sorted (GSList *list,
- gpointer data,
- GCompareFunc func)
-{
- return g_slist_insert_sorted_real (list, data, (GFunc) func, NULL);
-}
-
-/**
- * g_slist_insert_sorted_with_data:
- * @list: a #GSList
- * @data: the data for the new element
- * @func: the function to compare elements in the list.
- * It should return a number > 0 if the first parameter
- * comes after the second parameter in the sort order.
- * @user_data: data to pass to comparison function
- *
- * Inserts a new element into the list, using the given
- * comparison function to determine its position.
- *
- * Returns: the new start of the #GSList
- *
- * Since: 2.10
- */
-GSList*
-g_slist_insert_sorted_with_data (GSList *list,
- gpointer data,
- GCompareDataFunc func,
- gpointer user_data)
-{
- return g_slist_insert_sorted_real (list, data, (GFunc) func, user_data);
-}
-
-static GSList *
-g_slist_sort_merge (GSList *l1,
- GSList *l2,
- GFunc compare_func,
- gpointer user_data)
-{
- GSList list, *l;
- gint cmp;
-
- l=&list;
-
- while (l1 && l2)
- {
- cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
-
- if (cmp <= 0)
- {
- l=l->next=l1;
- l1=l1->next;
- }
- else
- {
- l=l->next=l2;
- l2=l2->next;
- }
- }
- l->next= l1 ? l1 : l2;
-
- return list.next;
-}
-
-static GSList *
-g_slist_sort_real (GSList *list,
- GFunc compare_func,
- gpointer user_data)
-{
- GSList *l1, *l2;
-
- if (!list)
- return NULL;
- if (!list->next)
- return list;
-
- l1 = list;
- l2 = list->next;
-
- while ((l2 = l2->next) != NULL)
- {
- if ((l2 = l2->next) == NULL)
- break;
- l1=l1->next;
- }
- l2 = l1->next;
- l1->next = NULL;
-
- return g_slist_sort_merge (g_slist_sort_real (list, compare_func, user_data),
- g_slist_sort_real (l2, compare_func, user_data),
- compare_func,
- user_data);
-}
-
-/**
- * g_slist_sort:
- * @list: a #GSList
- * @compare_func: the comparison function used to sort the #GSList.
- * This function is passed the data from 2 elements of the #GSList
- * and should return 0 if they are equal, a negative value if the
- * first element comes before the second, or a positive value if
- * the first element comes after the second.
- *
- * Sorts a #GSList using the given comparison function.
- *
- * Returns: the start of the sorted #GSList
- */
-GSList *
-g_slist_sort (GSList *list,
- GCompareFunc compare_func)
-{
- return g_slist_sort_real (list, (GFunc) compare_func, NULL);
-}
-
-/**
- * g_slist_sort_with_data:
- * @list: a #GSList
- * @compare_func: comparison function
- * @user_data: data to pass to comparison function
- *
- * Like g_slist_sort(), but the sort function accepts a user data argument.
- *
- * Returns: new head of the list
- */
-GSList *
-g_slist_sort_with_data (GSList *list,
- GCompareDataFunc compare_func,
- gpointer user_data)
-{
- return g_slist_sort_real (list, (GFunc) compare_func, user_data);
-}
+++ /dev/null
-/* gspawn-win32-helper.c - Helper program for process launching on Win32.
- *
- * Copyright 2000 Red Hat, Inc.
- * Copyright 2000 Tor Lillqvist
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <fcntl.h>
-
-#undef G_LOG_DOMAIN
-#include "glib.h"
-#define GSPAWN_HELPER
-#include "gspawn-win32.c" /* For shared definitions */
-
-
-static void
-write_err_and_exit (gint fd,
- gintptr msg)
-{
- gintptr en = errno;
-
- write (fd, &msg, sizeof(gintptr));
- write (fd, &en, sizeof(gintptr));
-
- _exit (1);
-}
-
-#ifdef __GNUC__
-# ifndef _stdcall
-# define _stdcall __attribute__((stdcall))
-# endif
-#endif
-
-/* We build gspawn-win32-helper.exe as a Windows GUI application
- * to avoid any temporarily flashing console windows in case
- * the gspawn function is invoked by a GUI program. Thus, no main()
- * but a WinMain(). We do, however, still use argc and argv tucked
- * away in the global __argc and __argv by the C runtime startup code.
- */
-
-/* Info peeked from mingw runtime's source code. __wgetmainargs() is a
- * function to get the program's argv in wide char format.
- */
-
-typedef struct {
- int newmode;
-} _startupinfo;
-
-extern void __wgetmainargs(int *argc,
- wchar_t ***wargv,
- wchar_t ***wenviron,
- int expand_wildcards,
- _startupinfo *startupinfo);
-
-/* Copy of protect_argv that handles wchar_t strings */
-
-static gint
-protect_wargv (wchar_t **wargv,
- wchar_t ***new_wargv)
-{
- gint i;
- gint argc = 0;
-
- while (wargv[argc])
- ++argc;
- *new_wargv = g_new (wchar_t *, argc+1);
-
- /* Quote each argv element if necessary, so that it will get
- * reconstructed correctly in the C runtime startup code. Note that
- * the unquoting algorithm in the C runtime is really weird, and
- * rather different than what Unix shells do. See stdargv.c in the C
- * runtime sources (in the Platform SDK, in src/crt).
- *
- * Note that an new_wargv[0] constructed by this function should
- * *not* be passed as the filename argument to a _wspawn* or _wexec*
- * family function. That argument should be the real file name
- * without any quoting.
- */
- for (i = 0; i < argc; i++)
- {
- wchar_t *p = wargv[i];
- wchar_t *q;
- gint len = 0;
- gboolean need_dblquotes = FALSE;
- while (*p)
- {
- if (*p == ' ' || *p == '\t')
- need_dblquotes = TRUE;
- else if (*p == '"')
- len++;
- else if (*p == '\\')
- {
- wchar_t *pp = p;
- while (*pp && *pp == '\\')
- pp++;
- if (*pp == '"')
- len++;
- }
- len++;
- p++;
- }
-
- q = (*new_wargv)[i] = g_new (wchar_t, len + need_dblquotes*2 + 1);
- p = wargv[i];
-
- if (need_dblquotes)
- *q++ = '"';
-
- while (*p)
- {
- if (*p == '"')
- *q++ = '\\';
- else if (*p == '\\')
- {
- wchar_t *pp = p;
- while (*pp && *pp == '\\')
- pp++;
- if (*pp == '"')
- *q++ = '\\';
- }
- *q++ = *p;
- p++;
- }
-
- if (need_dblquotes)
- *q++ = '"';
- *q++ = '\0';
- }
- (*new_wargv)[argc] = NULL;
-
- return argc;
-}
-
-#ifndef HELPER_CONSOLE
-int _stdcall
-WinMain (struct HINSTANCE__ *hInstance,
- struct HINSTANCE__ *hPrevInstance,
- char *lpszCmdLine,
- int nCmdShow)
-#else
-int
-main (int ignored_argc, char **ignored_argv)
-#endif
-{
- int child_err_report_fd = -1;
- int helper_sync_fd = -1;
- int i;
- int fd;
- int mode;
- gintptr handle;
- int saved_errno;
- gintptr no_error = CHILD_NO_ERROR;
- gint argv_zero_offset = ARG_PROGRAM;
- wchar_t **new_wargv;
- int argc;
- wchar_t **wargv, **wenvp;
- _startupinfo si = { 0 };
- char c;
-
- g_assert (__argc >= ARG_COUNT);
-
- /* Fetch the wide-char argument vector */
- __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
-
- /* We still have the system codepage args in __argv. We can look
- * at the first args in which gspawn-win32.c passes us flags and
- * fd numbers in __argv, as we know those are just ASCII anyway.
- */
- g_assert (argc == __argc);
-
- /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
- * which write error messages.
- */
- child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
-
- /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
- * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
- * the program to run and its argv[0] separately.
- */
- if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
- argv_zero_offset++;
-
- /* argv[ARG_HELPER_SYNC] is the file descriptor number we read a
- * byte that tells us it is OK to exit. We have to wait until the
- * parent allows us to exit, so that the parent has had time to
- * duplicate the process handle we sent it. Duplicating a handle
- * from another process works only if that other process exists.
- */
- helper_sync_fd = atoi (__argv[ARG_HELPER_SYNC]);
-
- /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
- * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
- * should be left alone, and 'z' if it should be connected to the
- * bit bucket NUL:.
- */
- if (__argv[ARG_STDIN][0] == '-')
- ; /* Nothing */
- else if (__argv[ARG_STDIN][0] == 'z')
- {
- fd = open ("NUL:", O_RDONLY);
- if (fd != 0)
- {
- dup2 (fd, 0);
- close (fd);
- }
- }
- else
- {
- fd = atoi (__argv[ARG_STDIN]);
- if (fd != 0)
- {
- dup2 (fd, 0);
- close (fd);
- }
- }
-
- if (__argv[ARG_STDOUT][0] == '-')
- ; /* Nothing */
- else if (__argv[ARG_STDOUT][0] == 'z')
- {
- fd = open ("NUL:", O_WRONLY);
- if (fd != 1)
- {
- dup2 (fd, 1);
- close (fd);
- }
- }
- else
- {
- fd = atoi (__argv[ARG_STDOUT]);
- if (fd != 1)
- {
- dup2 (fd, 1);
- close (fd);
- }
- }
-
- if (__argv[ARG_STDERR][0] == '-')
- ; /* Nothing */
- else if (__argv[ARG_STDERR][0] == 'z')
- {
- fd = open ("NUL:", O_WRONLY);
- if (fd != 2)
- {
- dup2 (fd, 2);
- close (fd);
- }
- }
- else
- {
- fd = atoi (__argv[ARG_STDERR]);
- if (fd != 2)
- {
- dup2 (fd, 2);
- close (fd);
- }
- }
-
- /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
- * process. If "-", don't change directory.
- */
- if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
- __argv[ARG_WORKING_DIRECTORY][1] == 0)
- ; /* Nothing */
- else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
- write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
-
- /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
- * upwards should be closed
- */
- if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
- for (i = 3; i < 1000; i++) /* FIXME real limit? */
- if (i != child_err_report_fd && i != helper_sync_fd)
- close (i);
-
- /* We don't want our child to inherit the error report and
- * helper sync fds.
- */
- child_err_report_fd = dup_noninherited (child_err_report_fd, _O_WRONLY);
- helper_sync_fd = dup_noninherited (helper_sync_fd, _O_RDONLY);
-
- /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
- if (__argv[ARG_WAIT][0] == 'w')
- mode = P_WAIT;
- else
- mode = P_NOWAIT;
-
- /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
-
- /* __argv[ARG_PROGRAM] is executable file to run,
- * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
- * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
- * case we have a separate executable name and argv[0].
- */
-
- /* For the program name passed to spawnv(), don't use the quoted
- * version.
- */
- protect_wargv (wargv + argv_zero_offset, &new_wargv);
-
- if (__argv[ARG_USE_PATH][0] == 'y')
- handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
- else
- handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
-
- saved_errno = errno;
-
- if (handle == -1 && saved_errno != 0)
- write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
-
- write (child_err_report_fd, &no_error, sizeof (no_error));
- write (child_err_report_fd, &handle, sizeof (handle));
-
- read (helper_sync_fd, &c, 1);
-
- return 0;
-}
+++ /dev/null
-/* gspawn-win32.c - Process launching on Win32
- *
- * Copyright 2000 Red Hat, Inc.
- * Copyright 2003 Tor Lillqvist
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Implementation details on Win32.
- *
- * - There is no way to set the no-inherit flag for
- * a "file descriptor" in the MS C runtime. The flag is there,
- * and the dospawn() function uses it, but unfortunately
- * this flag can only be set when opening the file.
- * - As there is no fork(), we cannot reliably change directory
- * before starting the child process. (There might be several threads
- * running, and the current directory is common for all threads.)
- *
- * Thus, we must in many cases use a helper program to handle closing
- * of (inherited) file descriptors and changing of directory. The
- * helper process is also needed if the standard input, standard
- * output, or standard error of the process to be run are supposed to
- * be redirected somewhere.
- *
- * The structure of the source code in this file is a mess, I know.
- */
-
-/* Define this to get some logging all the time */
-/* #define G_SPAWN_WIN32_DEBUG */
-
-#include "config.h"
-
-#include "glib.h"
-#include "gprintfint.h"
-#include "glibintl.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <windows.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <io.h>
-#include <process.h>
-#include <direct.h>
-#include <wchar.h>
-
-#ifdef G_SPAWN_WIN32_DEBUG
- static int debug = 1;
- #define SETUP_DEBUG() /* empty */
-#else
- static int debug = -1;
- #define SETUP_DEBUG() \
- G_STMT_START \
- { \
- if (debug == -1) \
- { \
- if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL) \
- debug = 1; \
- else \
- debug = 0; \
- } \
- } \
- G_STMT_END
-#endif
-
-enum
-{
- CHILD_NO_ERROR,
- CHILD_CHDIR_FAILED,
- CHILD_SPAWN_FAILED,
-};
-
-enum {
- ARG_CHILD_ERR_REPORT = 1,
- ARG_HELPER_SYNC,
- ARG_STDIN,
- ARG_STDOUT,
- ARG_STDERR,
- ARG_WORKING_DIRECTORY,
- ARG_CLOSE_DESCRIPTORS,
- ARG_USE_PATH,
- ARG_WAIT,
- ARG_PROGRAM,
- ARG_COUNT = ARG_PROGRAM
-};
-
-static int
-dup_noninherited (int fd,
- int mode)
-{
- HANDLE filehandle;
-
- DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
- GetCurrentProcess (), &filehandle,
- 0, FALSE, DUPLICATE_SAME_ACCESS);
- close (fd);
- return _open_osfhandle ((gintptr) filehandle, mode | _O_NOINHERIT);
-}
-
-#ifndef GSPAWN_HELPER
-
-#ifdef _WIN64
-#define HELPER_PROCESS "gspawn-win64-helper"
-#else
-#define HELPER_PROCESS "gspawn-win32-helper"
-#endif
-
-static gchar *
-protect_argv_string (const gchar *string)
-{
- const gchar *p = string;
- gchar *retval, *q;
- gint len = 0;
- gboolean need_dblquotes = FALSE;
- while (*p)
- {
- if (*p == ' ' || *p == '\t')
- need_dblquotes = TRUE;
- else if (*p == '"')
- len++;
- else if (*p == '\\')
- {
- const gchar *pp = p;
- while (*pp && *pp == '\\')
- pp++;
- if (*pp == '"')
- len++;
- }
- len++;
- p++;
- }
-
- q = retval = g_malloc (len + need_dblquotes*2 + 1);
- p = string;
-
- if (need_dblquotes)
- *q++ = '"';
-
- while (*p)
- {
- if (*p == '"')
- *q++ = '\\';
- else if (*p == '\\')
- {
- const gchar *pp = p;
- while (*pp && *pp == '\\')
- pp++;
- if (*pp == '"')
- *q++ = '\\';
- }
- *q++ = *p;
- p++;
- }
-
- if (need_dblquotes)
- *q++ = '"';
- *q++ = '\0';
-
- return retval;
-}
-
-static gint
-protect_argv (gchar **argv,
- gchar ***new_argv)
-{
- gint i;
- gint argc = 0;
-
- while (argv[argc])
- ++argc;
- *new_argv = g_new (gchar *, argc+1);
-
- /* Quote each argv element if necessary, so that it will get
- * reconstructed correctly in the C runtime startup code. Note that
- * the unquoting algorithm in the C runtime is really weird, and
- * rather different than what Unix shells do. See stdargv.c in the C
- * runtime sources (in the Platform SDK, in src/crt).
- *
- * Note that an new_argv[0] constructed by this function should
- * *not* be passed as the filename argument to a spawn* or exec*
- * family function. That argument should be the real file name
- * without any quoting.
- */
- for (i = 0; i < argc; i++)
- (*new_argv)[i] = protect_argv_string (argv[i]);
-
- (*new_argv)[argc] = NULL;
-
- return argc;
-}
-
-GQuark
-g_spawn_error_quark (void)
-{
- return g_quark_from_static_string ("g-exec-error-quark");
-}
-
-gboolean
-g_spawn_async_utf8 (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_handle,
- GError **error)
-{
- g_return_val_if_fail (argv != NULL, FALSE);
-
- return g_spawn_async_with_pipes_utf8 (working_directory,
- argv, envp,
- flags,
- child_setup,
- user_data,
- child_handle,
- NULL, NULL, NULL,
- error);
-}
-
-/* Avoids a danger in threaded situations (calling close()
- * on a file descriptor twice, and another thread has
- * re-opened it since the first close)
- */
-static void
-close_and_invalidate (gint *fd)
-{
- if (*fd < 0)
- return;
-
- close (*fd);
- *fd = -1;
-}
-
-typedef enum
-{
- READ_FAILED = 0, /* FALSE */
- READ_OK,
- READ_EOF
-} ReadResult;
-
-static ReadResult
-read_data (GString *str,
- GIOChannel *iochannel,
- GError **error)
-{
- GIOStatus giostatus;
- gsize bytes;
- gchar buf[4096];
-
- again:
-
- giostatus = g_io_channel_read_chars (iochannel, buf, sizeof (buf), &bytes, NULL);
-
- if (bytes == 0)
- return READ_EOF;
- else if (bytes > 0)
- {
- g_string_append_len (str, buf, bytes);
- return READ_OK;
- }
- else if (giostatus == G_IO_STATUS_AGAIN)
- goto again;
- else if (giostatus == G_IO_STATUS_ERROR)
- {
- g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
- _("Failed to read data from child process"));
-
- return READ_FAILED;
- }
- else
- return READ_OK;
-}
-
-static gboolean
-make_pipe (gint p[2],
- GError **error)
-{
- if (_pipe (p, 4096, _O_BINARY) < 0)
- {
- int errsv = errno;
-
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to create pipe for communicating with child process (%s)"),
- g_strerror (errsv));
- return FALSE;
- }
- else
- return TRUE;
-}
-
-/* The helper process writes a status report back to us, through a
- * pipe, consisting of two ints.
- */
-static gboolean
-read_helper_report (int fd,
- gintptr report[2],
- GError **error)
-{
- gint bytes = 0;
-
- while (bytes < sizeof(gintptr)*2)
- {
- gint chunk;
-
- if (debug)
- g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n",
- __FILE__,
- sizeof(gintptr)*2 - bytes);
-
- chunk = read (fd, ((gchar*)report) + bytes,
- sizeof(gintptr)*2 - bytes);
-
- if (debug)
- g_print ("...got %d bytes\n", chunk);
-
- if (chunk < 0)
- {
- int errsv = errno;
-
- /* Some weird shit happened, bail out */
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to read from child pipe (%s)"),
- g_strerror (errsv));
-
- return FALSE;
- }
- else if (chunk == 0)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to read from child pipe (%s)"),
- "EOF");
- break; /* EOF */
- }
- else
- bytes += chunk;
- }
-
- if (bytes < sizeof(gintptr)*2)
- return FALSE;
-
- return TRUE;
-}
-
-static void
-set_child_error (gintptr report[2],
- const gchar *working_directory,
- GError **error)
-{
- switch (report[0])
- {
- case CHILD_CHDIR_FAILED:
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
- _("Failed to change to directory '%s' (%s)"),
- working_directory,
- g_strerror (report[1]));
- break;
- case CHILD_SPAWN_FAILED:
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to execute child process (%s)"),
- g_strerror (report[1]));
- break;
- default:
- g_assert_not_reached ();
- }
-}
-
-static gboolean
-utf8_charv_to_wcharv (char **utf8_charv,
- wchar_t ***wcharv,
- int *error_index,
- GError **error)
-{
- wchar_t **retval = NULL;
-
- *wcharv = NULL;
- if (utf8_charv != NULL)
- {
- int n = 0, i;
-
- while (utf8_charv[n])
- n++;
- retval = g_new (wchar_t *, n + 1);
-
- for (i = 0; i < n; i++)
- {
- retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
- if (retval[i] == NULL)
- {
- if (error_index)
- *error_index = i;
- while (i)
- g_free (retval[--i]);
- g_free (retval);
- return FALSE;
- }
- }
-
- retval[n] = NULL;
- }
- *wcharv = retval;
- return TRUE;
-}
-
-static gboolean
-do_spawn_directly (gint *exit_status,
- gboolean do_return_handle,
- GSpawnFlags flags,
- gchar **argv,
- char **envp,
- char **protected_argv,
- GPid *child_handle,
- GError **error)
-{
- const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
- char **new_argv;
- gintptr rc = -1;
- int saved_errno;
- GError *conv_error = NULL;
- gint conv_error_index;
- wchar_t *wargv0, **wargv, **wenvp;
-
- new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
-
- wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
- if (wargv0 == NULL)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid program name: %s"),
- conv_error->message);
- g_error_free (conv_error);
-
- return FALSE;
- }
-
- if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in argument vector at %d: %s"),
- conv_error_index, conv_error->message);
- g_error_free (conv_error);
- g_free (wargv0);
-
- return FALSE;
- }
-
- if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in environment: %s"),
- conv_error->message);
- g_error_free (conv_error);
- g_free (wargv0);
- g_strfreev ((gchar **) wargv);
-
- return FALSE;
- }
-
- if (flags & G_SPAWN_SEARCH_PATH)
- if (wenvp != NULL)
- rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
- else
- rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
- else
- if (wenvp != NULL)
- rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
- else
- rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
-
- g_free (wargv0);
- g_strfreev ((gchar **) wargv);
- g_strfreev ((gchar **) wenvp);
-
- saved_errno = errno;
-
- if (rc == -1 && saved_errno != 0)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to execute child process (%s)"),
- g_strerror (saved_errno));
- return FALSE;
- }
-
- if (exit_status == NULL)
- {
- if (child_handle && do_return_handle)
- *child_handle = (GPid) rc;
- else
- {
- CloseHandle ((HANDLE) rc);
- if (child_handle)
- *child_handle = 0;
- }
- }
- else
- *exit_status = rc;
-
- return TRUE;
-}
-
-static gboolean
-do_spawn_with_pipes (gint *exit_status,
- gboolean do_return_handle,
- const gchar *working_directory,
- gchar **argv,
- char **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- GPid *child_handle,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- gint *err_report,
- GError **error)
-{
- char **protected_argv;
- char args[ARG_COUNT][10];
- char **new_argv;
- int i;
- gintptr rc = -1;
- int saved_errno;
- int argc;
- int stdin_pipe[2] = { -1, -1 };
- int stdout_pipe[2] = { -1, -1 };
- int stderr_pipe[2] = { -1, -1 };
- int child_err_report_pipe[2] = { -1, -1 };
- int helper_sync_pipe[2] = { -1, -1 };
- gintptr helper_report[2];
- static gboolean warned_about_child_setup = FALSE;
- GError *conv_error = NULL;
- gint conv_error_index;
- gchar *helper_process;
- CONSOLE_CURSOR_INFO cursor_info;
- wchar_t *whelper, **wargv, **wenvp;
- extern gchar *_glib_get_dll_directory (void);
- gchar *glib_dll_directory;
-
- if (child_setup && !warned_about_child_setup)
- {
- warned_about_child_setup = TRUE;
- g_warning ("passing a child setup function to the g_spawn functions is pointless on Windows and it is ignored");
- }
-
- argc = protect_argv (argv, &protected_argv);
-
- if (!standard_input && !standard_output && !standard_error &&
- (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
- !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
- !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
- (working_directory == NULL || !*working_directory) &&
- (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
- {
- /* We can do without the helper process */
- gboolean retval =
- do_spawn_directly (exit_status, do_return_handle, flags,
- argv, envp, protected_argv,
- child_handle, error);
- g_strfreev (protected_argv);
- return retval;
- }
-
- if (standard_input && !make_pipe (stdin_pipe, error))
- goto cleanup_and_fail;
-
- if (standard_output && !make_pipe (stdout_pipe, error))
- goto cleanup_and_fail;
-
- if (standard_error && !make_pipe (stderr_pipe, error))
- goto cleanup_and_fail;
-
- if (!make_pipe (child_err_report_pipe, error))
- goto cleanup_and_fail;
-
- if (!make_pipe (helper_sync_pipe, error))
- goto cleanup_and_fail;
-
- new_argv = g_new (char *, argc + 1 + ARG_COUNT);
- if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
- helper_process = HELPER_PROCESS "-console.exe";
- else
- helper_process = HELPER_PROCESS ".exe";
-
- glib_dll_directory = _glib_get_dll_directory ();
- if (glib_dll_directory != NULL)
- {
- helper_process = g_build_filename (glib_dll_directory, helper_process, NULL);
- g_free (glib_dll_directory);
- }
- else
- helper_process = g_strdup (helper_process);
-
- new_argv[0] = protect_argv_string (helper_process);
-
- _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
- new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
-
- /* Make the read end of the child error report pipe
- * noninherited. Otherwise it will needlessly be inherited by the
- * helper process, and the started actual user process. As such that
- * shouldn't harm, but it is unnecessary.
- */
- child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
-
- if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
- {
- /* Overload ARG_CHILD_ERR_REPORT to also encode the
- * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
- */
- strcat (args[ARG_CHILD_ERR_REPORT], "#");
- }
-
- _g_sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
- new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
-
- /* Make the write end of the sync pipe noninherited. Otherwise the
- * helper process will inherit it, and thus if this process happens
- * to crash before writing the sync byte to the pipe, the helper
- * process won't read but won't get any EOF either, as it has the
- * write end open itself.
- */
- helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
-
- if (standard_input)
- {
- _g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
- new_argv[ARG_STDIN] = args[ARG_STDIN];
- }
- else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
- {
- /* Let stdin be alone */
- new_argv[ARG_STDIN] = "-";
- }
- else
- {
- /* Keep process from blocking on a read of stdin */
- new_argv[ARG_STDIN] = "z";
- }
-
- if (standard_output)
- {
- _g_sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
- new_argv[ARG_STDOUT] = args[ARG_STDOUT];
- }
- else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
- {
- new_argv[ARG_STDOUT] = "z";
- }
- else
- {
- new_argv[ARG_STDOUT] = "-";
- }
-
- if (standard_error)
- {
- _g_sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
- new_argv[ARG_STDERR] = args[ARG_STDERR];
- }
- else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
- {
- new_argv[ARG_STDERR] = "z";
- }
- else
- {
- new_argv[ARG_STDERR] = "-";
- }
-
- if (working_directory && *working_directory)
- new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
- else
- new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
-
- if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
- new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
- else
- new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
-
- if (flags & G_SPAWN_SEARCH_PATH)
- new_argv[ARG_USE_PATH] = "y";
- else
- new_argv[ARG_USE_PATH] = "-";
-
- if (exit_status == NULL)
- new_argv[ARG_WAIT] = "-";
- else
- new_argv[ARG_WAIT] = "w";
-
- for (i = 0; i <= argc; i++)
- new_argv[ARG_PROGRAM + i] = protected_argv[i];
-
- SETUP_DEBUG();
-
- if (debug)
- {
- g_print ("calling %s with argv:\n", helper_process);
- for (i = 0; i < argc + 1 + ARG_COUNT; i++)
- g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
- }
-
- if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
- {
- if (conv_error_index == ARG_WORKING_DIRECTORY)
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
- _("Invalid working directory: %s"),
- conv_error->message);
- else
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in argument vector at %d: %s"),
- conv_error_index - ARG_PROGRAM, conv_error->message);
- g_error_free (conv_error);
- g_strfreev (protected_argv);
- g_free (new_argv[0]);
- g_free (new_argv[ARG_WORKING_DIRECTORY]);
- g_free (new_argv);
- g_free (helper_process);
-
- goto cleanup_and_fail;
- }
-
- if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in environment: %s"),
- conv_error->message);
- g_error_free (conv_error);
- g_strfreev (protected_argv);
- g_free (new_argv[0]);
- g_free (new_argv[ARG_WORKING_DIRECTORY]);
- g_free (new_argv);
- g_free (helper_process);
- g_strfreev ((gchar **) wargv);
-
- goto cleanup_and_fail;
- }
-
- whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
- g_free (helper_process);
-
- if (wenvp != NULL)
- rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
- else
- rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
-
- saved_errno = errno;
-
- g_free (whelper);
- g_strfreev ((gchar **) wargv);
- g_strfreev ((gchar **) wenvp);
-
- /* Close the other process's ends of the pipes in this process,
- * otherwise the reader will never get EOF.
- */
- close_and_invalidate (&child_err_report_pipe[1]);
- close_and_invalidate (&helper_sync_pipe[0]);
- close_and_invalidate (&stdin_pipe[0]);
- close_and_invalidate (&stdout_pipe[1]);
- close_and_invalidate (&stderr_pipe[1]);
-
- g_strfreev (protected_argv);
-
- g_free (new_argv[0]);
- g_free (new_argv[ARG_WORKING_DIRECTORY]);
- g_free (new_argv);
-
- /* Check if gspawn-win32-helper couldn't be run */
- if (rc == -1 && saved_errno != 0)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Failed to execute helper program (%s)"),
- g_strerror (saved_errno));
- goto cleanup_and_fail;
- }
-
- if (exit_status != NULL)
- {
- /* Synchronous case. Pass helper's report pipe back to caller,
- * which takes care of reading it after the grandchild has
- * finished.
- */
- g_assert (err_report != NULL);
- *err_report = child_err_report_pipe[0];
- write (helper_sync_pipe[1], " ", 1);
- close_and_invalidate (&helper_sync_pipe[1]);
- }
- else
- {
- /* Asynchronous case. We read the helper's report right away. */
- if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
- goto cleanup_and_fail;
-
- close_and_invalidate (&child_err_report_pipe[0]);
-
- switch (helper_report[0])
- {
- case CHILD_NO_ERROR:
- if (child_handle && do_return_handle)
- {
- /* rc is our HANDLE for gspawn-win32-helper. It has
- * told us the HANDLE of its child. Duplicate that into
- * a HANDLE valid in this process.
- */
- if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
- GetCurrentProcess (), (LPHANDLE) child_handle,
- 0, TRUE, DUPLICATE_SAME_ACCESS))
- {
- char *emsg = g_win32_error_message (GetLastError ());
- g_print("%s\n", emsg);
- *child_handle = 0;
- }
- }
- else if (child_handle)
- *child_handle = 0;
- write (helper_sync_pipe[1], " ", 1);
- close_and_invalidate (&helper_sync_pipe[1]);
- break;
-
- default:
- write (helper_sync_pipe[1], " ", 1);
- close_and_invalidate (&helper_sync_pipe[1]);
- set_child_error (helper_report, working_directory, error);
- goto cleanup_and_fail;
- }
- }
-
- /* Success against all odds! return the information */
-
- if (standard_input)
- *standard_input = stdin_pipe[1];
- if (standard_output)
- *standard_output = stdout_pipe[0];
- if (standard_error)
- *standard_error = stderr_pipe[0];
- if (rc != -1)
- CloseHandle ((HANDLE) rc);
-
- return TRUE;
-
- cleanup_and_fail:
-
- if (rc != -1)
- CloseHandle ((HANDLE) rc);
- if (child_err_report_pipe[0] != -1)
- close (child_err_report_pipe[0]);
- if (child_err_report_pipe[1] != -1)
- close (child_err_report_pipe[1]);
- if (helper_sync_pipe[0] != -1)
- close (helper_sync_pipe[0]);
- if (helper_sync_pipe[1] != -1)
- close (helper_sync_pipe[1]);
- if (stdin_pipe[0] != -1)
- close (stdin_pipe[0]);
- if (stdin_pipe[1] != -1)
- close (stdin_pipe[1]);
- if (stdout_pipe[0] != -1)
- close (stdout_pipe[0]);
- if (stdout_pipe[1] != -1)
- close (stdout_pipe[1]);
- if (stderr_pipe[0] != -1)
- close (stderr_pipe[0]);
- if (stderr_pipe[1] != -1)
- close (stderr_pipe[1]);
-
- return FALSE;
-}
-
-gboolean
-g_spawn_sync_utf8 (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gint outpipe = -1;
- gint errpipe = -1;
- gint reportpipe = -1;
- GIOChannel *outchannel = NULL;
- GIOChannel *errchannel = NULL;
- GPollFD outfd, errfd;
- GPollFD fds[2];
- gint nfds;
- gint outindex = -1;
- gint errindex = -1;
- gint ret;
- GString *outstr = NULL;
- GString *errstr = NULL;
- gboolean failed;
- gint status;
-
- g_return_val_if_fail (argv != NULL, FALSE);
- g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
- g_return_val_if_fail (standard_output == NULL ||
- !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
- g_return_val_if_fail (standard_error == NULL ||
- !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
-
- /* Just to ensure segfaults if callers try to use
- * these when an error is reported.
- */
- if (standard_output)
- *standard_output = NULL;
-
- if (standard_error)
- *standard_error = NULL;
-
- if (!do_spawn_with_pipes (&status,
- FALSE,
- working_directory,
- argv,
- envp,
- flags,
- child_setup,
- NULL,
- NULL,
- standard_output ? &outpipe : NULL,
- standard_error ? &errpipe : NULL,
- &reportpipe,
- error))
- return FALSE;
-
- /* Read data from child. */
-
- failed = FALSE;
-
- if (outpipe >= 0)
- {
- outstr = g_string_new (NULL);
- outchannel = g_io_channel_win32_new_fd (outpipe);
- g_io_channel_set_encoding (outchannel, NULL, NULL);
- g_io_channel_set_buffered (outchannel, FALSE);
- g_io_channel_win32_make_pollfd (outchannel,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- &outfd);
- if (debug)
- g_print ("outfd=%p\n", (HANDLE) outfd.fd);
- }
-
- if (errpipe >= 0)
- {
- errstr = g_string_new (NULL);
- errchannel = g_io_channel_win32_new_fd (errpipe);
- g_io_channel_set_encoding (errchannel, NULL, NULL);
- g_io_channel_set_buffered (errchannel, FALSE);
- g_io_channel_win32_make_pollfd (errchannel,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- &errfd);
- if (debug)
- g_print ("errfd=%p\n", (HANDLE) errfd.fd);
- }
-
- /* Read data until we get EOF on all pipes. */
- while (!failed && (outpipe >= 0 || errpipe >= 0))
- {
- nfds = 0;
- if (outpipe >= 0)
- {
- fds[nfds] = outfd;
- outindex = nfds;
- nfds++;
- }
- if (errpipe >= 0)
- {
- fds[nfds] = errfd;
- errindex = nfds;
- nfds++;
- }
-
- if (debug)
- g_print ("g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
- nfds);
-
- ret = g_io_channel_win32_poll (fds, nfds, -1);
-
- if (ret < 0)
- {
- failed = TRUE;
-
- g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
- _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
-
- break;
- }
-
- if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
- {
- switch (read_data (outstr, outchannel, error))
- {
- case READ_FAILED:
- if (debug)
- g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
- failed = TRUE;
- break;
- case READ_EOF:
- if (debug)
- g_print ("g_spawn_sync: outchannel: READ_EOF\n");
- g_io_channel_unref (outchannel);
- outchannel = NULL;
- close_and_invalidate (&outpipe);
- break;
- default:
- if (debug)
- g_print ("g_spawn_sync: outchannel: OK\n");
- break;
- }
-
- if (failed)
- break;
- }
-
- if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
- {
- switch (read_data (errstr, errchannel, error))
- {
- case READ_FAILED:
- if (debug)
- g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
- failed = TRUE;
- break;
- case READ_EOF:
- if (debug)
- g_print ("g_spawn_sync: errchannel: READ_EOF\n");
- g_io_channel_unref (errchannel);
- errchannel = NULL;
- close_and_invalidate (&errpipe);
- break;
- default:
- if (debug)
- g_print ("g_spawn_sync: errchannel: OK\n");
- break;
- }
-
- if (failed)
- break;
- }
- }
-
- if (reportpipe == -1)
- {
- /* No helper process, exit status of actual spawned process
- * already available.
- */
- if (exit_status)
- *exit_status = status;
- }
- else
- {
- /* Helper process was involved. Read its report now after the
- * grandchild has finished.
- */
- gintptr helper_report[2];
-
- if (!read_helper_report (reportpipe, helper_report, error))
- failed = TRUE;
- else
- {
- switch (helper_report[0])
- {
- case CHILD_NO_ERROR:
- if (exit_status)
- *exit_status = helper_report[1];
- break;
- default:
- set_child_error (helper_report, working_directory, error);
- failed = TRUE;
- break;
- }
- }
- close_and_invalidate (&reportpipe);
- }
-
-
- /* These should only be open still if we had an error. */
-
- if (outchannel != NULL)
- g_io_channel_unref (outchannel);
- if (errchannel != NULL)
- g_io_channel_unref (errchannel);
- if (outpipe >= 0)
- close_and_invalidate (&outpipe);
- if (errpipe >= 0)
- close_and_invalidate (&errpipe);
-
- if (failed)
- {
- if (outstr)
- g_string_free (outstr, TRUE);
- if (errstr)
- g_string_free (errstr, TRUE);
-
- return FALSE;
- }
- else
- {
- if (standard_output)
- *standard_output = g_string_free (outstr, FALSE);
-
- if (standard_error)
- *standard_error = g_string_free (errstr, FALSE);
-
- return TRUE;
- }
-}
-
-gboolean
-g_spawn_async_with_pipes_utf8 (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_handle,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- GError **error)
-{
- g_return_val_if_fail (argv != NULL, FALSE);
- g_return_val_if_fail (standard_output == NULL ||
- !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
- g_return_val_if_fail (standard_error == NULL ||
- !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
- /* can't inherit stdin if we have an input pipe. */
- g_return_val_if_fail (standard_input == NULL ||
- !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
-
- return do_spawn_with_pipes (NULL,
- (flags & G_SPAWN_DO_NOT_REAP_CHILD),
- working_directory,
- argv,
- envp,
- flags,
- child_setup,
- child_handle,
- standard_input,
- standard_output,
- standard_error,
- NULL,
- error);
-}
-
-gboolean
-g_spawn_command_line_sync_utf8 (const gchar *command_line,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gboolean retval;
- gchar **argv = 0;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_sync_utf8 (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- standard_output,
- standard_error,
- exit_status,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-gboolean
-g_spawn_command_line_async_utf8 (const gchar *command_line,
- GError **error)
-{
- gboolean retval;
- gchar **argv = 0;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_async_utf8 (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- NULL,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-void
-g_spawn_close_pid (GPid pid)
-{
- CloseHandle (pid);
-}
-
-#if !defined (_WIN64)
-
-/* Binary compatibility versions that take system codepage pathnames,
- * argument vectors and environments. These get used only by code
- * built against 2.8.1 or earlier. Code built against 2.8.2 or later
- * will use the _utf8 versions above (see the #defines in gspawn.h).
- */
-
-#undef g_spawn_async
-#undef g_spawn_async_with_pipes
-#undef g_spawn_sync
-#undef g_spawn_command_line_sync
-#undef g_spawn_command_line_async
-
-static gboolean
-setup_utf8_copies (const gchar *working_directory,
- gchar **utf8_working_directory,
- gchar **argv,
- gchar ***utf8_argv,
- gchar **envp,
- gchar ***utf8_envp,
- GError **error)
-{
- gint i, argc, envc;
-
- if (working_directory == NULL)
- *utf8_working_directory = NULL;
- else
- {
- GError *conv_error = NULL;
-
- *utf8_working_directory = g_locale_to_utf8 (working_directory, -1, NULL, NULL, &conv_error);
- if (*utf8_working_directory == NULL)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
- _("Invalid working directory: %s"),
- conv_error->message);
- g_error_free (conv_error);
- return FALSE;
- }
- }
-
- argc = 0;
- while (argv[argc])
- ++argc;
- *utf8_argv = g_new (gchar *, argc + 1);
- for (i = 0; i < argc; i++)
- {
- GError *conv_error = NULL;
-
- (*utf8_argv)[i] = g_locale_to_utf8 (argv[i], -1, NULL, NULL, &conv_error);
- if ((*utf8_argv)[i] == NULL)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in argument vector at %d: %s"),
- i, conv_error->message);
- g_error_free (conv_error);
-
- g_strfreev (*utf8_argv);
- *utf8_argv = NULL;
-
- g_free (*utf8_working_directory);
- *utf8_working_directory = NULL;
-
- return FALSE;
- }
- }
- (*utf8_argv)[argc] = NULL;
-
- if (envp == NULL)
- {
- *utf8_envp = NULL;
- }
- else
- {
- envc = 0;
- while (envp[envc])
- ++envc;
- *utf8_envp = g_new (gchar *, envc + 1);
- for (i = 0; i < envc; i++)
- {
- GError *conv_error = NULL;
-
- (*utf8_envp)[i] = g_locale_to_utf8 (envp[i], -1, NULL, NULL, &conv_error);
- if ((*utf8_envp)[i] == NULL)
- {
- g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
- _("Invalid string in environment: %s"),
- conv_error->message);
- g_error_free (conv_error);
-
- g_strfreev (*utf8_envp);
- *utf8_envp = NULL;
-
- g_strfreev (*utf8_argv);
- *utf8_argv = NULL;
-
- g_free (*utf8_working_directory);
- *utf8_working_directory = NULL;
-
- return FALSE;
- }
- }
- (*utf8_envp)[envc] = NULL;
- }
- return TRUE;
-}
-
-static void
-free_utf8_copies (gchar *utf8_working_directory,
- gchar **utf8_argv,
- gchar **utf8_envp)
-{
- g_free (utf8_working_directory);
- g_strfreev (utf8_argv);
- g_strfreev (utf8_envp);
-}
-
-gboolean
-g_spawn_async_with_pipes (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_handle,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- GError **error)
-{
- gchar *utf8_working_directory;
- gchar **utf8_argv;
- gchar **utf8_envp;
- gboolean retval;
-
- if (!setup_utf8_copies (working_directory, &utf8_working_directory,
- argv, &utf8_argv,
- envp, &utf8_envp,
- error))
- return FALSE;
-
- retval = g_spawn_async_with_pipes_utf8 (utf8_working_directory,
- utf8_argv, utf8_envp,
- flags, child_setup, user_data,
- child_handle,
- standard_input, standard_output, standard_error,
- error);
-
- free_utf8_copies (utf8_working_directory, utf8_argv, utf8_envp);
-
- return retval;
-}
-
-gboolean
-g_spawn_async (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_handle,
- GError **error)
-{
- return g_spawn_async_with_pipes (working_directory,
- argv, envp,
- flags,
- child_setup,
- user_data,
- child_handle,
- NULL, NULL, NULL,
- error);
-}
-
-gboolean
-g_spawn_sync (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gchar *utf8_working_directory;
- gchar **utf8_argv;
- gchar **utf8_envp;
- gboolean retval;
-
- if (!setup_utf8_copies (working_directory, &utf8_working_directory,
- argv, &utf8_argv,
- envp, &utf8_envp,
- error))
- return FALSE;
-
- retval = g_spawn_sync_utf8 (utf8_working_directory,
- utf8_argv, utf8_envp,
- flags, child_setup, user_data,
- standard_output, standard_error, exit_status,
- error);
-
- free_utf8_copies (utf8_working_directory, utf8_argv, utf8_envp);
-
- return retval;
-}
-
-gboolean
-g_spawn_command_line_sync (const gchar *command_line,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gboolean retval;
- gchar **argv = 0;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_sync (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- standard_output,
- standard_error,
- exit_status,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-gboolean
-g_spawn_command_line_async (const gchar *command_line,
- GError **error)
-{
- gboolean retval;
- gchar **argv = 0;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_async (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- NULL,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-#endif /* !_WIN64 */
-
-#endif /* !GSPAWN_HELPER */
+++ /dev/null
-/* gspawn.c - Process launching
- *
- * Copyright 2000 Red Hat, Inc.
- * g_execvpe implementation based on GNU libc execvp:
- * Copyright 1991, 92, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not, write
- * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h> /* for fdwalk */
-#include <dirent.h>
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif /* HAVE_SYS_RESOURCE_H */
-
-#include "gspawn.h"
-
-#include "gmem.h"
-#include "gshell.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gutils.h"
-#include "glibintl.h"
-
-static gint g_execute (const gchar *file,
- gchar **argv,
- gchar **envp,
- gboolean search_path);
-
-static gboolean make_pipe (gint p[2],
- GError **error);
-static gboolean fork_exec_with_pipes (gboolean intermediate_child,
- const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- gboolean close_descriptors,
- gboolean search_path,
- gboolean stdout_to_null,
- gboolean stderr_to_null,
- gboolean child_inherits_stdin,
- gboolean file_and_argv_zero,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_pid,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- GError **error);
-
-GQuark
-g_spawn_error_quark (void)
-{
- return g_quark_from_static_string ("g-exec-error-quark");
-}
-
-/**
- * g_spawn_async:
- * @working_directory: child's current working directory, or %NULL to inherit parent's
- * @argv: child's argument vector
- * @envp: child's environment, or %NULL to inherit parent's
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @child_pid: return location for child process reference, or %NULL
- * @error: return location for error
- *
- * See g_spawn_async_with_pipes() for a full description; this function
- * simply calls the g_spawn_async_with_pipes() without any pipes.
- *
- * You should call g_spawn_close_pid() on the returned child process
- * reference when you don't need it any more.
- *
- * <note><para>
- * If you are writing a GTK+ application, and the program you
- * are spawning is a graphical application, too, then you may
- * want to use gdk_spawn_on_screen() instead to ensure that
- * the spawned program opens its windows on the right screen.
- * </para></note>
- *
- * <note><para> Note that the returned @child_pid on Windows is a
- * handle to the child process and not its identifier. Process handles
- * and process identifiers are different concepts on Windows.
- * </para></note>
- *
- * Return value: %TRUE on success, %FALSE if error is set
- **/
-gboolean
-g_spawn_async (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_pid,
- GError **error)
-{
- g_return_val_if_fail (argv != NULL, FALSE);
-
- return g_spawn_async_with_pipes (working_directory,
- argv, envp,
- flags,
- child_setup,
- user_data,
- child_pid,
- NULL, NULL, NULL,
- error);
-}
-
-/* Avoids a danger in threaded situations (calling close()
- * on a file descriptor twice, and another thread has
- * re-opened it since the first close)
- */
-static gint
-close_and_invalidate (gint *fd)
-{
- gint ret;
-
- if (*fd < 0)
- return -1;
- else
- {
- ret = close (*fd);
- *fd = -1;
- }
-
- return ret;
-}
-
-/* Some versions of OS X define READ_OK in public headers */
-#undef READ_OK
-
-typedef enum
-{
- READ_FAILED = 0, /* FALSE */
- READ_OK,
- READ_EOF
-} ReadResult;
-
-static ReadResult
-read_data (GString *str,
- gint fd,
- GError **error)
-{
- gssize bytes;
- gchar buf[4096];
-
- again:
-
- bytes = read (fd, buf, 4096);
-
- if (bytes == 0)
- return READ_EOF;
- else if (bytes > 0)
- {
- g_string_append_len (str, buf, bytes);
- return READ_OK;
- }
- else if (bytes < 0 && errno == EINTR)
- goto again;
- else if (bytes < 0)
- {
- int errsv = errno;
-
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_READ,
- _("Failed to read data from child process (%s)"),
- g_strerror (errsv));
-
- return READ_FAILED;
- }
- else
- return READ_OK;
-}
-
-/**
- * g_spawn_sync:
- * @working_directory: child's current working directory, or %NULL to inherit parent's
- * @argv: child's argument vector
- * @envp: child's environment, or %NULL to inherit parent's
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @standard_output: return location for child output, or %NULL
- * @standard_error: return location for child error messages, or %NULL
- * @exit_status: return location for child exit status, as returned by waitpid(), or %NULL
- * @error: return location for error, or %NULL
- *
- * Executes a child synchronously (waits for the child to exit before returning).
- * All output from the child is stored in @standard_output and @standard_error,
- * if those parameters are non-%NULL. Note that you must set the
- * %G_SPAWN_STDOUT_TO_DEV_NULL and %G_SPAWN_STDERR_TO_DEV_NULL flags when
- * passing %NULL for @standard_output and @standard_error.
- * If @exit_status is non-%NULL, the exit status of the child is stored
- * there as it would be returned by waitpid(); standard UNIX macros such
- * as WIFEXITED() and WEXITSTATUS() must be used to evaluate the exit status.
- * Note that this function call waitpid() even if @exit_status is %NULL, and
- * does not accept the %G_SPAWN_DO_NOT_REAP_CHILD flag.
- * If an error occurs, no data is returned in @standard_output,
- * @standard_error, or @exit_status.
- *
- * This function calls g_spawn_async_with_pipes() internally; see that
- * function for full details on the other parameters and details on
- * how these functions work on Windows.
- *
- * Return value: %TRUE on success, %FALSE if an error was set.
- **/
-gboolean
-g_spawn_sync (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gint outpipe = -1;
- gint errpipe = -1;
- GPid pid;
- fd_set fds;
- gint ret;
- GString *outstr = NULL;
- GString *errstr = NULL;
- gboolean failed;
- gint status;
-
- g_return_val_if_fail (argv != NULL, FALSE);
- g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
- g_return_val_if_fail (standard_output == NULL ||
- !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
- g_return_val_if_fail (standard_error == NULL ||
- !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
-
- /* Just to ensure segfaults if callers try to use
- * these when an error is reported.
- */
- if (standard_output)
- *standard_output = NULL;
-
- if (standard_error)
- *standard_error = NULL;
-
- if (!fork_exec_with_pipes (FALSE,
- working_directory,
- argv,
- envp,
- !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
- (flags & G_SPAWN_SEARCH_PATH) != 0,
- (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
- (flags & G_SPAWN_FILE_AND_ARGV_ZERO) != 0,
- child_setup,
- user_data,
- &pid,
- NULL,
- standard_output ? &outpipe : NULL,
- standard_error ? &errpipe : NULL,
- error))
- return FALSE;
-
- /* Read data from child. */
-
- failed = FALSE;
-
- if (outpipe >= 0)
- {
- outstr = g_string_new (NULL);
- }
-
- if (errpipe >= 0)
- {
- errstr = g_string_new (NULL);
- }
-
- /* Read data until we get EOF on both pipes. */
- while (!failed &&
- (outpipe >= 0 ||
- errpipe >= 0))
- {
- ret = 0;
-
- FD_ZERO (&fds);
- if (outpipe >= 0)
- FD_SET (outpipe, &fds);
- if (errpipe >= 0)
- FD_SET (errpipe, &fds);
-
- ret = select (MAX (outpipe, errpipe) + 1,
- &fds,
- NULL, NULL,
- NULL /* no timeout */);
-
- if (ret < 0 && errno != EINTR)
- {
- int errsv = errno;
-
- failed = TRUE;
-
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_READ,
- _("Unexpected error in select() reading data from a child process (%s)"),
- g_strerror (errsv));
-
- break;
- }
-
- if (outpipe >= 0 && FD_ISSET (outpipe, &fds))
- {
- switch (read_data (outstr, outpipe, error))
- {
- case READ_FAILED:
- failed = TRUE;
- break;
- case READ_EOF:
- close_and_invalidate (&outpipe);
- outpipe = -1;
- break;
- default:
- break;
- }
-
- if (failed)
- break;
- }
-
- if (errpipe >= 0 && FD_ISSET (errpipe, &fds))
- {
- switch (read_data (errstr, errpipe, error))
- {
- case READ_FAILED:
- failed = TRUE;
- break;
- case READ_EOF:
- close_and_invalidate (&errpipe);
- errpipe = -1;
- break;
- default:
- break;
- }
-
- if (failed)
- break;
- }
- }
-
- /* These should only be open still if we had an error. */
-
- if (outpipe >= 0)
- close_and_invalidate (&outpipe);
- if (errpipe >= 0)
- close_and_invalidate (&errpipe);
-
- /* Wait for child to exit, even if we have
- * an error pending.
- */
- again:
-
- ret = waitpid (pid, &status, 0);
-
- if (ret < 0)
- {
- if (errno == EINTR)
- goto again;
- else if (errno == ECHILD)
- {
- if (exit_status)
- {
- g_warning ("In call to g_spawn_sync(), exit status of a child process was requested but SIGCHLD action was set to SIG_IGN and ECHILD was received by waitpid(), so exit status can't be returned. This is a bug in the program calling g_spawn_sync(); either don't request the exit status, or don't set the SIGCHLD action.");
- }
- else
- {
- /* We don't need the exit status. */
- }
- }
- else
- {
- if (!failed) /* avoid error pileups */
- {
- int errsv = errno;
-
- failed = TRUE;
-
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_READ,
- _("Unexpected error in waitpid() (%s)"),
- g_strerror (errsv));
- }
- }
- }
-
- if (failed)
- {
- if (outstr)
- g_string_free (outstr, TRUE);
- if (errstr)
- g_string_free (errstr, TRUE);
-
- return FALSE;
- }
- else
- {
- if (exit_status)
- *exit_status = status;
-
- if (standard_output)
- *standard_output = g_string_free (outstr, FALSE);
-
- if (standard_error)
- *standard_error = g_string_free (errstr, FALSE);
-
- return TRUE;
- }
-}
-
-/**
- * g_spawn_async_with_pipes:
- * @working_directory: child's current working directory, or %NULL to inherit parent's, in the GLib file name encoding
- * @argv: child's argument vector, in the GLib file name encoding
- * @envp: child's environment, or %NULL to inherit parent's, in the GLib file name encoding
- * @flags: flags from #GSpawnFlags
- * @child_setup: function to run in the child just before exec()
- * @user_data: user data for @child_setup
- * @child_pid: return location for child process ID, or %NULL
- * @standard_input: return location for file descriptor to write to child's stdin, or %NULL
- * @standard_output: return location for file descriptor to read child's stdout, or %NULL
- * @standard_error: return location for file descriptor to read child's stderr, or %NULL
- * @error: return location for error
- *
- * Executes a child program asynchronously (your program will not
- * block waiting for the child to exit). The child program is
- * specified by the only argument that must be provided, @argv. @argv
- * should be a %NULL-terminated array of strings, to be passed as the
- * argument vector for the child. The first string in @argv is of
- * course the name of the program to execute. By default, the name of
- * the program must be a full path; the <envar>PATH</envar> shell variable
- * will only be searched if you pass the %G_SPAWN_SEARCH_PATH flag.
- *
- * On Windows, note that all the string or string vector arguments to
- * this function and the other g_spawn*() functions are in UTF-8, the
- * GLib file name encoding. Unicode characters that are not part of
- * the system codepage passed in these arguments will be correctly
- * available in the spawned program only if it uses wide character API
- * to retrieve its command line. For C programs built with Microsoft's
- * tools it is enough to make the program have a wmain() instead of
- * main(). wmain() has a wide character argument vector as parameter.
- *
- * At least currently, mingw doesn't support wmain(), so if you use
- * mingw to develop the spawned program, it will have to call the
- * undocumented function __wgetmainargs() to get the wide character
- * argument vector and environment. See gspawn-win32-helper.c in the
- * GLib sources or init.c in the mingw runtime sources for a prototype
- * for that function. Alternatively, you can retrieve the Win32 system
- * level wide character command line passed to the spawned program
- * using the GetCommandLineW() function.
- *
- * On Windows the low-level child process creation API
- * <function>CreateProcess()</function> doesn't use argument vectors,
- * but a command line. The C runtime library's
- * <function>spawn*()</function> family of functions (which
- * g_spawn_async_with_pipes() eventually calls) paste the argument
- * vector elements together into a command line, and the C runtime startup code
- * does a corresponding reconstruction of an argument vector from the
- * command line, to be passed to main(). Complications arise when you have
- * argument vector elements that contain spaces of double quotes. The
- * <function>spawn*()</function> functions don't do any quoting or
- * escaping, but on the other hand the startup code does do unquoting
- * and unescaping in order to enable receiving arguments with embedded
- * spaces or double quotes. To work around this asymmetry,
- * g_spawn_async_with_pipes() will do quoting and escaping on argument
- * vector elements that need it before calling the C runtime
- * spawn() function.
- *
- * The returned @child_pid on Windows is a handle to the child
- * process, not its identifier. Process handles and process
- * identifiers are different concepts on Windows.
- *
- * @envp is a %NULL-terminated array of strings, where each string
- * has the form <literal>KEY=VALUE</literal>. This will become
- * the child's environment. If @envp is %NULL, the child inherits its
- * parent's environment.
- *
- * @flags should be the bitwise OR of any flags you want to affect the
- * function's behaviour. The %G_SPAWN_DO_NOT_REAP_CHILD means that
- * the child will not automatically be reaped; you must use a
- * #GChildWatch source to be notified about the death of the child
- * process. Eventually you must call g_spawn_close_pid() on the
- * @child_pid, in order to free resources which may be associated
- * with the child process. (On Unix, using a #GChildWatch source is
- * equivalent to calling waitpid() or handling the %SIGCHLD signal
- * manually. On Windows, calling g_spawn_close_pid() is equivalent
- * to calling CloseHandle() on the process handle returned in
- * @child_pid).
- *
- * %G_SPAWN_LEAVE_DESCRIPTORS_OPEN means that the parent's open file
- * descriptors will be inherited by the child; otherwise all
- * descriptors except stdin/stdout/stderr will be closed before
- * calling exec() in the child. %G_SPAWN_SEARCH_PATH
- * means that <literal>argv[0]</literal> need not be an absolute path, it
- * will be looked for in the user's <envar>PATH</envar>.
- * %G_SPAWN_STDOUT_TO_DEV_NULL means that the child's standard output will
- * be discarded, instead of going to the same location as the parent's
- * standard output. If you use this flag, @standard_output must be %NULL.
- * %G_SPAWN_STDERR_TO_DEV_NULL means that the child's standard error
- * will be discarded, instead of going to the same location as the parent's
- * standard error. If you use this flag, @standard_error must be %NULL.
- * %G_SPAWN_CHILD_INHERITS_STDIN means that the child will inherit the parent's
- * standard input (by default, the child's standard input is attached to
- * /dev/null). If you use this flag, @standard_input must be %NULL.
- * %G_SPAWN_FILE_AND_ARGV_ZERO means that the first element of @argv is
- * the file to execute, while the remaining elements are the
- * actual argument vector to pass to the file. Normally
- * g_spawn_async_with_pipes() uses @argv[0] as the file to execute, and
- * passes all of @argv to the child.
- *
- * @child_setup and @user_data are a function and user data. On POSIX
- * platforms, the function is called in the child after GLib has
- * performed all the setup it plans to perform (including creating
- * pipes, closing file descriptors, etc.) but before calling
- * exec(). That is, @child_setup is called just
- * before calling exec() in the child. Obviously
- * actions taken in this function will only affect the child, not the
- * parent.
- *
- * On Windows, there is no separate fork() and exec()
- * functionality. Child processes are created and run with a single
- * API call, CreateProcess(). There is no sensible thing @child_setup
- * could be used for on Windows so it is ignored and not called.
- *
- * If non-%NULL, @child_pid will on Unix be filled with the child's
- * process ID. You can use the process ID to send signals to the
- * child, or to use g_child_watch_add() (or waitpid()) if you specified the
- * %G_SPAWN_DO_NOT_REAP_CHILD flag. On Windows, @child_pid will be
- * filled with a handle to the child process only if you specified the
- * %G_SPAWN_DO_NOT_REAP_CHILD flag. You can then access the child
- * process using the Win32 API, for example wait for its termination
- * with the <function>WaitFor*()</function> functions, or examine its
- * exit code with GetExitCodeProcess(). You should close the handle
- * with CloseHandle() or g_spawn_close_pid() when you no longer need it.
- *
- * If non-%NULL, the @standard_input, @standard_output, @standard_error
- * locations will be filled with file descriptors for writing to the child's
- * standard input or reading from its standard output or standard error.
- * The caller of g_spawn_async_with_pipes() must close these file descriptors
- * when they are no longer in use. If these parameters are %NULL, the corresponding
- * pipe won't be created.
- *
- * If @standard_input is NULL, the child's standard input is attached to
- * /dev/null unless %G_SPAWN_CHILD_INHERITS_STDIN is set.
- *
- * If @standard_error is NULL, the child's standard error goes to the same
- * location as the parent's standard error unless %G_SPAWN_STDERR_TO_DEV_NULL
- * is set.
- *
- * If @standard_output is NULL, the child's standard output goes to the same
- * location as the parent's standard output unless %G_SPAWN_STDOUT_TO_DEV_NULL
- * is set.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report errors.
- * If an error is set, the function returns %FALSE. Errors
- * are reported even if they occur in the child (for example if the
- * executable in <literal>argv[0]</literal> is not found). Typically
- * the <literal>message</literal> field of returned errors should be displayed
- * to users. Possible errors are those from the #G_SPAWN_ERROR domain.
- *
- * If an error occurs, @child_pid, @standard_input, @standard_output,
- * and @standard_error will not be filled with valid values.
- *
- * If @child_pid is not %NULL and an error does not occur then the returned
- * process reference must be closed using g_spawn_close_pid().
- *
- * <note><para>
- * If you are writing a GTK+ application, and the program you
- * are spawning is a graphical application, too, then you may
- * want to use gdk_spawn_on_screen_with_pipes() instead to ensure that
- * the spawned program opens its windows on the right screen.
- * </para></note>
- *
- * Return value: %TRUE on success, %FALSE if an error was set
- **/
-gboolean
-g_spawn_async_with_pipes (const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- GSpawnFlags flags,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_pid,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- GError **error)
-{
- g_return_val_if_fail (argv != NULL, FALSE);
- g_return_val_if_fail (standard_output == NULL ||
- !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
- g_return_val_if_fail (standard_error == NULL ||
- !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
- /* can't inherit stdin if we have an input pipe. */
- g_return_val_if_fail (standard_input == NULL ||
- !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
-
- return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
- working_directory,
- argv,
- envp,
- !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
- (flags & G_SPAWN_SEARCH_PATH) != 0,
- (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
- (flags & G_SPAWN_FILE_AND_ARGV_ZERO) != 0,
- child_setup,
- user_data,
- child_pid,
- standard_input,
- standard_output,
- standard_error,
- error);
-}
-
-/**
- * g_spawn_command_line_sync:
- * @command_line: a command line
- * @standard_output: return location for child output
- * @standard_error: return location for child errors
- * @exit_status: return location for child exit status, as returned by waitpid()
- * @error: return location for errors
- *
- * A simple version of g_spawn_sync() with little-used parameters
- * removed, taking a command line instead of an argument vector. See
- * g_spawn_sync() for full details. @command_line will be parsed by
- * g_shell_parse_argv(). Unlike g_spawn_sync(), the %G_SPAWN_SEARCH_PATH flag
- * is enabled. Note that %G_SPAWN_SEARCH_PATH can have security
- * implications, so consider using g_spawn_sync() directly if
- * appropriate. Possible errors are those from g_spawn_sync() and those
- * from g_shell_parse_argv().
- *
- * If @exit_status is non-%NULL, the exit status of the child is stored there as
- * it would be returned by waitpid(); standard UNIX macros such as WIFEXITED()
- * and WEXITSTATUS() must be used to evaluate the exit status.
- *
- * On Windows, please note the implications of g_shell_parse_argv()
- * parsing @command_line. Parsing is done according to Unix shell rules, not
- * Windows command interpreter rules.
- * Space is a separator, and backslashes are
- * special. Thus you cannot simply pass a @command_line containing
- * canonical Windows paths, like "c:\\program files\\app\\app.exe", as
- * the backslashes will be eaten, and the space will act as a
- * separator. You need to enclose such paths with single quotes, like
- * "'c:\\program files\\app\\app.exe' 'e:\\folder\\argument.txt'".
- *
- * Return value: %TRUE on success, %FALSE if an error was set
- **/
-gboolean
-g_spawn_command_line_sync (const gchar *command_line,
- gchar **standard_output,
- gchar **standard_error,
- gint *exit_status,
- GError **error)
-{
- gboolean retval;
- gchar **argv = NULL;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_sync (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- standard_output,
- standard_error,
- exit_status,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-/**
- * g_spawn_command_line_async:
- * @command_line: a command line
- * @error: return location for errors
- *
- * A simple version of g_spawn_async() that parses a command line with
- * g_shell_parse_argv() and passes it to g_spawn_async(). Runs a
- * command line in the background. Unlike g_spawn_async(), the
- * %G_SPAWN_SEARCH_PATH flag is enabled, other flags are not. Note
- * that %G_SPAWN_SEARCH_PATH can have security implications, so
- * consider using g_spawn_async() directly if appropriate. Possible
- * errors are those from g_shell_parse_argv() and g_spawn_async().
- *
- * The same concerns on Windows apply as for g_spawn_command_line_sync().
- *
- * Return value: %TRUE on success, %FALSE if error is set.
- **/
-gboolean
-g_spawn_command_line_async (const gchar *command_line,
- GError **error)
-{
- gboolean retval;
- gchar **argv = NULL;
-
- g_return_val_if_fail (command_line != NULL, FALSE);
-
- if (!g_shell_parse_argv (command_line,
- NULL, &argv,
- error))
- return FALSE;
-
- retval = g_spawn_async (NULL,
- argv,
- NULL,
- G_SPAWN_SEARCH_PATH,
- NULL,
- NULL,
- NULL,
- error);
- g_strfreev (argv);
-
- return retval;
-}
-
-static gint
-exec_err_to_g_error (gint en)
-{
- switch (en)
- {
-#ifdef EACCES
- case EACCES:
- return G_SPAWN_ERROR_ACCES;
- break;
-#endif
-
-#ifdef EPERM
- case EPERM:
- return G_SPAWN_ERROR_PERM;
- break;
-#endif
-
-#ifdef E2BIG
- case E2BIG:
- return G_SPAWN_ERROR_2BIG;
- break;
-#endif
-
-#ifdef ENOEXEC
- case ENOEXEC:
- return G_SPAWN_ERROR_NOEXEC;
- break;
-#endif
-
-#ifdef ENAMETOOLONG
- case ENAMETOOLONG:
- return G_SPAWN_ERROR_NAMETOOLONG;
- break;
-#endif
-
-#ifdef ENOENT
- case ENOENT:
- return G_SPAWN_ERROR_NOENT;
- break;
-#endif
-
-#ifdef ENOMEM
- case ENOMEM:
- return G_SPAWN_ERROR_NOMEM;
- break;
-#endif
-
-#ifdef ENOTDIR
- case ENOTDIR:
- return G_SPAWN_ERROR_NOTDIR;
- break;
-#endif
-
-#ifdef ELOOP
- case ELOOP:
- return G_SPAWN_ERROR_LOOP;
- break;
-#endif
-
-#ifdef ETXTBUSY
- case ETXTBUSY:
- return G_SPAWN_ERROR_TXTBUSY;
- break;
-#endif
-
-#ifdef EIO
- case EIO:
- return G_SPAWN_ERROR_IO;
- break;
-#endif
-
-#ifdef ENFILE
- case ENFILE:
- return G_SPAWN_ERROR_NFILE;
- break;
-#endif
-
-#ifdef EMFILE
- case EMFILE:
- return G_SPAWN_ERROR_MFILE;
- break;
-#endif
-
-#ifdef EINVAL
- case EINVAL:
- return G_SPAWN_ERROR_INVAL;
- break;
-#endif
-
-#ifdef EISDIR
- case EISDIR:
- return G_SPAWN_ERROR_ISDIR;
- break;
-#endif
-
-#ifdef ELIBBAD
- case ELIBBAD:
- return G_SPAWN_ERROR_LIBBAD;
- break;
-#endif
-
- default:
- return G_SPAWN_ERROR_FAILED;
- break;
- }
-}
-
-static gssize
-write_all (gint fd, gconstpointer vbuf, gsize to_write)
-{
- gchar *buf = (gchar *) vbuf;
-
- while (to_write > 0)
- {
- gssize count = write (fd, buf, to_write);
- if (count < 0)
- {
- if (errno != EINTR)
- return FALSE;
- }
- else
- {
- to_write -= count;
- buf += count;
- }
- }
-
- return TRUE;
-}
-
-G_GNUC_NORETURN
-static void
-write_err_and_exit (gint fd, gint msg)
-{
- gint en = errno;
-
- write_all (fd, &msg, sizeof(msg));
- write_all (fd, &en, sizeof(en));
-
- _exit (1);
-}
-
-static int
-set_cloexec (void *data, gint fd)
-{
- if (fd >= GPOINTER_TO_INT (data))
- fcntl (fd, F_SETFD, FD_CLOEXEC);
-
- return 0;
-}
-
-#ifndef HAVE_FDWALK
-static int
-fdwalk (int (*cb)(void *data, int fd), void *data)
-{
- gint open_max;
- gint fd;
- gint res = 0;
-
-#ifdef HAVE_SYS_RESOURCE_H
- struct rlimit rl;
-#endif
-
-#ifdef __linux__
- DIR *d;
-
- if ((d = opendir("/proc/self/fd"))) {
- struct dirent *de;
-
- while ((de = readdir(d))) {
- glong l;
- gchar *e = NULL;
-
- if (de->d_name[0] == '.')
- continue;
-
- errno = 0;
- l = strtol(de->d_name, &e, 10);
- if (errno != 0 || !e || *e)
- continue;
-
- fd = (gint) l;
-
- if ((glong) fd != l)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if ((res = cb (data, fd)) != 0)
- break;
- }
-
- closedir(d);
- return res;
- }
-
- /* If /proc is not mounted or not accessible we fall back to the old
- * rlimit trick */
-
-#endif
-
-#ifdef HAVE_SYS_RESOURCE_H
-
- if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY)
- open_max = rl.rlim_max;
- else
-#endif
- open_max = sysconf (_SC_OPEN_MAX);
-
- for (fd = 0; fd < open_max; fd++)
- if ((res = cb (data, fd)) != 0)
- break;
-
- return res;
-}
-#endif
-
-static gint
-sane_dup2 (gint fd1, gint fd2)
-{
- gint ret;
-
- retry:
- ret = dup2 (fd1, fd2);
- if (ret < 0 && errno == EINTR)
- goto retry;
-
- return ret;
-}
-
-enum
-{
- CHILD_CHDIR_FAILED,
- CHILD_EXEC_FAILED,
- CHILD_DUP2_FAILED,
- CHILD_FORK_FAILED
-};
-
-static void
-do_exec (gint child_err_report_fd,
- gint stdin_fd,
- gint stdout_fd,
- gint stderr_fd,
- const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- gboolean close_descriptors,
- gboolean search_path,
- gboolean stdout_to_null,
- gboolean stderr_to_null,
- gboolean child_inherits_stdin,
- gboolean file_and_argv_zero,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data)
-{
- if (working_directory && chdir (working_directory) < 0)
- write_err_and_exit (child_err_report_fd,
- CHILD_CHDIR_FAILED);
-
- /* Close all file descriptors but stdin stdout and stderr as
- * soon as we exec. Note that this includes
- * child_err_report_fd, which keeps the parent from blocking
- * forever on the other end of that pipe.
- */
- if (close_descriptors)
- {
- fdwalk (set_cloexec, GINT_TO_POINTER(3));
- }
- else
- {
- /* We need to do child_err_report_fd anyway */
- set_cloexec (GINT_TO_POINTER(0), child_err_report_fd);
- }
-
- /* Redirect pipes as required */
-
- if (stdin_fd >= 0)
- {
- /* dup2 can't actually fail here I don't think */
-
- if (sane_dup2 (stdin_fd, 0) < 0)
- write_err_and_exit (child_err_report_fd,
- CHILD_DUP2_FAILED);
-
- /* ignore this if it doesn't work */
- close_and_invalidate (&stdin_fd);
- }
- else if (!child_inherits_stdin)
- {
- /* Keep process from blocking on a read of stdin */
- gint read_null = open ("/dev/null", O_RDONLY);
- sane_dup2 (read_null, 0);
- close_and_invalidate (&read_null);
- }
-
- if (stdout_fd >= 0)
- {
- /* dup2 can't actually fail here I don't think */
-
- if (sane_dup2 (stdout_fd, 1) < 0)
- write_err_and_exit (child_err_report_fd,
- CHILD_DUP2_FAILED);
-
- /* ignore this if it doesn't work */
- close_and_invalidate (&stdout_fd);
- }
- else if (stdout_to_null)
- {
- gint write_null = open ("/dev/null", O_WRONLY);
- sane_dup2 (write_null, 1);
- close_and_invalidate (&write_null);
- }
-
- if (stderr_fd >= 0)
- {
- /* dup2 can't actually fail here I don't think */
-
- if (sane_dup2 (stderr_fd, 2) < 0)
- write_err_and_exit (child_err_report_fd,
- CHILD_DUP2_FAILED);
-
- /* ignore this if it doesn't work */
- close_and_invalidate (&stderr_fd);
- }
- else if (stderr_to_null)
- {
- gint write_null = open ("/dev/null", O_WRONLY);
- sane_dup2 (write_null, 2);
- close_and_invalidate (&write_null);
- }
-
- /* Call user function just before we exec */
- if (child_setup)
- {
- (* child_setup) (user_data);
- }
-
- g_execute (argv[0],
- file_and_argv_zero ? argv + 1 : argv,
- envp, search_path);
-
- /* Exec failed */
- write_err_and_exit (child_err_report_fd,
- CHILD_EXEC_FAILED);
-}
-
-static gboolean
-read_ints (int fd,
- gint* buf,
- gint n_ints_in_buf,
- gint *n_ints_read,
- GError **error)
-{
- gsize bytes = 0;
-
- while (TRUE)
- {
- gssize chunk;
-
- if (bytes >= sizeof(gint)*2)
- break; /* give up, who knows what happened, should not be
- * possible.
- */
-
- again:
- chunk = read (fd,
- ((gchar*)buf) + bytes,
- sizeof(gint) * n_ints_in_buf - bytes);
- if (chunk < 0 && errno == EINTR)
- goto again;
-
- if (chunk < 0)
- {
- int errsv = errno;
-
- /* Some weird shit happened, bail out */
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Failed to read from child pipe (%s)"),
- g_strerror (errsv));
-
- return FALSE;
- }
- else if (chunk == 0)
- break; /* EOF */
- else /* chunk > 0 */
- bytes += chunk;
- }
-
- *n_ints_read = (gint)(bytes / sizeof(gint));
-
- return TRUE;
-}
-
-static gboolean
-fork_exec_with_pipes (gboolean intermediate_child,
- const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- gboolean close_descriptors,
- gboolean search_path,
- gboolean stdout_to_null,
- gboolean stderr_to_null,
- gboolean child_inherits_stdin,
- gboolean file_and_argv_zero,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- GPid *child_pid,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- GError **error)
-{
- GPid pid = -1;
- gint stdin_pipe[2] = { -1, -1 };
- gint stdout_pipe[2] = { -1, -1 };
- gint stderr_pipe[2] = { -1, -1 };
- gint child_err_report_pipe[2] = { -1, -1 };
- gint child_pid_report_pipe[2] = { -1, -1 };
- gint status;
-
- if (!make_pipe (child_err_report_pipe, error))
- return FALSE;
-
- if (intermediate_child && !make_pipe (child_pid_report_pipe, error))
- goto cleanup_and_fail;
-
- if (standard_input && !make_pipe (stdin_pipe, error))
- goto cleanup_and_fail;
-
- if (standard_output && !make_pipe (stdout_pipe, error))
- goto cleanup_and_fail;
-
- if (standard_error && !make_pipe (stderr_pipe, error))
- goto cleanup_and_fail;
-
- pid = fork ();
-
- if (pid < 0)
- {
- int errsv = errno;
-
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FORK,
- _("Failed to fork (%s)"),
- g_strerror (errsv));
-
- goto cleanup_and_fail;
- }
- else if (pid == 0)
- {
- /* Immediate child. This may or may not be the child that
- * actually execs the new process.
- */
-
- /* Be sure we crash if the parent exits
- * and we write to the err_report_pipe
- */
- signal (SIGPIPE, SIG_DFL);
-
- /* Close the parent's end of the pipes;
- * not needed in the close_descriptors case,
- * though
- */
- close_and_invalidate (&child_err_report_pipe[0]);
- close_and_invalidate (&child_pid_report_pipe[0]);
- close_and_invalidate (&stdin_pipe[1]);
- close_and_invalidate (&stdout_pipe[0]);
- close_and_invalidate (&stderr_pipe[0]);
-
- if (intermediate_child)
- {
- /* We need to fork an intermediate child that launches the
- * final child. The purpose of the intermediate child
- * is to exit, so we can waitpid() it immediately.
- * Then the grandchild will not become a zombie.
- */
- GPid grandchild_pid;
-
- grandchild_pid = fork ();
-
- if (grandchild_pid < 0)
- {
- /* report -1 as child PID */
- write_all (child_pid_report_pipe[1], &grandchild_pid,
- sizeof(grandchild_pid));
-
- write_err_and_exit (child_err_report_pipe[1],
- CHILD_FORK_FAILED);
- }
- else if (grandchild_pid == 0)
- {
- do_exec (child_err_report_pipe[1],
- stdin_pipe[0],
- stdout_pipe[1],
- stderr_pipe[1],
- working_directory,
- argv,
- envp,
- close_descriptors,
- search_path,
- stdout_to_null,
- stderr_to_null,
- child_inherits_stdin,
- file_and_argv_zero,
- child_setup,
- user_data);
- }
- else
- {
- write_all (child_pid_report_pipe[1], &grandchild_pid, sizeof(grandchild_pid));
- close_and_invalidate (&child_pid_report_pipe[1]);
-
- _exit (0);
- }
- }
- else
- {
- /* Just run the child.
- */
-
- do_exec (child_err_report_pipe[1],
- stdin_pipe[0],
- stdout_pipe[1],
- stderr_pipe[1],
- working_directory,
- argv,
- envp,
- close_descriptors,
- search_path,
- stdout_to_null,
- stderr_to_null,
- child_inherits_stdin,
- file_and_argv_zero,
- child_setup,
- user_data);
- }
- }
- else
- {
- /* Parent */
-
- gint buf[2];
- gint n_ints = 0;
-
- /* Close the uncared-about ends of the pipes */
- close_and_invalidate (&child_err_report_pipe[1]);
- close_and_invalidate (&child_pid_report_pipe[1]);
- close_and_invalidate (&stdin_pipe[0]);
- close_and_invalidate (&stdout_pipe[1]);
- close_and_invalidate (&stderr_pipe[1]);
-
- /* If we had an intermediate child, reap it */
- if (intermediate_child)
- {
- wait_again:
- if (waitpid (pid, &status, 0) < 0)
- {
- if (errno == EINTR)
- goto wait_again;
- else if (errno == ECHILD)
- ; /* do nothing, child already reaped */
- else
- g_warning ("waitpid() should not fail in "
- "'fork_exec_with_pipes'");
- }
- }
-
-
- if (!read_ints (child_err_report_pipe[0],
- buf, 2, &n_ints,
- error))
- goto cleanup_and_fail;
-
- if (n_ints >= 2)
- {
- /* Error from the child. */
-
- switch (buf[0])
- {
- case CHILD_CHDIR_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_CHDIR,
- _("Failed to change to directory '%s' (%s)"),
- working_directory,
- g_strerror (buf[1]));
-
- break;
-
- case CHILD_EXEC_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- exec_err_to_g_error (buf[1]),
- _("Failed to execute child process \"%s\" (%s)"),
- argv[0],
- g_strerror (buf[1]));
-
- break;
-
- case CHILD_DUP2_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Failed to redirect output or input of child process (%s)"),
- g_strerror (buf[1]));
-
- break;
-
- case CHILD_FORK_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FORK,
- _("Failed to fork child process (%s)"),
- g_strerror (buf[1]));
- break;
-
- default:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Unknown error executing child process \"%s\""),
- argv[0]);
- break;
- }
-
- goto cleanup_and_fail;
- }
-
- /* Get child pid from intermediate child pipe. */
- if (intermediate_child)
- {
- n_ints = 0;
-
- if (!read_ints (child_pid_report_pipe[0],
- buf, 1, &n_ints, error))
- goto cleanup_and_fail;
-
- if (n_ints < 1)
- {
- int errsv = errno;
-
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Failed to read enough data from child pid pipe (%s)"),
- g_strerror (errsv));
- goto cleanup_and_fail;
- }
- else
- {
- /* we have the child pid */
- pid = buf[0];
- }
- }
-
- /* Success against all odds! return the information */
- close_and_invalidate (&child_err_report_pipe[0]);
- close_and_invalidate (&child_pid_report_pipe[0]);
-
- if (child_pid)
- *child_pid = pid;
-
- if (standard_input)
- *standard_input = stdin_pipe[1];
- if (standard_output)
- *standard_output = stdout_pipe[0];
- if (standard_error)
- *standard_error = stderr_pipe[0];
-
- return TRUE;
- }
-
- cleanup_and_fail:
-
- /* There was an error from the Child, reap the child to avoid it being
- a zombie.
- */
-
- if (pid > 0)
- {
- wait_failed:
- if (waitpid (pid, NULL, 0) < 0)
- {
- if (errno == EINTR)
- goto wait_failed;
- else if (errno == ECHILD)
- ; /* do nothing, child already reaped */
- else
- g_warning ("waitpid() should not fail in "
- "'fork_exec_with_pipes'");
- }
- }
-
- close_and_invalidate (&child_err_report_pipe[0]);
- close_and_invalidate (&child_err_report_pipe[1]);
- close_and_invalidate (&child_pid_report_pipe[0]);
- close_and_invalidate (&child_pid_report_pipe[1]);
- close_and_invalidate (&stdin_pipe[0]);
- close_and_invalidate (&stdin_pipe[1]);
- close_and_invalidate (&stdout_pipe[0]);
- close_and_invalidate (&stdout_pipe[1]);
- close_and_invalidate (&stderr_pipe[0]);
- close_and_invalidate (&stderr_pipe[1]);
-
- return FALSE;
-}
-
-static gboolean
-make_pipe (gint p[2],
- GError **error)
-{
- if (pipe (p) < 0)
- {
- gint errsv = errno;
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Failed to create pipe for communicating with child process (%s)"),
- g_strerror (errsv));
- return FALSE;
- }
- else
- return TRUE;
-}
-
-/* Based on execvp from GNU C Library */
-
-static void
-script_execute (const gchar *file,
- gchar **argv,
- gchar **envp,
- gboolean search_path)
-{
- /* Count the arguments. */
- int argc = 0;
- while (argv[argc])
- ++argc;
-
- /* Construct an argument list for the shell. */
- {
- gchar **new_argv;
-
- new_argv = g_new0 (gchar*, argc + 2); /* /bin/sh and NULL */
-
- new_argv[0] = (char *) "/bin/sh";
- new_argv[1] = (char *) file;
- while (argc > 0)
- {
- new_argv[argc + 1] = argv[argc];
- --argc;
- }
-
- /* Execute the shell. */
- if (envp)
- execve (new_argv[0], new_argv, envp);
- else
- execv (new_argv[0], new_argv);
-
- g_free (new_argv);
- }
-}
-
-static gchar*
-my_strchrnul (const gchar *str, gchar c)
-{
- gchar *p = (gchar*) str;
- while (*p && (*p != c))
- ++p;
-
- return p;
-}
-
-static gint
-g_execute (const gchar *file,
- gchar **argv,
- gchar **envp,
- gboolean search_path)
-{
- if (*file == '\0')
- {
- /* We check the simple case first. */
- errno = ENOENT;
- return -1;
- }
-
- if (!search_path || strchr (file, '/') != NULL)
- {
- /* Don't search when it contains a slash. */
- if (envp)
- execve (file, argv, envp);
- else
- execv (file, argv);
-
- if (errno == ENOEXEC)
- script_execute (file, argv, envp, FALSE);
- }
- else
- {
- gboolean got_eacces = 0;
- const gchar *path, *p;
- gchar *name, *freeme;
- gsize len;
- gsize pathlen;
-
- path = g_getenv ("PATH");
- if (path == NULL)
- {
- /* There is no `PATH' in the environment. The default
- * search path in libc is the current directory followed by
- * the path `confstr' returns for `_CS_PATH'.
- */
-
- /* In GLib we put . last, for security, and don't use the
- * unportable confstr(); UNIX98 does not actually specify
- * what to search if PATH is unset. POSIX may, dunno.
- */
-
- path = "/bin:/usr/bin:.";
- }
-
- len = strlen (file) + 1;
- pathlen = strlen (path);
- freeme = name = g_malloc (pathlen + len + 1);
-
- /* Copy the file name at the top, including '\0' */
- memcpy (name + pathlen + 1, file, len);
- name = name + pathlen;
- /* And add the slash before the filename */
- *name = '/';
-
- p = path;
- do
- {
- char *startp;
-
- path = p;
- p = my_strchrnul (path, ':');
-
- if (p == path)
- /* Two adjacent colons, or a colon at the beginning or the end
- * of `PATH' means to search the current directory.
- */
- startp = name + 1;
- else
- startp = memcpy (name - (p - path), path, p - path);
-
- /* Try to execute this name. If it works, execv will not return. */
- if (envp)
- execve (startp, argv, envp);
- else
- execv (startp, argv);
-
- if (errno == ENOEXEC)
- script_execute (startp, argv, envp, search_path);
-
- switch (errno)
- {
- case EACCES:
- /* Record the we got a `Permission denied' error. If we end
- * up finding no executable we can use, we want to diagnose
- * that we did find one but were denied access.
- */
- got_eacces = TRUE;
-
- /* FALL THRU */
-
- case ENOENT:
-#ifdef ESTALE
- case ESTALE:
-#endif
-#ifdef ENOTDIR
- case ENOTDIR:
-#endif
- /* Those errors indicate the file is missing or not executable
- * by us, in which case we want to just try the next path
- * directory.
- */
- break;
-
- default:
- /* Some other error means we found an executable file, but
- * something went wrong executing it; return the error to our
- * caller.
- */
- g_free (freeme);
- return -1;
- }
- }
- while (*p++ != '\0');
-
- /* We tried every element and none of them worked. */
- if (got_eacces)
- /* At least one failure was due to permissions, so report that
- * error.
- */
- errno = EACCES;
-
- g_free (freeme);
- }
-
- /* Return the error from the last attempt (probably ENOENT). */
- return -1;
-}
-
-/**
- * g_spawn_close_pid:
- * @pid: The process reference to close
- *
- * On some platforms, notably Windows, the #GPid type represents a resource
- * which must be closed to prevent resource leaking. g_spawn_close_pid()
- * is provided for this purpose. It should be used on all platforms, even
- * though it doesn't do anything under UNIX.
- **/
-void
-g_spawn_close_pid (GPid pid)
-{
-}
+++ /dev/null
-/* gstdio.c - wrappers for C library functions
- *
- * Copyright 2004 Tor Lillqvist
- *
- * GLib 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 of the
- * License, or (at your option) any later version.
- *
- * GLib 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 GLib; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#define G_STDIO_NO_WRAP_ON_UNIX
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#include <errno.h>
-#include <wchar.h>
-#include <direct.h>
-#include <io.h>
-#include <sys/utime.h>
-#else
-#include <utime.h>
-#endif
-
-#include "gstdio.h"
-
-
-#if !defined (G_OS_UNIX) && !defined (G_OS_WIN32) && !defined (G_OS_BEOS)
-#error Please port this to your operating system
-#endif
-
-#if defined (_MSC_VER) && !defined(_WIN64)
-#undef _wstat
-#define _wstat _wstat32
-#endif
-
-/**
- * g_access:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: as in access()
- *
- * A wrapper for the POSIX access() function. This function is used to
- * test a pathname for one or several of read, write or execute
- * permissions, or just existence.
- *
- * On Windows, the file protection mechanism is not at all POSIX-like,
- * and the underlying function in the C library only checks the
- * FAT-style READONLY attribute, and does not look at the ACL of a
- * file at all. This function is this in practise almost useless on
- * Windows. Software that needs to handle file permissions on Windows
- * more exactly should use the Win32 API.
- *
- * See your C library manual for more details about access().
- *
- * Returns: zero if the pathname refers to an existing file system
- * object that has all the tested permissions, or -1 otherwise or on
- * error.
- *
- * Since: 2.8
- */
-int
-g_access (const gchar *filename,
- int mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
-#ifndef X_OK
-#define X_OK 1
-#endif
-
- retval = _waccess (wfilename, mode & ~X_OK);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return access (filename, mode);
-#endif
-}
-
-/**
- * g_chmod:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: as in chmod()
- *
- * A wrapper for the POSIX chmod() function. The chmod() function is
- * used to set the permissions of a file system object.
- *
- * On Windows the file protection mechanism is not at all POSIX-like,
- * and the underlying chmod() function in the C library just sets or
- * clears the FAT-style READONLY attribute. It does not touch any
- * ACL. Software that needs to manage file permissions on Windows
- * exactly should use the Win32 API.
- *
- * See your C library manual for more details about chmod().
- *
- * Returns: zero if the operation succeeded, -1 on error.
- *
- * Since: 2.8
- */
-int
-g_chmod (const gchar *filename,
- int mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wchmod (wfilename, mode);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return chmod (filename, mode);
-#endif
-}
-/**
- * g_open:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @flags: as in open()
- * @mode: as in open()
- *
- * A wrapper for the POSIX open() function. The open() function is
- * used to convert a pathname into a file descriptor.
- *
- * On POSIX systems file descriptors are implemented by the operating
- * system. On Windows, it's the C library that implements open() and
- * file descriptors. The actual Win32 API for opening files is quite
- * different, see MSDN documentation for CreateFile(). The Win32 API
- * uses file handles, which are more randomish integers, not small
- * integers like file descriptors.
- *
- * Because file descriptors are specific to the C library on Windows,
- * the file descriptor returned by this function makes sense only to
- * functions in the same C library. Thus if the GLib-using code uses a
- * different C library than GLib does, the file descriptor returned by
- * this function cannot be passed to C library functions like write()
- * or read().
- *
- * See your C library manual for more details about open().
- *
- * Returns: a new file descriptor, or -1 if an error occurred. The
- * return value can be used exactly like the return value from open().
- *
- * Since: 2.6
- */
-int
-g_open (const gchar *filename,
- int flags,
- int mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wopen (wfilename, flags, mode);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return open (filename, flags, mode);
-#endif
-}
-
-/**
- * g_creat:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: as in creat()
- *
- * A wrapper for the POSIX creat() function. The creat() function is
- * used to convert a pathname into a file descriptor, creating a file
- * if necessary.
-
- * On POSIX systems file descriptors are implemented by the operating
- * system. On Windows, it's the C library that implements creat() and
- * file descriptors. The actual Windows API for opening files is
- * different, see MSDN documentation for CreateFile(). The Win32 API
- * uses file handles, which are more randomish integers, not small
- * integers like file descriptors.
- *
- * Because file descriptors are specific to the C library on Windows,
- * the file descriptor returned by this function makes sense only to
- * functions in the same C library. Thus if the GLib-using code uses a
- * different C library than GLib does, the file descriptor returned by
- * this function cannot be passed to C library functions like write()
- * or read().
- *
- * See your C library manual for more details about creat().
- *
- * Returns: a new file descriptor, or -1 if an error occurred. The
- * return value can be used exactly like the return value from creat().
- *
- * Since: 2.8
- */
-int
-g_creat (const gchar *filename,
- int mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wcreat (wfilename, mode);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return creat (filename, mode);
-#endif
-}
-
-/**
- * g_rename:
- * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @newfilename: a pathname in the GLib file name encoding
- *
- * A wrapper for the POSIX rename() function. The rename() function
- * renames a file, moving it between directories if required.
- *
- * See your C library manual for more details about how rename() works
- * on your system. It is not possible in general on Windows to rename
- * a file that is open to some process.
- *
- * Returns: 0 if the renaming succeeded, -1 if an error occurred
- *
- * Since: 2.6
- */
-int
-g_rename (const gchar *oldfilename,
- const gchar *newfilename)
-{
-#ifdef G_OS_WIN32
- wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
- wchar_t *wnewfilename;
- int retval;
- int save_errno = 0;
-
- if (woldfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
-
- if (wnewfilename == NULL)
- {
- g_free (woldfilename);
- errno = EINVAL;
- return -1;
- }
-
- if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
- retval = 0;
- else
- {
- retval = -1;
- switch (GetLastError ())
- {
-#define CASE(a,b) case ERROR_##a: save_errno = b; break
- CASE (FILE_NOT_FOUND, ENOENT);
- CASE (PATH_NOT_FOUND, ENOENT);
- CASE (ACCESS_DENIED, EACCES);
- CASE (NOT_SAME_DEVICE, EXDEV);
- CASE (LOCK_VIOLATION, EACCES);
- CASE (SHARING_VIOLATION, EACCES);
- CASE (FILE_EXISTS, EEXIST);
- CASE (ALREADY_EXISTS, EEXIST);
-#undef CASE
- default: save_errno = EIO;
- }
- }
-
- g_free (woldfilename);
- g_free (wnewfilename);
-
- errno = save_errno;
- return retval;
-#else
- return rename (oldfilename, newfilename);
-#endif
-}
-
-/**
- * g_mkdir:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: permissions to use for the newly created directory
- *
- * A wrapper for the POSIX mkdir() function. The mkdir() function
- * attempts to create a directory with the given name and permissions.
- * The mode argument is ignored on Windows.
- *
- * See your C library manual for more details about mkdir().
- *
- * Returns: 0 if the directory was successfully created, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_mkdir (const gchar *filename,
- int mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wmkdir (wfilename);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return mkdir (filename, mode);
-#endif
-}
-
-/**
- * g_chdir:
- * @path: a pathname in the GLib file name encoding (UTF-8 on Windows)
- *
- * A wrapper for the POSIX chdir() function. The function changes the
- * current directory of the process to @path.
- *
- * See your C library manual for more details about chdir().
- *
- * Returns: 0 on success, -1 if an error occurred.
- *
- * Since: 2.8
- */
-int
-g_chdir (const gchar *path)
-{
-#ifdef G_OS_WIN32
- wchar_t *wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wpath == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wchdir (wpath);
- save_errno = errno;
-
- g_free (wpath);
-
- errno = save_errno;
- return retval;
-#else
- return chdir (path);
-#endif
-}
-
-/**
- * GStatBuf:
- *
- * A type corresponding to the appropriate struct type for the stat
- * system call, depending on the platform and/or compiler being used.
- *
- * See g_stat() for more information.
- **/
-/**
- * g_stat:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @buf: a pointer to a <structname>stat</structname> struct, which
- * will be filled with the file information
- *
- * A wrapper for the POSIX stat() function. The stat() function
- * returns information about a file. On Windows the stat() function in
- * the C library checks only the FAT-style READONLY attribute and does
- * not look at the ACL at all. Thus on Windows the protection bits in
- * the st_mode field are a fabrication of little use.
- *
- * On Windows the Microsoft C libraries have several variants of the
- * <structname>stat</structname> struct and stat() function with names
- * like "_stat", "_stat32", "_stat32i64" and "_stat64i32". The one
- * used here is for 32-bit code the one with 32-bit size and time
- * fields, specifically called "_stat32".
- *
- * In Microsoft's compiler, by default "struct stat" means one with
- * 64-bit time fields while in MinGW "struct stat" is the legacy one
- * with 32-bit fields. To hopefully clear up this messs, the gstdio.h
- * header defines a type GStatBuf which is the appropriate struct type
- * depending on the platform and/or compiler being used. On POSIX it
- * is just "struct stat", but note that even on POSIX platforms,
- * "stat" might be a macro.
- *
- * See your C library manual for more details about stat().
- *
- * Returns: 0 if the information was successfully retrieved, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_stat (const gchar *filename,
- GStatBuf *buf)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
- int len;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- len = wcslen (wfilename);
- while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
- len--;
- if (len > 0 &&
- (!g_path_is_absolute (filename) || len > g_path_skip_root (filename) - filename))
- wfilename[len] = '\0';
-
- retval = _wstat (wfilename, buf);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return stat (filename, buf);
-#endif
-}
-
-/**
- * g_lstat:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @buf: a pointer to a <structname>stat</structname> struct, which
- * will be filled with the file information
- *
- * A wrapper for the POSIX lstat() function. The lstat() function is
- * like stat() except that in the case of symbolic links, it returns
- * information about the symbolic link itself and not the file that it
- * refers to. If the system does not support symbolic links g_lstat()
- * is identical to g_stat().
- *
- * See your C library manual for more details about lstat().
- *
- * Returns: 0 if the information was successfully retrieved, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_lstat (const gchar *filename,
- GStatBuf *buf)
-{
-#ifdef HAVE_LSTAT
- /* This can't be Win32, so don't do the widechar dance. */
- return lstat (filename, buf);
-#else
- return g_stat (filename, buf);
-#endif
-}
-
-/**
- * g_unlink:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- *
- * A wrapper for the POSIX unlink() function. The unlink() function
- * deletes a name from the filesystem. If this was the last link to the
- * file and no processes have it opened, the diskspace occupied by the
- * file is freed.
- *
- * See your C library manual for more details about unlink(). Note
- * that on Windows, it is in general not possible to delete files that
- * are open to some process, or mapped into memory.
- *
- * Returns: 0 if the name was successfully deleted, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_unlink (const gchar *filename)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wunlink (wfilename);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return unlink (filename);
-#endif
-}
-
-/**
- * g_remove:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- *
- * A wrapper for the POSIX remove() function. The remove() function
- * deletes a name from the filesystem.
- *
- * See your C library manual for more details about how remove() works
- * on your system. On Unix, remove() removes also directories, as it
- * calls unlink() for files and rmdir() for directories. On Windows,
- * although remove() in the C library only works for files, this
- * function tries first remove() and then if that fails rmdir(), and
- * thus works for both files and directories. Note however, that on
- * Windows, it is in general not possible to remove a file that is
- * open to some process, or mapped into memory.
- *
- * If this function fails on Windows you can't infer too much from the
- * errno value. rmdir() is tried regardless of what caused remove() to
- * fail. Any errno value set by remove() will be overwritten by that
- * set by rmdir().
- *
- * Returns: 0 if the file was successfully removed, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_remove (const gchar *filename)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wremove (wfilename);
- if (retval == -1)
- retval = _wrmdir (wfilename);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return remove (filename);
-#endif
-}
-
-/**
- * g_rmdir:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- *
- * A wrapper for the POSIX rmdir() function. The rmdir() function
- * deletes a directory from the filesystem.
- *
- * See your C library manual for more details about how rmdir() works
- * on your system.
- *
- * Returns: 0 if the directory was successfully removed, -1 if an error
- * occurred
- *
- * Since: 2.6
- */
-int
-g_rmdir (const gchar *filename)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wrmdir (wfilename);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return rmdir (filename);
-#endif
-}
-
-/**
- * g_fopen:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: a string describing the mode in which the file should be
- * opened
- *
- * A wrapper for the stdio fopen() function. The fopen() function
- * opens a file and associates a new stream with it.
- *
- * Because file descriptors are specific to the C library on Windows,
- * and a file descriptor is partof the <type>FILE</type> struct, the
- * <type>FILE</type> pointer returned by this function makes sense
- * only to functions in the same C library. Thus if the GLib-using
- * code uses a different C library than GLib does, the
- * <type>FILE</type> pointer returned by this function cannot be
- * passed to C library functions like fprintf() or fread().
- *
- * See your C library manual for more details about fopen().
- *
- * Returns: A <type>FILE</type> pointer if the file was successfully
- * opened, or %NULL if an error occurred
- *
- * Since: 2.6
- */
-FILE *
-g_fopen (const gchar *filename,
- const gchar *mode)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- wchar_t *wmode;
- FILE *retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return NULL;
- }
-
- wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
-
- if (wmode == NULL)
- {
- g_free (wfilename);
- errno = EINVAL;
- return NULL;
- }
-
- retval = _wfopen (wfilename, wmode);
- save_errno = errno;
-
- g_free (wfilename);
- g_free (wmode);
-
- errno = save_errno;
- return retval;
-#else
- return fopen (filename, mode);
-#endif
-}
-
-/**
- * g_freopen:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: a string describing the mode in which the file should be
- * opened
- * @stream: an existing stream which will be reused, or %NULL
- *
- * A wrapper for the POSIX freopen() function. The freopen() function
- * opens a file and associates it with an existing stream.
- *
- * See your C library manual for more details about freopen().
- *
- * Returns: A <type>FILE</type> pointer if the file was successfully
- * opened, or %NULL if an error occurred.
- *
- * Since: 2.6
- */
-FILE *
-g_freopen (const gchar *filename,
- const gchar *mode,
- FILE *stream)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- wchar_t *wmode;
- FILE *retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return NULL;
- }
-
- wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
-
- if (wmode == NULL)
- {
- g_free (wfilename);
- errno = EINVAL;
- return NULL;
- }
-
- retval = _wfreopen (wfilename, wmode, stream);
- save_errno = errno;
-
- g_free (wfilename);
- g_free (wmode);
-
- errno = save_errno;
- return retval;
-#else
- return freopen (filename, mode, stream);
-#endif
-}
-
-/**
- * g_utime:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @utb: a pointer to a struct utimbuf.
- *
- * A wrapper for the POSIX utime() function. The utime() function
- * sets the access and modification timestamps of a file.
- *
- * See your C library manual for more details about how utime() works
- * on your system.
- *
- * Returns: 0 if the operation was successful, -1 if an error
- * occurred
- *
- * Since: 2.18
- */
-int
-g_utime (const gchar *filename,
- struct utimbuf *utb)
-{
-#ifdef G_OS_WIN32
- wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
- int retval;
- int save_errno;
-
- if (wfilename == NULL)
- {
- errno = EINVAL;
- return -1;
- }
-
- retval = _wutime (wfilename, (struct _utimbuf*) utb);
- save_errno = errno;
-
- g_free (wfilename);
-
- errno = save_errno;
- return retval;
-#else
- return utime (filename, utb);
-#endif
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#define _GNU_SOURCE /* For stpcpy */
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-#include <errno.h>
-#include <ctype.h> /* For tolower() */
-#if !defined (HAVE_STRSIGNAL) || !defined(NO_SYS_SIGLIST_DECL)
-#include <signal.h>
-#endif
-
-#include "gstrfuncs.h"
-
-#include "gprintf.h"
-#include "gprintfint.h"
-#include "glibintl.h"
-
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif
-
-/* do not include <unistd.h> in this place since it
- * interferes with g_strsignal() on some OSes
- */
-
-static const guint16 ascii_table_data[256] = {
- 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
- 0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
- 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
- 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
- 0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
- 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
- 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
- 0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
- 0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
- 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
- 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
- 0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
- 0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
- 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
- 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
- 0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
- /* the upper 128 are all zeroes */
-};
-
-const guint16 * const g_ascii_table = ascii_table_data;
-
-/**
- * g_strdup:
- * @str: the string to duplicate
- *
- * Duplicates a string. If @str is %NULL it returns %NULL.
- * The returned string should be freed with g_free()
- * when no longer needed.
- *
- * Returns: a newly-allocated copy of @str
- */
-gchar*
-g_strdup (const gchar *str)
-{
- gchar *new_str;
- gsize length;
-
- if (str)
- {
- length = strlen (str) + 1;
- new_str = g_new (char, length);
- memcpy (new_str, str, length);
- }
- else
- new_str = NULL;
-
- return new_str;
-}
-
-/**
- * g_memdup:
- * @mem: the memory to copy.
- * @byte_size: the number of bytes to copy.
- *
- * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it
- * from @mem. If @mem is %NULL it returns %NULL.
- *
- * Returns: a pointer to the newly-allocated copy of the memory, or %NULL if @mem
- * is %NULL.
- */
-gpointer
-g_memdup (gconstpointer mem,
- guint byte_size)
-{
- gpointer new_mem;
-
- if (mem)
- {
- new_mem = g_malloc (byte_size);
- memcpy (new_mem, mem, byte_size);
- }
- else
- new_mem = NULL;
-
- return new_mem;
-}
-
-/**
- * g_strndup:
- * @str: the string to duplicate
- * @n: the maximum number of bytes to copy from @str
- *
- * Duplicates the first @n bytes of a string, returning a newly-allocated
- * buffer @n + 1 bytes long which will always be nul-terminated.
- * If @str is less than @n bytes long the buffer is padded with nuls.
- * If @str is %NULL it returns %NULL.
- * The returned value should be freed when no longer needed.
- *
- * <note><para>
- * To copy a number of characters from a UTF-8 encoded string, use
- * g_utf8_strncpy() instead.
- * </para></note>
- *
- * Returns: a newly-allocated buffer containing the first @n bytes
- * of @str, nul-terminated
- */
-gchar*
-g_strndup (const gchar *str,
- gsize n)
-{
- gchar *new_str;
-
- if (str)
- {
- new_str = g_new (gchar, n + 1);
- strncpy (new_str, str, n);
- new_str[n] = '\0';
- }
- else
- new_str = NULL;
-
- return new_str;
-}
-
-/**
- * g_strnfill:
- * @length: the length of the new string
- * @fill_char: the byte to fill the string with
- *
- * Creates a new string @length bytes long filled with @fill_char.
- * The returned string should be freed when no longer needed.
- *
- * Returns: a newly-allocated string filled the @fill_char
- */
-gchar*
-g_strnfill (gsize length,
- gchar fill_char)
-{
- gchar *str;
-
- str = g_new (gchar, length + 1);
- memset (str, (guchar)fill_char, length);
- str[length] = '\0';
-
- return str;
-}
-
-/**
- * g_stpcpy:
- * @dest: destination buffer.
- * @src: source string.
- *
- * Copies a nul-terminated string into the dest buffer, include the
- * trailing nul, and return a pointer to the trailing nul byte.
- * This is useful for concatenating multiple strings together
- * without having to repeatedly scan for the end.
- *
- * Return value: a pointer to trailing nul byte.
- **/
-gchar *
-g_stpcpy (gchar *dest,
- const gchar *src)
-{
-#ifdef HAVE_STPCPY
- g_return_val_if_fail (dest != NULL, NULL);
- g_return_val_if_fail (src != NULL, NULL);
- return stpcpy (dest, src);
-#else
- register gchar *d = dest;
- register const gchar *s = src;
-
- g_return_val_if_fail (dest != NULL, NULL);
- g_return_val_if_fail (src != NULL, NULL);
- do
- *d++ = *s;
- while (*s++ != '\0');
-
- return d - 1;
-#endif
-}
-
-/**
- * g_strdup_vprintf:
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>
- * @args: the list of parameters to insert into the format string
- *
- * Similar to the standard C vsprintf() function but safer, since it
- * calculates the maximum space required and allocates memory to hold
- * the result. The returned string should be freed with g_free() when
- * no longer needed.
- *
- * See also g_vasprintf(), which offers the same functionality, but
- * additionally returns the length of the allocated string.
- *
- * Returns: a newly-allocated string holding the result
- */
-gchar*
-g_strdup_vprintf (const gchar *format,
- va_list args)
-{
- gchar *string = NULL;
-
- g_vasprintf (&string, format, args);
-
- return string;
-}
-
-/**
- * g_strdup_printf:
- * @format: a standard printf() format string, but notice
- * <link linkend="string-precision">string precision pitfalls</link>
- * @Varargs: the parameters to insert into the format string
- *
- * Similar to the standard C sprintf() function but safer, since it
- * calculates the maximum space required and allocates memory to hold
- * the result. The returned string should be freed with g_free() when no
- * longer needed.
- *
- * Returns: a newly-allocated string holding the result
- */
-gchar*
-g_strdup_printf (const gchar *format,
- ...)
-{
- gchar *buffer;
- va_list args;
-
- va_start (args, format);
- buffer = g_strdup_vprintf (format, args);
- va_end (args);
-
- return buffer;
-}
-
-/**
- * g_strconcat:
- * @string1: the first string to add, which must not be %NULL
- * @Varargs: a %NULL-terminated list of strings to append to the string
- *
- * Concatenates all of the given strings into one long string.
- * The returned string should be freed with g_free() when no longer needed.
- *
- * Note that this function is usually not the right function to use to
- * assemble a translated message from pieces, since proper translation
- * often requires the pieces to be reordered.
- *
- * <warning><para>The variable argument list <emphasis>must</emphasis> end
- * with %NULL. If you forget the %NULL, g_strconcat() will start appending
- * random memory junk to your string.</para></warning>
- *
- * Returns: a newly-allocated string containing all the string arguments
- */
-gchar*
-g_strconcat (const gchar *string1, ...)
-{
- gsize l;
- va_list args;
- gchar *s;
- gchar *concat;
- gchar *ptr;
-
- if (!string1)
- return NULL;
-
- l = 1 + strlen (string1);
- va_start (args, string1);
- s = va_arg (args, gchar*);
- while (s)
- {
- l += strlen (s);
- s = va_arg (args, gchar*);
- }
- va_end (args);
-
- concat = g_new (gchar, l);
- ptr = concat;
-
- ptr = g_stpcpy (ptr, string1);
- va_start (args, string1);
- s = va_arg (args, gchar*);
- while (s)
- {
- ptr = g_stpcpy (ptr, s);
- s = va_arg (args, gchar*);
- }
- va_end (args);
-
- return concat;
-}
-
-/**
- * g_strtod:
- * @nptr: the string to convert to a numeric value.
- * @endptr: if non-%NULL, it returns the character after
- * the last character used in the conversion.
- *
- * Converts a string to a #gdouble value.
- * It calls the standard strtod() function to handle the conversion, but
- * if the string is not completely converted it attempts the conversion
- * again with g_ascii_strtod(), and returns the best match.
- *
- * This function should seldomly be used. The normal situation when reading
- * numbers not for human consumption is to use g_ascii_strtod(). Only when
- * you know that you must expect both locale formatted and C formatted numbers
- * should you use this. Make sure that you don't pass strings such as comma
- * separated lists of values, since the commas may be interpreted as a decimal
- * point in some locales, causing unexpected results.
- *
- * Return value: the #gdouble value.
- **/
-gdouble
-g_strtod (const gchar *nptr,
- gchar **endptr)
-{
- gchar *fail_pos_1;
- gchar *fail_pos_2;
- gdouble val_1;
- gdouble val_2 = 0;
-
- g_return_val_if_fail (nptr != NULL, 0);
-
- fail_pos_1 = NULL;
- fail_pos_2 = NULL;
-
- val_1 = strtod (nptr, &fail_pos_1);
-
- if (fail_pos_1 && fail_pos_1[0] != 0)
- val_2 = g_ascii_strtod (nptr, &fail_pos_2);
-
- if (!fail_pos_1 || fail_pos_1[0] == 0 || fail_pos_1 >= fail_pos_2)
- {
- if (endptr)
- *endptr = fail_pos_1;
- return val_1;
- }
- else
- {
- if (endptr)
- *endptr = fail_pos_2;
- return val_2;
- }
-}
-
-/**
- * g_ascii_strtod:
- * @nptr: the string to convert to a numeric value.
- * @endptr: if non-%NULL, it returns the character after
- * the last character used in the conversion.
- *
- * Converts a string to a #gdouble value.
- *
- * This function behaves like the standard strtod() function
- * does in the C locale. It does this without actually changing
- * the current locale, since that would not be thread-safe.
- * A limitation of the implementation is that this function
- * will still accept localized versions of infinities and NANs.
- *
- * This function is typically used when reading configuration
- * files or other non-user input that should be locale independent.
- * To handle input from the user you should normally use the
- * locale-sensitive system strtod() function.
- *
- * To convert from a #gdouble to a string in a locale-insensitive
- * way, use g_ascii_dtostr().
- *
- * If the correct value would cause overflow, plus or minus %HUGE_VAL
- * is returned (according to the sign of the value), and %ERANGE is
- * stored in %errno. If the correct value would cause underflow,
- * zero is returned and %ERANGE is stored in %errno.
- *
- * This function resets %errno before calling strtod() so that
- * you can reliably detect overflow and underflow.
- *
- * Return value: the #gdouble value.
- **/
-gdouble
-g_ascii_strtod (const gchar *nptr,
- gchar **endptr)
-{
- gchar *fail_pos;
- gdouble val;
-#ifndef BUILD_WITH_ANDROID
- struct lconv *locale_data;
-#endif
- const char *decimal_point;
- int decimal_point_len;
- const char *p, *decimal_point_pos;
- const char *end = NULL; /* Silence gcc */
- int strtod_errno;
-
- g_return_val_if_fail (nptr != NULL, 0);
-
- fail_pos = NULL;
-
-#ifndef BUILD_WITH_ANDROID
- locale_data = localeconv ();
- decimal_point = locale_data->decimal_point;
-#else
- decimal_point = ".";
-#endif
- decimal_point_len = strlen (decimal_point);
-
- g_assert (decimal_point_len != 0);
-
- decimal_point_pos = NULL;
- end = NULL;
-
- if (decimal_point[0] != '.' ||
- decimal_point[1] != 0)
- {
- p = nptr;
- /* Skip leading space */
- while (g_ascii_isspace (*p))
- p++;
-
- /* Skip leading optional sign */
- if (*p == '+' || *p == '-')
- p++;
-
- if (p[0] == '0' &&
- (p[1] == 'x' || p[1] == 'X'))
- {
- p += 2;
- /* HEX - find the (optional) decimal point */
-
- while (g_ascii_isxdigit (*p))
- p++;
-
- if (*p == '.')
- decimal_point_pos = p++;
-
- while (g_ascii_isxdigit (*p))
- p++;
-
- if (*p == 'p' || *p == 'P')
- p++;
- if (*p == '+' || *p == '-')
- p++;
- while (g_ascii_isdigit (*p))
- p++;
-
- end = p;
- }
- else if (g_ascii_isdigit (*p) || *p == '.')
- {
- while (g_ascii_isdigit (*p))
- p++;
-
- if (*p == '.')
- decimal_point_pos = p++;
-
- while (g_ascii_isdigit (*p))
- p++;
-
- if (*p == 'e' || *p == 'E')
- p++;
- if (*p == '+' || *p == '-')
- p++;
- while (g_ascii_isdigit (*p))
- p++;
-
- end = p;
- }
- /* For the other cases, we need not convert the decimal point */
- }
-
- if (decimal_point_pos)
- {
- char *copy, *c;
-
- /* We need to convert the '.' to the locale specific decimal point */
- copy = g_malloc (end - nptr + 1 + decimal_point_len);
-
- c = copy;
- memcpy (c, nptr, decimal_point_pos - nptr);
- c += decimal_point_pos - nptr;
- memcpy (c, decimal_point, decimal_point_len);
- c += decimal_point_len;
- memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
- c += end - (decimal_point_pos + 1);
- *c = 0;
-
- errno = 0;
- val = strtod (copy, &fail_pos);
- strtod_errno = errno;
-
- if (fail_pos)
- {
- if (fail_pos - copy > decimal_point_pos - nptr)
- fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
- else
- fail_pos = (char *)nptr + (fail_pos - copy);
- }
-
- g_free (copy);
-
- }
- else if (end)
- {
- char *copy;
-
- copy = g_malloc (end - (char *)nptr + 1);
- memcpy (copy, nptr, end - nptr);
- *(copy + (end - (char *)nptr)) = 0;
-
- errno = 0;
- val = strtod (copy, &fail_pos);
- strtod_errno = errno;
-
- if (fail_pos)
- {
- fail_pos = (char *)nptr + (fail_pos - copy);
- }
-
- g_free (copy);
- }
- else
- {
- errno = 0;
- val = strtod (nptr, &fail_pos);
- strtod_errno = errno;
- }
-
- if (endptr)
- *endptr = fail_pos;
-
- errno = strtod_errno;
-
- return val;
-}
-
-
-/**
- * g_ascii_dtostr:
- * @buffer: A buffer to place the resulting string in
- * @buf_len: The length of the buffer.
- * @d: The #gdouble to convert
- *
- * Converts a #gdouble to a string, using the '.' as
- * decimal point.
- *
- * This functions generates enough precision that converting
- * the string back using g_ascii_strtod() gives the same machine-number
- * (on machines with IEEE compatible 64bit doubles). It is
- * guaranteed that the size of the resulting string will never
- * be larger than @G_ASCII_DTOSTR_BUF_SIZE bytes.
- *
- * Return value: The pointer to the buffer with the converted string.
- **/
-gchar *
-g_ascii_dtostr (gchar *buffer,
- gint buf_len,
- gdouble d)
-{
- return g_ascii_formatd (buffer, buf_len, "%.17g", d);
-}
-
-/**
- * g_ascii_formatd:
- * @buffer: A buffer to place the resulting string in
- * @buf_len: The length of the buffer.
- * @format: The printf()-style format to use for the
- * code to use for converting.
- * @d: The #gdouble to convert
- *
- * Converts a #gdouble to a string, using the '.' as
- * decimal point. To format the number you pass in
- * a printf()-style format string. Allowed conversion
- * specifiers are 'e', 'E', 'f', 'F', 'g' and 'G'.
- *
- * If you just want to want to serialize the value into a
- * string, use g_ascii_dtostr().
- *
- * Return value: The pointer to the buffer with the converted string.
- */
-gchar *
-g_ascii_formatd (gchar *buffer,
- gint buf_len,
- const gchar *format,
- gdouble d)
-{
-#ifndef BUILD_WITH_ANDROID
- struct lconv *locale_data;
-#endif
- const char *decimal_point;
- int decimal_point_len;
- gchar *p;
- int rest_len;
- gchar format_char;
-
- g_return_val_if_fail (buffer != NULL, NULL);
- g_return_val_if_fail (format[0] == '%', NULL);
- g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL);
-
- format_char = format[strlen (format) - 1];
-
- g_return_val_if_fail (format_char == 'e' || format_char == 'E' ||
- format_char == 'f' || format_char == 'F' ||
- format_char == 'g' || format_char == 'G',
- NULL);
-
- if (format[0] != '%')
- return NULL;
-
- if (strpbrk (format + 1, "'l%"))
- return NULL;
-
- if (!(format_char == 'e' || format_char == 'E' ||
- format_char == 'f' || format_char == 'F' ||
- format_char == 'g' || format_char == 'G'))
- return NULL;
-
- _g_snprintf (buffer, buf_len, format, d);
-
-#ifndef BUILD_WITH_ANDROID
- locale_data = localeconv ();
- decimal_point = locale_data->decimal_point;
-#else
- decimal_point = ".";
-#endif
- decimal_point_len = strlen (decimal_point);
-
- g_assert (decimal_point_len != 0);
-
- if (decimal_point[0] != '.' ||
- decimal_point[1] != 0)
- {
- p = buffer;
-
- while (g_ascii_isspace (*p))
- p++;
-
- if (*p == '+' || *p == '-')
- p++;
-
- while (isdigit ((guchar)*p))
- p++;
-
- if (strncmp (p, decimal_point, decimal_point_len) == 0)
- {
- *p = '.';
- p++;
- if (decimal_point_len > 1)
- {
- rest_len = strlen (p + (decimal_point_len-1));
- memmove (p, p + (decimal_point_len-1), rest_len);
- p[rest_len] = 0;
- }
- }
- }
-
- return buffer;
-}
-
-static guint64
-g_parse_long_long (const gchar *nptr,
- const gchar **endptr,
- guint base,
- gboolean *negative)
-{
- /* this code is based on on the strtol(3) code from GNU libc released under
- * the GNU Lesser General Public License.
- *
- * Copyright (C) 1991,92,94,95,96,97,98,99,2000,01,02
- * Free Software Foundation, Inc.
- */
-#define ISSPACE(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || \
- (c) == '\r' || (c) == '\t' || (c) == '\v')
-#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
-#define ISLOWER(c) ((c) >= 'a' && (c) <= 'z')
-#define ISALPHA(c) (ISUPPER (c) || ISLOWER (c))
-#define TOUPPER(c) (ISLOWER (c) ? (c) - 'a' + 'A' : (c))
-#define TOLOWER(c) (ISUPPER (c) ? (c) - 'A' + 'a' : (c))
- gboolean overflow;
- guint64 cutoff;
- guint64 cutlim;
- guint64 ui64;
- const gchar *s, *save;
- guchar c;
-
- g_return_val_if_fail (nptr != NULL, 0);
-
- *negative = FALSE;
- if (base == 1 || base > 36)
- {
- errno = EINVAL;
- if (endptr)
- *endptr = nptr;
- return 0;
- }
-
- save = s = nptr;
-
- /* Skip white space. */
- while (ISSPACE (*s))
- ++s;
-
- if (G_UNLIKELY (!*s))
- goto noconv;
-
- /* Check for a sign. */
- if (*s == '-')
- {
- *negative = TRUE;
- ++s;
- }
- else if (*s == '+')
- ++s;
-
- /* Recognize number prefix and if BASE is zero, figure it out ourselves. */
- if (*s == '0')
- {
- if ((base == 0 || base == 16) && TOUPPER (s[1]) == 'X')
- {
- s += 2;
- base = 16;
- }
- else if (base == 0)
- base = 8;
- }
- else if (base == 0)
- base = 10;
-
- /* Save the pointer so we can check later if anything happened. */
- save = s;
- cutoff = G_MAXUINT64 / base;
- cutlim = G_MAXUINT64 % base;
-
- overflow = FALSE;
- ui64 = 0;
- c = *s;
- for (; c; c = *++s)
- {
- if (c >= '0' && c <= '9')
- c -= '0';
- else if (ISALPHA (c))
- c = TOUPPER (c) - 'A' + 10;
- else
- break;
- if (c >= base)
- break;
- /* Check for overflow. */
- if (ui64 > cutoff || (ui64 == cutoff && c > cutlim))
- overflow = TRUE;
- else
- {
- ui64 *= base;
- ui64 += c;
- }
- }
-
- /* Check if anything actually happened. */
- if (s == save)
- goto noconv;
-
- /* Store in ENDPTR the address of one character
- past the last character we converted. */
- if (endptr)
- *endptr = s;
-
- if (G_UNLIKELY (overflow))
- {
- errno = ERANGE;
- return G_MAXUINT64;
- }
-
- return ui64;
-
- noconv:
- /* We must handle a special case here: the base is 0 or 16 and the
- first two characters are '0' and 'x', but the rest are no
- hexadecimal digits. This is no error case. We return 0 and
- ENDPTR points to the `x`. */
- if (endptr)
- {
- if (save - nptr >= 2 && TOUPPER (save[-1]) == 'X'
- && save[-2] == '0')
- *endptr = &save[-1];
- else
- /* There was no number to convert. */
- *endptr = nptr;
- }
- return 0;
-}
-
-/**
- * g_ascii_strtoull:
- * @nptr: the string to convert to a numeric value.
- * @endptr: if non-%NULL, it returns the character after
- * the last character used in the conversion.
- * @base: to be used for the conversion, 2..36 or 0
- *
- * Converts a string to a #guint64 value.
- * This function behaves like the standard strtoull() function
- * does in the C locale. It does this without actually
- * changing the current locale, since that would not be
- * thread-safe.
- *
- * This function is typically used when reading configuration
- * files or other non-user input that should be locale independent.
- * To handle input from the user you should normally use the
- * locale-sensitive system strtoull() function.
- *
- * If the correct value would cause overflow, %G_MAXUINT64
- * is returned, and %ERANGE is stored in %errno. If the base is
- * outside the valid range, zero is returned, and %EINVAL is stored
- * in %errno. If the string conversion fails, zero is returned, and
- * @endptr returns @nptr (if @endptr is non-%NULL).
- *
- * Return value: the #guint64 value or zero on error.
- *
- * Since: 2.2
- */
-guint64
-g_ascii_strtoull (const gchar *nptr,
- gchar **endptr,
- guint base)
-{
- gboolean negative;
- guint64 result;
-
- result = g_parse_long_long (nptr, (const gchar **) endptr, base, &negative);
-
- /* Return the result of the appropriate sign. */
- return negative ? -result : result;
-}
-
-/**
- * g_ascii_strtoll:
- * @nptr: the string to convert to a numeric value.
- * @endptr: if non-%NULL, it returns the character after
- * the last character used in the conversion.
- * @base: to be used for the conversion, 2..36 or 0
- *
- * Converts a string to a #gint64 value.
- * This function behaves like the standard strtoll() function
- * does in the C locale. It does this without actually
- * changing the current locale, since that would not be
- * thread-safe.
- *
- * This function is typically used when reading configuration
- * files or other non-user input that should be locale independent.
- * To handle input from the user you should normally use the
- * locale-sensitive system strtoll() function.
- *
- * If the correct value would cause overflow, %G_MAXINT64 or %G_MININT64
- * is returned, and %ERANGE is stored in %errno. If the base is
- * outside the valid range, zero is returned, and %EINVAL is stored
- * in %errno. If the string conversion fails, zero is returned, and
- * @endptr returns @nptr (if @endptr is non-%NULL).
- *
- * Return value: the #gint64 value or zero on error.
- *
- * Since: 2.12
- */
-gint64
-g_ascii_strtoll (const gchar *nptr,
- gchar **endptr,
- guint base)
-{
- gboolean negative;
- guint64 result;
-
- result = g_parse_long_long (nptr, (const gchar **) endptr, base, &negative);
-
- if (negative && result > (guint64) G_MININT64)
- {
- errno = ERANGE;
- return G_MININT64;
- }
- else if (!negative && result > (guint64) G_MAXINT64)
- {
- errno = ERANGE;
- return G_MAXINT64;
- }
- else if (negative)
- return - (gint64) result;
- else
- return (gint64) result;
-}
-
-/**
- * g_strerror:
- * @errnum: the system error number. See the standard C %errno
- * documentation
- *
- * Returns a string corresponding to the given error code, e.g.
- * "no such process". You should use this function in preference to
- * strerror(), because it returns a string in UTF-8 encoding, and since
- * not all platforms support the strerror() function.
- *
- * Returns: a UTF-8 string describing the error code. If the error code
- * is unknown, it returns "unknown error (<code>)". The string
- * can only be used until the next call to g_strerror()
- */
-G_CONST_RETURN gchar*
-g_strerror (gint errnum)
-{
- static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
- char *msg;
- int saved_errno = errno;
-
-#ifdef HAVE_STRERROR
- const char *msg_locale;
-
- msg_locale = strerror (errnum);
- if (g_get_charset (NULL))
- {
- errno = saved_errno;
- return msg_locale;
- }
- else
- {
- gchar *msg_utf8 = g_locale_to_utf8 (msg_locale, -1, NULL, NULL, NULL);
- if (msg_utf8)
- {
- /* Stick in the quark table so that we can return a static result
- */
- GQuark msg_quark = g_quark_from_string (msg_utf8);
- g_free (msg_utf8);
-
- msg_utf8 = (gchar *) g_quark_to_string (msg_quark);
- errno = saved_errno;
- return msg_utf8;
- }
- }
-#elif NO_SYS_ERRLIST
- switch (errnum)
- {
-#ifdef E2BIG
- case E2BIG: return "argument list too long";
-#endif
-#ifdef EACCES
- case EACCES: return "permission denied";
-#endif
-#ifdef EADDRINUSE
- case EADDRINUSE: return "address already in use";
-#endif
-#ifdef EADDRNOTAVAIL
- case EADDRNOTAVAIL: return "can't assign requested address";
-#endif
-#ifdef EADV
- case EADV: return "advertise error";
-#endif
-#ifdef EAFNOSUPPORT
- case EAFNOSUPPORT: return "address family not supported by protocol family";
-#endif
-#ifdef EAGAIN
- case EAGAIN: return "try again";
-#endif
-#ifdef EALIGN
- case EALIGN: return "EALIGN";
-#endif
-#ifdef EALREADY
- case EALREADY: return "operation already in progress";
-#endif
-#ifdef EBADE
- case EBADE: return "bad exchange descriptor";
-#endif
-#ifdef EBADF
- case EBADF: return "bad file number";
-#endif
-#ifdef EBADFD
- case EBADFD: return "file descriptor in bad state";
-#endif
-#ifdef EBADMSG
- case EBADMSG: return "not a data message";
-#endif
-#ifdef EBADR
- case EBADR: return "bad request descriptor";
-#endif
-#ifdef EBADRPC
- case EBADRPC: return "RPC structure is bad";
-#endif
-#ifdef EBADRQC
- case EBADRQC: return "bad request code";
-#endif
-#ifdef EBADSLT
- case EBADSLT: return "invalid slot";
-#endif
-#ifdef EBFONT
- case EBFONT: return "bad font file format";
-#endif
-#ifdef EBUSY
- case EBUSY: return "mount device busy";
-#endif
-#ifdef ECHILD
- case ECHILD: return "no children";
-#endif
-#ifdef ECHRNG
- case ECHRNG: return "channel number out of range";
-#endif
-#ifdef ECOMM
- case ECOMM: return "communication error on send";
-#endif
-#ifdef ECONNABORTED
- case ECONNABORTED: return "software caused connection abort";
-#endif
-#ifdef ECONNREFUSED
- case ECONNREFUSED: return "connection refused";
-#endif
-#ifdef ECONNRESET
- case ECONNRESET: return "connection reset by peer";
-#endif
-#if defined(EDEADLK) && (!defined(EWOULDBLOCK) || (EDEADLK != EWOULDBLOCK))
- case EDEADLK: return "resource deadlock avoided";
-#endif
-#if defined(EDEADLOCK) && (!defined(EDEADLK) || (EDEADLOCK != EDEADLK))
- case EDEADLOCK: return "resource deadlock avoided";
-#endif
-#ifdef EDESTADDRREQ
- case EDESTADDRREQ: return "destination address required";
-#endif
-#ifdef EDIRTY
- case EDIRTY: return "mounting a dirty fs w/o force";
-#endif
-#ifdef EDOM
- case EDOM: return "math argument out of range";
-#endif
-#ifdef EDOTDOT
- case EDOTDOT: return "cross mount point";
-#endif
-#ifdef EDQUOT
- case EDQUOT: return "disk quota exceeded";
-#endif
-#ifdef EDUPPKG
- case EDUPPKG: return "duplicate package name";
-#endif
-#ifdef EEXIST
- case EEXIST: return "file already exists";
-#endif
-#ifdef EFAULT
- case EFAULT: return "bad address in system call argument";
-#endif
-#ifdef EFBIG
- case EFBIG: return "file too large";
-#endif
-#ifdef EHOSTDOWN
- case EHOSTDOWN: return "host is down";
-#endif
-#ifdef EHOSTUNREACH
- case EHOSTUNREACH: return "host is unreachable";
-#endif
-#ifdef EIDRM
- case EIDRM: return "identifier removed";
-#endif
-#ifdef EINIT
- case EINIT: return "initialization error";
-#endif
-#ifdef EINPROGRESS
- case EINPROGRESS: return "operation now in progress";
-#endif
-#ifdef EINTR
- case EINTR: return "interrupted system call";
-#endif
-#ifdef EINVAL
- case EINVAL: return "invalid argument";
-#endif
-#ifdef EIO
- case EIO: return "I/O error";
-#endif
-#ifdef EISCONN
- case EISCONN: return "socket is already connected";
-#endif
-#ifdef EISDIR
- case EISDIR: return "is a directory";
-#endif
-#ifdef EISNAME
- case EISNAM: return "is a name file";
-#endif
-#ifdef ELBIN
- case ELBIN: return "ELBIN";
-#endif
-#ifdef EL2HLT
- case EL2HLT: return "level 2 halted";
-#endif
-#ifdef EL2NSYNC
- case EL2NSYNC: return "level 2 not synchronized";
-#endif
-#ifdef EL3HLT
- case EL3HLT: return "level 3 halted";
-#endif
-#ifdef EL3RST
- case EL3RST: return "level 3 reset";
-#endif
-#ifdef ELIBACC
- case ELIBACC: return "can not access a needed shared library";
-#endif
-#ifdef ELIBBAD
- case ELIBBAD: return "accessing a corrupted shared library";
-#endif
-#ifdef ELIBEXEC
- case ELIBEXEC: return "can not exec a shared library directly";
-#endif
-#ifdef ELIBMAX
- case ELIBMAX: return "attempting to link in more shared libraries than system limit";
-#endif
-#ifdef ELIBSCN
- case ELIBSCN: return ".lib section in a.out corrupted";
-#endif
-#ifdef ELNRNG
- case ELNRNG: return "link number out of range";
-#endif
-#ifdef ELOOP
- case ELOOP: return "too many levels of symbolic links";
-#endif
-#ifdef EMFILE
- case EMFILE: return "too many open files";
-#endif
-#ifdef EMLINK
- case EMLINK: return "too many links";
-#endif
-#ifdef EMSGSIZE
- case EMSGSIZE: return "message too long";
-#endif
-#ifdef EMULTIHOP
- case EMULTIHOP: return "multihop attempted";
-#endif
-#ifdef ENAMETOOLONG
- case ENAMETOOLONG: return "file name too long";
-#endif
-#ifdef ENAVAIL
- case ENAVAIL: return "not available";
-#endif
-#ifdef ENET
- case ENET: return "ENET";
-#endif
-#ifdef ENETDOWN
- case ENETDOWN: return "network is down";
-#endif
-#ifdef ENETRESET
- case ENETRESET: return "network dropped connection on reset";
-#endif
-#ifdef ENETUNREACH
- case ENETUNREACH: return "network is unreachable";
-#endif
-#ifdef ENFILE
- case ENFILE: return "file table overflow";
-#endif
-#ifdef ENOANO
- case ENOANO: return "anode table overflow";
-#endif
-#if defined(ENOBUFS) && (!defined(ENOSR) || (ENOBUFS != ENOSR))
- case ENOBUFS: return "no buffer space available";
-#endif
-#ifdef ENOCSI
- case ENOCSI: return "no CSI structure available";
-#endif
-#ifdef ENODATA
- case ENODATA: return "no data available";
-#endif
-#ifdef ENODEV
- case ENODEV: return "no such device";
-#endif
-#ifdef ENOENT
- case ENOENT: return "no such file or directory";
-#endif
-#ifdef ENOEXEC
- case ENOEXEC: return "exec format error";
-#endif
-#ifdef ENOLCK
- case ENOLCK: return "no locks available";
-#endif
-#ifdef ENOLINK
- case ENOLINK: return "link has be severed";
-#endif
-#ifdef ENOMEM
- case ENOMEM: return "not enough memory";
-#endif
-#ifdef ENOMSG
- case ENOMSG: return "no message of desired type";
-#endif
-#ifdef ENONET
- case ENONET: return "machine is not on the network";
-#endif
-#ifdef ENOPKG
- case ENOPKG: return "package not installed";
-#endif
-#ifdef ENOPROTOOPT
- case ENOPROTOOPT: return "bad proocol option";
-#endif
-#ifdef ENOSPC
- case ENOSPC: return "no space left on device";
-#endif
-#ifdef ENOSR
- case ENOSR: return "out of stream resources";
-#endif
-#ifdef ENOSTR
- case ENOSTR: return "not a stream device";
-#endif
-#ifdef ENOSYM
- case ENOSYM: return "unresolved symbol name";
-#endif
-#ifdef ENOSYS
- case ENOSYS: return "function not implemented";
-#endif
-#ifdef ENOTBLK
- case ENOTBLK: return "block device required";
-#endif
-#ifdef ENOTCONN
- case ENOTCONN: return "socket is not connected";
-#endif
-#ifdef ENOTDIR
- case ENOTDIR: return "not a directory";
-#endif
-#ifdef ENOTEMPTY
- case ENOTEMPTY: return "directory not empty";
-#endif
-#ifdef ENOTNAM
- case ENOTNAM: return "not a name file";
-#endif
-#ifdef ENOTSOCK
- case ENOTSOCK: return "socket operation on non-socket";
-#endif
-#ifdef ENOTTY
- case ENOTTY: return "inappropriate device for ioctl";
-#endif
-#ifdef ENOTUNIQ
- case ENOTUNIQ: return "name not unique on network";
-#endif
-#ifdef ENXIO
- case ENXIO: return "no such device or address";
-#endif
-#ifdef EOPNOTSUPP
- case EOPNOTSUPP: return "operation not supported on socket";
-#endif
-#ifdef EPERM
- case EPERM: return "not owner";
-#endif
-#ifdef EPFNOSUPPORT
- case EPFNOSUPPORT: return "protocol family not supported";
-#endif
-#ifdef EPIPE
- case EPIPE: return "broken pipe";
-#endif
-#ifdef EPROCLIM
- case EPROCLIM: return "too many processes";
-#endif
-#ifdef EPROCUNAVAIL
- case EPROCUNAVAIL: return "bad procedure for program";
-#endif
-#ifdef EPROGMISMATCH
- case EPROGMISMATCH: return "program version wrong";
-#endif
-#ifdef EPROGUNAVAIL
- case EPROGUNAVAIL: return "RPC program not available";
-#endif
-#ifdef EPROTO
- case EPROTO: return "protocol error";
-#endif
-#ifdef EPROTONOSUPPORT
- case EPROTONOSUPPORT: return "protocol not suppored";
-#endif
-#ifdef EPROTOTYPE
- case EPROTOTYPE: return "protocol wrong type for socket";
-#endif
-#ifdef ERANGE
- case ERANGE: return "math result unrepresentable";
-#endif
-#if defined(EREFUSED) && (!defined(ECONNREFUSED) || (EREFUSED != ECONNREFUSED))
- case EREFUSED: return "EREFUSED";
-#endif
-#ifdef EREMCHG
- case EREMCHG: return "remote address changed";
-#endif
-#ifdef EREMDEV
- case EREMDEV: return "remote device";
-#endif
-#ifdef EREMOTE
- case EREMOTE: return "pathname hit remote file system";
-#endif
-#ifdef EREMOTEIO
- case EREMOTEIO: return "remote i/o error";
-#endif
-#ifdef EREMOTERELEASE
- case EREMOTERELEASE: return "EREMOTERELEASE";
-#endif
-#ifdef EROFS
- case EROFS: return "read-only file system";
-#endif
-#ifdef ERPCMISMATCH
- case ERPCMISMATCH: return "RPC version is wrong";
-#endif
-#ifdef ERREMOTE
- case ERREMOTE: return "object is remote";
-#endif
-#ifdef ESHUTDOWN
- case ESHUTDOWN: return "can't send afer socket shutdown";
-#endif
-#ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT: return "socket type not supported";
-#endif
-#ifdef ESPIPE
- case ESPIPE: return "invalid seek";
-#endif
-#ifdef ESRCH
- case ESRCH: return "no such process";
-#endif
-#ifdef ESRMNT
- case ESRMNT: return "srmount error";
-#endif
-#ifdef ESTALE
- case ESTALE: return "stale remote file handle";
-#endif
-#ifdef ESUCCESS
- case ESUCCESS: return "Error 0";
-#endif
-#ifdef ETIME
- case ETIME: return "timer expired";
-#endif
-#ifdef ETIMEDOUT
- case ETIMEDOUT: return "connection timed out";
-#endif
-#ifdef ETOOMANYREFS
- case ETOOMANYREFS: return "too many references: can't splice";
-#endif
-#ifdef ETXTBSY
- case ETXTBSY: return "text file or pseudo-device busy";
-#endif
-#ifdef EUCLEAN
- case EUCLEAN: return "structure needs cleaning";
-#endif
-#ifdef EUNATCH
- case EUNATCH: return "protocol driver not attached";
-#endif
-#ifdef EUSERS
- case EUSERS: return "too many users";
-#endif
-#ifdef EVERSION
- case EVERSION: return "version mismatch";
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK: return "operation would block";
-#endif
-#ifdef EXDEV
- case EXDEV: return "cross-domain link";
-#endif
-#ifdef EXFULL
- case EXFULL: return "message tables full";
-#endif
- }
-#else /* NO_SYS_ERRLIST */
- extern int sys_nerr;
- extern char *sys_errlist[];
-
- if ((errnum > 0) && (errnum <= sys_nerr))
- return sys_errlist [errnum];
-#endif /* NO_SYS_ERRLIST */
-
- msg = g_static_private_get (&msg_private);
- if (!msg)
- {
- msg = g_new (gchar, 64);
- g_static_private_set (&msg_private, msg, g_free);
- }
-
- _g_sprintf (msg, "unknown error (%d)", errnum);
-
- errno = saved_errno;
- return msg;
-}
-
-/**
- * g_strsignal:
- * @signum: the signal number. See the <literal>signal</literal>
- * documentation
- *
- * Returns a string describing the given signal, e.g. "Segmentation fault".
- * You should use this function in preference to strsignal(), because it
- * returns a string in UTF-8 encoding, and since not all platforms support
- * the strsignal() function.
- *
- * Returns: a UTF-8 string describing the signal. If the signal is unknown,
- * it returns "unknown signal (<signum>)". The string can only be
- * used until the next call to g_strsignal()
- */
-G_CONST_RETURN gchar*
-g_strsignal (gint signum)
-{
- static GStaticPrivate msg_private = G_STATIC_PRIVATE_INIT;
- char *msg;
-
-#ifdef HAVE_STRSIGNAL
- const char *msg_locale;
-
-#if defined(G_OS_BEOS) || defined(G_WITH_CYGWIN)
-extern const char *strsignal(int);
-#else
- /* this is declared differently (const) in string.h on BeOS */
- extern char *strsignal (int sig);
-#endif /* !G_OS_BEOS && !G_WITH_CYGWIN */
- msg_locale = strsignal (signum);
- if (g_get_charset (NULL))
- return msg_locale;
- else
- {
- gchar *msg_utf8 = g_locale_to_utf8 (msg_locale, -1, NULL, NULL, NULL);
- if (msg_utf8)
- {
- /* Stick in the quark table so that we can return a static result
- */
- GQuark msg_quark = g_quark_from_string (msg_utf8);
- g_free (msg_utf8);
-
- return g_quark_to_string (msg_quark);
- }
- }
-#elif NO_SYS_SIGLIST
- switch (signum)
- {
-#ifdef SIGHUP
- case SIGHUP: return "Hangup";
-#endif
-#ifdef SIGINT
- case SIGINT: return "Interrupt";
-#endif
-#ifdef SIGQUIT
- case SIGQUIT: return "Quit";
-#endif
-#ifdef SIGILL
- case SIGILL: return "Illegal instruction";
-#endif
-#ifdef SIGTRAP
- case SIGTRAP: return "Trace/breakpoint trap";
-#endif
-#ifdef SIGABRT
- case SIGABRT: return "IOT trap/Abort";
-#endif
-#ifdef SIGBUS
- case SIGBUS: return "Bus error";
-#endif
-#ifdef SIGFPE
- case SIGFPE: return "Floating point exception";
-#endif
-#ifdef SIGKILL
- case SIGKILL: return "Killed";
-#endif
-#ifdef SIGUSR1
- case SIGUSR1: return "User defined signal 1";
-#endif
-#ifdef SIGSEGV
- case SIGSEGV: return "Segmentation fault";
-#endif
-#ifdef SIGUSR2
- case SIGUSR2: return "User defined signal 2";
-#endif
-#ifdef SIGPIPE
- case SIGPIPE: return "Broken pipe";
-#endif
-#ifdef SIGALRM
- case SIGALRM: return "Alarm clock";
-#endif
-#ifdef SIGTERM
- case SIGTERM: return "Terminated";
-#endif
-#ifdef SIGSTKFLT
- case SIGSTKFLT: return "Stack fault";
-#endif
-#ifdef SIGCHLD
- case SIGCHLD: return "Child exited";
-#endif
-#ifdef SIGCONT
- case SIGCONT: return "Continued";
-#endif
-#ifdef SIGSTOP
- case SIGSTOP: return "Stopped (signal)";
-#endif
-#ifdef SIGTSTP
- case SIGTSTP: return "Stopped";
-#endif
-#ifdef SIGTTIN
- case SIGTTIN: return "Stopped (tty input)";
-#endif
-#ifdef SIGTTOU
- case SIGTTOU: return "Stopped (tty output)";
-#endif
-#ifdef SIGURG
- case SIGURG: return "Urgent condition";
-#endif
-#ifdef SIGXCPU
- case SIGXCPU: return "CPU time limit exceeded";
-#endif
-#ifdef SIGXFSZ
- case SIGXFSZ: return "File size limit exceeded";
-#endif
-#ifdef SIGVTALRM
- case SIGVTALRM: return "Virtual time alarm";
-#endif
-#ifdef SIGPROF
- case SIGPROF: return "Profile signal";
-#endif
-#ifdef SIGWINCH
- case SIGWINCH: return "Window size changed";
-#endif
-#ifdef SIGIO
- case SIGIO: return "Possible I/O";
-#endif
-#ifdef SIGPWR
- case SIGPWR: return "Power failure";
-#endif
-#ifdef SIGUNUSED
- case SIGUNUSED: return "Unused signal";
-#endif
- }
-#else /* NO_SYS_SIGLIST */
-
-#ifdef NO_SYS_SIGLIST_DECL
- extern char *sys_siglist[]; /*(see Tue Jan 19 00:44:24 1999 in changelog)*/
-#endif
-
- return (char*) /* this function should return const --josh */ sys_siglist [signum];
-#endif /* NO_SYS_SIGLIST */
-
- msg = g_static_private_get (&msg_private);
- if (!msg)
- {
- msg = g_new (gchar, 64);
- g_static_private_set (&msg_private, msg, g_free);
- }
-
- _g_sprintf (msg, "unknown signal (%d)", signum);
-
- return msg;
-}
-
-/* Functions g_strlcpy and g_strlcat were originally developed by
- * Todd C. Miller <Todd.Miller@courtesan.com> to simplify writing secure code.
- * See ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/strlcpy.3
- * for more information.
- */
-
-#ifdef HAVE_STRLCPY
-/* Use the native ones, if available; they might be implemented in assembly */
-gsize
-g_strlcpy (gchar *dest,
- const gchar *src,
- gsize dest_size)
-{
- g_return_val_if_fail (dest != NULL, 0);
- g_return_val_if_fail (src != NULL, 0);
-
- return strlcpy (dest, src, dest_size);
-}
-
-gsize
-g_strlcat (gchar *dest,
- const gchar *src,
- gsize dest_size)
-{
- g_return_val_if_fail (dest != NULL, 0);
- g_return_val_if_fail (src != NULL, 0);
-
- return strlcat (dest, src, dest_size);
-}
-
-#else /* ! HAVE_STRLCPY */
-/**
- * g_strlcpy:
- * @dest: destination buffer
- * @src: source buffer
- * @dest_size: length of @dest in bytes
- *
- * Portability wrapper that calls strlcpy() on systems which have it,
- * and emulates strlcpy() otherwise. Copies @src to @dest; @dest is
- * guaranteed to be nul-terminated; @src must be nul-terminated;
- * @dest_size is the buffer size, not the number of chars to copy.
- *
- * At most dest_size - 1 characters will be copied. Always nul-terminates
- * (unless dest_size == 0). This function does <emphasis>not</emphasis>
- * allocate memory. Unlike strncpy(), this function doesn't pad dest (so
- * it's often faster). It returns the size of the attempted result,
- * strlen (src), so if @retval >= @dest_size, truncation occurred.
- *
- * <note><para>Caveat: strlcpy() is supposedly more secure than
- * strcpy() or strncpy(), but if you really want to avoid screwups,
- * g_strdup() is an even better idea.</para></note>
- *
- * Returns: length of @src
- */
-gsize
-g_strlcpy (gchar *dest,
- const gchar *src,
- gsize dest_size)
-{
- register gchar *d = dest;
- register const gchar *s = src;
- register gsize n = dest_size;
-
- g_return_val_if_fail (dest != NULL, 0);
- g_return_val_if_fail (src != NULL, 0);
-
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0)
- do
- {
- register gchar c = *s++;
-
- *d++ = c;
- if (c == 0)
- break;
- }
- while (--n != 0);
-
- /* If not enough room in dest, add NUL and traverse rest of src */
- if (n == 0)
- {
- if (dest_size != 0)
- *d = 0;
- while (*s++)
- ;
- }
-
- return s - src - 1; /* count does not include NUL */
-}
-
-/**
- * g_strlcat:
- * @dest: destination buffer, already containing one nul-terminated string
- * @src: source buffer
- * @dest_size: length of @dest buffer in bytes (not length of existing string
- * inside @dest)
- *
- * Portability wrapper that calls strlcat() on systems which have it,
- * and emulates it otherwise. Appends nul-terminated @src string to @dest,
- * guaranteeing nul-termination for @dest. The total size of @dest won't
- * exceed @dest_size.
- *
- * At most dest_size - 1 characters will be copied.
- * Unlike strncat, dest_size is the full size of dest, not the space left over.
- * This function does NOT allocate memory.
- * This always NUL terminates (unless siz == 0 or there were no NUL characters
- * in the dest_size characters of dest to start with).
- *
- * <note><para>Caveat: this is supposedly a more secure alternative to
- * strcat() or strncat(), but for real security g_strconcat() is harder
- * to mess up.</para></note>
- *
- * Returns: size of attempted result, which is MIN (dest_size, strlen
- * (original dest)) + strlen (src), so if retval >= dest_size,
- * truncation occurred.
- **/
-gsize
-g_strlcat (gchar *dest,
- const gchar *src,
- gsize dest_size)
-{
- register gchar *d = dest;
- register const gchar *s = src;
- register gsize bytes_left = dest_size;
- gsize dlength; /* Logically, MIN (strlen (d), dest_size) */
-
- g_return_val_if_fail (dest != NULL, 0);
- g_return_val_if_fail (src != NULL, 0);
-
- /* Find the end of dst and adjust bytes left but don't go past end */
- while (*d != 0 && bytes_left-- != 0)
- d++;
- dlength = d - dest;
- bytes_left = dest_size - dlength;
-
- if (bytes_left == 0)
- return dlength + strlen (s);
-
- while (*s != 0)
- {
- if (bytes_left != 1)
- {
- *d++ = *s;
- bytes_left--;
- }
- s++;
- }
- *d = 0;
-
- return dlength + (s - src); /* count does not include NUL */
-}
-#endif /* ! HAVE_STRLCPY */
-
-/**
- * g_ascii_strdown:
- * @str: a string.
- * @len: length of @str in bytes, or -1 if @str is nul-terminated.
- *
- * Converts all upper case ASCII letters to lower case ASCII letters.
- *
- * Return value: a newly-allocated string, with all the upper case
- * characters in @str converted to lower case, with
- * semantics that exactly match g_ascii_tolower(). (Note
- * that this is unlike the old g_strdown(), which modified
- * the string in place.)
- **/
-gchar*
-g_ascii_strdown (const gchar *str,
- gssize len)
-{
- gchar *result, *s;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- if (len < 0)
- len = strlen (str);
-
- result = g_strndup (str, len);
- for (s = result; *s; s++)
- *s = g_ascii_tolower (*s);
-
- return result;
-}
-
-/**
- * g_ascii_strup:
- * @str: a string.
- * @len: length of @str in bytes, or -1 if @str is nul-terminated.
- *
- * Converts all lower case ASCII letters to upper case ASCII letters.
- *
- * Return value: a newly allocated string, with all the lower case
- * characters in @str converted to upper case, with
- * semantics that exactly match g_ascii_toupper(). (Note
- * that this is unlike the old g_strup(), which modified
- * the string in place.)
- **/
-gchar*
-g_ascii_strup (const gchar *str,
- gssize len)
-{
- gchar *result, *s;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- if (len < 0)
- len = strlen (str);
-
- result = g_strndup (str, len);
- for (s = result; *s; s++)
- *s = g_ascii_toupper (*s);
-
- return result;
-}
-
-/**
- * g_strdown:
- * @string: the string to convert.
- *
- * Converts a string to lower case.
- *
- * Return value: the string
- *
- * Deprecated:2.2: This function is totally broken for the reasons discussed
- * in the g_strncasecmp() docs - use g_ascii_strdown() or g_utf8_strdown()
- * instead.
- **/
-gchar*
-g_strdown (gchar *string)
-{
- register guchar *s;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- s = (guchar *) string;
-
- while (*s)
- {
- if (isupper (*s))
- *s = tolower (*s);
- s++;
- }
-
- return (gchar *) string;
-}
-
-/**
- * g_strup:
- * @string: the string to convert.
- *
- * Converts a string to upper case.
- *
- * Return value: the string
- *
- * Deprecated:2.2: This function is totally broken for the reasons discussed
- * in the g_strncasecmp() docs - use g_ascii_strup() or g_utf8_strup() instead.
- **/
-gchar*
-g_strup (gchar *string)
-{
- register guchar *s;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- s = (guchar *) string;
-
- while (*s)
- {
- if (islower (*s))
- *s = toupper (*s);
- s++;
- }
-
- return (gchar *) string;
-}
-
-/**
- * g_strreverse:
- * @string: the string to reverse
- *
- * Reverses all of the bytes in a string. For example,
- * <literal>g_strreverse ("abcdef")</literal> will result
- * in "fedcba".
- *
- * Note that g_strreverse() doesn't work on UTF-8 strings
- * containing multibyte characters. For that purpose, use
- * g_utf8_strreverse().
- *
- * Returns: the same pointer passed in as @string
- */
-gchar*
-g_strreverse (gchar *string)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- if (*string)
- {
- register gchar *h, *t;
-
- h = string;
- t = string + strlen (string) - 1;
-
- while (h < t)
- {
- register gchar c;
-
- c = *h;
- *h = *t;
- h++;
- *t = c;
- t--;
- }
- }
-
- return string;
-}
-
-/**
- * g_ascii_tolower:
- * @c: any character.
- *
- * Convert a character to ASCII lower case.
- *
- * Unlike the standard C library tolower() function, this only
- * recognizes standard ASCII letters and ignores the locale, returning
- * all non-ASCII characters unchanged, even if they are lower case
- * letters in a particular character set. Also unlike the standard
- * library function, this takes and returns a char, not an int, so
- * don't call it on %EOF but no need to worry about casting to #guchar
- * before passing a possibly non-ASCII character in.
- *
- * Return value: the result of converting @c to lower case.
- * If @c is not an ASCII upper case letter,
- * @c is returned unchanged.
- **/
-gchar
-g_ascii_tolower (gchar c)
-{
- return g_ascii_isupper (c) ? c - 'A' + 'a' : c;
-}
-
-/**
- * g_ascii_toupper:
- * @c: any character.
- *
- * Convert a character to ASCII upper case.
- *
- * Unlike the standard C library toupper() function, this only
- * recognizes standard ASCII letters and ignores the locale, returning
- * all non-ASCII characters unchanged, even if they are upper case
- * letters in a particular character set. Also unlike the standard
- * library function, this takes and returns a char, not an int, so
- * don't call it on %EOF but no need to worry about casting to #guchar
- * before passing a possibly non-ASCII character in.
- *
- * Return value: the result of converting @c to upper case.
- * If @c is not an ASCII lower case letter,
- * @c is returned unchanged.
- **/
-gchar
-g_ascii_toupper (gchar c)
-{
- return g_ascii_islower (c) ? c - 'a' + 'A' : c;
-}
-
-/**
- * g_ascii_digit_value:
- * @c: an ASCII character.
- *
- * Determines the numeric value of a character as a decimal
- * digit. Differs from g_unichar_digit_value() because it takes
- * a char, so there's no worry about sign extension if characters
- * are signed.
- *
- * Return value: If @c is a decimal digit (according to
- * g_ascii_isdigit()), its numeric value. Otherwise, -1.
- **/
-int
-g_ascii_digit_value (gchar c)
-{
- if (g_ascii_isdigit (c))
- return c - '0';
- return -1;
-}
-
-/**
- * g_ascii_xdigit_value:
- * @c: an ASCII character.
- *
- * Determines the numeric value of a character as a hexidecimal
- * digit. Differs from g_unichar_xdigit_value() because it takes
- * a char, so there's no worry about sign extension if characters
- * are signed.
- *
- * Return value: If @c is a hex digit (according to
- * g_ascii_isxdigit()), its numeric value. Otherwise, -1.
- **/
-int
-g_ascii_xdigit_value (gchar c)
-{
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- return g_ascii_digit_value (c);
-}
-
-/**
- * g_ascii_strcasecmp:
- * @s1: string to compare with @s2.
- * @s2: string to compare with @s1.
- *
- * Compare two strings, ignoring the case of ASCII characters.
- *
- * Unlike the BSD strcasecmp() function, this only recognizes standard
- * ASCII letters and ignores the locale, treating all non-ASCII
- * bytes as if they are not letters.
- *
- * This function should be used only on strings that are known to be
- * in encodings where the bytes corresponding to ASCII letters always
- * represent themselves. This includes UTF-8 and the ISO-8859-*
- * charsets, but not for instance double-byte encodings like the
- * Windows Codepage 932, where the trailing bytes of double-byte
- * characters include all ASCII letters. If you compare two CP932
- * strings using this function, you will get false matches.
- *
- * Return value: 0 if the strings match, a negative value if @s1 < @s2,
- * or a positive value if @s1 > @s2.
- **/
-gint
-g_ascii_strcasecmp (const gchar *s1,
- const gchar *s2)
-{
- gint c1, c2;
-
- g_return_val_if_fail (s1 != NULL, 0);
- g_return_val_if_fail (s2 != NULL, 0);
-
- while (*s1 && *s2)
- {
- c1 = (gint)(guchar) TOLOWER (*s1);
- c2 = (gint)(guchar) TOLOWER (*s2);
- if (c1 != c2)
- return (c1 - c2);
- s1++; s2++;
- }
-
- return (((gint)(guchar) *s1) - ((gint)(guchar) *s2));
-}
-
-/**
- * g_ascii_strncasecmp:
- * @s1: string to compare with @s2.
- * @s2: string to compare with @s1.
- * @n: number of characters to compare.
- *
- * Compare @s1 and @s2, ignoring the case of ASCII characters and any
- * characters after the first @n in each string.
- *
- * Unlike the BSD strcasecmp() function, this only recognizes standard
- * ASCII letters and ignores the locale, treating all non-ASCII
- * characters as if they are not letters.
- *
- * The same warning as in g_ascii_strcasecmp() applies: Use this
- * function only on strings known to be in encodings where bytes
- * corresponding to ASCII letters always represent themselves.
- *
- * Return value: 0 if the strings match, a negative value if @s1 < @s2,
- * or a positive value if @s1 > @s2.
- **/
-gint
-g_ascii_strncasecmp (const gchar *s1,
- const gchar *s2,
- gsize n)
-{
- gint c1, c2;
-
- g_return_val_if_fail (s1 != NULL, 0);
- g_return_val_if_fail (s2 != NULL, 0);
-
- while (n && *s1 && *s2)
- {
- n -= 1;
- c1 = (gint)(guchar) TOLOWER (*s1);
- c2 = (gint)(guchar) TOLOWER (*s2);
- if (c1 != c2)
- return (c1 - c2);
- s1++; s2++;
- }
-
- if (n)
- return (((gint) (guchar) *s1) - ((gint) (guchar) *s2));
- else
- return 0;
-}
-
-/**
- * g_strcasecmp:
- * @s1: a string.
- * @s2: a string to compare with @s1.
- *
- * A case-insensitive string comparison, corresponding to the standard
- * strcasecmp() function on platforms which support it.
- *
- * Return value: 0 if the strings match, a negative value if @s1 < @s2,
- * or a positive value if @s1 > @s2.
- *
- * Deprecated:2.2: See g_strncasecmp() for a discussion of why this function
- * is deprecated and how to replace it.
- **/
-gint
-g_strcasecmp (const gchar *s1,
- const gchar *s2)
-{
-#ifdef HAVE_STRCASECMP
- g_return_val_if_fail (s1 != NULL, 0);
- g_return_val_if_fail (s2 != NULL, 0);
-
- return strcasecmp (s1, s2);
-#else
- gint c1, c2;
-
- g_return_val_if_fail (s1 != NULL, 0);
- g_return_val_if_fail (s2 != NULL, 0);
-
- while (*s1 && *s2)
- {
- /* According to A. Cox, some platforms have islower's that
- * don't work right on non-uppercase
- */
- c1 = isupper ((guchar)*s1) ? tolower ((guchar)*s1) : *s1;
- c2 = isupper ((guchar)*s2) ? tolower ((guchar)*s2) : *s2;
- if (c1 != c2)
- return (c1 - c2);
- s1++; s2++;
- }
-
- return (((gint)(guchar) *s1) - ((gint)(guchar) *s2));
-#endif
-}
-
-/**
- * g_strncasecmp:
- * @s1: a string.
- * @s2: a string to compare with @s1.
- * @n: the maximum number of characters to compare.
- *
- * A case-insensitive string comparison, corresponding to the standard
- * strncasecmp() function on platforms which support it.
- * It is similar to g_strcasecmp() except it only compares the first @n
- * characters of the strings.
- *
- * Return value: 0 if the strings match, a negative value if @s1 < @s2,
- * or a positive value if @s1 > @s2.
- *
- * Deprecated:2.2: The problem with g_strncasecmp() is that it does the
- * comparison by calling toupper()/tolower(). These functions are
- * locale-specific and operate on single bytes. However, it is impossible
- * to handle things correctly from an I18N standpoint by operating on
- * bytes, since characters may be multibyte. Thus g_strncasecmp() is
- * broken if your string is guaranteed to be ASCII, since it's
- * locale-sensitive, and it's broken if your string is localized, since
- * it doesn't work on many encodings at all, including UTF-8, EUC-JP,
- * etc.
- *
- * There are therefore two replacement functions: g_ascii_strncasecmp(),
- * which only works on ASCII and is not locale-sensitive, and
- * g_utf8_casefold(), which is good for case-insensitive sorting of UTF-8.
- **/
-gint
-g_strncasecmp (const gchar *s1,
- const gchar *s2,
- guint n)
-{
-#ifdef HAVE_STRNCASECMP
- return strncasecmp (s1, s2, n);
-#else
- gint c1, c2;
-
- g_return_val_if_fail (s1 != NULL, 0);
- g_return_val_if_fail (s2 != NULL, 0);
-
- while (n && *s1 && *s2)
- {
- n -= 1;
- /* According to A. Cox, some platforms have islower's that
- * don't work right on non-uppercase
- */
- c1 = isupper ((guchar)*s1) ? tolower ((guchar)*s1) : *s1;
- c2 = isupper ((guchar)*s2) ? tolower ((guchar)*s2) : *s2;
- if (c1 != c2)
- return (c1 - c2);
- s1++; s2++;
- }
-
- if (n)
- return (((gint) (guchar) *s1) - ((gint) (guchar) *s2));
- else
- return 0;
-#endif
-}
-
-gchar*
-g_strdelimit (gchar *string,
- const gchar *delimiters,
- gchar new_delim)
-{
- register gchar *c;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- if (!delimiters)
- delimiters = G_STR_DELIMITERS;
-
- for (c = string; *c; c++)
- {
- if (strchr (delimiters, *c))
- *c = new_delim;
- }
-
- return string;
-}
-
-gchar*
-g_strcanon (gchar *string,
- const gchar *valid_chars,
- gchar substitutor)
-{
- register gchar *c;
-
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (valid_chars != NULL, NULL);
-
- for (c = string; *c; c++)
- {
- if (!strchr (valid_chars, *c))
- *c = substitutor;
- }
-
- return string;
-}
-
-gchar*
-g_strcompress (const gchar *source)
-{
- const gchar *p = source, *octal;
- gchar *dest = g_malloc (strlen (source) + 1);
- gchar *q = dest;
-
- while (*p)
- {
- if (*p == '\\')
- {
- p++;
- switch (*p)
- {
- case '\0':
- g_warning ("g_strcompress: trailing \\");
- goto out;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7':
- *q = 0;
- octal = p;
- while ((p < octal + 3) && (*p >= '0') && (*p <= '7'))
- {
- *q = (*q * 8) + (*p - '0');
- p++;
- }
- q++;
- p--;
- break;
- case 'b':
- *q++ = '\b';
- break;
- case 'f':
- *q++ = '\f';
- break;
- case 'n':
- *q++ = '\n';
- break;
- case 'r':
- *q++ = '\r';
- break;
- case 't':
- *q++ = '\t';
- break;
- default: /* Also handles \" and \\ */
- *q++ = *p;
- break;
- }
- }
- else
- *q++ = *p;
- p++;
- }
-out:
- *q = 0;
-
- return dest;
-}
-
-gchar *
-g_strescape (const gchar *source,
- const gchar *exceptions)
-{
- const guchar *p;
- gchar *dest;
- gchar *q;
- guchar excmap[256];
-
- g_return_val_if_fail (source != NULL, NULL);
-
- p = (guchar *) source;
- /* Each source byte needs maximally four destination chars (\777) */
- q = dest = g_malloc (strlen (source) * 4 + 1);
-
- memset (excmap, 0, 256);
- if (exceptions)
- {
- guchar *e = (guchar *) exceptions;
-
- while (*e)
- {
- excmap[*e] = 1;
- e++;
- }
- }
-
- while (*p)
- {
- if (excmap[*p])
- *q++ = *p;
- else
- {
- switch (*p)
- {
- case '\b':
- *q++ = '\\';
- *q++ = 'b';
- break;
- case '\f':
- *q++ = '\\';
- *q++ = 'f';
- break;
- case '\n':
- *q++ = '\\';
- *q++ = 'n';
- break;
- case '\r':
- *q++ = '\\';
- *q++ = 'r';
- break;
- case '\t':
- *q++ = '\\';
- *q++ = 't';
- break;
- case '\\':
- *q++ = '\\';
- *q++ = '\\';
- break;
- case '"':
- *q++ = '\\';
- *q++ = '"';
- break;
- default:
- if ((*p < ' ') || (*p >= 0177))
- {
- *q++ = '\\';
- *q++ = '0' + (((*p) >> 6) & 07);
- *q++ = '0' + (((*p) >> 3) & 07);
- *q++ = '0' + ((*p) & 07);
- }
- else
- *q++ = *p;
- break;
- }
- }
- p++;
- }
- *q = 0;
- return dest;
-}
-
-gchar*
-g_strchug (gchar *string)
-{
- guchar *start;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- for (start = (guchar*) string; *start && g_ascii_isspace (*start); start++)
- ;
-
- g_memmove (string, start, strlen ((gchar *) start) + 1);
-
- return string;
-}
-
-gchar*
-g_strchomp (gchar *string)
-{
- gsize len;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- len = strlen (string);
- while (len--)
- {
- if (g_ascii_isspace ((guchar) string[len]))
- string[len] = '\0';
- else
- break;
- }
-
- return string;
-}
-
-/**
- * g_strsplit:
- * @string: a string to split.
- * @delimiter: a string which specifies the places at which to split the string.
- * The delimiter is not included in any of the resulting strings, unless
- * @max_tokens is reached.
- * @max_tokens: the maximum number of pieces to split @string into. If this is
- * less than 1, the string is split completely.
- *
- * Splits a string into a maximum of @max_tokens pieces, using the given
- * @delimiter. If @max_tokens is reached, the remainder of @string is appended
- * to the last token.
- *
- * As a special case, the result of splitting the empty string "" is an empty
- * vector, not a vector containing a single string. The reason for this
- * special case is that being able to represent a empty vector is typically
- * more useful than consistent handling of empty elements. If you do need
- * to represent empty elements, you'll need to check for the empty string
- * before calling g_strsplit().
- *
- * Return value: a newly-allocated %NULL-terminated array of strings. Use
- * g_strfreev() to free it.
- **/
-gchar**
-g_strsplit (const gchar *string,
- const gchar *delimiter,
- gint max_tokens)
-{
- GSList *string_list = NULL, *slist;
- gchar **str_array, *s;
- guint n = 0;
- const gchar *remainder;
-
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (delimiter != NULL, NULL);
- g_return_val_if_fail (delimiter[0] != '\0', NULL);
-
- if (max_tokens < 1)
- max_tokens = G_MAXINT;
-
- remainder = string;
- s = strstr (remainder, delimiter);
- if (s)
- {
- gsize delimiter_len = strlen (delimiter);
-
- while (--max_tokens && s)
- {
- gsize len;
-
- len = s - remainder;
- string_list = g_slist_prepend (string_list,
- g_strndup (remainder, len));
- n++;
- remainder = s + delimiter_len;
- s = strstr (remainder, delimiter);
- }
- }
- if (*string)
- {
- n++;
- string_list = g_slist_prepend (string_list, g_strdup (remainder));
- }
-
- str_array = g_new (gchar*, n + 1);
-
- str_array[n--] = NULL;
- for (slist = string_list; slist; slist = slist->next)
- str_array[n--] = slist->data;
-
- g_slist_free (string_list);
-
- return str_array;
-}
-
-/**
- * g_strsplit_set:
- * @string: The string to be tokenized
- * @delimiters: A nul-terminated string containing bytes that are used
- * to split the string.
- * @max_tokens: The maximum number of tokens to split @string into.
- * If this is less than 1, the string is split completely
- *
- * Splits @string into a number of tokens not containing any of the characters
- * in @delimiter. A token is the (possibly empty) longest string that does not
- * contain any of the characters in @delimiters. If @max_tokens is reached, the
- * remainder is appended to the last token.
- *
- * For example the result of g_strsplit_set ("abc:def/ghi", ":/", -1) is a
- * %NULL-terminated vector containing the three strings "abc", "def",
- * and "ghi".
- *
- * The result if g_strsplit_set (":def/ghi:", ":/", -1) is a %NULL-terminated
- * vector containing the four strings "", "def", "ghi", and "".
- *
- * As a special case, the result of splitting the empty string "" is an empty
- * vector, not a vector containing a single string. The reason for this
- * special case is that being able to represent a empty vector is typically
- * more useful than consistent handling of empty elements. If you do need
- * to represent empty elements, you'll need to check for the empty string
- * before calling g_strsplit_set().
- *
- * Note that this function works on bytes not characters, so it can't be used
- * to delimit UTF-8 strings for anything but ASCII characters.
- *
- * Return value: a newly-allocated %NULL-terminated array of strings. Use
- * g_strfreev() to free it.
- *
- * Since: 2.4
- **/
-gchar **
-g_strsplit_set (const gchar *string,
- const gchar *delimiters,
- gint max_tokens)
-{
- gboolean delim_table[256];
- GSList *tokens, *list;
- gint n_tokens;
- const gchar *s;
- const gchar *current;
- gchar *token;
- gchar **result;
-
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (delimiters != NULL, NULL);
-
- if (max_tokens < 1)
- max_tokens = G_MAXINT;
-
- if (*string == '\0')
- {
- result = g_new (char *, 1);
- result[0] = NULL;
- return result;
- }
-
- memset (delim_table, FALSE, sizeof (delim_table));
- for (s = delimiters; *s != '\0'; ++s)
- delim_table[*(guchar *)s] = TRUE;
-
- tokens = NULL;
- n_tokens = 0;
-
- s = current = string;
- while (*s != '\0')
- {
- if (delim_table[*(guchar *)s] && n_tokens + 1 < max_tokens)
- {
- token = g_strndup (current, s - current);
- tokens = g_slist_prepend (tokens, token);
- ++n_tokens;
-
- current = s + 1;
- }
-
- ++s;
- }
-
- token = g_strndup (current, s - current);
- tokens = g_slist_prepend (tokens, token);
- ++n_tokens;
-
- result = g_new (gchar *, n_tokens + 1);
-
- result[n_tokens] = NULL;
- for (list = tokens; list != NULL; list = list->next)
- result[--n_tokens] = list->data;
-
- g_slist_free (tokens);
-
- return result;
-}
-
-/**
- * g_strfreev:
- * @str_array: a %NULL-terminated array of strings to free.
-
- * Frees a %NULL-terminated array of strings, and the array itself.
- * If called on a %NULL value, g_strfreev() simply returns.
- **/
-void
-g_strfreev (gchar **str_array)
-{
- if (str_array)
- {
- int i;
-
- for (i = 0; str_array[i] != NULL; i++)
- g_free (str_array[i]);
-
- g_free (str_array);
- }
-}
-
-/**
- * g_strdupv:
- * @str_array: %NULL-terminated array of strings.
- *
- * Copies %NULL-terminated array of strings. The copy is a deep copy;
- * the new array should be freed by first freeing each string, then
- * the array itself. g_strfreev() does this for you. If called
- * on a %NULL value, g_strdupv() simply returns %NULL.
- *
- * Return value: a new %NULL-terminated array of strings.
- **/
-gchar**
-g_strdupv (gchar **str_array)
-{
- if (str_array)
- {
- gint i;
- gchar **retval;
-
- i = 0;
- while (str_array[i])
- ++i;
-
- retval = g_new (gchar*, i + 1);
-
- i = 0;
- while (str_array[i])
- {
- retval[i] = g_strdup (str_array[i]);
- ++i;
- }
- retval[i] = NULL;
-
- return retval;
- }
- else
- return NULL;
-}
-
-/**
- * g_strjoinv:
- * @separator: a string to insert between each of the strings, or %NULL
- * @str_array: a %NULL-terminated array of strings to join
- *
- * Joins a number of strings together to form one long string, with the
- * optional @separator inserted between each of them. The returned string
- * should be freed with g_free().
- *
- * Returns: a newly-allocated string containing all of the strings joined
- * together, with @separator between them
- */
-gchar*
-g_strjoinv (const gchar *separator,
- gchar **str_array)
-{
- gchar *string;
- gchar *ptr;
-
- g_return_val_if_fail (str_array != NULL, NULL);
-
- if (separator == NULL)
- separator = "";
-
- if (*str_array)
- {
- gint i;
- gsize len;
- gsize separator_len;
-
- separator_len = strlen (separator);
- /* First part, getting length */
- len = 1 + strlen (str_array[0]);
- for (i = 1; str_array[i] != NULL; i++)
- len += strlen (str_array[i]);
- len += separator_len * (i - 1);
-
- /* Second part, building string */
- string = g_new (gchar, len);
- ptr = g_stpcpy (string, *str_array);
- for (i = 1; str_array[i] != NULL; i++)
- {
- ptr = g_stpcpy (ptr, separator);
- ptr = g_stpcpy (ptr, str_array[i]);
- }
- }
- else
- string = g_strdup ("");
-
- return string;
-}
-
-/**
- * g_strjoin:
- * @separator: a string to insert between each of the strings, or %NULL
- * @Varargs: a %NULL-terminated list of strings to join
- *
- * Joins a number of strings together to form one long string, with the
- * optional @separator inserted between each of them. The returned string
- * should be freed with g_free().
- *
- * Returns: a newly-allocated string containing all of the strings joined
- * together, with @separator between them
- */
-gchar*
-g_strjoin (const gchar *separator,
- ...)
-{
- gchar *string, *s;
- va_list args;
- gsize len;
- gsize separator_len;
- gchar *ptr;
-
- if (separator == NULL)
- separator = "";
-
- separator_len = strlen (separator);
-
- va_start (args, separator);
-
- s = va_arg (args, gchar*);
-
- if (s)
- {
- /* First part, getting length */
- len = 1 + strlen (s);
-
- s = va_arg (args, gchar*);
- while (s)
- {
- len += separator_len + strlen (s);
- s = va_arg (args, gchar*);
- }
- va_end (args);
-
- /* Second part, building string */
- string = g_new (gchar, len);
-
- va_start (args, separator);
-
- s = va_arg (args, gchar*);
- ptr = g_stpcpy (string, s);
-
- s = va_arg (args, gchar*);
- while (s)
- {
- ptr = g_stpcpy (ptr, separator);
- ptr = g_stpcpy (ptr, s);
- s = va_arg (args, gchar*);
- }
- }
- else
- string = g_strdup ("");
-
- va_end (args);
-
- return string;
-}
-
-
-/**
- * g_strstr_len:
- * @haystack: a string.
- * @haystack_len: the maximum length of @haystack. Note that -1 is
- * a valid length, if @haystack is nul-terminated, meaning it will
- * search through the whole string.
- * @needle: the string to search for.
- *
- * Searches the string @haystack for the first occurrence
- * of the string @needle, limiting the length of the search
- * to @haystack_len.
- *
- * Return value: a pointer to the found occurrence, or
- * %NULL if not found.
- **/
-gchar *
-g_strstr_len (const gchar *haystack,
- gssize haystack_len,
- const gchar *needle)
-{
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- if (haystack_len < 0)
- return strstr (haystack, needle);
- else
- {
- const gchar *p = haystack;
- gsize needle_len = strlen (needle);
- const gchar *end;
- gsize i;
-
- if (needle_len == 0)
- return (gchar *)haystack;
-
- if (haystack_len < needle_len)
- return NULL;
-
- end = haystack + haystack_len - needle_len;
-
- while (p <= end && *p)
- {
- for (i = 0; i < needle_len; i++)
- if (p[i] != needle[i])
- goto next;
-
- return (gchar *)p;
-
- next:
- p++;
- }
-
- return NULL;
- }
-}
-
-/**
- * g_strrstr:
- * @haystack: a nul-terminated string.
- * @needle: the nul-terminated string to search for.
- *
- * Searches the string @haystack for the last occurrence
- * of the string @needle.
- *
- * Return value: a pointer to the found occurrence, or
- * %NULL if not found.
- **/
-gchar *
-g_strrstr (const gchar *haystack,
- const gchar *needle)
-{
- gsize i;
- gsize needle_len;
- gsize haystack_len;
- const gchar *p;
-
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- needle_len = strlen (needle);
- haystack_len = strlen (haystack);
-
- if (needle_len == 0)
- return (gchar *)haystack;
-
- if (haystack_len < needle_len)
- return NULL;
-
- p = haystack + haystack_len - needle_len;
-
- while (p >= haystack)
- {
- for (i = 0; i < needle_len; i++)
- if (p[i] != needle[i])
- goto next;
-
- return (gchar *)p;
-
- next:
- p--;
- }
-
- return NULL;
-}
-
-/**
- * g_strrstr_len:
- * @haystack: a nul-terminated string.
- * @haystack_len: the maximum length of @haystack.
- * @needle: the nul-terminated string to search for.
- *
- * Searches the string @haystack for the last occurrence
- * of the string @needle, limiting the length of the search
- * to @haystack_len.
- *
- * Return value: a pointer to the found occurrence, or
- * %NULL if not found.
- **/
-gchar *
-g_strrstr_len (const gchar *haystack,
- gssize haystack_len,
- const gchar *needle)
-{
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- if (haystack_len < 0)
- return g_strrstr (haystack, needle);
- else
- {
- gsize needle_len = strlen (needle);
- const gchar *haystack_max = haystack + haystack_len;
- const gchar *p = haystack;
- gsize i;
-
- while (p < haystack_max && *p)
- p++;
-
- if (p < haystack + needle_len)
- return NULL;
-
- p -= needle_len;
-
- while (p >= haystack)
- {
- for (i = 0; i < needle_len; i++)
- if (p[i] != needle[i])
- goto next;
-
- return (gchar *)p;
-
- next:
- p--;
- }
-
- return NULL;
- }
-}
-
-
-/**
- * g_str_has_suffix:
- * @str: a nul-terminated string.
- * @suffix: the nul-terminated suffix to look for.
- *
- * Looks whether the string @str ends with @suffix.
- *
- * Return value: %TRUE if @str end with @suffix, %FALSE otherwise.
- *
- * Since: 2.2
- **/
-gboolean
-g_str_has_suffix (const gchar *str,
- const gchar *suffix)
-{
- int str_len;
- int suffix_len;
-
- g_return_val_if_fail (str != NULL, FALSE);
- g_return_val_if_fail (suffix != NULL, FALSE);
-
- str_len = strlen (str);
- suffix_len = strlen (suffix);
-
- if (str_len < suffix_len)
- return FALSE;
-
- return strcmp (str + str_len - suffix_len, suffix) == 0;
-}
-
-/**
- * g_str_has_prefix:
- * @str: a nul-terminated string.
- * @prefix: the nul-terminated prefix to look for.
- *
- * Looks whether the string @str begins with @prefix.
- *
- * Return value: %TRUE if @str begins with @prefix, %FALSE otherwise.
- *
- * Since: 2.2
- **/
-gboolean
-g_str_has_prefix (const gchar *str,
- const gchar *prefix)
-{
- int str_len;
- int prefix_len;
-
- g_return_val_if_fail (str != NULL, FALSE);
- g_return_val_if_fail (prefix != NULL, FALSE);
-
- str_len = strlen (str);
- prefix_len = strlen (prefix);
-
- if (str_len < prefix_len)
- return FALSE;
-
- return strncmp (str, prefix, prefix_len) == 0;
-}
-
-
-/**
- * g_strip_context:
- * @msgid: a string
- * @msgval: another string
- *
- * An auxiliary function for gettext() support (see Q_()).
- *
- * Return value: @msgval, unless @msgval is identical to @msgid and contains
- * a '|' character, in which case a pointer to the substring of msgid after
- * the first '|' character is returned.
- *
- * Since: 2.4
- **/
-G_CONST_RETURN gchar *
-g_strip_context (const gchar *msgid,
- const gchar *msgval)
-{
- if (msgval == msgid)
- {
- const char *c = strchr (msgid, '|');
- if (c != NULL)
- return c + 1;
- }
-
- return msgval;
-}
-
-
-/**
- * g_strv_length:
- * @str_array: a %NULL-terminated array of strings.
- *
- * Returns the length of the given %NULL-terminated
- * string array @str_array.
- *
- * Return value: length of @str_array.
- *
- * Since: 2.6
- **/
-guint
-g_strv_length (gchar **str_array)
-{
- guint i = 0;
-
- g_return_val_if_fail (str_array != NULL, 0);
-
- while (str_array[i])
- ++i;
-
- return i;
-}
-
-
-/**
- * g_dpgettext:
- * @domain: the translation domain to use, or %NULL to use
- * the domain set with textdomain()
- * @msgctxtid: a combined message context and message id, separated
- * by a \004 character
- * @msgidoffset: the offset of the message id in @msgctxid
- *
- * This function is a variant of g_dgettext() which supports
- * a disambiguating message context. GNU gettext uses the
- * '\004' character to separate the message context and
- * message id in @msgctxtid.
- * If 0 is passed as @msgidoffset, this function will fall back to
- * trying to use the deprecated convention of using "|" as a separation
- * character.
- *
- * This uses g_dgettext() internally. See that functions for differences
- * with dgettext() proper.
- *
- * Applications should normally not use this function directly,
- * but use the C_() macro for translations with context.
- *
- * Returns: The translated string
- *
- * Since: 2.16
- */
-G_CONST_RETURN gchar *
-g_dpgettext (const gchar *domain,
- const gchar *msgctxtid,
- gsize msgidoffset)
-{
- const gchar *translation;
- gchar *sep;
-
- translation = g_dgettext (domain, msgctxtid);
-
- if (translation == msgctxtid)
- {
- if (msgidoffset > 0)
- return msgctxtid + msgidoffset;
-
- sep = strchr (msgctxtid, '|');
-
- if (sep)
- {
- /* try with '\004' instead of '|', in case
- * xgettext -kQ_:1g was used
- */
- gchar *tmp = g_alloca (strlen (msgctxtid) + 1);
- strcpy (tmp, msgctxtid);
- tmp[sep - msgctxtid] = '\004';
-
- translation = g_dgettext (domain, tmp);
-
- if (translation == tmp)
- return sep + 1;
- }
- }
-
- return translation;
-}
-
-/* This function is taken from gettext.h
- * GNU gettext uses '\004' to separate context and msgid in .mo files.
- */
-/**
- * g_dpgettext2:
- * @domain: the translation domain to use, or %NULL to use
- * the domain set with textdomain()
- * @context: the message context
- * @msgid: the message
- *
- * This function is a variant of g_dgettext() which supports
- * a disambiguating message context. GNU gettext uses the
- * '\004' character to separate the message context and
- * message id in @msgctxtid.
- *
- * This uses g_dgettext() internally. See that functions for differences
- * with dgettext() proper.
- *
- * This function differs from C_() in that it is not a macro and
- * thus you may use non-string-literals as context and msgid arguments.
- *
- * Returns: The translated string
- *
- * Since: 2.18
- */
-G_CONST_RETURN char *
-g_dpgettext2 (const char *domain,
- const char *msgctxt,
- const char *msgid)
-{
- size_t msgctxt_len = strlen (msgctxt) + 1;
- size_t msgid_len = strlen (msgid) + 1;
- const char *translation;
- char* msg_ctxt_id;
-
- msg_ctxt_id = g_alloca (msgctxt_len + msgid_len);
-
- memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
- msg_ctxt_id[msgctxt_len - 1] = '\004';
- memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
-
- translation = g_dgettext (domain, msg_ctxt_id);
-
- if (translation == msg_ctxt_id)
- {
- /* try the old way of doing message contexts, too */
- msg_ctxt_id[msgctxt_len - 1] = '|';
- translation = g_dgettext (domain, msg_ctxt_id);
-
- if (translation == msg_ctxt_id)
- return msgid;
- }
-
- return translation;
-}
-
-static gboolean
-_g_dgettext_should_translate (void)
-{
- static gsize translate = 0;
- enum {
- SHOULD_TRANSLATE = 1,
- SHOULD_NOT_TRANSLATE = 2
- };
-
- if (G_UNLIKELY (g_once_init_enter (&translate)))
- {
- gboolean should_translate = TRUE;
-
- const char *default_domain = textdomain (NULL);
- const char *translator_comment = gettext ("");
-#ifndef G_OS_WIN32
- const char *translate_locale = setlocale (LC_MESSAGES, NULL);
-#else
- const char *translate_locale = g_win32_getlocale ();
-#endif
- /* We should NOT translate only if all the following hold:
- * - user has called textdomain() and set textdomain to non-default
- * - default domain has no translations
- * - locale does not start with "en_" and is not "C"
- *
- * Rationale:
- * - If text domain is still the default domain, maybe user calls
- * it later. Continue with old behavior of translating.
- * - If locale starts with "en_", we can continue using the
- * translations even if the app doesn't have translations for
- * this locale. That is, en_UK and en_CA for example.
- * - If locale is "C", maybe user calls setlocale(LC_ALL,"") later.
- * Continue with old behavior of translating.
- */
- if (0 != strcmp (default_domain, "messages") &&
- '\0' == *translator_comment &&
- 0 != strncmp (translate_locale, "en_", 3) &&
- 0 != strcmp (translate_locale, "C"))
- should_translate = FALSE;
-
- g_once_init_leave (&translate,
- should_translate ?
- SHOULD_TRANSLATE :
- SHOULD_NOT_TRANSLATE);
- }
-
- return translate == SHOULD_TRANSLATE;
-}
-
-/**
- * g_dgettext:
- * @domain: the translation domain to use, or %NULL to use
- * the domain set with textdomain()
- * @msgid: message to translate
- *
- * This function is a wrapper of dgettext() which does not translate
- * the message if the default domain as set with textdomain() has no
- * translations for the current locale.
- *
- * The advantage of using this function over dgettext() proper is that
- * libraries using this function (like GTK+) will not use translations
- * if the application using the library does not have translations for
- * the current locale. This results in a consistent English-only
- * interface instead of one having partial translations. For this
- * feature to work, the call to textdomain() and setlocale() should
- * precede any g_dgettext() invocations. For GTK+, it means calling
- * textdomain() before gtk_init or its variants.
- *
- * This function disables translations if and only if upon its first
- * call all the following conditions hold:
- * <itemizedlist>
- * <listitem>@domain is not %NULL</listitem>
- * <listitem>textdomain() has been called to set a default text domain</listitem>
- * <listitem>there is no translations available for the default text domain
- * and the current locale</listitem>
- * <listitem>current locale is not "C" or any English locales (those
- * starting with "en_")</listitem>
- * </itemizedlist>
- *
- * Note that this behavior may not be desired for example if an application
- * has its untranslated messages in a language other than English. In those
- * cases the application should call textdomain() after initializing GTK+.
- *
- * Applications should normally not use this function directly,
- * but use the _() macro for translations.
- *
- * Returns: The translated string
- *
- * Since: 2.18
- */
-G_CONST_RETURN gchar *
-g_dgettext (const gchar *domain,
- const gchar *msgid)
-{
- if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
- return msgid;
-
- return dgettext (domain, msgid);
-}
-
-/**
- * g_dcgettext:
- * @domain: (allow-none): the translation domain to use, or %NULL to use
- * the domain set with textdomain()
- * @msgid: message to translate
- * @category: a locale category
- *
- * This is a variant of g_dgettext() that allows specifying a locale
- * category instead of always using %LC_MESSAGES. See g_dgettext() for
- * more information about how this functions differs from calling
- * dcgettext() directly.
- *
- * Returns: the translated string for the given locale category
- *
- * Since: 2.26
- */
-G_CONST_RETURN gchar *
-g_dcgettext (const gchar *domain,
- const gchar *msgid,
- int category)
-{
- if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
- return msgid;
-
- return dcgettext (domain, msgid, category);
-}
-
-/**
- * g_dngettext:
- * @domain: the translation domain to use, or %NULL to use
- * the domain set with textdomain()
- * @msgid: message to translate
- * @msgid_plural: plural form of the message
- * @n: the quantity for which translation is needed
- *
- * This function is a wrapper of dngettext() which does not translate
- * the message if the default domain as set with textdomain() has no
- * translations for the current locale.
- *
- * See g_dgettext() for details of how this differs from dngettext()
- * proper.
- *
- * Returns: The translated string
- *
- * Since: 2.18
- */
-G_CONST_RETURN gchar *
-g_dngettext (const gchar *domain,
- const gchar *msgid,
- const gchar *msgid_plural,
- gulong n)
-{
- if (domain && G_UNLIKELY (!_g_dgettext_should_translate ()))
- return n == 1 ? msgid : msgid_plural;
-
- return dngettext (domain, msgid, msgid_plural, n);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "gstring.h"
-
-#include "gprintf.h"
-
-
-/**
- * SECTION: string_chunks
- * @title: String Chunks
- * @short_description: efficient storage of groups of strings
- *
- * String chunks are used to store groups of strings. Memory is
- * allocated in blocks, and as strings are added to the #GStringChunk
- * they are copied into the next free position in a block. When a block
- * is full a new block is allocated.
- *
- * When storing a large number of strings, string chunks are more
- * efficient than using g_strdup() since fewer calls to malloc() are
- * needed, and less memory is wasted in memory allocation overheads.
- *
- * By adding strings with g_string_chunk_insert_const() it is also
- * possible to remove duplicates.
- *
- * To create a new #GStringChunk use g_string_chunk_new().
- *
- * To add strings to a #GStringChunk use g_string_chunk_insert().
- *
- * To add strings to a #GStringChunk, but without duplicating strings
- * which are already in the #GStringChunk, use
- * g_string_chunk_insert_const().
- *
- * To free the entire #GStringChunk use g_string_chunk_free(). It is
- * not possible to free individual strings.
- **/
-
-/**
- * GStringChunk:
- *
- * An opaque data structure representing String Chunks. It should only
- * be accessed by using the following functions.
- **/
-struct _GStringChunk
-{
- GHashTable *const_table;
- GSList *storage_list;
- gsize storage_next;
- gsize this_size;
- gsize default_size;
-};
-
-/* Hash Functions.
- */
-
-/**
- * g_str_equal:
- * @v1: a key
- * @v2: a key to compare with @v1
- *
- * Compares two strings for byte-by-byte equality and returns %TRUE
- * if they are equal. It can be passed to g_hash_table_new() as the
- * @key_equal_func parameter, when using strings as keys in a #GHashTable.
- *
- * Note that this function is primarily meant as a hash table comparison
- * function. For a general-purpose, %NULL-safe string comparison function,
- * see g_strcmp0().
- *
- * Returns: %TRUE if the two keys match
- */
-gboolean
-g_str_equal (gconstpointer v1,
- gconstpointer v2)
-{
- const gchar *string1 = v1;
- const gchar *string2 = v2;
-
- return strcmp (string1, string2) == 0;
-}
-
-/**
- * g_str_hash:
- * @v: a string key
- *
- * Converts a string to a hash value.
- * It can be passed to g_hash_table_new() as the @hash_func
- * parameter, when using strings as keys in a #GHashTable.
- *
- * Returns: a hash value corresponding to the key
- */
-guint
-g_str_hash (gconstpointer v)
-{
- /* 31 bit hash function */
- const signed char *p = v;
- guint32 h = *p;
-
- if (h)
- for (p += 1; *p != '\0'; p++)
- h = (h << 5) - h + *p;
-
- return h;
-}
-
-#define MY_MAXSIZE ((gsize)-1)
-
-static inline gsize
-nearest_power (gsize base, gsize num)
-{
- if (num > MY_MAXSIZE / 2)
- {
- return MY_MAXSIZE;
- }
- else
- {
- gsize n = base;
-
- while (n < num)
- n <<= 1;
-
- return n;
- }
-}
-
-/* String Chunks.
- */
-
-/**
- * g_string_chunk_new:
- * @size: the default size of the blocks of memory which are
- * allocated to store the strings. If a particular string
- * is larger than this default size, a larger block of
- * memory will be allocated for it.
- *
- * Creates a new #GStringChunk.
- *
- * Returns: a new #GStringChunk
- */
-GStringChunk*
-g_string_chunk_new (gsize size)
-{
- GStringChunk *new_chunk = g_new (GStringChunk, 1);
- gsize actual_size = 1;
-
- actual_size = nearest_power (1, size);
-
- new_chunk->const_table = NULL;
- new_chunk->storage_list = NULL;
- new_chunk->storage_next = actual_size;
- new_chunk->default_size = actual_size;
- new_chunk->this_size = actual_size;
-
- return new_chunk;
-}
-
-/**
- * g_string_chunk_free:
- * @chunk: a #GStringChunk
- *
- * Frees all memory allocated by the #GStringChunk.
- * After calling g_string_chunk_free() it is not safe to
- * access any of the strings which were contained within it.
- */
-void
-g_string_chunk_free (GStringChunk *chunk)
-{
- GSList *tmp_list;
-
- g_return_if_fail (chunk != NULL);
-
- if (chunk->storage_list)
- {
- for (tmp_list = chunk->storage_list; tmp_list; tmp_list = tmp_list->next)
- g_free (tmp_list->data);
-
- g_slist_free (chunk->storage_list);
- }
-
- if (chunk->const_table)
- g_hash_table_destroy (chunk->const_table);
-
- g_free (chunk);
-}
-
-/**
- * g_string_chunk_clear:
- * @chunk: a #GStringChunk
- *
- * Frees all strings contained within the #GStringChunk.
- * After calling g_string_chunk_clear() it is not safe to
- * access any of the strings which were contained within it.
- *
- * Since: 2.14
- */
-void
-g_string_chunk_clear (GStringChunk *chunk)
-{
- GSList *tmp_list;
-
- g_return_if_fail (chunk != NULL);
-
- if (chunk->storage_list)
- {
- for (tmp_list = chunk->storage_list; tmp_list; tmp_list = tmp_list->next)
- g_free (tmp_list->data);
-
- g_slist_free (chunk->storage_list);
-
- chunk->storage_list = NULL;
- chunk->storage_next = chunk->default_size;
- chunk->this_size = chunk->default_size;
- }
-
- if (chunk->const_table)
- g_hash_table_remove_all (chunk->const_table);
-}
-
-/**
- * g_string_chunk_insert:
- * @chunk: a #GStringChunk
- * @string: the string to add
- *
- * Adds a copy of @string to the #GStringChunk.
- * It returns a pointer to the new copy of the string
- * in the #GStringChunk. The characters in the string
- * can be changed, if necessary, though you should not
- * change anything after the end of the string.
- *
- * Unlike g_string_chunk_insert_const(), this function
- * does not check for duplicates. Also strings added
- * with g_string_chunk_insert() will not be searched
- * by g_string_chunk_insert_const() when looking for
- * duplicates.
- *
- * Returns: a pointer to the copy of @string within
- * the #GStringChunk
- */
-gchar*
-g_string_chunk_insert (GStringChunk *chunk,
- const gchar *string)
-{
- g_return_val_if_fail (chunk != NULL, NULL);
-
- return g_string_chunk_insert_len (chunk, string, -1);
-}
-
-/**
- * g_string_chunk_insert_const:
- * @chunk: a #GStringChunk
- * @string: the string to add
- *
- * Adds a copy of @string to the #GStringChunk, unless the same
- * string has already been added to the #GStringChunk with
- * g_string_chunk_insert_const().
- *
- * This function is useful if you need to copy a large number
- * of strings but do not want to waste space storing duplicates.
- * But you must remember that there may be several pointers to
- * the same string, and so any changes made to the strings
- * should be done very carefully.
- *
- * Note that g_string_chunk_insert_const() will not return a
- * pointer to a string added with g_string_chunk_insert(), even
- * if they do match.
- *
- * Returns: a pointer to the new or existing copy of @string
- * within the #GStringChunk
- */
-gchar*
-g_string_chunk_insert_const (GStringChunk *chunk,
- const gchar *string)
-{
- char* lookup;
-
- g_return_val_if_fail (chunk != NULL, NULL);
-
- if (!chunk->const_table)
- chunk->const_table = g_hash_table_new (g_str_hash, g_str_equal);
-
- lookup = (char*) g_hash_table_lookup (chunk->const_table, (gchar *)string);
-
- if (!lookup)
- {
- lookup = g_string_chunk_insert (chunk, string);
- g_hash_table_insert (chunk->const_table, lookup, lookup);
- }
-
- return lookup;
-}
-
-/**
- * g_string_chunk_insert_len:
- * @chunk: a #GStringChunk
- * @string: bytes to insert
- * @len: number of bytes of @string to insert, or -1 to insert a
- * nul-terminated string
- *
- * Adds a copy of the first @len bytes of @string to the #GStringChunk.
- * The copy is nul-terminated.
- *
- * Since this function does not stop at nul bytes, it is the caller's
- * responsibility to ensure that @string has at least @len addressable
- * bytes.
- *
- * The characters in the returned string can be changed, if necessary,
- * though you should not change anything after the end of the string.
- *
- * Return value: a pointer to the copy of @string within the #GStringChunk
- *
- * Since: 2.4
- */
-gchar*
-g_string_chunk_insert_len (GStringChunk *chunk,
- const gchar *string,
- gssize len)
-{
- gssize size;
- gchar* pos;
-
- g_return_val_if_fail (chunk != NULL, NULL);
-
- if (len < 0)
- size = strlen (string);
- else
- size = len;
-
- if ((chunk->storage_next + size + 1) > chunk->this_size)
- {
- gsize new_size = nearest_power (chunk->default_size, size + 1);
-
- chunk->storage_list = g_slist_prepend (chunk->storage_list,
- g_new (gchar, new_size));
-
- chunk->this_size = new_size;
- chunk->storage_next = 0;
- }
-
- pos = ((gchar *) chunk->storage_list->data) + chunk->storage_next;
-
- *(pos + size) = '\0';
-
- memcpy (pos, string, size);
-
- chunk->storage_next += size + 1;
-
- return pos;
-}
-
-/* Strings.
- */
-static void
-g_string_maybe_expand (GString* string,
- gsize len)
-{
- if (string->len + len >= string->allocated_len)
- {
- string->allocated_len = nearest_power (1, string->len + len + 1);
- string->str = g_realloc (string->str, string->allocated_len);
- }
-}
-
-/**
- * g_string_sized_new:
- * @dfl_size: the default size of the space allocated to
- * hold the string
- *
- * Creates a new #GString, with enough space for @dfl_size
- * bytes. This is useful if you are going to add a lot of
- * text to the string and don't want it to be reallocated
- * too often.
- *
- * Returns: the new #GString
- */
-GString*
-g_string_sized_new (gsize dfl_size)
-{
- GString *string = g_slice_new (GString);
-
- string->allocated_len = 0;
- string->len = 0;
- string->str = NULL;
-
- g_string_maybe_expand (string, MAX (dfl_size, 2));
- string->str[0] = 0;
-
- return string;
-}
-
-/**
- * g_string_new:
- * @init: the initial text to copy into the string
- *
- * Creates a new #GString, initialized with the given string.
- *
- * Returns: the new #GString
- */
-GString*
-g_string_new (const gchar *init)
-{
- GString *string;
-
- if (init == NULL || *init == '\0')
- string = g_string_sized_new (2);
- else
- {
- gint len;
-
- len = strlen (init);
- string = g_string_sized_new (len + 2);
-
- g_string_append_len (string, init, len);
- }
-
- return string;
-}
-
-/**
- * g_string_new_len:
- * @init: initial contents of the string
- * @len: length of @init to use
- *
- * Creates a new #GString with @len bytes of the @init buffer.
- * Because a length is provided, @init need not be nul-terminated,
- * and can contain embedded nul bytes.
- *
- * Since this function does not stop at nul bytes, it is the caller's
- * responsibility to ensure that @init has at least @len addressable
- * bytes.
- *
- * Returns: a new #GString
- */
-GString*
-g_string_new_len (const gchar *init,
- gssize len)
-{
- GString *string;
-
- if (len < 0)
- return g_string_new (init);
- else
- {
- string = g_string_sized_new (len);
-
- if (init)
- g_string_append_len (string, init, len);
-
- return string;
- }
-}
-
-/**
- * g_string_free:
- * @string: a #GString
- * @free_segment: if %TRUE the actual character data is freed as well
- *
- * Frees the memory allocated for the #GString.
- * If @free_segment is %TRUE it also frees the character data.
- *
- * Returns: the character data of @string
- * (i.e. %NULL if @free_segment is %TRUE)
- */
-gchar*
-g_string_free (GString *string,
- gboolean free_segment)
-{
- gchar *segment;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- if (free_segment)
- {
- g_free (string->str);
- segment = NULL;
- }
- else
- segment = string->str;
-
- g_slice_free (GString, string);
-
- return segment;
-}
-
-/**
- * g_string_equal:
- * @v: a #GString
- * @v2: another #GString
- *
- * Compares two strings for equality, returning %TRUE if they are equal.
- * For use with #GHashTable.
- *
- * Returns: %TRUE if they strings are the same length and contain the
- * same bytes
- */
-gboolean
-g_string_equal (const GString *v,
- const GString *v2)
-{
- gchar *p, *q;
- GString *string1 = (GString *) v;
- GString *string2 = (GString *) v2;
- gsize i = string1->len;
-
- if (i != string2->len)
- return FALSE;
-
- p = string1->str;
- q = string2->str;
- while (i)
- {
- if (*p != *q)
- return FALSE;
- p++;
- q++;
- i--;
- }
- return TRUE;
-}
-
-/**
- * g_string_hash:
- * @str: a string to hash
- *
- * Creates a hash code for @str; for use with #GHashTable.
- *
- * Returns: hash code for @str
- */
-/* 31 bit hash function */
-guint
-g_string_hash (const GString *str)
-{
- const gchar *p = str->str;
- gsize n = str->len;
- guint h = 0;
-
- while (n--)
- {
- h = (h << 5) - h + *p;
- p++;
- }
-
- return h;
-}
-
-/**
- * g_string_assign:
- * @string: the destination #GString. Its current contents
- * are destroyed.
- * @rval: the string to copy into @string
- *
- * Copies the bytes from a string into a #GString,
- * destroying any previous contents. It is rather like
- * the standard strcpy() function, except that you do not
- * have to worry about having enough space to copy the string.
- *
- * Returns: @string
- */
-GString*
-g_string_assign (GString *string,
- const gchar *rval)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (rval != NULL, string);
-
- /* Make sure assigning to itself doesn't corrupt the string. */
- if (string->str != rval)
- {
- /* Assigning from substring should be ok since g_string_truncate
- does not realloc. */
- g_string_truncate (string, 0);
- g_string_append (string, rval);
- }
-
- return string;
-}
-
-/**
- * g_string_truncate:
- * @string: a #GString
- * @len: the new size of @string
- *
- * Cuts off the end of the GString, leaving the first @len bytes.
- *
- * Returns: @string
- */
-GString*
-g_string_truncate (GString *string,
- gsize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- string->len = MIN (len, string->len);
- string->str[string->len] = 0;
-
- return string;
-}
-
-/**
- * g_string_set_size:
- * @string: a #GString
- * @len: the new length
- *
- * Sets the length of a #GString. If the length is less than
- * the current length, the string will be truncated. If the
- * length is greater than the current length, the contents
- * of the newly added area are undefined. (However, as
- * always, string->str[string->len] will be a nul byte.)
- *
- * Return value: @string
- **/
-GString*
-g_string_set_size (GString *string,
- gsize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- if (len >= string->allocated_len)
- g_string_maybe_expand (string, len - string->len);
-
- string->len = len;
- string->str[len] = 0;
-
- return string;
-}
-
-/**
- * g_string_insert_len:
- * @string: a #GString
- * @pos: position in @string where insertion should
- * happen, or -1 for at the end
- * @val: bytes to insert
- * @len: number of bytes of @val to insert
- *
- * Inserts @len bytes of @val into @string at @pos.
- * Because @len is provided, @val may contain embedded
- * nuls and need not be nul-terminated. If @pos is -1,
- * bytes are inserted at the end of the string.
- *
- * Since this function does not stop at nul bytes, it is
- * the caller's responsibility to ensure that @val has at
- * least @len addressable bytes.
- *
- * Returns: @string
- */
-GString*
-g_string_insert_len (GString *string,
- gssize pos,
- const gchar *val,
- gssize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (len == 0 || val != NULL, string);
-
- if (len == 0)
- return string;
-
- if (len < 0)
- len = strlen (val);
-
- if (pos < 0)
- pos = string->len;
- else
- g_return_val_if_fail (pos <= string->len, string);
-
- /* Check whether val represents a substring of string. This test
- probably violates chapter and verse of the C standards, since
- ">=" and "<=" are only valid when val really is a substring.
- In practice, it will work on modern archs. */
- if (val >= string->str && val <= string->str + string->len)
- {
- gsize offset = val - string->str;
- gsize precount = 0;
-
- g_string_maybe_expand (string, len);
- val = string->str + offset;
- /* At this point, val is valid again. */
-
- /* Open up space where we are going to insert. */
- if (pos < string->len)
- g_memmove (string->str + pos + len, string->str + pos, string->len - pos);
-
- /* Move the source part before the gap, if any. */
- if (offset < pos)
- {
- precount = MIN (len, pos - offset);
- memcpy (string->str + pos, val, precount);
- }
-
- /* Move the source part after the gap, if any. */
- if (len > precount)
- memcpy (string->str + pos + precount,
- val + /* Already moved: */ precount + /* Space opened up: */ len,
- len - precount);
- }
- else
- {
- g_string_maybe_expand (string, len);
-
- /* If we aren't appending at the end, move a hunk
- * of the old string to the end, opening up space
- */
- if (pos < string->len)
- g_memmove (string->str + pos + len, string->str + pos, string->len - pos);
-
- /* insert the new string */
- if (len == 1)
- string->str[pos] = *val;
- else
- memcpy (string->str + pos, val, len);
- }
-
- string->len += len;
-
- string->str[string->len] = 0;
-
- return string;
-}
-
-#define SUB_DELIM_CHARS "!$&'()*+,;="
-
-static gboolean
-is_valid (char c, const char *reserved_chars_allowed)
-{
- if (g_ascii_isalnum (c) ||
- c == '-' ||
- c == '.' ||
- c == '_' ||
- c == '~')
- return TRUE;
-
- if (reserved_chars_allowed &&
- strchr (reserved_chars_allowed, c) != NULL)
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean
-gunichar_ok (gunichar c)
-{
- return
- (c != (gunichar) -2) &&
- (c != (gunichar) -1);
-}
-
-/**
- * g_string_append_uri_escaped:
- * @string: a #GString
- * @unescaped: a string
- * @reserved_chars_allowed: a string of reserved characters allowed to be used, or %NULL
- * @allow_utf8: set %TRUE if the escaped string may include UTF8 characters
- *
- * Appends @unescaped to @string, escaped any characters that
- * are reserved in URIs using URI-style escape sequences.
- *
- * Returns: @string
- *
- * Since: 2.16
- **/
-GString *
-g_string_append_uri_escaped (GString *string,
- const char *unescaped,
- const char *reserved_chars_allowed,
- gboolean allow_utf8)
-{
- unsigned char c;
- const char *end;
- static const gchar hex[16] = "0123456789ABCDEF";
-
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (unescaped != NULL, NULL);
-
- end = unescaped + strlen (unescaped);
-
- while ((c = *unescaped) != 0)
- {
- if (c >= 0x80 && allow_utf8 &&
- gunichar_ok (g_utf8_get_char_validated (unescaped, end - unescaped)))
- {
- int len = g_utf8_skip [c];
- g_string_append_len (string, unescaped, len);
- unescaped += len;
- }
- else if (is_valid (c, reserved_chars_allowed))
- {
- g_string_append_c (string, c);
- unescaped++;
- }
- else
- {
- g_string_append_c (string, '%');
- g_string_append_c (string, hex[((guchar)c) >> 4]);
- g_string_append_c (string, hex[((guchar)c) & 0xf]);
- unescaped++;
- }
- }
-
- return string;
-}
-
-/**
- * g_string_append:
- * @string: a #GString
- * @val: the string to append onto the end of @string
- *
- * Adds a string onto the end of a #GString, expanding
- * it if necessary.
- *
- * Returns: @string
- */
-GString*
-g_string_append (GString *string,
- const gchar *val)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (val != NULL, string);
-
- return g_string_insert_len (string, -1, val, -1);
-}
-
-/**
- * g_string_append_len:
- * @string: a #GString
- * @val: bytes to append
- * @len: number of bytes of @val to use
- *
- * Appends @len bytes of @val to @string. Because @len is
- * provided, @val may contain embedded nuls and need not
- * be nul-terminated.
- *
- * Since this function does not stop at nul bytes, it is
- * the caller's responsibility to ensure that @val has at
- * least @len addressable bytes.
- *
- * Returns: @string
- */
-GString*
-g_string_append_len (GString *string,
- const gchar *val,
- gssize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (len == 0 || val != NULL, string);
-
- return g_string_insert_len (string, -1, val, len);
-}
-
-/**
- * g_string_append_c:
- * @string: a #GString
- * @c: the byte to append onto the end of @string
- *
- * Adds a byte onto the end of a #GString, expanding
- * it if necessary.
- *
- * Returns: @string
- */
-#undef g_string_append_c
-GString*
-g_string_append_c (GString *string,
- gchar c)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- return g_string_insert_c (string, -1, c);
-}
-
-/**
- * g_string_append_unichar:
- * @string: a #GString
- * @wc: a Unicode character
- *
- * Converts a Unicode character into UTF-8, and appends it
- * to the string.
- *
- * Return value: @string
- **/
-GString*
-g_string_append_unichar (GString *string,
- gunichar wc)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- return g_string_insert_unichar (string, -1, wc);
-}
-
-/**
- * g_string_prepend:
- * @string: a #GString
- * @val: the string to prepend on the start of @string
- *
- * Adds a string on to the start of a #GString,
- * expanding it if necessary.
- *
- * Returns: @string
- */
-GString*
-g_string_prepend (GString *string,
- const gchar *val)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (val != NULL, string);
-
- return g_string_insert_len (string, 0, val, -1);
-}
-
-/**
- * g_string_prepend_len:
- * @string: a #GString
- * @val: bytes to prepend
- * @len: number of bytes in @val to prepend
- *
- * Prepends @len bytes of @val to @string.
- * Because @len is provided, @val may contain
- * embedded nuls and need not be nul-terminated.
- *
- * Since this function does not stop at nul bytes,
- * it is the caller's responsibility to ensure that
- * @val has at least @len addressable bytes.
- *
- * Returns: @string
- */
-GString*
-g_string_prepend_len (GString *string,
- const gchar *val,
- gssize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (val != NULL, string);
-
- return g_string_insert_len (string, 0, val, len);
-}
-
-/**
- * g_string_prepend_c:
- * @string: a #GString
- * @c: the byte to prepend on the start of the #GString
- *
- * Adds a byte onto the start of a #GString,
- * expanding it if necessary.
- *
- * Returns: @string
- */
-GString*
-g_string_prepend_c (GString *string,
- gchar c)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- return g_string_insert_c (string, 0, c);
-}
-
-/**
- * g_string_prepend_unichar:
- * @string: a #GString
- * @wc: a Unicode character
- *
- * Converts a Unicode character into UTF-8, and prepends it
- * to the string.
- *
- * Return value: @string
- **/
-GString*
-g_string_prepend_unichar (GString *string,
- gunichar wc)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- return g_string_insert_unichar (string, 0, wc);
-}
-
-/**
- * g_string_insert:
- * @string: a #GString
- * @pos: the position to insert the copy of the string
- * @val: the string to insert
- *
- * Inserts a copy of a string into a #GString,
- * expanding it if necessary.
- *
- * Returns: @string
- */
-GString*
-g_string_insert (GString *string,
- gssize pos,
- const gchar *val)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (val != NULL, string);
- if (pos >= 0)
- g_return_val_if_fail (pos <= string->len, string);
-
- return g_string_insert_len (string, pos, val, -1);
-}
-
-/**
- * g_string_insert_c:
- * @string: a #GString
- * @pos: the position to insert the byte
- * @c: the byte to insert
- *
- * Inserts a byte into a #GString, expanding it if necessary.
- *
- * Returns: @string
- */
-GString*
-g_string_insert_c (GString *string,
- gssize pos,
- gchar c)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- g_string_maybe_expand (string, 1);
-
- if (pos < 0)
- pos = string->len;
- else
- g_return_val_if_fail (pos <= string->len, string);
-
- /* If not just an append, move the old stuff */
- if (pos < string->len)
- g_memmove (string->str + pos + 1, string->str + pos, string->len - pos);
-
- string->str[pos] = c;
-
- string->len += 1;
-
- string->str[string->len] = 0;
-
- return string;
-}
-
-/**
- * g_string_insert_unichar:
- * @string: a #GString
- * @pos: the position at which to insert character, or -1 to
- * append at the end of the string
- * @wc: a Unicode character
- *
- * Converts a Unicode character into UTF-8, and insert it
- * into the string at the given position.
- *
- * Return value: @string
- **/
-GString*
-g_string_insert_unichar (GString *string,
- gssize pos,
- gunichar wc)
-{
- gint charlen, first, i;
- gchar *dest;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- /* Code copied from g_unichar_to_utf() */
- if (wc < 0x80)
- {
- first = 0;
- charlen = 1;
- }
- else if (wc < 0x800)
- {
- first = 0xc0;
- charlen = 2;
- }
- else if (wc < 0x10000)
- {
- first = 0xe0;
- charlen = 3;
- }
- else if (wc < 0x200000)
- {
- first = 0xf0;
- charlen = 4;
- }
- else if (wc < 0x4000000)
- {
- first = 0xf8;
- charlen = 5;
- }
- else
- {
- first = 0xfc;
- charlen = 6;
- }
- /* End of copied code */
-
- g_string_maybe_expand (string, charlen);
-
- if (pos < 0)
- pos = string->len;
- else
- g_return_val_if_fail (pos <= string->len, string);
-
- /* If not just an append, move the old stuff */
- if (pos < string->len)
- g_memmove (string->str + pos + charlen, string->str + pos, string->len - pos);
-
- dest = string->str + pos;
- /* Code copied from g_unichar_to_utf() */
- for (i = charlen - 1; i > 0; --i)
- {
- dest[i] = (wc & 0x3f) | 0x80;
- wc >>= 6;
- }
- dest[0] = wc | first;
- /* End of copied code */
-
- string->len += charlen;
-
- string->str[string->len] = 0;
-
- return string;
-}
-
-/**
- * g_string_overwrite:
- * @string: a #GString
- * @pos: the position at which to start overwriting
- * @val: the string that will overwrite the @string starting at @pos
- *
- * Overwrites part of a string, lengthening it if necessary.
- *
- * Return value: @string
- *
- * Since: 2.14
- **/
-GString *
-g_string_overwrite (GString *string,
- gsize pos,
- const gchar *val)
-{
- g_return_val_if_fail (val != NULL, string);
- return g_string_overwrite_len (string, pos, val, strlen (val));
-}
-
-/**
- * g_string_overwrite_len:
- * @string: a #GString
- * @pos: the position at which to start overwriting
- * @val: the string that will overwrite the @string starting at @pos
- * @len: the number of bytes to write from @val
- *
- * Overwrites part of a string, lengthening it if necessary.
- * This function will work with embedded nuls.
- *
- * Return value: @string
- *
- * Since: 2.14
- **/
-GString *
-g_string_overwrite_len (GString *string,
- gsize pos,
- const gchar *val,
- gssize len)
-{
- gsize end;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- if (!len)
- return string;
-
- g_return_val_if_fail (val != NULL, string);
- g_return_val_if_fail (pos <= string->len, string);
-
- if (len < 0)
- len = strlen (val);
-
- end = pos + len;
-
- if (end > string->len)
- g_string_maybe_expand (string, end - string->len);
-
- memcpy (string->str + pos, val, len);
-
- if (end > string->len)
- {
- string->str[end] = '\0';
- string->len = end;
- }
-
- return string;
-}
-
-/**
- * g_string_erase:
- * @string: a #GString
- * @pos: the position of the content to remove
- * @len: the number of bytes to remove, or -1 to remove all
- * following bytes
- *
- * Removes @len bytes from a #GString, starting at position @pos.
- * The rest of the #GString is shifted down to fill the gap.
- *
- * Returns: @string
- */
-GString*
-g_string_erase (GString *string,
- gssize pos,
- gssize len)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (pos >= 0, string);
- g_return_val_if_fail (pos <= string->len, string);
-
- if (len < 0)
- len = string->len - pos;
- else
- {
- g_return_val_if_fail (pos + len <= string->len, string);
-
- if (pos + len < string->len)
- g_memmove (string->str + pos, string->str + pos + len, string->len - (pos + len));
- }
-
- string->len -= len;
-
- string->str[string->len] = 0;
-
- return string;
-}
-
-/**
- * g_string_ascii_down:
- * @string: a GString
- *
- * Converts all upper case ASCII letters to lower case ASCII letters.
- *
- * Return value: passed-in @string pointer, with all the upper case
- * characters converted to lower case in place, with
- * semantics that exactly match g_ascii_tolower().
- **/
-GString*
-g_string_ascii_down (GString *string)
-{
- gchar *s;
- gint n;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- n = string->len;
- s = string->str;
-
- while (n)
- {
- *s = g_ascii_tolower (*s);
- s++;
- n--;
- }
-
- return string;
-}
-
-/**
- * g_string_ascii_up:
- * @string: a GString
- *
- * Converts all lower case ASCII letters to upper case ASCII letters.
- *
- * Return value: passed-in @string pointer, with all the lower case
- * characters converted to upper case in place, with
- * semantics that exactly match g_ascii_toupper().
- **/
-GString*
-g_string_ascii_up (GString *string)
-{
- gchar *s;
- gint n;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- n = string->len;
- s = string->str;
-
- while (n)
- {
- *s = g_ascii_toupper (*s);
- s++;
- n--;
- }
-
- return string;
-}
-
-/**
- * g_string_down:
- * @string: a #GString
- *
- * Converts a #GString to lowercase.
- *
- * Returns: the #GString.
- *
- * Deprecated:2.2: This function uses the locale-specific
- * tolower() function, which is almost never the right thing.
- * Use g_string_ascii_down() or g_utf8_strdown() instead.
- */
-GString*
-g_string_down (GString *string)
-{
- guchar *s;
- glong n;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- n = string->len;
- s = (guchar *) string->str;
-
- while (n)
- {
- if (isupper (*s))
- *s = tolower (*s);
- s++;
- n--;
- }
-
- return string;
-}
-
-/**
- * g_string_up:
- * @string: a #GString
- *
- * Converts a #GString to uppercase.
- *
- * Return value: @string
- *
- * Deprecated:2.2: This function uses the locale-specific
- * toupper() function, which is almost never the right thing.
- * Use g_string_ascii_up() or g_utf8_strup() instead.
- **/
-GString*
-g_string_up (GString *string)
-{
- guchar *s;
- glong n;
-
- g_return_val_if_fail (string != NULL, NULL);
-
- n = string->len;
- s = (guchar *) string->str;
-
- while (n)
- {
- if (islower (*s))
- *s = toupper (*s);
- s++;
- n--;
- }
-
- return string;
-}
-
-/**
- * g_string_append_vprintf:
- * @string: a #GString
- * @format: the string format. See the printf() documentation
- * @args: the list of arguments to insert in the output
- *
- * Appends a formatted string onto the end of a #GString.
- * This function is similar to g_string_append_printf()
- * except that the arguments to the format string are passed
- * as a va_list.
- *
- * Since: 2.14
- */
-void
-g_string_append_vprintf (GString *string,
- const gchar *format,
- va_list args)
-{
- gchar *buf;
- gint len;
-
- g_return_if_fail (string != NULL);
- g_return_if_fail (format != NULL);
-
- len = g_vasprintf (&buf, format, args);
-
- if (len >= 0)
- {
- g_string_maybe_expand (string, len);
- memcpy (string->str + string->len, buf, len + 1);
- string->len += len;
- g_free (buf);
- }
-}
-
-/**
- * g_string_vprintf:
- * @string: a #GString
- * @format: the string format. See the printf() documentation
- * @args: the parameters to insert into the format string
- *
- * Writes a formatted string into a #GString.
- * This function is similar to g_string_printf() except that
- * the arguments to the format string are passed as a va_list.
- *
- * Since: 2.14
- */
-void
-g_string_vprintf (GString *string,
- const gchar *format,
- va_list args)
-{
- g_string_truncate (string, 0);
- g_string_append_vprintf (string, format, args);
-}
-
-/**
- * g_string_sprintf:
- * @string: a #GString
- * @format: the string format. See the sprintf() documentation
- * @Varargs: the parameters to insert into the format string
- *
- * Writes a formatted string into a #GString.
- * This is similar to the standard sprintf() function,
- * except that the #GString buffer automatically expands
- * to contain the results. The previous contents of the
- * #GString are destroyed.
- *
- * Deprecated: This function has been renamed to g_string_printf().
- */
-
-/**
- * g_string_printf:
- * @string: a #GString
- * @format: the string format. See the printf() documentation
- * @Varargs: the parameters to insert into the format string
- *
- * Writes a formatted string into a #GString.
- * This is similar to the standard sprintf() function,
- * except that the #GString buffer automatically expands
- * to contain the results. The previous contents of the
- * #GString are destroyed.
- */
-void
-g_string_printf (GString *string,
- const gchar *format,
- ...)
-{
- va_list args;
-
- g_string_truncate (string, 0);
-
- va_start (args, format);
- g_string_append_vprintf (string, format, args);
- va_end (args);
-}
-
-/**
- * g_string_sprintfa:
- * @string: a #GString
- * @format: the string format. See the sprintf() documentation
- * @Varargs: the parameters to insert into the format string
- *
- * Appends a formatted string onto the end of a #GString.
- * This function is similar to g_string_sprintf() except that
- * the text is appended to the #GString.
- *
- * Deprecated: This function has been renamed to g_string_append_printf()
- */
-
-/**
- * g_string_append_printf:
- * @string: a #GString
- * @format: the string format. See the printf() documentation
- * @Varargs: the parameters to insert into the format string
- *
- * Appends a formatted string onto the end of a #GString.
- * This function is similar to g_string_printf() except
- * that the text is appended to the #GString.
- */
-void
-g_string_append_printf (GString *string,
- const gchar *format,
- ...)
-{
- va_list args;
-
- va_start (args, format);
- g_string_append_vprintf (string, format, args);
- va_end (args);
-}
+++ /dev/null
-/* GLib testing framework runner
- * Copyright (C) 2007 Sven Herzberg
- * Copyright (C) 2007 Tim Janik
- *
- * 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 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 <glib.h>
-#include <gstdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <signal.h>
-
-/* the read buffer size in bytes */
-#define READ_BUFFER_SIZE 4096
-
-/* --- prototypes --- */
-static int main_selftest (int argc,
- char **argv);
-static void parse_args (gint *argc_p,
- gchar ***argv_p);
-
-/* --- variables --- */
-static GIOChannel *ioc_report = NULL;
-static gboolean gtester_quiet = FALSE;
-static gboolean gtester_verbose = FALSE;
-static gboolean gtester_list_tests = FALSE;
-static gboolean gtester_selftest = FALSE;
-static gboolean subtest_running = FALSE;
-static gint subtest_exitstatus = 0;
-static gboolean subtest_io_pending = FALSE;
-static gboolean subtest_quiet = TRUE;
-static gboolean subtest_verbose = FALSE;
-static gboolean subtest_mode_fatal = TRUE;
-static gboolean subtest_mode_perf = FALSE;
-static gboolean subtest_mode_quick = TRUE;
-static const gchar *subtest_seedstr = NULL;
-static gchar *subtest_last_seed = NULL;
-static GSList *subtest_paths = NULL;
-static GSList *subtest_args = NULL;
-static gboolean testcase_open = FALSE;
-static guint testcase_count = 0;
-static guint testcase_fail_count = 0;
-static const gchar *output_filename = NULL;
-static guint log_indent = 0;
-static gint log_fd = -1;
-
-/* --- functions --- */
-static const char*
-sindent (guint n)
-{
- static const char spaces[] = " ";
- int l = sizeof (spaces) - 1;
- n = MIN (n, l);
- return spaces + l - n;
-}
-
-static void G_GNUC_PRINTF (1, 2)
-test_log_printfe (const char *format,
- ...)
-{
- char *result;
- int r;
- va_list args;
- va_start (args, format);
- result = g_markup_vprintf_escaped (format, args);
- va_end (args);
- do
- r = write (log_fd, result, strlen (result));
- while (r < 0 && errno == EINTR);
- g_free (result);
-}
-
-static void
-terminate (void)
-{
- kill (getpid(), SIGTERM);
- abort();
-}
-
-static void
-testcase_close (long double duration,
- gint exit_status,
- guint n_forks)
-{
- g_return_if_fail (testcase_open > 0);
- test_log_printfe ("%s<duration>%.6Lf</duration>\n", sindent (log_indent), duration);
- test_log_printfe ("%s<status exit-status=\"%d\" n-forks=\"%d\" result=\"%s\"/>\n",
- sindent (log_indent), exit_status, n_forks,
- exit_status ? "failed" : "success");
- log_indent -= 2;
- test_log_printfe ("%s</testcase>\n", sindent (log_indent));
- testcase_open--;
- if (gtester_verbose)
- g_print ("%s\n", exit_status ? "FAIL" : "OK");
- if (exit_status && subtest_last_seed)
- g_print ("GTester: last random seed: %s\n", subtest_last_seed);
- if (exit_status)
- testcase_fail_count += 1;
- if (subtest_mode_fatal && testcase_fail_count)
- terminate();
-}
-
-static void
-test_log_msg (GTestLogMsg *msg)
-{
- switch (msg->log_type)
- {
- guint i;
- gchar **strv;
- case G_TEST_LOG_NONE:
- break;
- case G_TEST_LOG_ERROR:
- strv = g_strsplit (msg->strings[0], "\n", -1);
- for (i = 0; strv[i]; i++)
- test_log_printfe ("%s<error>%s</error>\n", sindent (log_indent), strv[i]);
- g_strfreev (strv);
- break;
- case G_TEST_LOG_START_BINARY:
- test_log_printfe ("%s<binary file=\"%s\"/>\n", sindent (log_indent), msg->strings[0]);
- subtest_last_seed = g_strdup (msg->strings[1]);
- test_log_printfe ("%s<random-seed>%s</random-seed>\n", sindent (log_indent), subtest_last_seed);
- break;
- case G_TEST_LOG_LIST_CASE:
- g_print ("%s\n", msg->strings[0]);
- break;
- case G_TEST_LOG_START_CASE:
- testcase_count++;
- if (gtester_verbose)
- {
- gchar *sc = g_strconcat (msg->strings[0], ":", NULL);
- gchar *sleft = g_strdup_printf ("%-68s", sc);
- g_free (sc);
- g_print ("%70s ", sleft);
- g_free (sleft);
- }
- g_return_if_fail (testcase_open == 0);
- testcase_open++;
- test_log_printfe ("%s<testcase path=\"%s\">\n", sindent (log_indent), msg->strings[0]);
- log_indent += 2;
- break;
- case G_TEST_LOG_SKIP_CASE:
- if (FALSE && gtester_verbose) /* enable to debug test case skipping logic */
- {
- gchar *sc = g_strconcat (msg->strings[0], ":", NULL);
- gchar *sleft = g_strdup_printf ("%-68s", sc);
- g_free (sc);
- g_print ("%70s SKIPPED\n", sleft);
- g_free (sleft);
- }
- test_log_printfe ("%s<testcase path=\"%s\" skipped=\"1\"/>\n", sindent (log_indent), msg->strings[0]);
- break;
- case G_TEST_LOG_STOP_CASE:
- testcase_close (msg->nums[2], (int) msg->nums[0], (int) msg->nums[1]);
- break;
- case G_TEST_LOG_MIN_RESULT:
- case G_TEST_LOG_MAX_RESULT:
- test_log_printfe ("%s<performance minimize=\"%d\" maximize=\"%d\" value=\"%.16Lg\">\n",
- sindent (log_indent), msg->log_type == G_TEST_LOG_MIN_RESULT, msg->log_type == G_TEST_LOG_MAX_RESULT, msg->nums[0]);
- test_log_printfe ("%s%s\n", sindent (log_indent + 2), msg->strings[0]);
- test_log_printfe ("%s</performance>\n", sindent (log_indent));
- break;
- case G_TEST_LOG_MESSAGE:
- test_log_printfe ("%s<message>\n%s\n%s</message>\n", sindent (log_indent), msg->strings[0], sindent (log_indent));
- break;
- }
-}
-
-static gboolean
-child_report_cb (GIOChannel *source,
- GIOCondition condition,
- gpointer data)
-{
- GTestLogBuffer *tlb = data;
- GIOStatus status = G_IO_STATUS_NORMAL;
- gboolean first_read_eof = FALSE, first_read = TRUE;
- gsize length = 0;
- do
- {
- guint8 buffer[READ_BUFFER_SIZE];
- GError *error = NULL;
- status = g_io_channel_read_chars (source, (gchar*) buffer, sizeof (buffer), &length, &error);
- if (first_read && (condition & G_IO_IN))
- {
- /* on some unixes (MacOS) we need to detect non-blocking fd EOF
- * by an IO_IN select/poll followed by read()==0.
- */
- first_read_eof = length == 0;
- }
- first_read = FALSE;
- if (length)
- {
- GTestLogMsg *msg;
- g_test_log_buffer_push (tlb, length, buffer);
- do
- {
- msg = g_test_log_buffer_pop (tlb);
- if (msg)
- {
- test_log_msg (msg);
- g_test_log_msg_free (msg);
- }
- }
- while (msg);
- }
- g_clear_error (&error);
- /* ignore the io channel status, which will report intermediate EOFs for non blocking fds */
- (void) status;
- }
- while (length > 0);
- /* g_print ("LASTIOSTATE: first_read_eof=%d condition=%d\n", first_read_eof, condition); */
- if (first_read_eof || (condition & (G_IO_ERR | G_IO_HUP)))
- {
- /* if there's no data to read and select() reports an error or hangup,
- * the fd must have been closed remotely
- */
- subtest_io_pending = FALSE;
- return FALSE;
- }
- return TRUE; /* keep polling */
-}
-
-static void
-child_watch_cb (GPid pid,
- gint status,
- gpointer data)
-{
- g_spawn_close_pid (pid);
- if (WIFEXITED (status)) /* normal exit */
- subtest_exitstatus = WEXITSTATUS (status);
- else /* signal or core dump, etc */
- subtest_exitstatus = 0xffffffff;
- subtest_running = FALSE;
-}
-
-static gchar*
-queue_gfree (GSList **slistp,
- gchar *string)
-{
- *slistp = g_slist_prepend (*slistp, string);
- return string;
-}
-
-static void
-unset_cloexec_fdp (gpointer fdp_data)
-{
- int r, *fdp = fdp_data;
- do
- r = fcntl (*fdp, F_SETFD, 0 /* FD_CLOEXEC */);
- while (r < 0 && errno == EINTR);
-}
-
-static gboolean
-launch_test_binary (const char *binary,
- guint skip_tests)
-{
- GTestLogBuffer *tlb;
- GSList *slist, *free_list = NULL;
- GError *error = NULL;
- int argc = 0;
- const gchar **argv;
- GPid pid = 0;
- gint report_pipe[2] = { -1, -1 };
- guint child_report_cb_id = 0;
- gboolean loop_pending;
- gint i = 0;
-
- if (pipe (report_pipe) < 0)
- {
- if (subtest_mode_fatal)
- g_error ("Failed to open pipe for test binary: %s: %s", binary, g_strerror (errno));
- else
- g_warning ("Failed to open pipe for test binary: %s: %s", binary, g_strerror (errno));
- return FALSE;
- }
-
- /* setup argc */
- for (slist = subtest_args; slist; slist = slist->next)
- argc++;
- /* argc++; */
- if (subtest_quiet)
- argc++;
- if (subtest_verbose)
- argc++;
- if (!subtest_mode_fatal)
- argc++;
- if (subtest_mode_quick)
- argc++;
- else
- argc++;
- if (subtest_mode_perf)
- argc++;
- if (gtester_list_tests)
- argc++;
- if (subtest_seedstr)
- argc++;
- argc++;
- if (skip_tests)
- argc++;
- for (slist = subtest_paths; slist; slist = slist->next)
- argc++;
-
- /* setup argv */
- argv = g_malloc ((argc + 2) * sizeof(gchar *));
- argv[i++] = binary;
- for (slist = subtest_args; slist; slist = slist->next)
- argv[i++] = (gchar*) slist->data;
- /* argv[i++] = "--debug-log"; */
- if (subtest_quiet)
- argv[i++] = "--quiet";
- if (subtest_verbose)
- argv[i++] = "--verbose";
- if (!subtest_mode_fatal)
- argv[i++] = "--keep-going";
- if (subtest_mode_quick)
- argv[i++] = "-m=quick";
- else
- argv[i++] = "-m=slow";
- if (subtest_mode_perf)
- argv[i++] = "-m=perf";
- if (gtester_list_tests)
- argv[i++] = "-l";
- if (subtest_seedstr)
- argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--seed=%s", subtest_seedstr));
- argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--GTestLogFD=%u", report_pipe[1]));
- if (skip_tests)
- argv[i++] = queue_gfree (&free_list, g_strdup_printf ("--GTestSkipCount=%u", skip_tests));
- for (slist = subtest_paths; slist; slist = slist->next)
- argv[i++] = queue_gfree (&free_list, g_strdup_printf ("-p=%s", (gchar*) slist->data));
- argv[i++] = NULL;
-
- g_spawn_async_with_pipes (NULL, /* g_get_current_dir() */
- (gchar**) argv,
- NULL, /* envp */
- G_SPAWN_DO_NOT_REAP_CHILD, /* G_SPAWN_SEARCH_PATH */
- unset_cloexec_fdp, &report_pipe[1], /* pre-exec callback */
- &pid,
- NULL, /* standard_input */
- NULL, /* standard_output */
- NULL, /* standard_error */
- &error);
- g_slist_foreach (free_list, (void(*)(void*,void*)) g_free, NULL);
- g_slist_free (free_list);
- free_list = NULL;
- close (report_pipe[1]);
-
- if (!gtester_quiet)
- g_print ("(pid=%lu)\n", (unsigned long) pid);
-
- if (error)
- {
- close (report_pipe[0]);
- if (subtest_mode_fatal)
- g_error ("Failed to execute test binary: %s: %s", argv[0], error->message);
- else
- g_warning ("Failed to execute test binary: %s: %s", argv[0], error->message);
- g_clear_error (&error);
- g_free (argv);
- return FALSE;
- }
- g_free (argv);
-
- subtest_running = TRUE;
- subtest_io_pending = TRUE;
- tlb = g_test_log_buffer_new();
- if (report_pipe[0] >= 0)
- {
- ioc_report = g_io_channel_unix_new (report_pipe[0]);
- g_io_channel_set_flags (ioc_report, G_IO_FLAG_NONBLOCK, NULL);
- g_io_channel_set_encoding (ioc_report, NULL, NULL);
- g_io_channel_set_buffered (ioc_report, FALSE);
- child_report_cb_id = g_io_add_watch_full (ioc_report, G_PRIORITY_DEFAULT - 1, G_IO_IN | G_IO_ERR | G_IO_HUP, child_report_cb, tlb, NULL);
- g_io_channel_unref (ioc_report);
- }
- g_child_watch_add_full (G_PRIORITY_DEFAULT + 1, pid, child_watch_cb, NULL, NULL);
-
- loop_pending = g_main_context_pending (NULL);
- while (subtest_running || /* FALSE once child exits */
- subtest_io_pending || /* FALSE once ioc_report closes */
- loop_pending) /* TRUE while idler, etc are running */
- {
- /* g_print ("LOOPSTATE: subtest_running=%d subtest_io_pending=%d\n", subtest_running, subtest_io_pending); */
- /* check for unexpected hangs that are not signalled on report_pipe */
- if (!subtest_running && /* child exited */
- subtest_io_pending && /* no EOF detected on report_pipe */
- !loop_pending) /* no IO events pending however */
- break;
- g_main_context_iteration (NULL, TRUE);
- loop_pending = g_main_context_pending (NULL);
- }
-
- g_source_remove (child_report_cb_id);
- close (report_pipe[0]);
- g_test_log_buffer_free (tlb);
-
- return TRUE;
-}
-
-static void
-launch_test (const char *binary)
-{
- gboolean success = TRUE;
- GTimer *btimer = g_timer_new();
- gboolean need_restart;
- testcase_count = 0;
- testcase_fail_count = 0;
- if (!gtester_quiet)
- g_print ("TEST: %s... ", binary);
-
- retry:
- test_log_printfe ("%s<testbinary path=\"%s\">\n", sindent (log_indent), binary);
- log_indent += 2;
- g_timer_start (btimer);
- subtest_exitstatus = 0;
- success &= launch_test_binary (binary, testcase_count);
- success &= subtest_exitstatus == 0;
- need_restart = testcase_open != 0;
- if (testcase_open)
- testcase_close (0, -256, 0);
- g_timer_stop (btimer);
- test_log_printfe ("%s<duration>%.6f</duration>\n", sindent (log_indent), g_timer_elapsed (btimer, NULL));
- log_indent -= 2;
- test_log_printfe ("%s</testbinary>\n", sindent (log_indent));
- g_free (subtest_last_seed);
- subtest_last_seed = NULL;
- if (need_restart)
- {
- /* restart test binary, skipping processed test cases */
- goto retry;
- }
-
- if (!gtester_quiet)
- g_print ("%s: %s\n", testcase_fail_count || !success ? "FAIL" : "PASS", binary);
- g_timer_destroy (btimer);
- if (subtest_mode_fatal && !success)
- terminate();
-}
-
-static void
-usage (gboolean just_version)
-{
- if (just_version)
- {
- g_print ("gtester version %d.%d.%d\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
- return;
- }
- g_print ("Usage: gtester [OPTIONS] testprogram...\n");
- /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 */
- g_print ("Options:\n");
- g_print (" -h, --help show this help message\n");
- g_print (" -v, --version print version informations\n");
- g_print (" --g-fatal-warnings make warnings fatal (abort)\n");
- g_print (" -k, --keep-going continue running after tests failed\n");
- g_print (" -l list paths of available test cases\n");
- g_print (" -m=perf, -m=slow, -m=quick -m=thorough\n");
- g_print (" run test cases in mode perf, slow/thorough or quick (default)\n");
- g_print (" -p=TESTPATH only start test cases matching TESTPATH\n");
- g_print (" --seed=SEEDSTRING start all tests with random number seed SEEDSTRING\n");
- g_print (" -o=LOGFILE write the test log to LOGFILE\n");
- g_print (" -q, --quiet suppress per test binary output\n");
- g_print (" --verbose report success per testcase\n");
-}
-
-static void
-parse_args (gint *argc_p,
- gchar ***argv_p)
-{
- guint argc = *argc_p;
- gchar **argv = *argv_p;
- guint i, e;
- /* parse known args */
- for (i = 1; i < argc; i++)
- {
- if (strcmp (argv[i], "--g-fatal-warnings") == 0)
- {
- GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
- fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
- g_log_set_always_fatal (fatal_mask);
- argv[i] = NULL;
- }
- else if (strcmp (argv[i], "--gtester-selftest") == 0)
- {
- gtester_selftest = TRUE;
- argv[i] = NULL;
- break; /* stop parsing regular gtester arguments */
- }
- else if (strcmp (argv[i], "-h") == 0 || strcmp (argv[i], "--help") == 0)
- {
- usage (FALSE);
- exit (0);
- argv[i] = NULL;
- }
- else if (strcmp (argv[i], "-v") == 0 || strcmp (argv[i], "--version") == 0)
- {
- usage (TRUE);
- exit (0);
- argv[i] = NULL;
- }
- else if (strcmp (argv[i], "--keep-going") == 0 ||
- strcmp (argv[i], "-k") == 0)
- {
- subtest_mode_fatal = FALSE;
- argv[i] = NULL;
- }
- else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
- {
- gchar *equal = argv[i] + 2;
- if (*equal == '=')
- subtest_paths = g_slist_prepend (subtest_paths, equal + 1);
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- subtest_paths = g_slist_prepend (subtest_paths, argv[i]);
- }
- argv[i] = NULL;
- }
- else if (strcmp ("--test-arg", argv[i]) == 0 || strncmp ("--test-arg=", argv[i], 11) == 0)
- {
- gchar *equal = argv[i] + 10;
- if (*equal == '=')
- subtest_args = g_slist_prepend (subtest_args, equal + 1);
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- subtest_args = g_slist_prepend (subtest_args, argv[i]);
- }
- argv[i] = NULL;
- }
- else if (strcmp ("-o", argv[i]) == 0 || strncmp ("-o=", argv[i], 3) == 0)
- {
- gchar *equal = argv[i] + 2;
- if (*equal == '=')
- output_filename = equal + 1;
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- output_filename = argv[i];
- }
- argv[i] = NULL;
- }
- else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
- {
- gchar *equal = argv[i] + 2;
- const gchar *mode = "";
- if (*equal == '=')
- mode = equal + 1;
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- mode = argv[i];
- }
- if (strcmp (mode, "perf") == 0)
- subtest_mode_perf = TRUE;
- else if (strcmp (mode, "slow") == 0 || strcmp (mode, "thorough") == 0)
- subtest_mode_quick = FALSE;
- else if (strcmp (mode, "quick") == 0)
- {
- subtest_mode_quick = TRUE;
- subtest_mode_perf = FALSE;
- }
- else
- g_error ("unknown test mode: -m %s", mode);
- argv[i] = NULL;
- }
- else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
- {
- gtester_quiet = TRUE;
- gtester_verbose = FALSE;
- argv[i] = NULL;
- }
- else if (strcmp ("--verbose", argv[i]) == 0)
- {
- gtester_quiet = FALSE;
- gtester_verbose = TRUE;
- argv[i] = NULL;
- }
- else if (strcmp ("-l", argv[i]) == 0)
- {
- gtester_list_tests = TRUE;
- argv[i] = NULL;
- }
- else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
- {
- gchar *equal = argv[i] + 6;
- if (*equal == '=')
- subtest_seedstr = equal + 1;
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- subtest_seedstr = argv[i];
- }
- argv[i] = NULL;
- }
- }
- /* collapse argv */
- e = 1;
- for (i = 1; i < argc; i++)
- if (argv[i])
- {
- argv[e++] = argv[i];
- if (i >= e)
- argv[i] = NULL;
- }
- *argc_p = e;
-}
-
-static gboolean
-do_nothing (gpointer data)
-{
- return TRUE;
-}
-
-int
-main (int argc,
- char **argv)
-{
- guint ui;
-
- /* See #578295 */
- g_timeout_add_seconds (5, do_nothing, NULL);
-
- /* some unices need SA_RESTART for SIGCHLD to return -EAGAIN for io.
- * we must fiddle with sigaction() *before* glib is used, otherwise
- * we could revoke signal hanmdler setups from glib initialization code.
- */
- if (TRUE)
- {
- struct sigaction sa;
- struct sigaction osa;
- sa.sa_handler = SIG_DFL;
- sigfillset (&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction (SIGCHLD, &sa, &osa);
- }
-
- g_set_prgname (argv[0]);
- parse_args (&argc, &argv);
- if (gtester_selftest)
- return main_selftest (argc, argv);
-
- if (argc <= 1)
- {
- usage (FALSE);
- return 1;
- }
-
- if (output_filename)
- {
- log_fd = g_open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (log_fd < 0)
- g_error ("Failed to open log file '%s': %s", output_filename, g_strerror (errno));
- }
-
- test_log_printfe ("<?xml version=\"1.0\"?>\n");
- test_log_printfe ("%s<gtester>\n", sindent (log_indent));
- log_indent += 2;
- for (ui = 1; ui < argc; ui++)
- {
- const char *binary = argv[ui];
- launch_test (binary);
- /* we only get here on success or if !subtest_mode_fatal */
- }
- log_indent -= 2;
- test_log_printfe ("%s</gtester>\n", sindent (log_indent));
-
- close (log_fd);
-
- return testcase_fail_count == 0 ? 0 : 1;
-}
-
-static void
-fixture_setup (guint *fix,
- gconstpointer test_data)
-{
- g_assert_cmphex (*fix, ==, 0);
- *fix = 0xdeadbeef;
-}
-static void
-fixture_test (guint *fix,
- gconstpointer test_data)
-{
- g_assert_cmphex (*fix, ==, 0xdeadbeef);
- g_test_message ("This is a test message API test message.");
- g_test_bug_base ("http://www.example.com/bugtracker/");
- g_test_bug ("123");
- g_test_bug_base ("http://www.example.com/bugtracker?bugnum=%s;cmd=showbug");
- g_test_bug ("456");
-}
-static void
-fixture_teardown (guint *fix,
- gconstpointer test_data)
-{
- g_assert_cmphex (*fix, ==, 0xdeadbeef);
-}
-
-static int
-main_selftest (int argc,
- char **argv)
-{
- /* gtester main() for --gtester-selftest invokations */
- g_test_init (&argc, &argv, NULL);
- g_test_add ("/gtester/fixture-test", guint, NULL, fixture_setup, fixture_test, fixture_teardown);
- return g_test_run();
-}
+++ /dev/null
-/* GLib testing utilities
- * Copyright (C) 2007 Imendio AB
- * Authors: Tim Janik, Sven Herzberg
- *
- * 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 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 "config.h"
-
-#include "gtestutils.h"
-
-#include <sys/types.h>
-#ifdef G_OS_UNIX
-#include <sys/wait.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#endif
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-#endif
-#include <errno.h>
-#include <signal.h>
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-
-#include "gmain.h"
-#include "gpattern.h"
-#include "grand.h"
-#include "gstrfuncs.h"
-#include "gtimer.h"
-
-
-/* Global variable for storing assertion messages; this is the counterpart to
- * glibc's (private) __abort_msg variable, and allows developers and crash
- * analysis systems like Apport and ABRT to fish out assertion messages from
- * core dumps, instead of having to catch them on screen output. */
-char *__glib_assert_msg = NULL;
-
-/* --- structures --- */
-struct GTestCase
-{
- gchar *name;
- guint fixture_size;
- void (*fixture_setup) (void*, gconstpointer);
- void (*fixture_test) (void*, gconstpointer);
- void (*fixture_teardown) (void*, gconstpointer);
- gpointer test_data;
-};
-struct GTestSuite
-{
- gchar *name;
- GSList *suites;
- GSList *cases;
-};
-typedef struct DestroyEntry DestroyEntry;
-struct DestroyEntry
-{
- DestroyEntry *next;
- GDestroyNotify destroy_func;
- gpointer destroy_data;
-};
-
-/* --- prototypes --- */
-static void test_run_seed (const gchar *rseed);
-static void test_trap_clear (void);
-static guint8* g_test_log_dump (GTestLogMsg *msg,
- guint *len);
-static void gtest_default_log_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer unused_data);
-
-
-/* --- variables --- */
-static int test_log_fd = -1;
-static gboolean test_mode_fatal = TRUE;
-static gboolean g_test_run_once = TRUE;
-static gboolean test_run_list = FALSE;
-static gchar *test_run_seedstr = NULL;
-static GRand *test_run_rand = NULL;
-static gchar *test_run_name = "";
-static guint test_run_forks = 0;
-static guint test_run_count = 0;
-static guint test_skip_count = 0;
-static GTimer *test_user_timer = NULL;
-static double test_user_stamp = 0;
-static GSList *test_paths = NULL;
-static GTestSuite *test_suite_root = NULL;
-static int test_trap_last_status = 0;
-static int test_trap_last_pid = 0;
-static char *test_trap_last_stdout = NULL;
-static char *test_trap_last_stderr = NULL;
-static char *test_uri_base = NULL;
-static gboolean test_debug_log = FALSE;
-static DestroyEntry *test_destroy_queue = NULL;
-static GTestConfig mutable_test_config_vars = {
- FALSE, /* test_initialized */
- TRUE, /* test_quick */
- FALSE, /* test_perf */
- FALSE, /* test_verbose */
- FALSE, /* test_quiet */
-};
-const GTestConfig * const g_test_config_vars = &mutable_test_config_vars;
-
-/* --- functions --- */
-const char*
-g_test_log_type_name (GTestLogType log_type)
-{
- switch (log_type)
- {
- case G_TEST_LOG_NONE: return "none";
- case G_TEST_LOG_ERROR: return "error";
- case G_TEST_LOG_START_BINARY: return "binary";
- case G_TEST_LOG_LIST_CASE: return "list";
- case G_TEST_LOG_SKIP_CASE: return "skip";
- case G_TEST_LOG_START_CASE: return "start";
- case G_TEST_LOG_STOP_CASE: return "stop";
- case G_TEST_LOG_MIN_RESULT: return "minperf";
- case G_TEST_LOG_MAX_RESULT: return "maxperf";
- case G_TEST_LOG_MESSAGE: return "message";
- }
- return "???";
-}
-
-static void
-g_test_log_send (guint n_bytes,
- const guint8 *buffer)
-{
- if (test_log_fd >= 0)
- {
- int r;
- do
- r = write (test_log_fd, buffer, n_bytes);
- while (r < 0 && errno == EINTR);
- }
- if (test_debug_log)
- {
- GTestLogBuffer *lbuffer = g_test_log_buffer_new ();
- GTestLogMsg *msg;
- guint ui;
- g_test_log_buffer_push (lbuffer, n_bytes, buffer);
- msg = g_test_log_buffer_pop (lbuffer);
- g_warn_if_fail (msg != NULL);
- g_warn_if_fail (lbuffer->data->len == 0);
- g_test_log_buffer_free (lbuffer);
- /* print message */
- g_printerr ("{*LOG(%s)", g_test_log_type_name (msg->log_type));
- for (ui = 0; ui < msg->n_strings; ui++)
- g_printerr (":{%s}", msg->strings[ui]);
- if (msg->n_nums)
- {
- g_printerr (":(");
- for (ui = 0; ui < msg->n_nums; ui++)
- g_printerr ("%s%.16Lg", ui ? ";" : "", msg->nums[ui]);
- g_printerr (")");
- }
- g_printerr (":LOG*}\n");
- g_test_log_msg_free (msg);
- }
-}
-
-static void
-g_test_log (GTestLogType lbit,
- const gchar *string1,
- const gchar *string2,
- guint n_args,
- long double *largs)
-{
- gboolean fail = lbit == G_TEST_LOG_STOP_CASE && largs[0] != 0;
- GTestLogMsg msg;
- gchar *astrings[3] = { NULL, NULL, NULL };
- guint8 *dbuffer;
- guint32 dbufferlen;
-
- switch (lbit)
- {
- case G_TEST_LOG_START_BINARY:
- if (g_test_verbose())
- g_print ("GTest: random seed: %s\n", string2);
- break;
- case G_TEST_LOG_STOP_CASE:
- if (g_test_verbose())
- g_print ("GTest: result: %s\n", fail ? "FAIL" : "OK");
- else if (!g_test_quiet())
- g_print ("%s\n", fail ? "FAIL" : "OK");
- if (fail && test_mode_fatal)
- abort();
- break;
- case G_TEST_LOG_MIN_RESULT:
- if (g_test_verbose())
- g_print ("(MINPERF:%s)\n", string1);
- break;
- case G_TEST_LOG_MAX_RESULT:
- if (g_test_verbose())
- g_print ("(MAXPERF:%s)\n", string1);
- break;
- case G_TEST_LOG_MESSAGE:
- if (g_test_verbose())
- g_print ("(MSG: %s)\n", string1);
- break;
- default: ;
- }
-
- msg.log_type = lbit;
- msg.n_strings = (string1 != NULL) + (string1 && string2);
- msg.strings = astrings;
- astrings[0] = (gchar*) string1;
- astrings[1] = astrings[0] ? (gchar*) string2 : NULL;
- msg.n_nums = n_args;
- msg.nums = largs;
- dbuffer = g_test_log_dump (&msg, &dbufferlen);
- g_test_log_send (dbufferlen, dbuffer);
- g_free (dbuffer);
-
- switch (lbit)
- {
- case G_TEST_LOG_START_CASE:
- if (g_test_verbose())
- g_print ("GTest: run: %s\n", string1);
- else if (!g_test_quiet())
- g_print ("%s: ", string1);
- break;
- default: ;
- }
-}
-
-/* We intentionally parse the command line without GOptionContext
- * because otherwise you would never be able to test it.
- */
-static void
-parse_args (gint *argc_p,
- gchar ***argv_p)
-{
- guint argc = *argc_p;
- gchar **argv = *argv_p;
- guint i, e;
- /* parse known args */
- for (i = 1; i < argc; i++)
- {
- if (strcmp (argv[i], "--g-fatal-warnings") == 0)
- {
- GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
- fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
- g_log_set_always_fatal (fatal_mask);
- argv[i] = NULL;
- }
- else if (strcmp (argv[i], "--keep-going") == 0 ||
- strcmp (argv[i], "-k") == 0)
- {
- test_mode_fatal = FALSE;
- argv[i] = NULL;
- }
- else if (strcmp (argv[i], "--debug-log") == 0)
- {
- test_debug_log = TRUE;
- argv[i] = NULL;
- }
- else if (strcmp ("--GTestLogFD", argv[i]) == 0 || strncmp ("--GTestLogFD=", argv[i], 13) == 0)
- {
- gchar *equal = argv[i] + 12;
- if (*equal == '=')
- test_log_fd = g_ascii_strtoull (equal + 1, NULL, 0);
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- test_log_fd = g_ascii_strtoull (argv[i], NULL, 0);
- }
- argv[i] = NULL;
- }
- else if (strcmp ("--GTestSkipCount", argv[i]) == 0 || strncmp ("--GTestSkipCount=", argv[i], 17) == 0)
- {
- gchar *equal = argv[i] + 16;
- if (*equal == '=')
- test_skip_count = g_ascii_strtoull (equal + 1, NULL, 0);
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- test_skip_count = g_ascii_strtoull (argv[i], NULL, 0);
- }
- argv[i] = NULL;
- }
- else if (strcmp ("-p", argv[i]) == 0 || strncmp ("-p=", argv[i], 3) == 0)
- {
- gchar *equal = argv[i] + 2;
- if (*equal == '=')
- test_paths = g_slist_prepend (test_paths, equal + 1);
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- test_paths = g_slist_prepend (test_paths, argv[i]);
- }
- argv[i] = NULL;
- }
- else if (strcmp ("-m", argv[i]) == 0 || strncmp ("-m=", argv[i], 3) == 0)
- {
- gchar *equal = argv[i] + 2;
- const gchar *mode = "";
- if (*equal == '=')
- mode = equal + 1;
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- mode = argv[i];
- }
- if (strcmp (mode, "perf") == 0)
- mutable_test_config_vars.test_perf = TRUE;
- else if (strcmp (mode, "slow") == 0)
- mutable_test_config_vars.test_quick = FALSE;
- else if (strcmp (mode, "thorough") == 0)
- mutable_test_config_vars.test_quick = FALSE;
- else if (strcmp (mode, "quick") == 0)
- {
- mutable_test_config_vars.test_quick = TRUE;
- mutable_test_config_vars.test_perf = FALSE;
- }
- else
- g_error ("unknown test mode: -m %s", mode);
- argv[i] = NULL;
- }
- else if (strcmp ("-q", argv[i]) == 0 || strcmp ("--quiet", argv[i]) == 0)
- {
- mutable_test_config_vars.test_quiet = TRUE;
- mutable_test_config_vars.test_verbose = FALSE;
- argv[i] = NULL;
- }
- else if (strcmp ("--verbose", argv[i]) == 0)
- {
- mutable_test_config_vars.test_quiet = FALSE;
- mutable_test_config_vars.test_verbose = TRUE;
- argv[i] = NULL;
- }
- else if (strcmp ("-l", argv[i]) == 0)
- {
- test_run_list = TRUE;
- argv[i] = NULL;
- }
- else if (strcmp ("--seed", argv[i]) == 0 || strncmp ("--seed=", argv[i], 7) == 0)
- {
- gchar *equal = argv[i] + 6;
- if (*equal == '=')
- test_run_seedstr = equal + 1;
- else if (i + 1 < argc)
- {
- argv[i++] = NULL;
- test_run_seedstr = argv[i];
- }
- argv[i] = NULL;
- }
- else if (strcmp ("-?", argv[i]) == 0 || strcmp ("--help", argv[i]) == 0)
- {
- printf ("Usage:\n"
- " %s [OPTION...]\n\n"
- "Help Options:\n"
- " -?, --help Show help options\n"
- "Test Options:\n"
- " -l List test cases available in a test executable\n"
- " -seed=RANDOMSEED Provide a random seed to reproduce test\n"
- " runs using random numbers\n"
- " --verbose Run tests verbosely\n"
- " -q, --quiet Run tests quietly\n"
- " -p TESTPATH execute all tests matching TESTPATH\n"
- " -m {perf|slow|thorough|quick} Execute tests according modes\n"
- " --debug-log debug test logging output\n"
- " -k, --keep-going gtester-specific argument\n"
- " --GTestLogFD=N gtester-specific argument\n"
- " --GTestSkipCount=N gtester-specific argument\n",
- argv[0]);
- exit (0);
- }
- }
- /* collapse argv */
- e = 1;
- for (i = 1; i < argc; i++)
- if (argv[i])
- {
- argv[e++] = argv[i];
- if (i >= e)
- argv[i] = NULL;
- }
- *argc_p = e;
-}
-
-/**
- * g_test_init:
- * @argc: Address of the @argc parameter of the main() function.
- * Changed if any arguments were handled.
- * @argv: Address of the @argv parameter of main().
- * Any parameters understood by g_test_init() stripped before return.
- * @Varargs: Reserved for future extension. Currently, you must pass %NULL.
- *
- * Initialize the GLib testing framework, e.g. by seeding the
- * test random number generator, the name for g_get_prgname()
- * and parsing test related command line args.
- * So far, the following arguments are understood:
- * <variablelist>
- * <varlistentry>
- * <term><option>-l</option></term>
- * <listitem><para>
- * list test cases available in a test executable.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>--seed=<replaceable>RANDOMSEED</replaceable></option></term>
- * <listitem><para>
- * provide a random seed to reproduce test runs using random numbers.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>--verbose</option></term>
- * <listitem><para>run tests verbosely.</para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>-q</option>, <option>--quiet</option></term>
- * <listitem><para>run tests quietly.</para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>-p <replaceable>TESTPATH</replaceable></option></term>
- * <listitem><para>
- * execute all tests matching <replaceable>TESTPATH</replaceable>.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>-m {perf|slow|thorough|quick}</option></term>
- * <listitem><para>
- * execute tests according to these test modes:
- * <variablelist>
- * <varlistentry>
- * <term>perf</term>
- * <listitem><para>
- * performance tests, may take long and report results.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>slow, thorough</term>
- * <listitem><para>
- * slow and thorough tests, may take quite long and
- * maximize coverage.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>quick</term>
- * <listitem><para>
- * quick tests, should run really quickly and give good coverage.
- * </para></listitem>
- * </varlistentry>
- * </variablelist>
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>--debug-log</option></term>
- * <listitem><para>debug test logging output.</para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>-k</option>, <option>--keep-going</option></term>
- * <listitem><para>gtester-specific argument.</para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>--GTestLogFD <replaceable>N</replaceable></option></term>
- * <listitem><para>gtester-specific argument.</para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term><option>--GTestSkipCount <replaceable>N</replaceable></option></term>
- * <listitem><para>gtester-specific argument.</para></listitem>
- * </varlistentry>
- * </variablelist>
- *
- * Since: 2.16
- */
-void
-g_test_init (int *argc,
- char ***argv,
- ...)
-{
- static char seedstr[4 + 4 * 8 + 1];
- va_list args;
- gpointer vararg1;
- /* make warnings and criticals fatal for all test programs */
- GLogLevelFlags fatal_mask = (GLogLevelFlags) g_log_set_always_fatal ((GLogLevelFlags) G_LOG_FATAL_MASK);
- fatal_mask = (GLogLevelFlags) (fatal_mask | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
- g_log_set_always_fatal (fatal_mask);
- /* check caller args */
- g_return_if_fail (argc != NULL);
- g_return_if_fail (argv != NULL);
- g_return_if_fail (g_test_config_vars->test_initialized == FALSE);
- mutable_test_config_vars.test_initialized = TRUE;
-
- va_start (args, argv);
- vararg1 = va_arg (args, gpointer); /* reserved for future extensions */
- va_end (args);
- g_return_if_fail (vararg1 == NULL);
-
- /* setup random seed string */
- g_snprintf (seedstr, sizeof (seedstr), "R02S%08x%08x%08x%08x", g_random_int(), g_random_int(), g_random_int(), g_random_int());
- test_run_seedstr = seedstr;
-
- /* parse args, sets up mode, changes seed, etc. */
- parse_args (argc, argv);
- if (!g_get_prgname())
- g_set_prgname ((*argv)[0]);
-
- /* verify GRand reliability, needed for reliable seeds */
- if (1)
- {
- GRand *rg = g_rand_new_with_seed (0xc8c49fb6);
- guint32 t1 = g_rand_int (rg), t2 = g_rand_int (rg), t3 = g_rand_int (rg), t4 = g_rand_int (rg);
- /* g_print ("GRand-current: 0x%x 0x%x 0x%x 0x%x\n", t1, t2, t3, t4); */
- if (t1 != 0xfab39f9b || t2 != 0xb948fb0e || t3 != 0x3d31be26 || t4 != 0x43a19d66)
- g_warning ("random numbers are not GRand-2.2 compatible, seeds may be broken (check $G_RANDOM_VERSION)");
- g_rand_free (rg);
- }
-
- /* check rand seed */
- test_run_seed (test_run_seedstr);
-
- /* report program start */
- g_log_set_default_handler (gtest_default_log_handler, NULL);
- g_test_log (G_TEST_LOG_START_BINARY, g_get_prgname(), test_run_seedstr, 0, NULL);
-}
-
-static void
-test_run_seed (const gchar *rseed)
-{
- guint seed_failed = 0;
- if (test_run_rand)
- g_rand_free (test_run_rand);
- test_run_rand = NULL;
- while (strchr (" \t\v\r\n\f", *rseed))
- rseed++;
- if (strncmp (rseed, "R02S", 4) == 0) /* seed for random generator 02 (GRand-2.2) */
- {
- const char *s = rseed + 4;
- if (strlen (s) >= 32) /* require 4 * 8 chars */
- {
- guint32 seedarray[4];
- gchar *p, hexbuf[9] = { 0, };
- memcpy (hexbuf, s + 0, 8);
- seedarray[0] = g_ascii_strtoull (hexbuf, &p, 16);
- seed_failed += p != NULL && *p != 0;
- memcpy (hexbuf, s + 8, 8);
- seedarray[1] = g_ascii_strtoull (hexbuf, &p, 16);
- seed_failed += p != NULL && *p != 0;
- memcpy (hexbuf, s + 16, 8);
- seedarray[2] = g_ascii_strtoull (hexbuf, &p, 16);
- seed_failed += p != NULL && *p != 0;
- memcpy (hexbuf, s + 24, 8);
- seedarray[3] = g_ascii_strtoull (hexbuf, &p, 16);
- seed_failed += p != NULL && *p != 0;
- if (!seed_failed)
- {
- test_run_rand = g_rand_new_with_seed_array (seedarray, 4);
- return;
- }
- }
- }
- g_error ("Unknown or invalid random seed: %s", rseed);
-}
-
-/**
- * g_test_rand_int:
- *
- * Get a reproducible random integer number.
- *
- * The random numbers generated by the g_test_rand_*() family of functions
- * change with every new test program start, unless the --seed option is
- * given when starting test programs.
- *
- * For individual test cases however, the random number generator is
- * reseeded, to avoid dependencies between tests and to make --seed
- * effective for all test cases.
- *
- * Returns: a random number from the seeded random number generator.
- *
- * Since: 2.16
- */
-gint32
-g_test_rand_int (void)
-{
- return g_rand_int (test_run_rand);
-}
-
-/**
- * g_test_rand_int_range:
- * @begin: the minimum value returned by this function
- * @end: the smallest value not to be returned by this function
- *
- * Get a reproducible random integer number out of a specified range,
- * see g_test_rand_int() for details on test case random numbers.
- *
- * Returns: a number with @begin <= number < @end.
- *
- * Since: 2.16
- */
-gint32
-g_test_rand_int_range (gint32 begin,
- gint32 end)
-{
- return g_rand_int_range (test_run_rand, begin, end);
-}
-
-/**
- * g_test_rand_double:
- *
- * Get a reproducible random floating point number,
- * see g_test_rand_int() for details on test case random numbers.
- *
- * Returns: a random number from the seeded random number generator.
- *
- * Since: 2.16
- */
-double
-g_test_rand_double (void)
-{
- return g_rand_double (test_run_rand);
-}
-
-/**
- * g_test_rand_double_range:
- * @range_start: the minimum value returned by this function
- * @range_end: the minimum value not returned by this function
- *
- * Get a reproducible random floating pointer number out of a specified range,
- * see g_test_rand_int() for details on test case random numbers.
- *
- * Returns: a number with @range_start <= number < @range_end.
- *
- * Since: 2.16
- */
-double
-g_test_rand_double_range (double range_start,
- double range_end)
-{
- return g_rand_double_range (test_run_rand, range_start, range_end);
-}
-
-/**
- * g_test_timer_start:
- *
- * Start a timing test. Call g_test_timer_elapsed() when the task is supposed
- * to be done. Call this function again to restart the timer.
- *
- * Since: 2.16
- */
-void
-g_test_timer_start (void)
-{
- if (!test_user_timer)
- test_user_timer = g_timer_new();
- test_user_stamp = 0;
- g_timer_start (test_user_timer);
-}
-
-/**
- * g_test_timer_elapsed:
- *
- * Get the time since the last start of the timer with g_test_timer_start().
- *
- * Returns: the time since the last start of the timer, as a double
- *
- * Since: 2.16
- */
-double
-g_test_timer_elapsed (void)
-{
- test_user_stamp = test_user_timer ? g_timer_elapsed (test_user_timer, NULL) : 0;
- return test_user_stamp;
-}
-
-/**
- * g_test_timer_last:
- *
- * Report the last result of g_test_timer_elapsed().
- *
- * Returns: the last result of g_test_timer_elapsed(), as a double
- *
- * Since: 2.16
- */
-double
-g_test_timer_last (void)
-{
- return test_user_stamp;
-}
-
-/**
- * g_test_minimized_result:
- * @minimized_quantity: the reported value
- * @format: the format string of the report message
- * @Varargs: arguments to pass to the printf() function
- *
- * Report the result of a performance or measurement test.
- * The test should generally strive to minimize the reported
- * quantities (smaller values are better than larger ones),
- * this and @minimized_quantity can determine sorting
- * order for test result reports.
- *
- * Since: 2.16
- */
-void
-g_test_minimized_result (double minimized_quantity,
- const char *format,
- ...)
-{
- long double largs = minimized_quantity;
- gchar *buffer;
- va_list args;
- va_start (args, format);
- buffer = g_strdup_vprintf (format, args);
- va_end (args);
- g_test_log (G_TEST_LOG_MIN_RESULT, buffer, NULL, 1, &largs);
- g_free (buffer);
-}
-
-/**
- * g_test_maximized_result:
- * @maximized_quantity: the reported value
- * @format: the format string of the report message
- * @Varargs: arguments to pass to the printf() function
- *
- * Report the result of a performance or measurement test.
- * The test should generally strive to maximize the reported
- * quantities (larger values are better than smaller ones),
- * this and @maximized_quantity can determine sorting
- * order for test result reports.
- *
- * Since: 2.16
- */
-void
-g_test_maximized_result (double maximized_quantity,
- const char *format,
- ...)
-{
- long double largs = maximized_quantity;
- gchar *buffer;
- va_list args;
- va_start (args, format);
- buffer = g_strdup_vprintf (format, args);
- va_end (args);
- g_test_log (G_TEST_LOG_MAX_RESULT, buffer, NULL, 1, &largs);
- g_free (buffer);
-}
-
-/**
- * g_test_message:
- * @format: the format string
- * @...: printf-like arguments to @format
- *
- * Add a message to the test report.
- *
- * Since: 2.16
- */
-void
-g_test_message (const char *format,
- ...)
-{
- gchar *buffer;
- va_list args;
- va_start (args, format);
- buffer = g_strdup_vprintf (format, args);
- va_end (args);
- g_test_log (G_TEST_LOG_MESSAGE, buffer, NULL, 0, NULL);
- g_free (buffer);
-}
-
-/**
- * g_test_bug_base:
- * @uri_pattern: the base pattern for bug URIs
- *
- * Specify the base URI for bug reports.
- *
- * The base URI is used to construct bug report messages for
- * g_test_message() when g_test_bug() is called.
- * Calling this function outside of a test case sets the
- * default base URI for all test cases. Calling it from within
- * a test case changes the base URI for the scope of the test
- * case only.
- * Bug URIs are constructed by appending a bug specific URI
- * portion to @uri_pattern, or by replacing the special string
- * '%s' within @uri_pattern if that is present.
- *
- * Since: 2.16
- */
-void
-g_test_bug_base (const char *uri_pattern)
-{
- g_free (test_uri_base);
- test_uri_base = g_strdup (uri_pattern);
-}
-
-/**
- * g_test_bug:
- * @bug_uri_snippet: Bug specific bug tracker URI portion.
- *
- * This function adds a message to test reports that
- * associates a bug URI with a test case.
- * Bug URIs are constructed from a base URI set with g_test_bug_base()
- * and @bug_uri_snippet.
- *
- * Since: 2.16
- */
-void
-g_test_bug (const char *bug_uri_snippet)
-{
- char *c;
- g_return_if_fail (test_uri_base != NULL);
- g_return_if_fail (bug_uri_snippet != NULL);
- c = strstr (test_uri_base, "%s");
- if (c)
- {
- char *b = g_strndup (test_uri_base, c - test_uri_base);
- char *s = g_strconcat (b, bug_uri_snippet, c + 2, NULL);
- g_free (b);
- g_test_message ("Bug Reference: %s", s);
- g_free (s);
- }
- else
- g_test_message ("Bug Reference: %s%s", test_uri_base, bug_uri_snippet);
-}
-
-/**
- * g_test_get_root:
- *
- * Get the toplevel test suite for the test path API.
- *
- * Returns: the toplevel #GTestSuite
- *
- * Since: 2.16
- */
-GTestSuite*
-g_test_get_root (void)
-{
- if (!test_suite_root)
- {
- test_suite_root = g_test_create_suite ("root");
- g_free (test_suite_root->name);
- test_suite_root->name = g_strdup ("");
- }
- return test_suite_root;
-}
-
-/**
- * g_test_run:
- *
- * Runs all tests under the toplevel suite which can be retrieved
- * with g_test_get_root(). Similar to g_test_run_suite(), the test
- * cases to be run are filtered according to
- * test path arguments (-p <replaceable>testpath</replaceable>) as
- * parsed by g_test_init().
- * g_test_run_suite() or g_test_run() may only be called once
- * in a program.
- *
- * Returns: 0 on success
- *
- * Since: 2.16
- */
-int
-g_test_run (void)
-{
- return g_test_run_suite (g_test_get_root());
-}
-
-/**
- * g_test_create_case:
- * @test_name: the name for the test case
- * @data_size: the size of the fixture data structure
- * @test_data: test data argument for the test functions
- * @data_setup: the function to set up the fixture data
- * @data_test: the actual test function
- * @data_teardown: the function to teardown the fixture data
- *
- * Create a new #GTestCase, named @test_name, this API is fairly
- * low level, calling g_test_add() or g_test_add_func() is preferable.
- * When this test is executed, a fixture structure of size @data_size
- * will be allocated and filled with 0s. Then data_setup() is called
- * to initialize the fixture. After fixture setup, the actual test
- * function data_test() is called. Once the test run completed, the
- * fixture structure is torn down by calling data_teardown() and
- * after that the memory is released.
- *
- * Splitting up a test run into fixture setup, test function and
- * fixture teardown is most usful if the same fixture is used for
- * multiple tests. In this cases, g_test_create_case() will be
- * called with the same fixture, but varying @test_name and
- * @data_test arguments.
- *
- * Returns: a newly allocated #GTestCase.
- *
- * Since: 2.16
- */
-GTestCase*
-g_test_create_case (const char *test_name,
- gsize data_size,
- gconstpointer test_data,
- GTestFixtureFunc data_setup,
- GTestFixtureFunc data_test,
- GTestFixtureFunc data_teardown)
-{
- GTestCase *tc;
- g_return_val_if_fail (test_name != NULL, NULL);
- g_return_val_if_fail (strchr (test_name, '/') == NULL, NULL);
- g_return_val_if_fail (test_name[0] != 0, NULL);
- g_return_val_if_fail (data_test != NULL, NULL);
- tc = g_slice_new0 (GTestCase);
- tc->name = g_strdup (test_name);
- tc->test_data = (gpointer) test_data;
- tc->fixture_size = data_size;
- tc->fixture_setup = (void*) data_setup;
- tc->fixture_test = (void*) data_test;
- tc->fixture_teardown = (void*) data_teardown;
- return tc;
-}
-
-/**
- * GTestFixtureFunc:
- * @fixture: the test fixture
- * @user_data: the data provided when registering the test
- *
- * The type used for functions that operate on test fixtures. This is
- * used for the fixture setup and teardown functions as well as for the
- * testcases themselves.
- *
- * @user_data is a pointer to the data that was given when registering
- * the test case.
- *
- * @fixture will be a pointer to the area of memory allocated by the
- * test framework, of the size requested. If the requested size was
- * zero then @fixture will be equal to @user_data.
- **/
-void
-g_test_add_vtable (const char *testpath,
- gsize data_size,
- gconstpointer test_data,
- GTestFixtureFunc data_setup,
- GTestFixtureFunc fixture_test_func,
- GTestFixtureFunc data_teardown)
-{
- gchar **segments;
- guint ui;
- GTestSuite *suite;
-
- g_return_if_fail (testpath != NULL);
- g_return_if_fail (testpath[0] == '/');
- g_return_if_fail (fixture_test_func != NULL);
-
- suite = g_test_get_root();
- segments = g_strsplit (testpath, "/", -1);
- for (ui = 0; segments[ui] != NULL; ui++)
- {
- const char *seg = segments[ui];
- gboolean islast = segments[ui + 1] == NULL;
- if (islast && !seg[0])
- g_error ("invalid test case path: %s", testpath);
- else if (!seg[0])
- continue; /* initial or duplicate slash */
- else if (!islast)
- {
- GTestSuite *csuite = g_test_create_suite (seg);
- g_test_suite_add_suite (suite, csuite);
- suite = csuite;
- }
- else /* islast */
- {
- GTestCase *tc = g_test_create_case (seg, data_size, test_data, data_setup, fixture_test_func, data_teardown);
- g_test_suite_add (suite, tc);
- }
- }
- g_strfreev (segments);
-}
-
-/**
- * GTestFunc:
- *
- * The type used for test case functions.
- **/
-/**
- * g_test_add_func:
- * @testpath: Slash-separated test case path name for the test.
- * @test_func: The test function to invoke for this test.
- *
- * Create a new test case, similar to g_test_create_case(). However
- * the test is assumed to use no fixture, and test suites are automatically
- * created on the fly and added to the root fixture, based on the
- * slash-separated portions of @testpath.
- *
- * Since: 2.16
- */
-void
-g_test_add_func (const char *testpath,
- GTestFunc test_func)
-{
- g_return_if_fail (testpath != NULL);
- g_return_if_fail (testpath[0] == '/');
- g_return_if_fail (test_func != NULL);
- g_test_add_vtable (testpath, 0, NULL, NULL, (GTestFixtureFunc) test_func, NULL);
-}
-
-/**
- * GTestDataFunc:
- * @user_data: the data provided when registering the test
- *
- * The type used for test case functions that take an extra pointer
- * argument.
- **/
-/**
- * g_test_add_data_func:
- * @testpath: Slash-separated test case path name for the test.
- * @test_data: Test data argument for the test function.
- * @test_func: The test function to invoke for this test.
- *
- * Create a new test case, similar to g_test_create_case(). However
- * the test is assumed to use no fixture, and test suites are automatically
- * created on the fly and added to the root fixture, based on the
- * slash-separated portions of @testpath. The @test_data argument
- * will be passed as first argument to @test_func.
- *
- * Since: 2.16
- */
-void
-g_test_add_data_func (const char *testpath,
- gconstpointer test_data,
- GTestDataFunc test_func)
-{
- g_return_if_fail (testpath != NULL);
- g_return_if_fail (testpath[0] == '/');
- g_return_if_fail (test_func != NULL);
- g_test_add_vtable (testpath, 0, test_data, NULL, (GTestFixtureFunc) test_func, NULL);
-}
-
-/**
- * g_test_create_suite:
- * @suite_name: a name for the suite
- *
- * Create a new test suite with the name @suite_name.
- *
- * Returns: A newly allocated #GTestSuite instance.
- *
- * Since: 2.16
- */
-GTestSuite*
-g_test_create_suite (const char *suite_name)
-{
- GTestSuite *ts;
- g_return_val_if_fail (suite_name != NULL, NULL);
- g_return_val_if_fail (strchr (suite_name, '/') == NULL, NULL);
- g_return_val_if_fail (suite_name[0] != 0, NULL);
- ts = g_slice_new0 (GTestSuite);
- ts->name = g_strdup (suite_name);
- return ts;
-}
-
-/**
- * g_test_suite_add:
- * @suite: a #GTestSuite
- * @test_case: a #GTestCase
- *
- * Adds @test_case to @suite.
- *
- * Since: 2.16
- */
-void
-g_test_suite_add (GTestSuite *suite,
- GTestCase *test_case)
-{
- g_return_if_fail (suite != NULL);
- g_return_if_fail (test_case != NULL);
- suite->cases = g_slist_prepend (suite->cases, test_case);
-}
-
-/**
- * g_test_suite_add_suite:
- * @suite: a #GTestSuite
- * @nestedsuite: another #GTestSuite
- *
- * Adds @nestedsuite to @suite.
- *
- * Since: 2.16
- */
-void
-g_test_suite_add_suite (GTestSuite *suite,
- GTestSuite *nestedsuite)
-{
- g_return_if_fail (suite != NULL);
- g_return_if_fail (nestedsuite != NULL);
- suite->suites = g_slist_prepend (suite->suites, nestedsuite);
-}
-
-/**
- * g_test_queue_free:
- * @gfree_pointer: the pointer to be stored.
- *
- * Enqueue a pointer to be released with g_free() during the next
- * teardown phase. This is equivalent to calling g_test_queue_destroy()
- * with a destroy callback of g_free().
- *
- * Since: 2.16
- */
-void
-g_test_queue_free (gpointer gfree_pointer)
-{
- if (gfree_pointer)
- g_test_queue_destroy (g_free, gfree_pointer);
-}
-
-/**
- * g_test_queue_destroy:
- * @destroy_func: Destroy callback for teardown phase.
- * @destroy_data: Destroy callback data.
- *
- * This function enqueus a callback @destroy_func() to be executed
- * during the next test case teardown phase. This is most useful
- * to auto destruct allocted test resources at the end of a test run.
- * Resources are released in reverse queue order, that means enqueueing
- * callback A before callback B will cause B() to be called before
- * A() during teardown.
- *
- * Since: 2.16
- */
-void
-g_test_queue_destroy (GDestroyNotify destroy_func,
- gpointer destroy_data)
-{
- DestroyEntry *dentry;
- g_return_if_fail (destroy_func != NULL);
- dentry = g_slice_new0 (DestroyEntry);
- dentry->destroy_func = destroy_func;
- dentry->destroy_data = destroy_data;
- dentry->next = test_destroy_queue;
- test_destroy_queue = dentry;
-}
-
-static int
-test_case_run (GTestCase *tc)
-{
- gchar *old_name = test_run_name, *old_base = g_strdup (test_uri_base);
- test_run_name = g_strconcat (old_name, "/", tc->name, NULL);
- if (++test_run_count <= test_skip_count)
- g_test_log (G_TEST_LOG_SKIP_CASE, test_run_name, NULL, 0, NULL);
- else if (test_run_list)
- {
- g_print ("%s\n", test_run_name);
- g_test_log (G_TEST_LOG_LIST_CASE, test_run_name, NULL, 0, NULL);
- }
- else
- {
- GTimer *test_run_timer = g_timer_new();
- long double largs[3];
- void *fixture;
- g_test_log (G_TEST_LOG_START_CASE, test_run_name, NULL, 0, NULL);
- test_run_forks = 0;
- g_test_log_set_fatal_handler (NULL, NULL);
- g_timer_start (test_run_timer);
- fixture = tc->fixture_size ? g_malloc0 (tc->fixture_size) : tc->test_data;
- test_run_seed (test_run_seedstr);
- if (tc->fixture_setup)
- tc->fixture_setup (fixture, tc->test_data);
- tc->fixture_test (fixture, tc->test_data);
- test_trap_clear();
- while (test_destroy_queue)
- {
- DestroyEntry *dentry = test_destroy_queue;
- test_destroy_queue = dentry->next;
- dentry->destroy_func (dentry->destroy_data);
- g_slice_free (DestroyEntry, dentry);
- }
- if (tc->fixture_teardown)
- tc->fixture_teardown (fixture, tc->test_data);
- if (tc->fixture_size)
- g_free (fixture);
- g_timer_stop (test_run_timer);
- largs[0] = 0; /* OK */
- largs[1] = test_run_forks;
- largs[2] = g_timer_elapsed (test_run_timer, NULL);
- g_test_log (G_TEST_LOG_STOP_CASE, NULL, NULL, G_N_ELEMENTS (largs), largs);
- g_timer_destroy (test_run_timer);
- }
- g_free (test_run_name);
- test_run_name = old_name;
- g_free (test_uri_base);
- test_uri_base = old_base;
- return 0;
-}
-
-static int
-g_test_run_suite_internal (GTestSuite *suite,
- const char *path)
-{
- guint n_bad = 0, n_good = 0, bad_suite = 0, l;
- gchar *rest, *old_name = test_run_name;
- GSList *slist, *reversed;
- g_return_val_if_fail (suite != NULL, -1);
- while (path[0] == '/')
- path++;
- l = strlen (path);
- rest = strchr (path, '/');
- l = rest ? MIN (l, rest - path) : l;
- test_run_name = suite->name[0] == 0 ? g_strdup (test_run_name) : g_strconcat (old_name, "/", suite->name, NULL);
- reversed = g_slist_reverse (g_slist_copy (suite->cases));
- for (slist = reversed; slist; slist = slist->next)
- {
- GTestCase *tc = slist->data;
- guint n = l ? strlen (tc->name) : 0;
- if (l == n && strncmp (path, tc->name, n) == 0)
- {
- n_good++;
- n_bad += test_case_run (tc) != 0;
- }
- }
- g_slist_free (reversed);
- reversed = g_slist_reverse (g_slist_copy (suite->suites));
- for (slist = reversed; slist; slist = slist->next)
- {
- GTestSuite *ts = slist->data;
- guint n = l ? strlen (ts->name) : 0;
- if (l == n && strncmp (path, ts->name, n) == 0)
- bad_suite += g_test_run_suite_internal (ts, rest ? rest : "") != 0;
- }
- g_slist_free (reversed);
- g_free (test_run_name);
- test_run_name = old_name;
- return n_bad || bad_suite;
-}
-
-/**
- * g_test_run_suite:
- * @suite: a #GTestSuite
- *
- * Execute the tests within @suite and all nested #GTestSuites.
- * The test suites to be executed are filtered according to
- * test path arguments (-p <replaceable>testpath</replaceable>)
- * as parsed by g_test_init().
- * g_test_run_suite() or g_test_run() may only be called once
- * in a program.
- *
- * Returns: 0 on success
- *
- * Since: 2.16
- */
-int
-g_test_run_suite (GTestSuite *suite)
-{
- guint n_bad = 0;
- g_return_val_if_fail (g_test_config_vars->test_initialized, -1);
- g_return_val_if_fail (g_test_run_once == TRUE, -1);
- g_test_run_once = FALSE;
- if (!test_paths)
- test_paths = g_slist_prepend (test_paths, "");
- while (test_paths)
- {
- const char *rest, *path = test_paths->data;
- guint l, n = strlen (suite->name);
- test_paths = g_slist_delete_link (test_paths, test_paths);
- while (path[0] == '/')
- path++;
- if (!n) /* root suite, run unconditionally */
- {
- n_bad += 0 != g_test_run_suite_internal (suite, path);
- continue;
- }
- /* regular suite, match path */
- rest = strchr (path, '/');
- l = strlen (path);
- l = rest ? MIN (l, rest - path) : l;
- if ((!l || l == n) && strncmp (path, suite->name, n) == 0)
- n_bad += 0 != g_test_run_suite_internal (suite, rest ? rest : "");
- }
- return n_bad;
-}
-
-static void
-gtest_default_log_handler (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *message,
- gpointer unused_data)
-{
- const gchar *strv[16];
- gboolean fatal = FALSE;
- gchar *msg;
- guint i = 0;
- if (log_domain)
- {
- strv[i++] = log_domain;
- strv[i++] = "-";
- }
- if (log_level & G_LOG_FLAG_FATAL)
- {
- strv[i++] = "FATAL-";
- fatal = TRUE;
- }
- if (log_level & G_LOG_FLAG_RECURSION)
- strv[i++] = "RECURSIVE-";
- if (log_level & G_LOG_LEVEL_ERROR)
- strv[i++] = "ERROR";
- if (log_level & G_LOG_LEVEL_CRITICAL)
- strv[i++] = "CRITICAL";
- if (log_level & G_LOG_LEVEL_WARNING)
- strv[i++] = "WARNING";
- if (log_level & G_LOG_LEVEL_MESSAGE)
- strv[i++] = "MESSAGE";
- if (log_level & G_LOG_LEVEL_INFO)
- strv[i++] = "INFO";
- if (log_level & G_LOG_LEVEL_DEBUG)
- strv[i++] = "DEBUG";
- strv[i++] = ": ";
- strv[i++] = message;
- strv[i++] = NULL;
- msg = g_strjoinv ("", (gchar**) strv);
- g_test_log (fatal ? G_TEST_LOG_ERROR : G_TEST_LOG_MESSAGE, msg, NULL, 0, NULL);
- g_log_default_handler (log_domain, log_level, message, unused_data);
- g_free (msg);
-}
-
-void
-g_assertion_message (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *message)
-{
- char lstr[32];
- char *s;
- if (!message)
- message = "code should not be reached";
- g_snprintf (lstr, 32, "%d", line);
- s = g_strconcat (domain ? domain : "", domain && domain[0] ? ":" : "",
- "ERROR:", file, ":", lstr, ":",
- func, func[0] ? ":" : "",
- " ", message, NULL);
- g_printerr ("**\n%s\n", s);
-
- /* store assertion message in global variable, so that it can be found in a
- * core dump */
- if (__glib_assert_msg != NULL)
- /* free the old one */
- free (__glib_assert_msg);
- __glib_assert_msg = (char*) malloc (strlen (s) + 1);
- strcpy (__glib_assert_msg, s);
-
- g_test_log (G_TEST_LOG_ERROR, s, NULL, 0, NULL);
- g_free (s);
- abort();
-}
-
-void
-g_assertion_message_expr (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *expr)
-{
- char *s = g_strconcat ("assertion failed: (", expr, ")", NULL);
- g_assertion_message (domain, file, line, func, s);
- g_free (s);
-}
-
-void
-g_assertion_message_cmpnum (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *expr,
- long double arg1,
- const char *cmp,
- long double arg2,
- char numtype)
-{
- char *s = NULL;
- switch (numtype)
- {
- case 'i': s = g_strdup_printf ("assertion failed (%s): (%.0Lf %s %.0Lf)", expr, arg1, cmp, arg2); break;
- case 'x': s = g_strdup_printf ("assertion failed (%s): (0x%08" G_GINT64_MODIFIER "x %s 0x%08" G_GINT64_MODIFIER "x)", expr, (guint64) arg1, cmp, (guint64) arg2); break;
- case 'f': s = g_strdup_printf ("assertion failed (%s): (%.9Lg %s %.9Lg)", expr, arg1, cmp, arg2); break;
- /* ideally use: floats=%.7g double=%.17g */
- }
- g_assertion_message (domain, file, line, func, s);
- g_free (s);
-}
-
-void
-g_assertion_message_cmpstr (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *expr,
- const char *arg1,
- const char *cmp,
- const char *arg2)
-{
- char *a1, *a2, *s, *t1 = NULL, *t2 = NULL;
- a1 = arg1 ? g_strconcat ("\"", t1 = g_strescape (arg1, NULL), "\"", NULL) : g_strdup ("NULL");
- a2 = arg2 ? g_strconcat ("\"", t2 = g_strescape (arg2, NULL), "\"", NULL) : g_strdup ("NULL");
- g_free (t1);
- g_free (t2);
- s = g_strdup_printf ("assertion failed (%s): (%s %s %s)", expr, a1, cmp, a2);
- g_free (a1);
- g_free (a2);
- g_assertion_message (domain, file, line, func, s);
- g_free (s);
-}
-
-void
-g_assertion_message_error (const char *domain,
- const char *file,
- int line,
- const char *func,
- const char *expr,
- const GError *error,
- GQuark error_domain,
- int error_code)
-{
- GString *gstring;
-
- /* This is used by both g_assert_error() and g_assert_no_error(), so there
- * are three cases: expected an error but got the wrong error, expected
- * an error but got no error, and expected no error but got an error.
- */
-
- gstring = g_string_new ("assertion failed ");
- if (error_domain)
- g_string_append_printf (gstring, "(%s == (%s, %d)): ", expr,
- g_quark_to_string (error_domain), error_code);
- else
- g_string_append_printf (gstring, "(%s == NULL): ", expr);
-
- if (error)
- g_string_append_printf (gstring, "%s (%s, %d)", error->message,
- g_quark_to_string (error->domain), error->code);
- else
- g_string_append_printf (gstring, "%s is NULL", expr);
-
- g_assertion_message (domain, file, line, func, gstring->str);
- g_string_free (gstring, TRUE);
-}
-
-/**
- * g_strcmp0:
- * @str1: a C string or %NULL
- * @str2: another C string or %NULL
- *
- * Compares @str1 and @str2 like strcmp(). Handles %NULL
- * gracefully by sorting it before non-%NULL strings.
- * Comparing two %NULL pointers returns 0.
- *
- * Returns: -1, 0 or 1, if @str1 is <, == or > than @str2.
- *
- * Since: 2.16
- */
-int
-g_strcmp0 (const char *str1,
- const char *str2)
-{
- if (!str1)
- return -(str1 != str2);
- if (!str2)
- return str1 != str2;
- return strcmp (str1, str2);
-}
-
-#ifdef G_OS_UNIX
-static int /* 0 on success */
-kill_child (int pid,
- int *status,
- int patience)
-{
- int wr;
- if (patience >= 3) /* try graceful reap */
- {
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- }
- if (patience >= 2) /* try SIGHUP */
- {
- kill (pid, SIGHUP);
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- g_usleep (20 * 1000); /* give it some scheduling/shutdown time */
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- g_usleep (50 * 1000); /* give it some scheduling/shutdown time */
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- g_usleep (100 * 1000); /* give it some scheduling/shutdown time */
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- }
- if (patience >= 1) /* try SIGTERM */
- {
- kill (pid, SIGTERM);
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- g_usleep (200 * 1000); /* give it some scheduling/shutdown time */
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- g_usleep (400 * 1000); /* give it some scheduling/shutdown time */
- if (waitpid (pid, status, WNOHANG) > 0)
- return 0;
- }
- /* finish it off */
- kill (pid, SIGKILL);
- do
- wr = waitpid (pid, status, 0);
- while (wr < 0 && errno == EINTR);
- return wr;
-}
-#endif
-
-static inline int
-g_string_must_read (GString *gstring,
- int fd)
-{
-#define STRING_BUFFER_SIZE 4096
- char buf[STRING_BUFFER_SIZE];
- gssize bytes;
- again:
- bytes = read (fd, buf, sizeof (buf));
- if (bytes == 0)
- return 0; /* EOF, calling this function assumes data is available */
- else if (bytes > 0)
- {
- g_string_append_len (gstring, buf, bytes);
- return 1;
- }
- else if (bytes < 0 && errno == EINTR)
- goto again;
- else /* bytes < 0 */
- {
- g_warning ("failed to read() from child process (%d): %s", test_trap_last_pid, g_strerror (errno));
- return 1; /* ignore error after warning */
- }
-}
-
-static inline void
-g_string_write_out (GString *gstring,
- int outfd,
- int *stringpos)
-{
- if (*stringpos < gstring->len)
- {
- int r;
- do
- r = write (outfd, gstring->str + *stringpos, gstring->len - *stringpos);
- while (r < 0 && errno == EINTR);
- *stringpos += MAX (r, 0);
- }
-}
-
-static void
-test_trap_clear (void)
-{
- test_trap_last_status = 0;
- test_trap_last_pid = 0;
- g_free (test_trap_last_stdout);
- test_trap_last_stdout = NULL;
- g_free (test_trap_last_stderr);
- test_trap_last_stderr = NULL;
-}
-
-#ifdef G_OS_UNIX
-
-static int
-sane_dup2 (int fd1,
- int fd2)
-{
- int ret;
- do
- ret = dup2 (fd1, fd2);
- while (ret < 0 && errno == EINTR);
- return ret;
-}
-
-static guint64
-test_time_stamp (void)
-{
- GTimeVal tv;
- guint64 stamp;
- g_get_current_time (&tv);
- stamp = tv.tv_sec;
- stamp = stamp * 1000000 + tv.tv_usec;
- return stamp;
-}
-
-#endif
-
-/**
- * g_test_trap_fork:
- * @usec_timeout: Timeout for the forked test in micro seconds.
- * @test_trap_flags: Flags to modify forking behaviour.
- *
- * Fork the current test program to execute a test case that might
- * not return or that might abort. The forked test case is aborted
- * and considered failing if its run time exceeds @usec_timeout.
- *
- * The forking behavior can be configured with the #GTestTrapFlags flags.
- *
- * In the following example, the test code forks, the forked child
- * process produces some sample output and exits successfully.
- * The forking parent process then asserts successful child program
- * termination and validates child program outputs.
- *
- * |[
- * static void
- * test_fork_patterns (void)
- * {
- * if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
- * {
- * g_print ("some stdout text: somagic17\n");
- * g_printerr ("some stderr text: semagic43\n");
- * exit (0); /* successful test run */
- * }
- * g_test_trap_assert_passed();
- * g_test_trap_assert_stdout ("*somagic17*");
- * g_test_trap_assert_stderr ("*semagic43*");
- * }
- * ]|
- *
- * This function is implemented only on Unix platforms.
- *
- * Returns: %TRUE for the forked child and %FALSE for the executing parent process.
- *
- * Since: 2.16
- */
-gboolean
-g_test_trap_fork (guint64 usec_timeout,
- GTestTrapFlags test_trap_flags)
-{
-#ifdef G_OS_UNIX
- gboolean pass_on_forked_log = FALSE;
- int stdout_pipe[2] = { -1, -1 };
- int stderr_pipe[2] = { -1, -1 };
- int stdtst_pipe[2] = { -1, -1 };
- test_trap_clear();
- if (pipe (stdout_pipe) < 0 || pipe (stderr_pipe) < 0 || pipe (stdtst_pipe) < 0)
- g_error ("failed to create pipes to fork test program: %s", g_strerror (errno));
- signal (SIGCHLD, SIG_DFL);
- test_trap_last_pid = fork ();
- if (test_trap_last_pid < 0)
- g_error ("failed to fork test program: %s", g_strerror (errno));
- if (test_trap_last_pid == 0) /* child */
- {
- int fd0 = -1;
- close (stdout_pipe[0]);
- close (stderr_pipe[0]);
- close (stdtst_pipe[0]);
- if (!(test_trap_flags & G_TEST_TRAP_INHERIT_STDIN))
- fd0 = open ("/dev/null", O_RDONLY);
- if (sane_dup2 (stdout_pipe[1], 1) < 0 || sane_dup2 (stderr_pipe[1], 2) < 0 || (fd0 >= 0 && sane_dup2 (fd0, 0) < 0))
- g_error ("failed to dup2() in forked test program: %s", g_strerror (errno));
- if (fd0 >= 3)
- close (fd0);
- if (stdout_pipe[1] >= 3)
- close (stdout_pipe[1]);
- if (stderr_pipe[1] >= 3)
- close (stderr_pipe[1]);
- test_log_fd = stdtst_pipe[1];
- return TRUE;
- }
- else /* parent */
- {
- GString *sout = g_string_new (NULL);
- GString *serr = g_string_new (NULL);
- guint64 sstamp;
- int soutpos = 0, serrpos = 0, wr, need_wait = TRUE;
- test_run_forks++;
- close (stdout_pipe[1]);
- close (stderr_pipe[1]);
- close (stdtst_pipe[1]);
- sstamp = test_time_stamp();
- /* read data until we get EOF on all pipes */
- while (stdout_pipe[0] >= 0 || stderr_pipe[0] >= 0 || stdtst_pipe[0] > 0)
- {
- fd_set fds;
- struct timeval tv;
- int ret;
- FD_ZERO (&fds);
- if (stdout_pipe[0] >= 0)
- FD_SET (stdout_pipe[0], &fds);
- if (stderr_pipe[0] >= 0)
- FD_SET (stderr_pipe[0], &fds);
- if (stdtst_pipe[0] >= 0)
- FD_SET (stdtst_pipe[0], &fds);
- tv.tv_sec = 0;
- tv.tv_usec = MIN (usec_timeout ? usec_timeout : 1000000, 100 * 1000); /* sleep at most 0.5 seconds to catch clock skews, etc. */
- ret = select (MAX (MAX (stdout_pipe[0], stderr_pipe[0]), stdtst_pipe[0]) + 1, &fds, NULL, NULL, &tv);
- if (ret < 0 && errno != EINTR)
- {
- g_warning ("Unexpected error in select() while reading from child process (%d): %s", test_trap_last_pid, g_strerror (errno));
- break;
- }
- if (stdout_pipe[0] >= 0 && FD_ISSET (stdout_pipe[0], &fds) &&
- g_string_must_read (sout, stdout_pipe[0]) == 0)
- {
- close (stdout_pipe[0]);
- stdout_pipe[0] = -1;
- }
- if (stderr_pipe[0] >= 0 && FD_ISSET (stderr_pipe[0], &fds) &&
- g_string_must_read (serr, stderr_pipe[0]) == 0)
- {
- close (stderr_pipe[0]);
- stderr_pipe[0] = -1;
- }
- if (stdtst_pipe[0] >= 0 && FD_ISSET (stdtst_pipe[0], &fds))
- {
- guint8 buffer[4096];
- gint l, r = read (stdtst_pipe[0], buffer, sizeof (buffer));
- if (r > 0 && test_log_fd > 0)
- do
- l = write (pass_on_forked_log ? test_log_fd : -1, buffer, r);
- while (l < 0 && errno == EINTR);
- if (r == 0 || (r < 0 && errno != EINTR && errno != EAGAIN))
- {
- close (stdtst_pipe[0]);
- stdtst_pipe[0] = -1;
- }
- }
- if (!(test_trap_flags & G_TEST_TRAP_SILENCE_STDOUT))
- g_string_write_out (sout, 1, &soutpos);
- if (!(test_trap_flags & G_TEST_TRAP_SILENCE_STDERR))
- g_string_write_out (serr, 2, &serrpos);
- if (usec_timeout)
- {
- guint64 nstamp = test_time_stamp();
- int status = 0;
- sstamp = MIN (sstamp, nstamp); /* guard against backwards clock skews */
- if (usec_timeout < nstamp - sstamp)
- {
- /* timeout reached, need to abort the child now */
- kill_child (test_trap_last_pid, &status, 3);
- test_trap_last_status = 1024; /* timeout */
- if (0 && WIFSIGNALED (status))
- g_printerr ("%s: child timed out and received: %s\n", G_STRFUNC, g_strsignal (WTERMSIG (status)));
- need_wait = FALSE;
- break;
- }
- }
- }
- close (stdout_pipe[0]);
- close (stderr_pipe[0]);
- close (stdtst_pipe[0]);
- if (need_wait)
- {
- int status = 0;
- do
- wr = waitpid (test_trap_last_pid, &status, 0);
- while (wr < 0 && errno == EINTR);
- if (WIFEXITED (status)) /* normal exit */
- test_trap_last_status = WEXITSTATUS (status); /* 0..255 */
- else if (WIFSIGNALED (status))
- test_trap_last_status = (WTERMSIG (status) << 12); /* signalled */
- else /* WCOREDUMP (status) */
- test_trap_last_status = 512; /* coredump */
- }
- test_trap_last_stdout = g_string_free (sout, FALSE);
- test_trap_last_stderr = g_string_free (serr, FALSE);
- return FALSE;
- }
-#else
- g_message ("Not implemented: g_test_trap_fork");
-
- return FALSE;
-#endif
-}
-
-/**
- * g_test_trap_has_passed:
- *
- * Check the result of the last g_test_trap_fork() call.
- *
- * Returns: %TRUE if the last forked child terminated successfully.
- *
- * Since: 2.16
- */
-gboolean
-g_test_trap_has_passed (void)
-{
- return test_trap_last_status == 0; /* exit_status == 0 && !signal && !coredump */
-}
-
-/**
- * g_test_trap_reached_timeout:
- *
- * Check the result of the last g_test_trap_fork() call.
- *
- * Returns: %TRUE if the last forked child got killed due to a fork timeout.
- *
- * Since: 2.16
- */
-gboolean
-g_test_trap_reached_timeout (void)
-{
- return 0 != (test_trap_last_status & 1024); /* timeout flag */
-}
-
-void
-g_test_trap_assertions (const char *domain,
- const char *file,
- int line,
- const char *func,
- guint64 assertion_flags, /* 0-pass, 1-fail, 2-outpattern, 4-errpattern */
- const char *pattern)
-{
-#ifdef G_OS_UNIX
- gboolean must_pass = assertion_flags == 0;
- gboolean must_fail = assertion_flags == 1;
- gboolean match_result = 0 == (assertion_flags & 1);
- const char *stdout_pattern = (assertion_flags & 2) ? pattern : NULL;
- const char *stderr_pattern = (assertion_flags & 4) ? pattern : NULL;
- const char *match_error = match_result ? "failed to match" : "contains invalid match";
- if (test_trap_last_pid == 0)
- g_error ("child process failed to exit after g_test_trap_fork() and before g_test_trap_assert*()");
- if (must_pass && !g_test_trap_has_passed())
- {
- char *msg = g_strdup_printf ("child process (%d) of test trap failed unexpectedly", test_trap_last_pid);
- g_assertion_message (domain, file, line, func, msg);
- g_free (msg);
- }
- if (must_fail && g_test_trap_has_passed())
- {
- char *msg = g_strdup_printf ("child process (%d) did not fail as expected", test_trap_last_pid);
- g_assertion_message (domain, file, line, func, msg);
- g_free (msg);
- }
- if (stdout_pattern && match_result == !g_pattern_match_simple (stdout_pattern, test_trap_last_stdout))
- {
- char *msg = g_strdup_printf ("stdout of child process (%d) %s: %s", test_trap_last_pid, match_error, stdout_pattern);
- g_assertion_message (domain, file, line, func, msg);
- g_free (msg);
- }
- if (stderr_pattern && match_result == !g_pattern_match_simple (stderr_pattern, test_trap_last_stderr))
- {
- char *msg = g_strdup_printf ("stderr of child process (%d) %s: %s", test_trap_last_pid, match_error, stderr_pattern);
- g_assertion_message (domain, file, line, func, msg);
- g_free (msg);
- }
-#endif
-}
-
-static void
-gstring_overwrite_int (GString *gstring,
- guint pos,
- guint32 vuint)
-{
- vuint = g_htonl (vuint);
- g_string_overwrite_len (gstring, pos, (const gchar*) &vuint, 4);
-}
-
-static void
-gstring_append_int (GString *gstring,
- guint32 vuint)
-{
- vuint = g_htonl (vuint);
- g_string_append_len (gstring, (const gchar*) &vuint, 4);
-}
-
-static void
-gstring_append_double (GString *gstring,
- double vdouble)
-{
- union { double vdouble; guint64 vuint64; } u;
- u.vdouble = vdouble;
- u.vuint64 = GUINT64_TO_BE (u.vuint64);
- g_string_append_len (gstring, (const gchar*) &u.vuint64, 8);
-}
-
-static guint8*
-g_test_log_dump (GTestLogMsg *msg,
- guint *len)
-{
- GString *gstring = g_string_sized_new (1024);
- guint ui;
- gstring_append_int (gstring, 0); /* message length */
- gstring_append_int (gstring, msg->log_type);
- gstring_append_int (gstring, msg->n_strings);
- gstring_append_int (gstring, msg->n_nums);
- gstring_append_int (gstring, 0); /* reserved */
- for (ui = 0; ui < msg->n_strings; ui++)
- {
- guint l = strlen (msg->strings[ui]);
- gstring_append_int (gstring, l);
- g_string_append_len (gstring, msg->strings[ui], l);
- }
- for (ui = 0; ui < msg->n_nums; ui++)
- gstring_append_double (gstring, msg->nums[ui]);
- *len = gstring->len;
- gstring_overwrite_int (gstring, 0, *len); /* message length */
- return (guint8*) g_string_free (gstring, FALSE);
-}
-
-static inline long double
-net_double (const gchar **ipointer)
-{
- union { guint64 vuint64; double vdouble; } u;
- guint64 aligned_int64;
- memcpy (&aligned_int64, *ipointer, 8);
- *ipointer += 8;
- u.vuint64 = GUINT64_FROM_BE (aligned_int64);
- return u.vdouble;
-}
-
-static inline guint32
-net_int (const gchar **ipointer)
-{
- guint32 aligned_int;
- memcpy (&aligned_int, *ipointer, 4);
- *ipointer += 4;
- return g_ntohl (aligned_int);
-}
-
-static gboolean
-g_test_log_extract (GTestLogBuffer *tbuffer)
-{
- const gchar *p = tbuffer->data->str;
- GTestLogMsg msg;
- guint mlength;
- if (tbuffer->data->len < 4 * 5)
- return FALSE;
- mlength = net_int (&p);
- if (tbuffer->data->len < mlength)
- return FALSE;
- msg.log_type = net_int (&p);
- msg.n_strings = net_int (&p);
- msg.n_nums = net_int (&p);
- if (net_int (&p) == 0)
- {
- guint ui;
- msg.strings = g_new0 (gchar*, msg.n_strings + 1);
- msg.nums = g_new0 (long double, msg.n_nums);
- for (ui = 0; ui < msg.n_strings; ui++)
- {
- guint sl = net_int (&p);
- msg.strings[ui] = g_strndup (p, sl);
- p += sl;
- }
- for (ui = 0; ui < msg.n_nums; ui++)
- msg.nums[ui] = net_double (&p);
- if (p <= tbuffer->data->str + mlength)
- {
- g_string_erase (tbuffer->data, 0, mlength);
- tbuffer->msgs = g_slist_prepend (tbuffer->msgs, g_memdup (&msg, sizeof (msg)));
- return TRUE;
- }
- }
- g_free (msg.nums);
- g_strfreev (msg.strings);
- g_error ("corrupt log stream from test program");
- return FALSE;
-}
-
-/**
- * g_test_log_buffer_new:
- *
- * Internal function for gtester to decode test log messages, no ABI guarantees provided.
- */
-GTestLogBuffer*
-g_test_log_buffer_new (void)
-{
- GTestLogBuffer *tb = g_new0 (GTestLogBuffer, 1);
- tb->data = g_string_sized_new (1024);
- return tb;
-}
-
-/**
- * g_test_log_buffer_free
- *
- * Internal function for gtester to free test log messages, no ABI guarantees provided.
- */
-void
-g_test_log_buffer_free (GTestLogBuffer *tbuffer)
-{
- g_return_if_fail (tbuffer != NULL);
- while (tbuffer->msgs)
- g_test_log_msg_free (g_test_log_buffer_pop (tbuffer));
- g_string_free (tbuffer->data, TRUE);
- g_free (tbuffer);
-}
-
-/**
- * g_test_log_buffer_push
- *
- * Internal function for gtester to decode test log messages, no ABI guarantees provided.
- */
-void
-g_test_log_buffer_push (GTestLogBuffer *tbuffer,
- guint n_bytes,
- const guint8 *bytes)
-{
- g_return_if_fail (tbuffer != NULL);
- if (n_bytes)
- {
- gboolean more_messages;
- g_return_if_fail (bytes != NULL);
- g_string_append_len (tbuffer->data, (const gchar*) bytes, n_bytes);
- do
- more_messages = g_test_log_extract (tbuffer);
- while (more_messages);
- }
-}
-
-/**
- * g_test_log_buffer_pop:
- *
- * Internal function for gtester to retrieve test log messages, no ABI guarantees provided.
- */
-GTestLogMsg*
-g_test_log_buffer_pop (GTestLogBuffer *tbuffer)
-{
- GTestLogMsg *msg = NULL;
- g_return_val_if_fail (tbuffer != NULL, NULL);
- if (tbuffer->msgs)
- {
- GSList *slist = g_slist_last (tbuffer->msgs);
- msg = slist->data;
- tbuffer->msgs = g_slist_delete_link (tbuffer->msgs, slist);
- }
- return msg;
-}
-
-/**
- * g_test_log_msg_free:
- *
- * Internal function for gtester to free test log messages, no ABI guarantees provided.
- */
-void
-g_test_log_msg_free (GTestLogMsg *tmsg)
-{
- g_return_if_fail (tmsg != NULL);
- g_strfreev (tmsg->strings);
- g_free (tmsg->nums);
- g_free (tmsg);
-}
-
-/* --- macros docs START --- */
-/**
- * g_test_add:
- * @testpath: The test path for a new test case.
- * @Fixture: The type of a fixture data structure.
- * @tdata: Data argument for the test functions.
- * @fsetup: The function to set up the fixture data.
- * @ftest: The actual test function.
- * @fteardown: The function to tear down the fixture data.
- *
- * Hook up a new test case at @testpath, similar to g_test_add_func().
- * A fixture data structure with setup and teardown function may be provided
- * though, similar to g_test_create_case().
- * g_test_add() is implemented as a macro, so that the fsetup(), ftest() and
- * fteardown() callbacks can expect a @Fixture pointer as first argument in
- * a type safe manner.
- *
- * Since: 2.16
- **/
-/* --- macros docs END --- */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gthread.c: MT safety related functions
- * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
- * Owen Taylor
- *
- * 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 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.
- */
-
-/* Prelude {{{1 ----------------------------------------------------------- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-/* implement gthread.h's inline functions */
-#define G_IMPLEMENT_INLINES 1
-#define __G_THREAD_C__
-
-#include "config.h"
-
-#include "gthread.h"
-#include "gthreadprivate.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifndef G_OS_WIN32
-#include <sys/time.h>
-#include <time.h>
-#else
-#include <windows.h>
-#endif /* G_OS_WIN32 */
-
-#include <string.h>
-
-#include "garray.h"
-#include "gslist.h"
-#include "gtestutils.h"
-#include "gtimer.h"
-
-
-/**
- * SECTION: threads
- * @title: Threads
- * @short_description: thread abstraction; including threads, different
- * mutexes, conditions and thread private data
- * @see_also: #GThreadPool, #GAsyncQueue
- *
- * Threads act almost like processes, but unlike processes all threads
- * of one process share the same memory. This is good, as it provides
- * easy communication between the involved threads via this shared
- * memory, and it is bad, because strange things (so called
- * "Heisenbugs") might happen if the program is not carefully designed.
- * In particular, due to the concurrent nature of threads, no
- * assumptions on the order of execution of code running in different
- * threads can be made, unless order is explicitly forced by the
- * programmer through synchronization primitives.
- *
- * The aim of the thread related functions in GLib is to provide a
- * portable means for writing multi-threaded software. There are
- * primitives for mutexes to protect the access to portions of memory
- * (#GMutex, #GStaticMutex, #G_LOCK_DEFINE, #GStaticRecMutex and
- * #GStaticRWLock). There are primitives for condition variables to
- * allow synchronization of threads (#GCond). There are primitives for
- * thread-private data - data that every thread has a private instance
- * of (#GPrivate, #GStaticPrivate). Last but definitely not least there
- * are primitives to portably create and manage threads (#GThread).
- *
- * The threading system is initialized with g_thread_init(), which
- * takes an optional custom thread implementation or %NULL for the
- * default implementation. If you want to call g_thread_init() with a
- * non-%NULL argument this must be done before executing any other GLib
- * functions (except g_mem_set_vtable()). This is a requirement even if
- * no threads are in fact ever created by the process.
- *
- * Calling g_thread_init() with a %NULL argument is somewhat more
- * relaxed. You may call any other glib functions in the main thread
- * before g_thread_init() as long as g_thread_init() is not called from
- * a glib callback, or with any locks held. However, many libraries
- * above glib does not support late initialization of threads, so doing
- * this should be avoided if possible.
- *
- * Please note that since version 2.24 the GObject initialization
- * function g_type_init() initializes threads (with a %NULL argument),
- * so most applications, including those using Gtk+ will run with
- * threads enabled. If you want a special thread implementation, make
- * sure you call g_thread_init() before g_type_init() is called.
- *
- * After calling g_thread_init(), GLib is completely thread safe (all
- * global data is automatically locked), but individual data structure
- * instances are not automatically locked for performance reasons. So,
- * for example you must coordinate accesses to the same #GHashTable
- * from multiple threads. The two notable exceptions from this rule
- * are #GMainLoop and #GAsyncQueue, which <emphasis>are</emphasis>
- * threadsafe and need no further application-level locking to be
- * accessed from multiple threads.
- *
- * To help debugging problems in multithreaded applications, GLib
- * supports error-checking mutexes that will give you helpful error
- * messages on common problems. To use error-checking mutexes, define
- * the symbol #G_ERRORCHECK_MUTEXES when compiling the application.
- **/
-
-/**
- * G_THREADS_IMPL_POSIX:
- *
- * This macro is defined if POSIX style threads are used.
- **/
-
-/**
- * G_THREADS_ENABLED:
- *
- * This macro is defined if GLib was compiled with thread support. This
- * does not necessarily mean that there is a thread implementation
- * available, but it does mean that the infrastructure is in place and
- * that once you provide a thread implementation to g_thread_init(),
- * GLib will be multi-thread safe. If #G_THREADS_ENABLED is not
- * defined, then Glib is not, and cannot be, multi-thread safe.
- **/
-
-/**
- * G_THREADS_IMPL_NONE:
- *
- * This macro is defined if no thread implementation is used. You can,
- * however, provide one to g_thread_init() to make GLib multi-thread
- * safe.
- **/
-
-/* G_LOCK Documentation {{{1 ---------------------------------------------- */
-
-/* IMPLEMENTATION NOTE:
- *
- * G_LOCK_DEFINE and friends are convenience macros defined in
- * gthread.h. Their documentation lives here.
- */
-
-/**
- * G_LOCK_DEFINE:
- * @name: the name of the lock.
- *
- * The %G_LOCK_* macros provide a convenient interface to #GStaticMutex
- * with the advantage that they will expand to nothing in programs
- * compiled against a thread-disabled GLib, saving code and memory
- * there. #G_LOCK_DEFINE defines a lock. It can appear anywhere
- * variable definitions may appear in programs, i.e. in the first block
- * of a function or outside of functions. The @name parameter will be
- * mangled to get the name of the #GStaticMutex. This means that you
- * can use names of existing variables as the parameter - e.g. the name
- * of the variable you intent to protect with the lock. Look at our
- * <function>give_me_next_number()</function> example using the
- * %G_LOCK_* macros:
- *
- * <example>
- * <title>Using the %G_LOCK_* convenience macros</title>
- * <programlisting>
- * G_LOCK_DEFINE (current_number);
- *
- * int
- * give_me_next_number (void)
- * {
- * static int current_number = 0;
- * int ret_val;
- *
- * G_LOCK (current_number);
- * ret_val = current_number = calc_next_number (current_number);
- * G_UNLOCK (current_number);
- *
- * return ret_val;
- * }
- * </programlisting>
- * </example>
- **/
-
-/**
- * G_LOCK_DEFINE_STATIC:
- * @name: the name of the lock.
- *
- * This works like #G_LOCK_DEFINE, but it creates a static object.
- **/
-
-/**
- * G_LOCK_EXTERN:
- * @name: the name of the lock.
- *
- * This declares a lock, that is defined with #G_LOCK_DEFINE in another
- * module.
- **/
-
-/**
- * G_LOCK:
- * @name: the name of the lock.
- *
- * Works like g_mutex_lock(), but for a lock defined with
- * #G_LOCK_DEFINE.
- **/
-
-/**
- * G_TRYLOCK:
- * @name: the name of the lock.
- * @Returns: %TRUE, if the lock could be locked.
- *
- * Works like g_mutex_trylock(), but for a lock defined with
- * #G_LOCK_DEFINE.
- **/
-
-/**
- * G_UNLOCK:
- * @name: the name of the lock.
- *
- * Works like g_mutex_unlock(), but for a lock defined with
- * #G_LOCK_DEFINE.
- **/
-
-/* GThreadError {{{1 ------------------------------------------------------- */
-/**
- * GThreadError:
- * @G_THREAD_ERROR_AGAIN: a thread couldn't be created due to resource
- * shortage. Try again later.
- *
- * Possible errors of thread related functions.
- **/
-
-/**
- * G_THREAD_ERROR:
- *
- * The error domain of the GLib thread subsystem.
- **/
-GQuark
-g_thread_error_quark (void)
-{
- return g_quark_from_static_string ("g_thread_error");
-}
-
-/* Miscellaneous Structures {{{1 ------------------------------------------ */
-/* Keep this in sync with GRealThread in gmain.c! */
-typedef struct _GRealThread GRealThread;
-struct _GRealThread
-{
- GThread thread;
- gpointer private_data;
- GRealThread *next;
- gpointer retval;
- GSystemThread system_thread;
-};
-
-typedef struct _GStaticPrivateNode GStaticPrivateNode;
-struct _GStaticPrivateNode
-{
- gpointer data;
- GDestroyNotify destroy;
-};
-
-static void g_thread_cleanup (gpointer data);
-static void g_thread_fail (void);
-static guint64 gettime (void);
-
-guint64 (*g_thread_gettime) (void) = gettime;
-
-/* Global Variables {{{1 -------------------------------------------------- */
-
-static GSystemThread zero_thread; /* This is initialized to all zero */
-gboolean g_thread_use_default_impl = TRUE;
-
-/**
- * g_thread_supported:
- * @Returns: %TRUE, if the thread system is initialized.
- *
- * This function returns %TRUE if the thread system is initialized, and
- * %FALSE if it is not.
- *
- * <note><para>This function is actually a macro. Apart from taking the
- * address of it you can however use it as if it was a
- * function.</para></note>
- **/
-
-/* IMPLEMENTATION NOTE:
- *
- * g_thread_supported() is just returns g_threads_got_initialized
- */
-gboolean g_threads_got_initialized = FALSE;
-
-
-/* Thread Implementation Virtual Function Table {{{1 ---------------------- */
-/* Virtual Function Table Documentation {{{2 ------------------------------ */
-/**
- * GThreadFunctions:
- * @mutex_new: virtual function pointer for g_mutex_new()
- * @mutex_lock: virtual function pointer for g_mutex_lock()
- * @mutex_trylock: virtual function pointer for g_mutex_trylock()
- * @mutex_unlock: virtual function pointer for g_mutex_unlock()
- * @mutex_free: virtual function pointer for g_mutex_free()
- * @cond_new: virtual function pointer for g_cond_new()
- * @cond_signal: virtual function pointer for g_cond_signal()
- * @cond_broadcast: virtual function pointer for g_cond_broadcast()
- * @cond_wait: virtual function pointer for g_cond_wait()
- * @cond_timed_wait: virtual function pointer for g_cond_timed_wait()
- * @cond_free: virtual function pointer for g_cond_free()
- * @private_new: virtual function pointer for g_private_new()
- * @private_get: virtual function pointer for g_private_get()
- * @private_set: virtual function pointer for g_private_set()
- * @thread_create: virtual function pointer for g_thread_create()
- * @thread_yield: virtual function pointer for g_thread_yield()
- * @thread_join: virtual function pointer for g_thread_join()
- * @thread_exit: virtual function pointer for g_thread_exit()
- * @thread_set_priority: virtual function pointer for
- * g_thread_set_priority()
- * @thread_self: virtual function pointer for g_thread_self()
- * @thread_equal: used internally by recursive mutex locks and by some
- * assertion checks
- *
- * This function table is used by g_thread_init() to initialize the
- * thread system. The functions in the table are directly used by their
- * g_* prepended counterparts (described in this document). For
- * example, if you call g_mutex_new() then mutex_new() from the table
- * provided to g_thread_init() will be called.
- *
- * <note><para>Do not use this struct unless you know what you are
- * doing.</para></note>
- **/
-
-/* IMPLEMENTATION NOTE:
- *
- * g_thread_functions_for_glib_use is a global symbol that gets used by
- * most of the "primative" threading calls. g_mutex_lock(), for
- * example, is just a macro that calls the appropriate virtual function
- * out of this table.
- *
- * For that reason, all of those macros are documented here.
- */
-GThreadFunctions g_thread_functions_for_glib_use = {
-/* GMutex Virtual Functions {{{2 ------------------------------------------ */
-
-/**
- * GMutex:
- *
- * The #GMutex struct is an opaque data structure to represent a mutex
- * (mutual exclusion). It can be used to protect data against shared
- * access. Take for example the following function:
- *
- * <example>
- * <title>A function which will not work in a threaded environment</title>
- * <programlisting>
- * int
- * give_me_next_number (void)
- * {
- * static int current_number = 0;
- *
- * /<!-- -->* now do a very complicated calculation to calculate the new
- * * number, this might for example be a random number generator
- * *<!-- -->/
- * current_number = calc_next_number (current_number);
- *
- * return current_number;
- * }
- * </programlisting>
- * </example>
- *
- * It is easy to see that this won't work in a multi-threaded
- * application. There current_number must be protected against shared
- * access. A first naive implementation would be:
- *
- * <example>
- * <title>The wrong way to write a thread-safe function</title>
- * <programlisting>
- * int
- * give_me_next_number (void)
- * {
- * static int current_number = 0;
- * int ret_val;
- * static GMutex * mutex = NULL;
- *
- * if (!mutex) mutex = g_mutex_new (<!-- -->);
- *
- * g_mutex_lock (mutex);
- * ret_val = current_number = calc_next_number (current_number);
- * g_mutex_unlock (mutex);
- *
- * return ret_val;
- * }
- * </programlisting>
- * </example>
- *
- * This looks like it would work, but there is a race condition while
- * constructing the mutex and this code cannot work reliable. Please do
- * not use such constructs in your own programs! One working solution
- * is:
- *
- * <example>
- * <title>A correct thread-safe function</title>
- * <programlisting>
- * static GMutex *give_me_next_number_mutex = NULL;
- *
- * /<!-- -->* this function must be called before any call to
- * * give_me_next_number(<!-- -->)
- * *
- * * it must be called exactly once.
- * *<!-- -->/
- * void
- * init_give_me_next_number (void)
- * {
- * g_assert (give_me_next_number_mutex == NULL);
- * give_me_next_number_mutex = g_mutex_new (<!-- -->);
- * }
- *
- * int
- * give_me_next_number (void)
- * {
- * static int current_number = 0;
- * int ret_val;
- *
- * g_mutex_lock (give_me_next_number_mutex);
- * ret_val = current_number = calc_next_number (current_number);
- * g_mutex_unlock (give_me_next_number_mutex);
- *
- * return ret_val;
- * }
- * </programlisting>
- * </example>
- *
- * #GStaticMutex provides a simpler and safer way of doing this.
- *
- * If you want to use a mutex, and your code should also work without
- * calling g_thread_init() first, then you can not use a #GMutex, as
- * g_mutex_new() requires that the thread system be initialized. Use a
- * #GStaticMutex instead.
- *
- * A #GMutex should only be accessed via the following functions.
- *
- * <note><para>All of the <function>g_mutex_*</function> functions are
- * actually macros. Apart from taking their addresses, you can however
- * use them as if they were functions.</para></note>
- **/
-
-/**
- * g_mutex_new:
- * @Returns: a new #GMutex.
- *
- * Creates a new #GMutex.
- *
- * <note><para>This function will abort if g_thread_init() has not been
- * called yet.</para></note>
- **/
- (GMutex*(*)())g_thread_fail,
-
-/**
- * g_mutex_lock:
- * @mutex: a #GMutex.
- *
- * Locks @mutex. If @mutex is already locked by another thread, the
- * current thread will block until @mutex is unlocked by the other
- * thread.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will do nothing.
- *
- * <note><para>#GMutex is neither guaranteed to be recursive nor to be
- * non-recursive, i.e. a thread could deadlock while calling
- * g_mutex_lock(), if it already has locked @mutex. Use
- * #GStaticRecMutex, if you need recursive mutexes.</para></note>
- **/
- NULL,
-
-/**
- * g_mutex_trylock:
- * @mutex: a #GMutex.
- * @Returns: %TRUE, if @mutex could be locked.
- *
- * Tries to lock @mutex. If @mutex is already locked by another thread,
- * it immediately returns %FALSE. Otherwise it locks @mutex and returns
- * %TRUE.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will immediately return %TRUE.
- *
- * <note><para>#GMutex is neither guaranteed to be recursive nor to be
- * non-recursive, i.e. the return value of g_mutex_trylock() could be
- * both %FALSE or %TRUE, if the current thread already has locked
- * @mutex. Use #GStaticRecMutex, if you need recursive
- * mutexes.</para></note>
- **/
- NULL,
-
-/**
- * g_mutex_unlock:
- * @mutex: a #GMutex.
- *
- * Unlocks @mutex. If another thread is blocked in a g_mutex_lock()
- * call for @mutex, it will be woken and can lock @mutex itself.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will do nothing.
- **/
- NULL,
-
-/**
- * g_mutex_free:
- * @mutex: a #GMutex.
- *
- * Destroys @mutex.
- *
- * <note><para>Calling g_mutex_free() on a locked mutex may result in
- * undefined behaviour.</para></note>
- **/
- NULL,
-
-/* GCond Virtual Functions {{{2 ------------------------------------------ */
-
-/**
- * GCond:
- *
- * The #GCond struct is an opaque data structure that represents a
- * condition. Threads can block on a #GCond if they find a certain
- * condition to be false. If other threads change the state of this
- * condition they signal the #GCond, and that causes the waiting
- * threads to be woken up.
- *
- * <example>
- * <title>
- * Using GCond to block a thread until a condition is satisfied
- * </title>
- * <programlisting>
- * GCond* data_cond = NULL; /<!-- -->* Must be initialized somewhere *<!-- -->/
- * GMutex* data_mutex = NULL; /<!-- -->* Must be initialized somewhere *<!-- -->/
- * gpointer current_data = NULL;
- *
- * void
- * push_data (gpointer data)
- * {
- * g_mutex_lock (data_mutex);
- * current_data = data;
- * g_cond_signal (data_cond);
- * g_mutex_unlock (data_mutex);
- * }
- *
- * gpointer
- * pop_data (void)
- * {
- * gpointer data;
- *
- * g_mutex_lock (data_mutex);
- * while (!current_data)
- * g_cond_wait (data_cond, data_mutex);
- * data = current_data;
- * current_data = NULL;
- * g_mutex_unlock (data_mutex);
- *
- * return data;
- * }
- * </programlisting>
- * </example>
- *
- * Whenever a thread calls <function>pop_data()</function> now, it will
- * wait until current_data is non-%NULL, i.e. until some other thread
- * has called <function>push_data()</function>.
- *
- * <note><para>It is important to use the g_cond_wait() and
- * g_cond_timed_wait() functions only inside a loop which checks for the
- * condition to be true. It is not guaranteed that the waiting thread
- * will find the condition fulfilled after it wakes up, even if the
- * signaling thread left the condition in that state: another thread may
- * have altered the condition before the waiting thread got the chance
- * to be woken up, even if the condition itself is protected by a
- * #GMutex, like above.</para></note>
- *
- * A #GCond should only be accessed via the following functions.
- *
- * <note><para>All of the <function>g_cond_*</function> functions are
- * actually macros. Apart from taking their addresses, you can however
- * use them as if they were functions.</para></note>
- **/
-
-/**
- * g_cond_new:
- * @Returns: a new #GCond.
- *
- * Creates a new #GCond. This function will abort, if g_thread_init()
- * has not been called yet.
- **/
- (GCond*(*)())g_thread_fail,
-
-/**
- * g_cond_signal:
- * @cond: a #GCond.
- *
- * If threads are waiting for @cond, exactly one of them is woken up.
- * It is good practice to hold the same lock as the waiting thread
- * while calling this function, though not required.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will do nothing.
- **/
- NULL,
-
-/**
- * g_cond_broadcast:
- * @cond: a #GCond.
- *
- * If threads are waiting for @cond, all of them are woken up. It is
- * good practice to lock the same mutex as the waiting threads, while
- * calling this function, though not required.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will do nothing.
- **/
- NULL,
-
-/**
- * g_cond_wait:
- * @cond: a #GCond.
- * @mutex: a #GMutex, that is currently locked.
- *
- * Waits until this thread is woken up on @cond. The @mutex is unlocked
- * before falling asleep and locked again before resuming.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will immediately return.
- **/
- NULL,
-
-/**
- * g_cond_timed_wait:
- * @cond: a #GCond.
- * @mutex: a #GMutex that is currently locked.
- * @abs_time: a #GTimeVal, determining the final time.
- * @Returns: %TRUE if @cond was signalled, or %FALSE on timeout.
- *
- * Waits until this thread is woken up on @cond, but not longer than
- * until the time specified by @abs_time. The @mutex is unlocked before
- * falling asleep and locked again before resuming.
- *
- * If @abs_time is %NULL, g_cond_timed_wait() acts like g_cond_wait().
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will immediately return %TRUE.
- *
- * To easily calculate @abs_time a combination of g_get_current_time()
- * and g_time_val_add() can be used.
- **/
- NULL,
-
-/**
- * g_cond_free:
- * @cond: a #GCond.
- *
- * Destroys the #GCond.
- **/
- NULL,
-
-/* GPrivate Virtual Functions {{{2 --------------------------------------- */
-
-/**
- * GPrivate:
- *
- * The #GPrivate struct is an opaque data structure to represent a
- * thread private data key. Threads can thereby obtain and set a
- * pointer which is private to the current thread. Take our
- * <function>give_me_next_number(<!-- -->)</function> example from
- * above. Suppose we don't want <literal>current_number</literal> to be
- * shared between the threads, but instead to be private to each thread.
- * This can be done as follows:
- *
- * <example>
- * <title>Using GPrivate for per-thread data</title>
- * <programlisting>
- * GPrivate* current_number_key = NULL; /<!-- -->* Must be initialized somewhere
- * with g_private_new (g_free); *<!-- -->/
- *
- * int
- * give_me_next_number (void)
- * {
- * int *current_number = g_private_get (current_number_key);
- *
- * if (!current_number)
- * {
- * current_number = g_new (int, 1);
- * *current_number = 0;
- * g_private_set (current_number_key, current_number);
- * }
- *
- * *current_number = calc_next_number (*current_number);
- *
- * return *current_number;
- * }
- * </programlisting>
- * </example>
- *
- * Here the pointer belonging to the key
- * <literal>current_number_key</literal> is read. If it is %NULL, it has
- * not been set yet. Then get memory for an integer value, assign this
- * memory to the pointer and write the pointer back. Now we have an
- * integer value that is private to the current thread.
- *
- * The #GPrivate struct should only be accessed via the following
- * functions.
- *
- * <note><para>All of the <function>g_private_*</function> functions are
- * actually macros. Apart from taking their addresses, you can however
- * use them as if they were functions.</para></note>
- **/
-
-/**
- * g_private_new:
- * @destructor: a function to destroy the data keyed to #GPrivate when
- * a thread ends.
- * @Returns: a new #GPrivate.
- *
- * Creates a new #GPrivate. If @destructor is non-%NULL, it is a
- * pointer to a destructor function. Whenever a thread ends and the
- * corresponding pointer keyed to this instance of #GPrivate is
- * non-%NULL, the destructor is called with this pointer as the
- * argument.
- *
- * <note><para>@destructor is used quite differently from @notify in
- * g_static_private_set().</para></note>
- *
- * <note><para>A #GPrivate can not be freed. Reuse it instead, if you
- * can, to avoid shortage, or use #GStaticPrivate.</para></note>
- *
- * <note><para>This function will abort if g_thread_init() has not been
- * called yet.</para></note>
- **/
- (GPrivate*(*)(GDestroyNotify))g_thread_fail,
-
-/**
- * g_private_get:
- * @private_key: a #GPrivate.
- * @Returns: the corresponding pointer.
- *
- * Returns the pointer keyed to @private_key for the current thread. If
- * g_private_set() hasn't been called for the current @private_key and
- * thread yet, this pointer will be %NULL.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will return the value of @private_key
- * casted to #gpointer. Note however, that private data set
- * <emphasis>before</emphasis> g_thread_init() will
- * <emphasis>not</emphasis> be retained <emphasis>after</emphasis> the
- * call. Instead, %NULL will be returned in all threads directly after
- * g_thread_init(), regardless of any g_private_set() calls issued
- * before threading system intialization.
- **/
- NULL,
-
-/**
- * g_private_set:
- * @private_key: a #GPrivate.
- * @data: the new pointer.
- *
- * Sets the pointer keyed to @private_key for the current thread.
- *
- * This function can be used even if g_thread_init() has not yet been
- * called, and, in that case, will set @private_key to @data casted to
- * #GPrivate*. See g_private_get() for resulting caveats.
- **/
- NULL,
-
-/* GThread Virtual Functions {{{2 ---------------------------------------- */
-/**
- * GThread:
- *
- * The #GThread struct represents a running thread. It has three public
- * read-only members, but the underlying struct is bigger, so you must
- * not copy this struct.
- *
- * <note><para>Resources for a joinable thread are not fully released
- * until g_thread_join() is called for that thread.</para></note>
- **/
-
-/**
- * GThreadFunc:
- * @data: data passed to the thread.
- * @Returns: the return value of the thread, which will be returned by
- * g_thread_join().
- *
- * Specifies the type of the @func functions passed to
- * g_thread_create() or g_thread_create_full().
- **/
-
-/**
- * GThreadPriority:
- * @G_THREAD_PRIORITY_LOW: a priority lower than normal
- * @G_THREAD_PRIORITY_NORMAL: the default priority
- * @G_THREAD_PRIORITY_HIGH: a priority higher than normal
- * @G_THREAD_PRIORITY_URGENT: the highest priority
- *
- * Specifies the priority of a thread.
- *
- * <note><para>It is not guaranteed that threads with different priorities
- * really behave accordingly. On some systems (e.g. Linux) there are no
- * thread priorities. On other systems (e.g. Solaris) there doesn't
- * seem to be different scheduling for different priorities. All in all
- * try to avoid being dependent on priorities.</para></note>
- **/
-
-/**
- * g_thread_create:
- * @func: a function to execute in the new thread.
- * @data: an argument to supply to the new thread.
- * @joinable: should this thread be joinable?
- * @error: return location for error.
- * @Returns: the new #GThread on success.
- *
- * This function creates a new thread with the default priority.
- *
- * If @joinable is %TRUE, you can wait for this threads termination
- * calling g_thread_join(). Otherwise the thread will just disappear
- * when it terminates.
- *
- * The new thread executes the function @func with the argument @data.
- * If the thread was created successfully, it is returned.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report errors.
- * The error is set, if and only if the function returns %NULL.
- **/
- (void(*)(GThreadFunc, gpointer, gulong,
- gboolean, gboolean, GThreadPriority,
- gpointer, GError**))g_thread_fail,
-
-/**
- * g_thread_yield:
- *
- * Gives way to other threads waiting to be scheduled.
- *
- * This function is often used as a method to make busy wait less evil.
- * But in most cases you will encounter, there are better methods to do
- * that. So in general you shouldn't use this function.
- **/
- NULL,
-
- NULL, /* thread_join */
- NULL, /* thread_exit */
- NULL, /* thread_set_priority */
- NULL, /* thread_self */
- NULL /* thread_equal */
-};
-
-/* Local Data {{{1 -------------------------------------------------------- */
-
-static GMutex *g_once_mutex = NULL;
-static GCond *g_once_cond = NULL;
-static GPrivate *g_thread_specific_private = NULL;
-static GRealThread *g_thread_all_threads = NULL;
-static GSList *g_thread_free_indeces = NULL;
-static GSList* g_once_init_list = NULL;
-
-G_LOCK_DEFINE_STATIC (g_thread);
-
-/* Initialisation {{{1 ---------------------------------------------------- */
-
-#ifdef G_THREADS_ENABLED
-/**
- * g_thread_init:
- * @vtable: a function table of type #GThreadFunctions, that provides
- * the entry points to the thread system to be used.
- *
- * If you use GLib from more than one thread, you must initialize the
- * thread system by calling g_thread_init(). Most of the time you will
- * only have to call <literal>g_thread_init (NULL)</literal>.
- *
- * <note><para>Do not call g_thread_init() with a non-%NULL parameter unless
- * you really know what you are doing.</para></note>
- *
- * <note><para>g_thread_init() must not be called directly or indirectly as a
- * callback from GLib. Also no mutexes may be currently locked while
- * calling g_thread_init().</para></note>
- *
- * <note><para>g_thread_init() changes the way in which #GTimer measures
- * elapsed time. As a consequence, timers that are running while
- * g_thread_init() is called may report unreliable times.</para></note>
- *
- * Calling g_thread_init() multiple times is allowed (since version
- * 2.24), but nothing happens except for the first call. If the
- * argument is non-%NULL on such a call a warning will be printed, but
- * otherwise the argument is ignored.
- *
- * If no thread system is available and @vtable is %NULL or if not all
- * elements of @vtable are non-%NULL, then g_thread_init() will abort.
- *
- * <note><para>To use g_thread_init() in your program, you have to link with
- * the libraries that the command <command>pkg-config --libs
- * gthread-2.0</command> outputs. This is not the case for all the
- * other thread related functions of GLib. Those can be used without
- * having to link with the thread libraries.</para></note>
- **/
-
-/* This must be called only once, before any threads are created.
- * It will only be called from g_thread_init() in -lgthread.
- */
-void
-g_thread_init_glib (void)
-{
- /* We let the main thread (the one that calls g_thread_init) inherit
- * the static_private data set before calling g_thread_init
- */
- GRealThread* main_thread = (GRealThread*) g_thread_self ();
-
- /* mutex and cond creation works without g_threads_got_initialized */
- g_once_mutex = g_mutex_new ();
- g_once_cond = g_cond_new ();
-
- /* we may only create mutex and cond in here */
- _g_mem_thread_init_noprivate_nomessage ();
-
- /* setup the basic threading system */
- g_threads_got_initialized = TRUE;
- g_thread_specific_private = g_private_new (g_thread_cleanup);
- g_private_set (g_thread_specific_private, main_thread);
- G_THREAD_UF (thread_self, (&main_thread->system_thread));
-
- /* complete memory system initialization, g_private_*() works now */
- _g_slice_thread_init_nomessage ();
-
- /* accomplish log system initialization to enable messaging */
- _g_messages_thread_init_nomessage ();
-
- /* we may run full-fledged initializers from here */
- _g_atomic_thread_init ();
- _g_convert_thread_init ();
- _g_rand_thread_init ();
- _g_main_thread_init ();
- _g_utils_thread_init ();
- _g_futex_thread_init ();
-#ifdef G_OS_WIN32
- _g_win32_thread_init ();
-#endif
-}
-#endif /* G_THREADS_ENABLED */
-
-/* The following sections implement: GOnce, GStaticMutex, GStaticRecMutex,
- * GStaticPrivate,
- **/
-
-/* GOnce {{{1 ------------------------------------------------------------- */
-
-/**
- * GOnce:
- * @status: the status of the #GOnce
- * @retval: the value returned by the call to the function, if @status
- * is %G_ONCE_STATUS_READY
- *
- * A #GOnce struct controls a one-time initialization function. Any
- * one-time initialization function must have its own unique #GOnce
- * struct.
- *
- * Since: 2.4
- **/
-
-/**
- * G_ONCE_INIT:
- *
- * A #GOnce must be initialized with this macro before it can be used.
- *
- * <informalexample>
- * <programlisting>
- * GOnce my_once = G_ONCE_INIT;
- * </programlisting>
- * </informalexample>
- *
- * Since: 2.4
- **/
-
-/**
- * GOnceStatus:
- * @G_ONCE_STATUS_NOTCALLED: the function has not been called yet.
- * @G_ONCE_STATUS_PROGRESS: the function call is currently in progress.
- * @G_ONCE_STATUS_READY: the function has been called.
- *
- * The possible statuses of a one-time initialization function
- * controlled by a #GOnce struct.
- *
- * Since: 2.4
- **/
-
-/**
- * g_once:
- * @once: a #GOnce structure
- * @func: the #GThreadFunc function associated to @once. This function
- * is called only once, regardless of the number of times it and
- * its associated #GOnce struct are passed to g_once().
- * @arg: data to be passed to @func
- *
- * The first call to this routine by a process with a given #GOnce
- * struct calls @func with the given argument. Thereafter, subsequent
- * calls to g_once() with the same #GOnce struct do not call @func
- * again, but return the stored result of the first call. On return
- * from g_once(), the status of @once will be %G_ONCE_STATUS_READY.
- *
- * For example, a mutex or a thread-specific data key must be created
- * exactly once. In a threaded environment, calling g_once() ensures
- * that the initialization is serialized across multiple threads.
- *
- * <note><para>Calling g_once() recursively on the same #GOnce struct in
- * @func will lead to a deadlock.</para></note>
- *
- * <informalexample>
- * <programlisting>
- * gpointer
- * get_debug_flags (void)
- * {
- * static GOnce my_once = G_ONCE_INIT;
- *
- * g_once (&my_once, parse_debug_flags, NULL);
- *
- * return my_once.retval;
- * }
- * </programlisting>
- * </informalexample>
- *
- * Since: 2.4
- **/
-gpointer
-g_once_impl (GOnce *once,
- GThreadFunc func,
- gpointer arg)
-{
- g_mutex_lock (g_once_mutex);
-
- while (once->status == G_ONCE_STATUS_PROGRESS)
- g_cond_wait (g_once_cond, g_once_mutex);
-
- if (once->status != G_ONCE_STATUS_READY)
- {
- once->status = G_ONCE_STATUS_PROGRESS;
- g_mutex_unlock (g_once_mutex);
-
- once->retval = func (arg);
-
- g_mutex_lock (g_once_mutex);
- once->status = G_ONCE_STATUS_READY;
- g_cond_broadcast (g_once_cond);
- }
-
- g_mutex_unlock (g_once_mutex);
-
- return once->retval;
-}
-
-/**
- * g_once_init_enter:
- * @value_location: location of a static initializable variable
- * containing 0.
- * @Returns: %TRUE if the initialization section should be entered,
- * %FALSE and blocks otherwise
- *
- * Function to be called when starting a critical initialization
- * section. The argument @value_location must point to a static
- * 0-initialized variable that will be set to a value other than 0 at
- * the end of the initialization section. In combination with
- * g_once_init_leave() and the unique address @value_location, it can
- * be ensured that an initialization section will be executed only once
- * during a program's life time, and that concurrent threads are
- * blocked until initialization completed. To be used in constructs
- * like this:
- *
- * <informalexample>
- * <programlisting>
- * static gsize initialization_value = 0;
- *
- * if (g_once_init_enter (&initialization_value))
- * {
- * gsize setup_value = 42; /<!-- -->* initialization code here *<!-- -->/
- *
- * g_once_init_leave (&initialization_value, setup_value);
- * }
- *
- * /<!-- -->* use initialization_value here *<!-- -->/
- * </programlisting>
- * </informalexample>
- *
- * Since: 2.14
- **/
-gboolean
-g_once_init_enter_impl (volatile gsize *value_location)
-{
- gboolean need_init = FALSE;
- g_mutex_lock (g_once_mutex);
- if (g_atomic_pointer_get (value_location) == NULL)
- {
- if (!g_slist_find (g_once_init_list, (void*) value_location))
- {
- need_init = TRUE;
- g_once_init_list = g_slist_prepend (g_once_init_list, (void*) value_location);
- }
- else
- do
- g_cond_wait (g_once_cond, g_once_mutex);
- while (g_slist_find (g_once_init_list, (void*) value_location));
- }
- g_mutex_unlock (g_once_mutex);
- return need_init;
-}
-
-/**
- * g_once_init_leave:
- * @value_location: location of a static initializable variable
- * containing 0.
- * @initialization_value: new non-0 value for *@value_location.
- *
- * Counterpart to g_once_init_enter(). Expects a location of a static
- * 0-initialized initialization variable, and an initialization value
- * other than 0. Sets the variable to the initialization value, and
- * releases concurrent threads blocking in g_once_init_enter() on this
- * initialization variable.
- *
- * Since: 2.14
- **/
-void
-g_once_init_leave (volatile gsize *value_location,
- gsize initialization_value)
-{
- g_return_if_fail (g_atomic_pointer_get (value_location) == NULL);
- g_return_if_fail (initialization_value != 0);
- g_return_if_fail (g_once_init_list != NULL);
-
- g_atomic_pointer_set ((void**)value_location, (void*) initialization_value);
- g_mutex_lock (g_once_mutex);
- g_once_init_list = g_slist_remove (g_once_init_list, (void*) value_location);
- g_cond_broadcast (g_once_cond);
- g_mutex_unlock (g_once_mutex);
-}
-
-/* GStaticMutex {{{1 ------------------------------------------------------ */
-
-/**
- * GStaticMutex:
- *
- * A #GStaticMutex works like a #GMutex, but it has one significant
- * advantage. It doesn't need to be created at run-time like a #GMutex,
- * but can be defined at compile-time. Here is a shorter, easier and
- * safer version of our <function>give_me_next_number()</function>
- * example:
- *
- * <example>
- * <title>
- * Using <structname>GStaticMutex</structname>
- * to simplify thread-safe programming
- * </title>
- * <programlisting>
- * int
- * give_me_next_number (void)
- * {
- * static int current_number = 0;
- * int ret_val;
- * static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
- *
- * g_static_mutex_lock (&mutex);
- * ret_val = current_number = calc_next_number (current_number);
- * g_static_mutex_unlock (&mutex);
- *
- * return ret_val;
- * }
- * </programlisting>
- * </example>
- *
- * Sometimes you would like to dynamically create a mutex. If you don't
- * want to require prior calling to g_thread_init(), because your code
- * should also be usable in non-threaded programs, you are not able to
- * use g_mutex_new() and thus #GMutex, as that requires a prior call to
- * g_thread_init(). In theses cases you can also use a #GStaticMutex.
- * It must be initialized with g_static_mutex_init() before using it
- * and freed with with g_static_mutex_free() when not needed anymore to
- * free up any allocated resources.
- *
- * Even though #GStaticMutex is not opaque, it should only be used with
- * the following functions, as it is defined differently on different
- * platforms.
- *
- * All of the <function>g_static_mutex_*</function> functions apart
- * from <function>g_static_mutex_get_mutex</function> can also be used
- * even if g_thread_init() has not yet been called. Then they do
- * nothing, apart from <function>g_static_mutex_trylock</function>,
- * which does nothing but returning %TRUE.
- *
- * <note><para>All of the <function>g_static_mutex_*</function>
- * functions are actually macros. Apart from taking their addresses, you
- * can however use them as if they were functions.</para></note>
- **/
-
-/**
- * G_STATIC_MUTEX_INIT:
- *
- * A #GStaticMutex must be initialized with this macro, before it can
- * be used. This macro can used be to initialize a variable, but it
- * cannot be assigned to a variable. In that case you have to use
- * g_static_mutex_init().
- *
- * <informalexample>
- * <programlisting>
- * GStaticMutex my_mutex = G_STATIC_MUTEX_INIT;
- * </programlisting>
- * </informalexample>
- **/
-
-/**
- * g_static_mutex_init:
- * @mutex: a #GStaticMutex to be initialized.
- *
- * Initializes @mutex. Alternatively you can initialize it with
- * #G_STATIC_MUTEX_INIT.
- **/
-void
-g_static_mutex_init (GStaticMutex *mutex)
-{
- static const GStaticMutex init_mutex = G_STATIC_MUTEX_INIT;
-
- g_return_if_fail (mutex);
-
- *mutex = init_mutex;
-}
-
-/* IMPLEMENTATION NOTE:
- *
- * On some platforms a GStaticMutex is actually a normal GMutex stored
- * inside of a structure instead of being allocated dynamically. We can
- * only do this for platforms on which we know, in advance, how to
- * allocate (size) and initialise (value) that memory.
- *
- * On other platforms, a GStaticMutex is nothing more than a pointer to
- * a GMutex. In that case, the first access we make to the static mutex
- * must first allocate the normal GMutex and store it into the pointer.
- *
- * configure.ac writes macros into glibconfig.h to determine if
- * g_static_mutex_get_mutex() accesses the sturcture in memory directly
- * (on platforms where we are able to do that) or if it ends up here,
- * where we may have to allocate the GMutex before returning it.
- */
-
-/**
- * g_static_mutex_get_mutex:
- * @mutex: a #GStaticMutex.
- * @Returns: the #GMutex corresponding to @mutex.
- *
- * For some operations (like g_cond_wait()) you must have a #GMutex
- * instead of a #GStaticMutex. This function will return the
- * corresponding #GMutex for @mutex.
- **/
-GMutex *
-g_static_mutex_get_mutex_impl (GMutex** mutex)
-{
- if (!g_thread_supported ())
- return NULL;
-
- g_assert (g_once_mutex);
-
- g_mutex_lock (g_once_mutex);
-
- if (!(*mutex))
- g_atomic_pointer_set (mutex, g_mutex_new());
-
- g_mutex_unlock (g_once_mutex);
-
- return *mutex;
-}
-
-/* IMPLEMENTATION NOTE:
- *
- * g_static_mutex_lock(), g_static_mutex_trylock() and
- * g_static_mutex_unlock() are all preprocessor macros that wrap the
- * corresponding g_mutex_*() function around a call to
- * g_static_mutex_get_mutex().
- */
-
-/**
- * g_static_mutex_lock:
- * @mutex: a #GStaticMutex.
- *
- * Works like g_mutex_lock(), but for a #GStaticMutex.
- **/
-
-/**
- * g_static_mutex_trylock:
- * @mutex: a #GStaticMutex.
- * @Returns: %TRUE, if the #GStaticMutex could be locked.
- *
- * Works like g_mutex_trylock(), but for a #GStaticMutex.
- **/
-
-/**
- * g_static_mutex_unlock:
- * @mutex: a #GStaticMutex.
- *
- * Works like g_mutex_unlock(), but for a #GStaticMutex.
- **/
-
-/**
- * g_static_mutex_free:
- * @mutex: a #GStaticMutex to be freed.
- *
- * Releases all resources allocated to @mutex.
- *
- * You don't have to call this functions for a #GStaticMutex with an
- * unbounded lifetime, i.e. objects declared 'static', but if you have
- * a #GStaticMutex as a member of a structure and the structure is
- * freed, you should also free the #GStaticMutex.
- *
- * <note><para>Calling g_static_mutex_free() on a locked mutex may
- * result in undefined behaviour.</para></note>
- **/
-void
-g_static_mutex_free (GStaticMutex* mutex)
-{
- GMutex **runtime_mutex;
-
- g_return_if_fail (mutex);
-
- /* The runtime_mutex is the first (or only) member of GStaticMutex,
- * see both versions (of glibconfig.h) in configure.ac. Note, that
- * this variable is NULL, if g_thread_init() hasn't been called or
- * if we're using the default thread implementation and it provides
- * static mutexes. */
- runtime_mutex = ((GMutex**)mutex);
-
- if (*runtime_mutex)
- g_mutex_free (*runtime_mutex);
-
- *runtime_mutex = NULL;
-}
-
-/* ------------------------------------------------------------------------ */
-
-/**
- * GStaticRecMutex:
- *
- * A #GStaticRecMutex works like a #GStaticMutex, but it can be locked
- * multiple times by one thread. If you enter it n times, you have to
- * unlock it n times again to let other threads lock it. An exception
- * is the function g_static_rec_mutex_unlock_full(): that allows you to
- * unlock a #GStaticRecMutex completely returning the depth, (i.e. the
- * number of times this mutex was locked). The depth can later be used
- * to restore the state of the #GStaticRecMutex by calling
- * g_static_rec_mutex_lock_full().
- *
- * Even though #GStaticRecMutex is not opaque, it should only be used
- * with the following functions.
- *
- * All of the <function>g_static_rec_mutex_*</function> functions can
- * be used even if g_thread_init() has not been called. Then they do
- * nothing, apart from <function>g_static_rec_mutex_trylock</function>,
- * which does nothing but returning %TRUE.
- **/
-
-/**
- * G_STATIC_REC_MUTEX_INIT:
- *
- * A #GStaticRecMutex must be initialized with this macro before it can
- * be used. This macro can used be to initialize a variable, but it
- * cannot be assigned to a variable. In that case you have to use
- * g_static_rec_mutex_init().
- *
- * <informalexample>
- * <programlisting>
- * GStaticRecMutex my_mutex = G_STATIC_REC_MUTEX_INIT;
- * </programlisting>
- </informalexample>
- **/
-
-/**
- * g_static_rec_mutex_init:
- * @mutex: a #GStaticRecMutex to be initialized.
- *
- * A #GStaticRecMutex must be initialized with this function before it
- * can be used. Alternatively you can initialize it with
- * #G_STATIC_REC_MUTEX_INIT.
- **/
-void
-g_static_rec_mutex_init (GStaticRecMutex *mutex)
-{
- static const GStaticRecMutex init_mutex = G_STATIC_REC_MUTEX_INIT;
-
- g_return_if_fail (mutex);
-
- *mutex = init_mutex;
-}
-
-/**
- * g_static_rec_mutex_lock:
- * @mutex: a #GStaticRecMutex to lock.
- *
- * Locks @mutex. If @mutex is already locked by another thread, the
- * current thread will block until @mutex is unlocked by the other
- * thread. If @mutex is already locked by the calling thread, this
- * functions increases the depth of @mutex and returns immediately.
- **/
-void
-g_static_rec_mutex_lock (GStaticRecMutex* mutex)
-{
- GSystemThread self;
-
- g_return_if_fail (mutex);
-
- if (!g_thread_supported ())
- return;
-
- G_THREAD_UF (thread_self, (&self));
-
- if (g_system_thread_equal (self, mutex->owner))
- {
- mutex->depth++;
- return;
- }
- g_static_mutex_lock (&mutex->mutex);
- g_system_thread_assign (mutex->owner, self);
- mutex->depth = 1;
-}
-
-/**
- * g_static_rec_mutex_trylock:
- * @mutex: a #GStaticRecMutex to lock.
- * @Returns: %TRUE, if @mutex could be locked.
- *
- * Tries to lock @mutex. If @mutex is already locked by another thread,
- * it immediately returns %FALSE. Otherwise it locks @mutex and returns
- * %TRUE. If @mutex is already locked by the calling thread, this
- * functions increases the depth of @mutex and immediately returns
- * %TRUE.
- **/
-gboolean
-g_static_rec_mutex_trylock (GStaticRecMutex* mutex)
-{
- GSystemThread self;
-
- g_return_val_if_fail (mutex, FALSE);
-
- if (!g_thread_supported ())
- return TRUE;
-
- G_THREAD_UF (thread_self, (&self));
-
- if (g_system_thread_equal (self, mutex->owner))
- {
- mutex->depth++;
- return TRUE;
- }
-
- if (!g_static_mutex_trylock (&mutex->mutex))
- return FALSE;
-
- g_system_thread_assign (mutex->owner, self);
- mutex->depth = 1;
- return TRUE;
-}
-
-/**
- * g_static_rec_mutex_unlock:
- * @mutex: a #GStaticRecMutex to unlock.
- *
- * Unlocks @mutex. Another thread will be allowed to lock @mutex only
- * when it has been unlocked as many times as it had been locked
- * before. If @mutex is completely unlocked and another thread is
- * blocked in a g_static_rec_mutex_lock() call for @mutex, it will be
- * woken and can lock @mutex itself.
- **/
-void
-g_static_rec_mutex_unlock (GStaticRecMutex* mutex)
-{
- g_return_if_fail (mutex);
-
- if (!g_thread_supported ())
- return;
-
- if (mutex->depth > 1)
- {
- mutex->depth--;
- return;
- }
- g_system_thread_assign (mutex->owner, zero_thread);
- g_static_mutex_unlock (&mutex->mutex);
-}
-
-/**
- * g_static_rec_mutex_lock_full:
- * @mutex: a #GStaticRecMutex to lock.
- * @depth: number of times this mutex has to be unlocked to be
- * completely unlocked.
- *
- * Works like calling g_static_rec_mutex_lock() for @mutex @depth times.
- **/
-void
-g_static_rec_mutex_lock_full (GStaticRecMutex *mutex,
- guint depth)
-{
- GSystemThread self;
- g_return_if_fail (mutex);
-
- if (!g_thread_supported ())
- return;
-
- if (depth == 0)
- return;
-
- G_THREAD_UF (thread_self, (&self));
-
- if (g_system_thread_equal (self, mutex->owner))
- {
- mutex->depth += depth;
- return;
- }
- g_static_mutex_lock (&mutex->mutex);
- g_system_thread_assign (mutex->owner, self);
- mutex->depth = depth;
-}
-
-/**
- * g_static_rec_mutex_unlock_full:
- * @mutex: a #GStaticRecMutex to completely unlock.
- * @Returns: number of times @mutex has been locked by the current
- * thread.
- *
- * Completely unlocks @mutex. If another thread is blocked in a
- * g_static_rec_mutex_lock() call for @mutex, it will be woken and can
- * lock @mutex itself. This function returns the number of times that
- * @mutex has been locked by the current thread. To restore the state
- * before the call to g_static_rec_mutex_unlock_full() you can call
- * g_static_rec_mutex_lock_full() with the depth returned by this
- * function.
- **/
-guint
-g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex)
-{
- guint depth;
-
- g_return_val_if_fail (mutex, 0);
-
- if (!g_thread_supported ())
- return 1;
-
- depth = mutex->depth;
-
- g_system_thread_assign (mutex->owner, zero_thread);
- mutex->depth = 0;
- g_static_mutex_unlock (&mutex->mutex);
-
- return depth;
-}
-
-/**
- * g_static_rec_mutex_free:
- * @mutex: a #GStaticRecMutex to be freed.
- *
- * Releases all resources allocated to a #GStaticRecMutex.
- *
- * You don't have to call this functions for a #GStaticRecMutex with an
- * unbounded lifetime, i.e. objects declared 'static', but if you have
- * a #GStaticRecMutex as a member of a structure and the structure is
- * freed, you should also free the #GStaticRecMutex.
- **/
-void
-g_static_rec_mutex_free (GStaticRecMutex *mutex)
-{
- g_return_if_fail (mutex);
-
- g_static_mutex_free (&mutex->mutex);
-}
-
-/* GStaticPrivate {{{1 ---------------------------------------------------- */
-
-/**
- * GStaticPrivate:
- *
- * A #GStaticPrivate works almost like a #GPrivate, but it has one
- * significant advantage. It doesn't need to be created at run-time
- * like a #GPrivate, but can be defined at compile-time. This is
- * similar to the difference between #GMutex and #GStaticMutex. Now
- * look at our <function>give_me_next_number()</function> example with
- * #GStaticPrivate:
- *
- * <example>
- * <title>Using GStaticPrivate for per-thread data</title>
- * <programlisting>
- * int
- * give_me_next_number (<!-- -->)
- * {
- * static GStaticPrivate current_number_key = G_STATIC_PRIVATE_INIT;
- * int *current_number = g_static_private_get (&current_number_key);
- *
- * if (!current_number)
- * {
- * current_number = g_new (int,1);
- * *current_number = 0;
- * g_static_private_set (&current_number_key, current_number, g_free);
- * }
- *
- * *current_number = calc_next_number (*current_number);
- *
- * return *current_number;
- * }
- * </programlisting>
- * </example>
- **/
-
-/**
- * G_STATIC_PRIVATE_INIT:
- *
- * Every #GStaticPrivate must be initialized with this macro, before it
- * can be used.
- *
- * <informalexample>
- * <programlisting>
- * GStaticPrivate my_private = G_STATIC_PRIVATE_INIT;
- * </programlisting>
- * </informalexample>
- **/
-
-/**
- * g_static_private_init:
- * @private_key: a #GStaticPrivate to be initialized.
- *
- * Initializes @private_key. Alternatively you can initialize it with
- * #G_STATIC_PRIVATE_INIT.
- **/
-void
-g_static_private_init (GStaticPrivate *private_key)
-{
- private_key->index = 0;
-}
-
-/**
- * g_static_private_get:
- * @private_key: a #GStaticPrivate.
- * @Returns: the corresponding pointer.
- *
- * Works like g_private_get() only for a #GStaticPrivate.
- *
- * This function works even if g_thread_init() has not yet been called.
- **/
-gpointer
-g_static_private_get (GStaticPrivate *private_key)
-{
- GRealThread *self = (GRealThread*) g_thread_self ();
- GArray *array;
-
- array = self->private_data;
- if (!array)
- return NULL;
-
- if (!private_key->index)
- return NULL;
- else if (private_key->index <= array->len)
- return g_array_index (array, GStaticPrivateNode,
- private_key->index - 1).data;
- else
- return NULL;
-}
-
-/**
- * g_static_private_set:
- * @private_key: a #GStaticPrivate.
- * @data: the new pointer.
- * @notify: a function to be called with the pointer whenever the
- * current thread ends or sets this pointer again.
- *
- * Sets the pointer keyed to @private_key for the current thread and
- * the function @notify to be called with that pointer (%NULL or
- * non-%NULL), whenever the pointer is set again or whenever the
- * current thread ends.
- *
- * This function works even if g_thread_init() has not yet been called.
- * If g_thread_init() is called later, the @data keyed to @private_key
- * will be inherited only by the main thread, i.e. the one that called
- * g_thread_init().
- *
- * <note><para>@notify is used quite differently from @destructor in
- * g_private_new().</para></note>
- **/
-void
-g_static_private_set (GStaticPrivate *private_key,
- gpointer data,
- GDestroyNotify notify)
-{
- GRealThread *self = (GRealThread*) g_thread_self ();
- GArray *array;
- static guint next_index = 0;
- GStaticPrivateNode *node;
-
- array = self->private_data;
- if (!array)
- {
- array = g_array_new (FALSE, TRUE, sizeof (GStaticPrivateNode));
- self->private_data = array;
- }
-
- if (!private_key->index)
- {
- G_LOCK (g_thread);
-
- if (!private_key->index)
- {
- if (g_thread_free_indeces)
- {
- private_key->index =
- GPOINTER_TO_UINT (g_thread_free_indeces->data);
- g_thread_free_indeces =
- g_slist_delete_link (g_thread_free_indeces,
- g_thread_free_indeces);
- }
- else
- private_key->index = ++next_index;
- }
-
- G_UNLOCK (g_thread);
- }
-
- if (private_key->index > array->len)
- g_array_set_size (array, private_key->index);
-
- node = &g_array_index (array, GStaticPrivateNode, private_key->index - 1);
- if (node->destroy)
- {
- gpointer ddata = node->data;
- GDestroyNotify ddestroy = node->destroy;
-
- node->data = data;
- node->destroy = notify;
-
- ddestroy (ddata);
- }
- else
- {
- node->data = data;
- node->destroy = notify;
- }
-}
-
-/**
- * g_static_private_free:
- * @private_key: a #GStaticPrivate to be freed.
- *
- * Releases all resources allocated to @private_key.
- *
- * You don't have to call this functions for a #GStaticPrivate with an
- * unbounded lifetime, i.e. objects declared 'static', but if you have
- * a #GStaticPrivate as a member of a structure and the structure is
- * freed, you should also free the #GStaticPrivate.
- **/
-void
-g_static_private_free (GStaticPrivate *private_key)
-{
- guint idx = private_key->index;
- GRealThread *thread;
-
- if (!idx)
- return;
-
- private_key->index = 0;
-
- G_LOCK (g_thread);
-
- thread = g_thread_all_threads;
- while (thread)
- {
- GArray *array = thread->private_data;
- thread = thread->next;
-
- if (array && idx <= array->len)
- {
- GStaticPrivateNode *node = &g_array_index (array,
- GStaticPrivateNode,
- idx - 1);
- gpointer ddata = node->data;
- GDestroyNotify ddestroy = node->destroy;
-
- node->data = NULL;
- node->destroy = NULL;
-
- if (ddestroy)
- {
- G_UNLOCK (g_thread);
- ddestroy (ddata);
- G_LOCK (g_thread);
- }
- }
- }
- g_thread_free_indeces = g_slist_prepend (g_thread_free_indeces,
- GUINT_TO_POINTER (idx));
- G_UNLOCK (g_thread);
-}
-
-/* GThread Extra Functions {{{1 ------------------------------------------- */
-static void
-g_thread_cleanup (gpointer data)
-{
- if (data)
- {
- GRealThread* thread = data;
- if (thread->private_data)
- {
- GArray* array = thread->private_data;
- guint i;
-
- for (i = 0; i < array->len; i++ )
- {
- GStaticPrivateNode *node =
- &g_array_index (array, GStaticPrivateNode, i);
- if (node->destroy)
- node->destroy (node->data);
- }
- g_array_free (array, TRUE);
- }
-
- /* We only free the thread structure, if it isn't joinable. If
- it is, the structure is freed in g_thread_join */
- if (!thread->thread.joinable)
- {
- GRealThread *t, *p;
-
- G_LOCK (g_thread);
- for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
- {
- if (t == thread)
- {
- if (p)
- p->next = t->next;
- else
- g_thread_all_threads = t->next;
- break;
- }
- }
- G_UNLOCK (g_thread);
-
- /* Just to make sure, this isn't used any more */
- g_system_thread_assign (thread->system_thread, zero_thread);
- g_free (thread);
- }
- }
-}
-
-static void
-g_thread_fail (void)
-{
- g_error ("The thread system is not yet initialized.");
-}
-
-#define G_NSEC_PER_SEC 1000000000
-
-static guint64
-gettime (void)
-{
-#ifdef G_OS_WIN32
- guint64 v;
-
- /* Returns 100s of nanoseconds since start of 1601 */
- GetSystemTimeAsFileTime ((FILETIME *)&v);
-
- /* Offset to Unix epoch */
- v -= G_GINT64_CONSTANT (116444736000000000);
- /* Convert to nanoseconds */
- v *= 100;
-
- return v;
-#else
- struct timeval tv;
-
- gettimeofday (&tv, NULL);
-
- return (guint64) tv.tv_sec * G_NSEC_PER_SEC + tv.tv_usec * (G_NSEC_PER_SEC / G_USEC_PER_SEC);
-#endif
-}
-
-static gpointer
-g_thread_create_proxy (gpointer data)
-{
- GRealThread* thread = data;
-
- g_assert (data);
-
- /* This has to happen before G_LOCK, as that might call g_thread_self */
- g_private_set (g_thread_specific_private, data);
-
- /* the lock makes sure, that thread->system_thread is written,
- before thread->thread.func is called. See g_thread_create. */
- G_LOCK (g_thread);
- G_UNLOCK (g_thread);
-
- thread->retval = thread->thread.func (thread->thread.data);
-
- return NULL;
-}
-
-/**
- * g_thread_create_full:
- * @func: a function to execute in the new thread.
- * @data: an argument to supply to the new thread.
- * @stack_size: a stack size for the new thread.
- * @joinable: should this thread be joinable?
- * @bound: should this thread be bound to a system thread?
- * @priority: a priority for the thread.
- * @error: return location for error.
- * @Returns: the new #GThread on success.
- *
- * This function creates a new thread with the priority @priority. If
- * the underlying thread implementation supports it, the thread gets a
- * stack size of @stack_size or the default value for the current
- * platform, if @stack_size is 0.
- *
- * If @joinable is %TRUE, you can wait for this threads termination
- * calling g_thread_join(). Otherwise the thread will just disappear
- * when it terminates. If @bound is %TRUE, this thread will be
- * scheduled in the system scope, otherwise the implementation is free
- * to do scheduling in the process scope. The first variant is more
- * expensive resource-wise, but generally faster. On some systems (e.g.
- * Linux) all threads are bound.
- *
- * The new thread executes the function @func with the argument @data.
- * If the thread was created successfully, it is returned.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report errors.
- * The error is set, if and only if the function returns %NULL.
- *
- * <note><para>It is not guaranteed that threads with different priorities
- * really behave accordingly. On some systems (e.g. Linux) there are no
- * thread priorities. On other systems (e.g. Solaris) there doesn't
- * seem to be different scheduling for different priorities. All in all
- * try to avoid being dependent on priorities. Use
- * %G_THREAD_PRIORITY_NORMAL here as a default.</para></note>
- *
- * <note><para>Only use g_thread_create_full() if you really can't use
- * g_thread_create() instead. g_thread_create() does not take
- * @stack_size, @bound, and @priority as arguments, as they should only
- * be used in cases in which it is unavoidable.</para></note>
- **/
-GThread*
-g_thread_create_full (GThreadFunc func,
- gpointer data,
- gulong stack_size,
- gboolean joinable,
- gboolean bound,
- GThreadPriority priority,
- GError **error)
-{
- GRealThread* result;
- GError *local_error = NULL;
- g_return_val_if_fail (func, NULL);
- g_return_val_if_fail (priority >= G_THREAD_PRIORITY_LOW, NULL);
- g_return_val_if_fail (priority <= G_THREAD_PRIORITY_URGENT, NULL);
-
- result = g_new0 (GRealThread, 1);
-
- result->thread.joinable = joinable;
- result->thread.priority = priority;
- result->thread.func = func;
- result->thread.data = data;
- result->private_data = NULL;
- G_LOCK (g_thread);
- G_THREAD_UF (thread_create, (g_thread_create_proxy, result,
- stack_size, joinable, bound, priority,
- &result->system_thread, &local_error));
- if (!local_error)
- {
- result->next = g_thread_all_threads;
- g_thread_all_threads = result;
- }
- G_UNLOCK (g_thread);
-
- if (local_error)
- {
- g_propagate_error (error, local_error);
- g_free (result);
- return NULL;
- }
-
- return (GThread*) result;
-}
-
-/**
- * g_thread_exit:
- * @retval: the return value of this thread.
- *
- * Exits the current thread. If another thread is waiting for that
- * thread using g_thread_join() and the current thread is joinable, the
- * waiting thread will be woken up and get @retval as the return value
- * of g_thread_join(). If the current thread is not joinable, @retval
- * is ignored. Calling
- *
- * <informalexample>
- * <programlisting>
- * g_thread_exit (retval);
- * </programlisting>
- * </informalexample>
- *
- * is equivalent to returning @retval from the function @func, as given
- * to g_thread_create().
- *
- * <note><para>Never call g_thread_exit() from within a thread of a
- * #GThreadPool, as that will mess up the bookkeeping and lead to funny
- * and unwanted results.</para></note>
- **/
-void
-g_thread_exit (gpointer retval)
-{
- GRealThread* real = (GRealThread*) g_thread_self ();
- real->retval = retval;
- G_THREAD_CF (thread_exit, (void)0, ());
-}
-
-/**
- * g_thread_join:
- * @thread: a #GThread to be waited for.
- * @Returns: the return value of the thread.
- *
- * Waits until @thread finishes, i.e. the function @func, as given to
- * g_thread_create(), returns or g_thread_exit() is called by @thread.
- * All resources of @thread including the #GThread struct are released.
- * @thread must have been created with @joinable=%TRUE in
- * g_thread_create(). The value returned by @func or given to
- * g_thread_exit() by @thread is returned by this function.
- **/
-gpointer
-g_thread_join (GThread* thread)
-{
- GRealThread* real = (GRealThread*) thread;
- GRealThread *p, *t;
- gpointer retval;
-
- g_return_val_if_fail (thread, NULL);
- g_return_val_if_fail (thread->joinable, NULL);
- g_return_val_if_fail (!g_system_thread_equal (real->system_thread,
- zero_thread), NULL);
-
- G_THREAD_UF (thread_join, (&real->system_thread));
-
- retval = real->retval;
-
- G_LOCK (g_thread);
- for (t = g_thread_all_threads, p = NULL; t; p = t, t = t->next)
- {
- if (t == (GRealThread*) thread)
- {
- if (p)
- p->next = t->next;
- else
- g_thread_all_threads = t->next;
- break;
- }
- }
- G_UNLOCK (g_thread);
-
- /* Just to make sure, this isn't used any more */
- thread->joinable = 0;
- g_system_thread_assign (real->system_thread, zero_thread);
-
- /* the thread structure for non-joinable threads is freed upon
- thread end. We free the memory here. This will leave a loose end,
- if a joinable thread is not joined. */
-
- g_free (thread);
-
- return retval;
-}
-
-/**
- * g_thread_set_priority:
- * @thread: a #GThread.
- * @priority: a new priority for @thread.
- *
- * Changes the priority of @thread to @priority.
- *
- * <note><para>It is not guaranteed that threads with different
- * priorities really behave accordingly. On some systems (e.g. Linux)
- * there are no thread priorities. On other systems (e.g. Solaris) there
- * doesn't seem to be different scheduling for different priorities. All
- * in all try to avoid being dependent on priorities.</para></note>
- **/
-void
-g_thread_set_priority (GThread* thread,
- GThreadPriority priority)
-{
- GRealThread* real = (GRealThread*) thread;
-
- g_return_if_fail (thread);
- g_return_if_fail (!g_system_thread_equal (real->system_thread, zero_thread));
- g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
- g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
-
- thread->priority = priority;
-
- G_THREAD_CF (thread_set_priority, (void)0,
- (&real->system_thread, priority));
-}
-
-/**
- * g_thread_self:
- * @Returns: the current thread.
- *
- * This functions returns the #GThread corresponding to the calling
- * thread.
- **/
-GThread*
-g_thread_self (void)
-{
- GRealThread* thread = g_private_get (g_thread_specific_private);
-
- if (!thread)
- {
- /* If no thread data is available, provide and set one. This
- can happen for the main thread and for threads, that are not
- created by GLib. */
- thread = g_new0 (GRealThread, 1);
- thread->thread.joinable = FALSE; /* This is a save guess */
- thread->thread.priority = G_THREAD_PRIORITY_NORMAL; /* This is
- just a guess */
- thread->thread.func = NULL;
- thread->thread.data = NULL;
- thread->private_data = NULL;
-
- if (g_thread_supported ())
- G_THREAD_UF (thread_self, (&thread->system_thread));
-
- g_private_set (g_thread_specific_private, thread);
-
- G_LOCK (g_thread);
- thread->next = g_thread_all_threads;
- g_thread_all_threads = thread;
- G_UNLOCK (g_thread);
- }
-
- return (GThread*)thread;
-}
-
-/* GStaticRWLock {{{1 ----------------------------------------------------- */
-
-/**
- * GStaticRWLock:
- *
- * The #GStaticRWLock struct represents a read-write lock. A read-write
- * lock can be used for protecting data that some portions of code only
- * read from, while others also write. In such situations it is
- * desirable that several readers can read at once, whereas of course
- * only one writer may write at a time. Take a look at the following
- * example:
- *
- * <example>
- * <title>An array with access functions</title>
- * <programlisting>
- * GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT;
- * GPtrArray *array;
- *
- * gpointer
- * my_array_get (guint index)
- * {
- * gpointer retval = NULL;
- *
- * if (!array)
- * return NULL;
- *
- * g_static_rw_lock_reader_lock (&rwlock);
- * if (index < array->len)
- * retval = g_ptr_array_index (array, index);
- * g_static_rw_lock_reader_unlock (&rwlock);
- *
- * return retval;
- * }
- *
- * void
- * my_array_set (guint index, gpointer data)
- * {
- * g_static_rw_lock_writer_lock (&rwlock);
- *
- * if (!array)
- * array = g_ptr_array_new (<!-- -->);
- *
- * if (index >= array->len)
- * g_ptr_array_set_size (array, index+1);
- * g_ptr_array_index (array, index) = data;
- *
- * g_static_rw_lock_writer_unlock (&rwlock);
- * }
- * </programlisting>
- * </example>
- *
- * This example shows an array which can be accessed by many readers
- * (the <function>my_array_get()</function> function) simultaneously,
- * whereas the writers (the <function>my_array_set()</function>
- * function) will only be allowed once at a time and only if no readers
- * currently access the array. This is because of the potentially
- * dangerous resizing of the array. Using these functions is fully
- * multi-thread safe now.
- *
- * Most of the time, writers should have precedence over readers. That
- * means, for this implementation, that as soon as a writer wants to
- * lock the data, no other reader is allowed to lock the data, whereas,
- * of course, the readers that already have locked the data are allowed
- * to finish their operation. As soon as the last reader unlocks the
- * data, the writer will lock it.
- *
- * Even though #GStaticRWLock is not opaque, it should only be used
- * with the following functions.
- *
- * All of the <function>g_static_rw_lock_*</function> functions can be
- * used even if g_thread_init() has not been called. Then they do
- * nothing, apart from <function>g_static_rw_lock_*_trylock</function>,
- * which does nothing but returning %TRUE.
- *
- * <note><para>A read-write lock has a higher overhead than a mutex. For
- * example, both g_static_rw_lock_reader_lock() and
- * g_static_rw_lock_reader_unlock() have to lock and unlock a
- * #GStaticMutex, so it takes at least twice the time to lock and unlock
- * a #GStaticRWLock that it does to lock and unlock a #GStaticMutex. So
- * only data structures that are accessed by multiple readers, and which
- * keep the lock for a considerable time justify a #GStaticRWLock. The
- * above example most probably would fare better with a
- * #GStaticMutex.</para></note>
- **/
-
-/**
- * G_STATIC_RW_LOCK_INIT:
- *
- * A #GStaticRWLock must be initialized with this macro before it can
- * be used. This macro can used be to initialize a variable, but it
- * cannot be assigned to a variable. In that case you have to use
- * g_static_rw_lock_init().
- *
- * <informalexample>
- * <programlisting>
- * GStaticRWLock my_lock = G_STATIC_RW_LOCK_INIT;
- * </programlisting>
- * </informalexample>
- **/
-
-/**
- * g_static_rw_lock_init:
- * @lock: a #GStaticRWLock to be initialized.
- *
- * A #GStaticRWLock must be initialized with this function before it
- * can be used. Alternatively you can initialize it with
- * #G_STATIC_RW_LOCK_INIT.
- **/
-void
-g_static_rw_lock_init (GStaticRWLock* lock)
-{
- static const GStaticRWLock init_lock = G_STATIC_RW_LOCK_INIT;
-
- g_return_if_fail (lock);
-
- *lock = init_lock;
-}
-
-inline static void
-g_static_rw_lock_wait (GCond** cond, GStaticMutex* mutex)
-{
- if (!*cond)
- *cond = g_cond_new ();
- g_cond_wait (*cond, g_static_mutex_get_mutex (mutex));
-}
-
-inline static void
-g_static_rw_lock_signal (GStaticRWLock* lock)
-{
- if (lock->want_to_write && lock->write_cond)
- g_cond_signal (lock->write_cond);
- else if (lock->want_to_read && lock->read_cond)
- g_cond_broadcast (lock->read_cond);
-}
-
-/**
- * g_static_rw_lock_reader_lock:
- * @lock: a #GStaticRWLock to lock for reading.
- *
- * Locks @lock for reading. There may be unlimited concurrent locks for
- * reading of a #GStaticRWLock at the same time. If @lock is already
- * locked for writing by another thread or if another thread is already
- * waiting to lock @lock for writing, this function will block until
- * @lock is unlocked by the other writing thread and no other writing
- * threads want to lock @lock. This lock has to be unlocked by
- * g_static_rw_lock_reader_unlock().
- *
- * #GStaticRWLock is not recursive. It might seem to be possible to
- * recursively lock for reading, but that can result in a deadlock, due
- * to writer preference.
- **/
-void
-g_static_rw_lock_reader_lock (GStaticRWLock* lock)
-{
- g_return_if_fail (lock);
-
- if (!g_threads_got_initialized)
- return;
-
- g_static_mutex_lock (&lock->mutex);
- lock->want_to_read++;
- while (lock->have_writer || lock->want_to_write)
- g_static_rw_lock_wait (&lock->read_cond, &lock->mutex);
- lock->want_to_read--;
- lock->read_counter++;
- g_static_mutex_unlock (&lock->mutex);
-}
-
-/**
- * g_static_rw_lock_reader_trylock:
- * @lock: a #GStaticRWLock to lock for reading.
- * @Returns: %TRUE, if @lock could be locked for reading.
- *
- * Tries to lock @lock for reading. If @lock is already locked for
- * writing by another thread or if another thread is already waiting to
- * lock @lock for writing, immediately returns %FALSE. Otherwise locks
- * @lock for reading and returns %TRUE. This lock has to be unlocked by
- * g_static_rw_lock_reader_unlock().
- **/
-gboolean
-g_static_rw_lock_reader_trylock (GStaticRWLock* lock)
-{
- gboolean ret_val = FALSE;
-
- g_return_val_if_fail (lock, FALSE);
-
- if (!g_threads_got_initialized)
- return TRUE;
-
- g_static_mutex_lock (&lock->mutex);
- if (!lock->have_writer && !lock->want_to_write)
- {
- lock->read_counter++;
- ret_val = TRUE;
- }
- g_static_mutex_unlock (&lock->mutex);
- return ret_val;
-}
-
-/**
- * g_static_rw_lock_reader_unlock:
- * @lock: a #GStaticRWLock to unlock after reading.
- *
- * Unlocks @lock. If a thread waits to lock @lock for writing and all
- * locks for reading have been unlocked, the waiting thread is woken up
- * and can lock @lock for writing.
- **/
-void
-g_static_rw_lock_reader_unlock (GStaticRWLock* lock)
-{
- g_return_if_fail (lock);
-
- if (!g_threads_got_initialized)
- return;
-
- g_static_mutex_lock (&lock->mutex);
- lock->read_counter--;
- if (lock->read_counter == 0)
- g_static_rw_lock_signal (lock);
- g_static_mutex_unlock (&lock->mutex);
-}
-
-/**
- * g_static_rw_lock_writer_lock:
- * @lock: a #GStaticRWLock to lock for writing.
- *
- * Locks @lock for writing. If @lock is already locked for writing or
- * reading by other threads, this function will block until @lock is
- * completely unlocked and then lock @lock for writing. While this
- * functions waits to lock @lock, no other thread can lock @lock for
- * reading. When @lock is locked for writing, no other thread can lock
- * @lock (neither for reading nor writing). This lock has to be
- * unlocked by g_static_rw_lock_writer_unlock().
- **/
-void
-g_static_rw_lock_writer_lock (GStaticRWLock* lock)
-{
- g_return_if_fail (lock);
-
- if (!g_threads_got_initialized)
- return;
-
- g_static_mutex_lock (&lock->mutex);
- lock->want_to_write++;
- while (lock->have_writer || lock->read_counter)
- g_static_rw_lock_wait (&lock->write_cond, &lock->mutex);
- lock->want_to_write--;
- lock->have_writer = TRUE;
- g_static_mutex_unlock (&lock->mutex);
-}
-
-/**
- * g_static_rw_lock_writer_trylock:
- * @lock: a #GStaticRWLock to lock for writing.
- * @Returns: %TRUE, if @lock could be locked for writing.
- *
- * Tries to lock @lock for writing. If @lock is already locked (for
- * either reading or writing) by another thread, it immediately returns
- * %FALSE. Otherwise it locks @lock for writing and returns %TRUE. This
- * lock has to be unlocked by g_static_rw_lock_writer_unlock().
- **/
-gboolean
-g_static_rw_lock_writer_trylock (GStaticRWLock* lock)
-{
- gboolean ret_val = FALSE;
-
- g_return_val_if_fail (lock, FALSE);
-
- if (!g_threads_got_initialized)
- return TRUE;
-
- g_static_mutex_lock (&lock->mutex);
- if (!lock->have_writer && !lock->read_counter)
- {
- lock->have_writer = TRUE;
- ret_val = TRUE;
- }
- g_static_mutex_unlock (&lock->mutex);
- return ret_val;
-}
-
-/**
- * g_static_rw_lock_writer_unlock:
- * @lock: a #GStaticRWLock to unlock after writing.
- *
- * Unlocks @lock. If a thread is waiting to lock @lock for writing and
- * all locks for reading have been unlocked, the waiting thread is
- * woken up and can lock @lock for writing. If no thread is waiting to
- * lock @lock for writing, and some thread or threads are waiting to
- * lock @lock for reading, the waiting threads are woken up and can
- * lock @lock for reading.
- **/
-void
-g_static_rw_lock_writer_unlock (GStaticRWLock* lock)
-{
- g_return_if_fail (lock);
-
- if (!g_threads_got_initialized)
- return;
-
- g_static_mutex_lock (&lock->mutex);
- lock->have_writer = FALSE;
- g_static_rw_lock_signal (lock);
- g_static_mutex_unlock (&lock->mutex);
-}
-
-/**
- * g_static_rw_lock_free:
- * @lock: a #GStaticRWLock to be freed.
- *
- * Releases all resources allocated to @lock.
- *
- * You don't have to call this functions for a #GStaticRWLock with an
- * unbounded lifetime, i.e. objects declared 'static', but if you have
- * a #GStaticRWLock as a member of a structure, and the structure is
- * freed, you should also free the #GStaticRWLock.
- **/
-void
-g_static_rw_lock_free (GStaticRWLock* lock)
-{
- g_return_if_fail (lock);
-
- if (lock->read_cond)
- {
- g_cond_free (lock->read_cond);
- lock->read_cond = NULL;
- }
- if (lock->write_cond)
- {
- g_cond_free (lock->write_cond);
- lock->write_cond = NULL;
- }
- g_static_mutex_free (&lock->mutex);
-}
-
-/* Unsorted {{{1 ---------------------------------------------------------- */
-
-/**
- * g_thread_foreach
- * @thread_func: function to call for all GThread structures
- * @user_data: second argument to @thread_func
- *
- * Call @thread_func on all existing #GThread structures. Note that
- * threads may decide to exit while @thread_func is running, so
- * without intimate knowledge about the lifetime of foreign threads,
- * @thread_func shouldn't access the GThread* pointer passed in as
- * first argument. However, @thread_func will not be called for threads
- * which are known to have exited already.
- *
- * Due to thread lifetime checks, this function has an execution complexity
- * which is quadratic in the number of existing threads.
- *
- * Since: 2.10
- */
-void
-g_thread_foreach (GFunc thread_func,
- gpointer user_data)
-{
- GSList *slist = NULL;
- GRealThread *thread;
- g_return_if_fail (thread_func != NULL);
- /* snapshot the list of threads for iteration */
- G_LOCK (g_thread);
- for (thread = g_thread_all_threads; thread; thread = thread->next)
- slist = g_slist_prepend (slist, thread);
- G_UNLOCK (g_thread);
- /* walk the list, skipping non-existant threads */
- while (slist)
- {
- GSList *node = slist;
- slist = node->next;
- /* check whether the current thread still exists */
- G_LOCK (g_thread);
- for (thread = g_thread_all_threads; thread; thread = thread->next)
- if (thread == node->data)
- break;
- G_UNLOCK (g_thread);
- if (thread)
- thread_func (thread, user_data);
- g_slist_free_1 (node);
- }
-}
-
-/**
- * g_thread_get_initialized
- *
- * Indicates if g_thread_init() has been called.
- *
- * Returns: %TRUE if threads have been initialized.
- *
- * Since: 2.20
- */
-gboolean
-g_thread_get_initialized ()
-{
- return g_thread_supported ();
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * GAsyncQueue: thread pool implementation.
- * Copyright (C) 2000 Sebastian Wilhelmi; University of Karlsruhe
- *
- * 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 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.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gthreadpool.h"
-
-#include "gasyncqueue.h"
-#include "gmain.h"
-#include "gtestutils.h"
-#include "gtimer.h"
-
-/**
- * SECTION: thread_pools
- * @title: Thread Pools
- * @short_description: pools of threads to execute work concurrently
- * @see_also: <para> <variablelist> <varlistentry>
- * <term>#GThread</term> <listitem><para>GLib thread
- * system.</para></listitem> </varlistentry> </variablelist>
- * </para>
- *
- * Sometimes you wish to asynchronously fork out the execution of work
- * and continue working in your own thread. If that will happen often,
- * the overhead of starting and destroying a thread each time might be
- * too high. In such cases reusing already started threads seems like a
- * good idea. And it indeed is, but implementing this can be tedious
- * and error-prone.
- *
- * Therefore GLib provides thread pools for your convenience. An added
- * advantage is, that the threads can be shared between the different
- * subsystems of your program, when they are using GLib.
- *
- * To create a new thread pool, you use g_thread_pool_new(). It is
- * destroyed by g_thread_pool_free().
- *
- * If you want to execute a certain task within a thread pool, you call
- * g_thread_pool_push().
- *
- * To get the current number of running threads you call
- * g_thread_pool_get_num_threads(). To get the number of still
- * unprocessed tasks you call g_thread_pool_unprocessed(). To control
- * the maximal number of threads for a thread pool, you use
- * g_thread_pool_get_max_threads() and g_thread_pool_set_max_threads().
- *
- * Finally you can control the number of unused threads, that are kept
- * alive by GLib for future use. The current number can be fetched with
- * g_thread_pool_get_num_unused_threads(). The maximal number can be
- * controlled by g_thread_pool_get_max_unused_threads() and
- * g_thread_pool_set_max_unused_threads(). All currently unused threads
- * can be stopped by calling g_thread_pool_stop_unused_threads().
- **/
-
-#define DEBUG_MSG(x)
-/* #define DEBUG_MSG(args) g_printerr args ; g_printerr ("\n"); */
-
-typedef struct _GRealThreadPool GRealThreadPool;
-
-/**
- * GThreadPool:
- * @func: the function to execute in the threads of this pool
- * @user_data: the user data for the threads of this pool
- * @exclusive: are all threads exclusive to this pool
- *
- * The #GThreadPool struct represents a thread pool. It has three
- * public read-only members, but the underlying struct is bigger, so
- * you must not copy this struct.
- **/
-struct _GRealThreadPool
-{
- GThreadPool pool;
- GAsyncQueue* queue;
- GCond* cond;
- gint max_threads;
- gint num_threads;
- gboolean running;
- gboolean immediate;
- gboolean waiting;
- GCompareDataFunc sort_func;
- gpointer sort_user_data;
-};
-
-/* The following is just an address to mark the wakeup order for a
- * thread, it could be any address (as long, as it isn't a valid
- * GThreadPool address) */
-static const gpointer wakeup_thread_marker = (gpointer) &g_thread_pool_new;
-static gint wakeup_thread_serial = 0;
-
-/* Here all unused threads are waiting */
-static GAsyncQueue *unused_thread_queue = NULL;
-static gint unused_threads = 0;
-static gint max_unused_threads = 0;
-static gint kill_unused_threads = 0;
-static guint max_idle_time = 0;
-
-static void g_thread_pool_queue_push_unlocked (GRealThreadPool *pool,
- gpointer data);
-static void g_thread_pool_free_internal (GRealThreadPool *pool);
-static gpointer g_thread_pool_thread_proxy (gpointer data);
-static void g_thread_pool_start_thread (GRealThreadPool *pool,
- GError **error);
-static void g_thread_pool_wakeup_and_stop_all (GRealThreadPool *pool);
-static GRealThreadPool* g_thread_pool_wait_for_new_pool (void);
-static gpointer g_thread_pool_wait_for_new_task (GRealThreadPool *pool);
-
-static void
-g_thread_pool_queue_push_unlocked (GRealThreadPool *pool,
- gpointer data)
-{
- if (pool->sort_func)
- g_async_queue_push_sorted_unlocked (pool->queue,
- data,
- pool->sort_func,
- pool->sort_user_data);
- else
- g_async_queue_push_unlocked (pool->queue, data);
-}
-
-static GRealThreadPool*
-g_thread_pool_wait_for_new_pool (void)
-{
- GRealThreadPool *pool;
- gint local_wakeup_thread_serial;
- guint local_max_unused_threads;
- gint local_max_idle_time;
- gint last_wakeup_thread_serial;
- gboolean have_relayed_thread_marker = FALSE;
-
- local_max_unused_threads = g_atomic_int_get (&max_unused_threads);
- local_max_idle_time = g_atomic_int_get (&max_idle_time);
- last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
-
- g_atomic_int_inc (&unused_threads);
-
- do
- {
- if (g_atomic_int_get (&unused_threads) >= local_max_unused_threads)
- {
- /* If this is a superfluous thread, stop it. */
- pool = NULL;
- }
- else if (local_max_idle_time > 0)
- {
- /* If a maximal idle time is given, wait for the given time. */
- GTimeVal end_time;
-
- g_get_current_time (&end_time);
- g_time_val_add (&end_time, local_max_idle_time * 1000);
-
- DEBUG_MSG (("thread %p waiting in global pool for %f seconds.",
- g_thread_self (), local_max_idle_time / 1000.0));
-
- pool = g_async_queue_timed_pop (unused_thread_queue, &end_time);
- }
- else
- {
- /* If no maximal idle time is given, wait indefinitely. */
- DEBUG_MSG (("thread %p waiting in global pool.",
- g_thread_self ()));
- pool = g_async_queue_pop (unused_thread_queue);
- }
-
- if (pool == wakeup_thread_marker)
- {
- local_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
- if (last_wakeup_thread_serial == local_wakeup_thread_serial)
- {
- if (!have_relayed_thread_marker)
- {
- /* If this wakeup marker has been received for
- * the second time, relay it.
- */
- DEBUG_MSG (("thread %p relaying wakeup message to "
- "waiting thread with lower serial.",
- g_thread_self ()));
-
- g_async_queue_push (unused_thread_queue, wakeup_thread_marker);
- have_relayed_thread_marker = TRUE;
-
- /* If a wakeup marker has been relayed, this thread
- * will get out of the way for 100 microseconds to
- * avoid receiving this marker again. */
- g_usleep (100);
- }
- }
- else
- {
- if (g_atomic_int_exchange_and_add (&kill_unused_threads, -1) > 0)
- {
- pool = NULL;
- break;
- }
-
- DEBUG_MSG (("thread %p updating to new limits.",
- g_thread_self ()));
-
- local_max_unused_threads = g_atomic_int_get (&max_unused_threads);
- local_max_idle_time = g_atomic_int_get (&max_idle_time);
- last_wakeup_thread_serial = local_wakeup_thread_serial;
-
- have_relayed_thread_marker = FALSE;
- }
- }
- }
- while (pool == wakeup_thread_marker);
-
- g_atomic_int_add (&unused_threads, -1);
-
- return pool;
-}
-
-static gpointer
-g_thread_pool_wait_for_new_task (GRealThreadPool *pool)
-{
- gpointer task = NULL;
-
- if (pool->running || (!pool->immediate &&
- g_async_queue_length_unlocked (pool->queue) > 0))
- {
- /* This thread pool is still active. */
- if (pool->num_threads > pool->max_threads && pool->max_threads != -1)
- {
- /* This is a superfluous thread, so it goes to the global pool. */
- DEBUG_MSG (("superfluous thread %p in pool %p.",
- g_thread_self (), pool));
- }
- else if (pool->pool.exclusive)
- {
- /* Exclusive threads stay attached to the pool. */
- task = g_async_queue_pop_unlocked (pool->queue);
-
- DEBUG_MSG (("thread %p in exclusive pool %p waits for task "
- "(%d running, %d unprocessed).",
- g_thread_self (), pool, pool->num_threads,
- g_async_queue_length_unlocked (pool->queue)));
- }
- else
- {
- /* A thread will wait for new tasks for at most 1/2
- * second before going to the global pool.
- */
- GTimeVal end_time;
-
- g_get_current_time (&end_time);
- g_time_val_add (&end_time, G_USEC_PER_SEC / 2); /* 1/2 second */
-
- DEBUG_MSG (("thread %p in pool %p waits for up to a 1/2 second for task "
- "(%d running, %d unprocessed).",
- g_thread_self (), pool, pool->num_threads,
- g_async_queue_length_unlocked (pool->queue)));
-
- task = g_async_queue_timed_pop_unlocked (pool->queue, &end_time);
- }
- }
- else
- {
- /* This thread pool is inactive, it will no longer process tasks. */
- DEBUG_MSG (("pool %p not active, thread %p will go to global pool "
- "(running: %s, immediate: %s, len: %d).",
- pool, g_thread_self (),
- pool->running ? "true" : "false",
- pool->immediate ? "true" : "false",
- g_async_queue_length_unlocked (pool->queue)));
- }
-
- return task;
-}
-
-
-static gpointer
-g_thread_pool_thread_proxy (gpointer data)
-{
- GRealThreadPool *pool;
-
- pool = data;
-
- DEBUG_MSG (("thread %p started for pool %p.",
- g_thread_self (), pool));
-
- g_async_queue_lock (pool->queue);
-
- while (TRUE)
- {
- gpointer task;
-
- task = g_thread_pool_wait_for_new_task (pool);
- if (task)
- {
- if (pool->running || !pool->immediate)
- {
- /* A task was received and the thread pool is active, so
- * execute the function.
- */
- g_async_queue_unlock (pool->queue);
- DEBUG_MSG (("thread %p in pool %p calling func.",
- g_thread_self (), pool));
- pool->pool.func (task, pool->pool.user_data);
- g_async_queue_lock (pool->queue);
- }
- }
- else
- {
- /* No task was received, so this thread goes to the global
- * pool.
- */
- gboolean free_pool = FALSE;
-
- DEBUG_MSG (("thread %p leaving pool %p for global pool.",
- g_thread_self (), pool));
- pool->num_threads--;
-
- if (!pool->running)
- {
- if (!pool->waiting)
- {
- if (pool->num_threads == 0)
- {
- /* If the pool is not running and no other
- * thread is waiting for this thread pool to
- * finish and this is the last thread of this
- * pool, free the pool.
- */
- free_pool = TRUE;
- }
- else
- {
- /* If the pool is not running and no other
- * thread is waiting for this thread pool to
- * finish and this is not the last thread of
- * this pool and there are no tasks left in the
- * queue, wakeup the remaining threads.
- */
- if (g_async_queue_length_unlocked (pool->queue) ==
- - pool->num_threads)
- g_thread_pool_wakeup_and_stop_all (pool);
- }
- }
- else if (pool->immediate ||
- g_async_queue_length_unlocked (pool->queue) <= 0)
- {
- /* If the pool is not running and another thread is
- * waiting for this thread pool to finish and there
- * are either no tasks left or the pool shall stop
- * immediatly, inform the waiting thread of a change
- * of the thread pool state.
- */
- g_cond_broadcast (pool->cond);
- }
- }
-
- g_async_queue_unlock (pool->queue);
-
- if (free_pool)
- g_thread_pool_free_internal (pool);
-
- if ((pool = g_thread_pool_wait_for_new_pool ()) == NULL)
- break;
-
- g_async_queue_lock (pool->queue);
-
- DEBUG_MSG (("thread %p entering pool %p from global pool.",
- g_thread_self (), pool));
-
- /* pool->num_threads++ is not done here, but in
- * g_thread_pool_start_thread to make the new started thread
- * known to the pool, before itself can do it.
- */
- }
- }
-
- return NULL;
-}
-
-static void
-g_thread_pool_start_thread (GRealThreadPool *pool,
- GError **error)
-{
- gboolean success = FALSE;
-
- if (pool->num_threads >= pool->max_threads && pool->max_threads != -1)
- /* Enough threads are already running */
- return;
-
- g_async_queue_lock (unused_thread_queue);
-
- if (g_async_queue_length_unlocked (unused_thread_queue) < 0)
- {
- g_async_queue_push_unlocked (unused_thread_queue, pool);
- success = TRUE;
- }
-
- g_async_queue_unlock (unused_thread_queue);
-
- if (!success)
- {
- GError *local_error = NULL;
- /* No thread was found, we have to start a new one */
- g_thread_create (g_thread_pool_thread_proxy, pool, FALSE, &local_error);
-
- if (local_error)
- {
- g_propagate_error (error, local_error);
- return;
- }
- }
-
- /* See comment in g_thread_pool_thread_proxy as to why this is done
- * here and not there
- */
- pool->num_threads++;
-}
-
-/**
- * g_thread_pool_new:
- * @func: a function to execute in the threads of the new thread pool
- * @user_data: user data that is handed over to @func every time it
- * is called
- * @max_threads: the maximal number of threads to execute concurrently in
- * the new thread pool, -1 means no limit
- * @exclusive: should this thread pool be exclusive?
- * @error: return location for error
- *
- * This function creates a new thread pool.
- *
- * Whenever you call g_thread_pool_push(), either a new thread is
- * created or an unused one is reused. At most @max_threads threads
- * are running concurrently for this thread pool. @max_threads = -1
- * allows unlimited threads to be created for this thread pool. The
- * newly created or reused thread now executes the function @func with
- * the two arguments. The first one is the parameter to
- * g_thread_pool_push() and the second one is @user_data.
- *
- * The parameter @exclusive determines, whether the thread pool owns
- * all threads exclusive or whether the threads are shared
- * globally. If @exclusive is %TRUE, @max_threads threads are started
- * immediately and they will run exclusively for this thread pool until
- * it is destroyed by g_thread_pool_free(). If @exclusive is %FALSE,
- * threads are created, when needed and shared between all
- * non-exclusive thread pools. This implies that @max_threads may not
- * be -1 for exclusive thread pools.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report
- * errors. An error can only occur when @exclusive is set to %TRUE and
- * not all @max_threads threads could be created.
- *
- * Return value: the new #GThreadPool
- **/
-GThreadPool*
-g_thread_pool_new (GFunc func,
- gpointer user_data,
- gint max_threads,
- gboolean exclusive,
- GError **error)
-{
- GRealThreadPool *retval;
- G_LOCK_DEFINE_STATIC (init);
-
- g_return_val_if_fail (func, NULL);
- g_return_val_if_fail (!exclusive || max_threads != -1, NULL);
- g_return_val_if_fail (max_threads >= -1, NULL);
- g_return_val_if_fail (g_thread_supported (), NULL);
-
- retval = g_new (GRealThreadPool, 1);
-
- retval->pool.func = func;
- retval->pool.user_data = user_data;
- retval->pool.exclusive = exclusive;
- retval->queue = g_async_queue_new ();
- retval->cond = NULL;
- retval->max_threads = max_threads;
- retval->num_threads = 0;
- retval->running = TRUE;
- retval->sort_func = NULL;
- retval->sort_user_data = NULL;
-
- G_LOCK (init);
- if (!unused_thread_queue)
- unused_thread_queue = g_async_queue_new ();
- G_UNLOCK (init);
-
- if (retval->pool.exclusive)
- {
- g_async_queue_lock (retval->queue);
-
- while (retval->num_threads < retval->max_threads)
- {
- GError *local_error = NULL;
- g_thread_pool_start_thread (retval, &local_error);
- if (local_error)
- {
- g_propagate_error (error, local_error);
- break;
- }
- }
-
- g_async_queue_unlock (retval->queue);
- }
-
- return (GThreadPool*) retval;
-}
-
-/**
- * g_thread_pool_push:
- * @pool: a #GThreadPool
- * @data: a new task for @pool
- * @error: return location for error
- *
- * Inserts @data into the list of tasks to be executed by @pool. When
- * the number of currently running threads is lower than the maximal
- * allowed number of threads, a new thread is started (or reused) with
- * the properties given to g_thread_pool_new (). Otherwise @data stays
- * in the queue until a thread in this pool finishes its previous task
- * and processes @data.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report
- * errors. An error can only occur when a new thread couldn't be
- * created. In that case @data is simply appended to the queue of work
- * to do.
- **/
-void
-g_thread_pool_push (GThreadPool *pool,
- gpointer data,
- GError **error)
-{
- GRealThreadPool *real;
-
- real = (GRealThreadPool*) pool;
-
- g_return_if_fail (real);
- g_return_if_fail (real->running);
-
- g_async_queue_lock (real->queue);
-
- if (g_async_queue_length_unlocked (real->queue) >= 0)
- /* No thread is waiting in the queue */
- g_thread_pool_start_thread (real, error);
-
- g_thread_pool_queue_push_unlocked (real, data);
- g_async_queue_unlock (real->queue);
-}
-
-/**
- * g_thread_pool_set_max_threads:
- * @pool: a #GThreadPool
- * @max_threads: a new maximal number of threads for @pool
- * @error: return location for error
- *
- * Sets the maximal allowed number of threads for @pool. A value of -1
- * means, that the maximal number of threads is unlimited.
- *
- * Setting @max_threads to 0 means stopping all work for @pool. It is
- * effectively frozen until @max_threads is set to a non-zero value
- * again.
- *
- * A thread is never terminated while calling @func, as supplied by
- * g_thread_pool_new (). Instead the maximal number of threads only
- * has effect for the allocation of new threads in g_thread_pool_push().
- * A new thread is allocated, whenever the number of currently
- * running threads in @pool is smaller than the maximal number.
- *
- * @error can be %NULL to ignore errors, or non-%NULL to report
- * errors. An error can only occur when a new thread couldn't be
- * created.
- **/
-void
-g_thread_pool_set_max_threads (GThreadPool *pool,
- gint max_threads,
- GError **error)
-{
- GRealThreadPool *real;
- gint to_start;
-
- real = (GRealThreadPool*) pool;
-
- g_return_if_fail (real);
- g_return_if_fail (real->running);
- g_return_if_fail (!real->pool.exclusive || max_threads != -1);
- g_return_if_fail (max_threads >= -1);
-
- g_async_queue_lock (real->queue);
-
- real->max_threads = max_threads;
-
- if (pool->exclusive)
- to_start = real->max_threads - real->num_threads;
- else
- to_start = g_async_queue_length_unlocked (real->queue);
-
- for ( ; to_start > 0; to_start--)
- {
- GError *local_error = NULL;
-
- g_thread_pool_start_thread (real, &local_error);
- if (local_error)
- {
- g_propagate_error (error, local_error);
- break;
- }
- }
-
- g_async_queue_unlock (real->queue);
-}
-
-/**
- * g_thread_pool_get_max_threads:
- * @pool: a #GThreadPool
- *
- * Returns the maximal number of threads for @pool.
- *
- * Return value: the maximal number of threads
- **/
-gint
-g_thread_pool_get_max_threads (GThreadPool *pool)
-{
- GRealThreadPool *real;
- gint retval;
-
- real = (GRealThreadPool*) pool;
-
- g_return_val_if_fail (real, 0);
- g_return_val_if_fail (real->running, 0);
-
- g_async_queue_lock (real->queue);
- retval = real->max_threads;
- g_async_queue_unlock (real->queue);
-
- return retval;
-}
-
-/**
- * g_thread_pool_get_num_threads:
- * @pool: a #GThreadPool
- *
- * Returns the number of threads currently running in @pool.
- *
- * Return value: the number of threads currently running
- **/
-guint
-g_thread_pool_get_num_threads (GThreadPool *pool)
-{
- GRealThreadPool *real;
- guint retval;
-
- real = (GRealThreadPool*) pool;
-
- g_return_val_if_fail (real, 0);
- g_return_val_if_fail (real->running, 0);
-
- g_async_queue_lock (real->queue);
- retval = real->num_threads;
- g_async_queue_unlock (real->queue);
-
- return retval;
-}
-
-/**
- * g_thread_pool_unprocessed:
- * @pool: a #GThreadPool
- *
- * Returns the number of tasks still unprocessed in @pool.
- *
- * Return value: the number of unprocessed tasks
- **/
-guint
-g_thread_pool_unprocessed (GThreadPool *pool)
-{
- GRealThreadPool *real;
- gint unprocessed;
-
- real = (GRealThreadPool*) pool;
-
- g_return_val_if_fail (real, 0);
- g_return_val_if_fail (real->running, 0);
-
- unprocessed = g_async_queue_length (real->queue);
-
- return MAX (unprocessed, 0);
-}
-
-/**
- * g_thread_pool_free:
- * @pool: a #GThreadPool
- * @immediate: should @pool shut down immediately?
- * @wait_: should the function wait for all tasks to be finished?
- *
- * Frees all resources allocated for @pool.
- *
- * If @immediate is %TRUE, no new task is processed for
- * @pool. Otherwise @pool is not freed before the last task is
- * processed. Note however, that no thread of this pool is
- * interrupted, while processing a task. Instead at least all still
- * running threads can finish their tasks before the @pool is freed.
- *
- * If @wait_ is %TRUE, the functions does not return before all tasks
- * to be processed (dependent on @immediate, whether all or only the
- * currently running) are ready. Otherwise the function returns immediately.
- *
- * After calling this function @pool must not be used anymore.
- **/
-void
-g_thread_pool_free (GThreadPool *pool,
- gboolean immediate,
- gboolean wait_)
-{
- GRealThreadPool *real;
-
- real = (GRealThreadPool*) pool;
-
- g_return_if_fail (real);
- g_return_if_fail (real->running);
-
- /* If there's no thread allowed here, there is not much sense in
- * not stopping this pool immediately, when it's not empty
- */
- g_return_if_fail (immediate ||
- real->max_threads != 0 ||
- g_async_queue_length (real->queue) == 0);
-
- g_async_queue_lock (real->queue);
-
- real->running = FALSE;
- real->immediate = immediate;
- real->waiting = wait_;
-
- if (wait_)
- {
- real->cond = g_cond_new ();
-
- while (g_async_queue_length_unlocked (real->queue) != -real->num_threads &&
- !(immediate && real->num_threads == 0))
- g_cond_wait (real->cond, _g_async_queue_get_mutex (real->queue));
- }
-
- if (immediate || g_async_queue_length_unlocked (real->queue) == -real->num_threads)
- {
- /* No thread is currently doing something (and nothing is left
- * to process in the queue)
- */
- if (real->num_threads == 0)
- {
- /* No threads left, we clean up */
- g_async_queue_unlock (real->queue);
- g_thread_pool_free_internal (real);
- return;
- }
-
- g_thread_pool_wakeup_and_stop_all (real);
- }
-
- /* The last thread should cleanup the pool */
- real->waiting = FALSE;
- g_async_queue_unlock (real->queue);
-}
-
-static void
-g_thread_pool_free_internal (GRealThreadPool* pool)
-{
- g_return_if_fail (pool);
- g_return_if_fail (pool->running == FALSE);
- g_return_if_fail (pool->num_threads == 0);
-
- g_async_queue_unref (pool->queue);
-
- if (pool->cond)
- g_cond_free (pool->cond);
-
- g_free (pool);
-}
-
-static void
-g_thread_pool_wakeup_and_stop_all (GRealThreadPool* pool)
-{
- guint i;
-
- g_return_if_fail (pool);
- g_return_if_fail (pool->running == FALSE);
- g_return_if_fail (pool->num_threads != 0);
-
- pool->immediate = TRUE;
-
- for (i = 0; i < pool->num_threads; i++)
- g_thread_pool_queue_push_unlocked (pool, GUINT_TO_POINTER (1));
-}
-
-/**
- * g_thread_pool_set_max_unused_threads:
- * @max_threads: maximal number of unused threads
- *
- * Sets the maximal number of unused threads to @max_threads. If
- * @max_threads is -1, no limit is imposed on the number of unused
- * threads.
- **/
-void
-g_thread_pool_set_max_unused_threads (gint max_threads)
-{
- g_return_if_fail (max_threads >= -1);
-
- g_atomic_int_set (&max_unused_threads, max_threads);
-
- if (max_threads != -1)
- {
- max_threads -= g_atomic_int_get (&unused_threads);
- if (max_threads < 0)
- {
- g_atomic_int_set (&kill_unused_threads, -max_threads);
- g_atomic_int_inc (&wakeup_thread_serial);
-
- g_async_queue_lock (unused_thread_queue);
-
- do
- {
- g_async_queue_push_unlocked (unused_thread_queue,
- wakeup_thread_marker);
- }
- while (++max_threads);
-
- g_async_queue_unlock (unused_thread_queue);
- }
- }
-}
-
-/**
- * g_thread_pool_get_max_unused_threads:
- *
- * Returns the maximal allowed number of unused threads.
- *
- * Return value: the maximal number of unused threads
- **/
-gint
-g_thread_pool_get_max_unused_threads (void)
-{
- return g_atomic_int_get (&max_unused_threads);
-}
-
-/**
- * g_thread_pool_get_num_unused_threads:
- *
- * Returns the number of currently unused threads.
- *
- * Return value: the number of currently unused threads
- **/
-guint
-g_thread_pool_get_num_unused_threads (void)
-{
- return g_atomic_int_get (&unused_threads);
-}
-
-/**
- * g_thread_pool_stop_unused_threads:
- *
- * Stops all currently unused threads. This does not change the
- * maximal number of unused threads. This function can be used to
- * regularly stop all unused threads e.g. from g_timeout_add().
- **/
-void
-g_thread_pool_stop_unused_threads (void)
-{
- guint oldval;
-
- oldval = g_thread_pool_get_max_unused_threads ();
-
- g_thread_pool_set_max_unused_threads (0);
- g_thread_pool_set_max_unused_threads (oldval);
-}
-
-/**
- * g_thread_pool_set_sort_function:
- * @pool: a #GThreadPool
- * @func: the #GCompareDataFunc used to sort the list of tasks.
- * This function is passed two tasks. It should return
- * 0 if the order in which they are handled does not matter,
- * a negative value if the first task should be processed before
- * the second or a positive value if the second task should be
- * processed first.
- * @user_data: user data passed to @func.
- *
- * Sets the function used to sort the list of tasks. This allows the
- * tasks to be processed by a priority determined by @func, and not
- * just in the order in which they were added to the pool.
- *
- * Note, if the maximum number of threads is more than 1, the order
- * that threads are executed can not be guranteed 100%. Threads are
- * scheduled by the operating system and are executed at random. It
- * cannot be assumed that threads are executed in the order they are
- * created.
- *
- * Since: 2.10
- **/
-void
-g_thread_pool_set_sort_function (GThreadPool *pool,
- GCompareDataFunc func,
- gpointer user_data)
-{
- GRealThreadPool *real;
-
- real = (GRealThreadPool*) pool;
-
- g_return_if_fail (real);
- g_return_if_fail (real->running);
-
- g_async_queue_lock (real->queue);
-
- real->sort_func = func;
- real->sort_user_data = user_data;
-
- if (func)
- g_async_queue_sort_unlocked (real->queue,
- real->sort_func,
- real->sort_user_data);
-
- g_async_queue_unlock (real->queue);
-}
-
-/**
- * g_thread_pool_set_max_idle_time:
- * @interval: the maximum @interval (1/1000ths of a second) a thread
- * can be idle.
- *
- * This function will set the maximum @interval that a thread waiting
- * in the pool for new tasks can be idle for before being
- * stopped. This function is similar to calling
- * g_thread_pool_stop_unused_threads() on a regular timeout, except,
- * this is done on a per thread basis.
- *
- * By setting @interval to 0, idle threads will not be stopped.
- *
- * This function makes use of g_async_queue_timed_pop () using
- * @interval.
- *
- * Since: 2.10
- **/
-void
-g_thread_pool_set_max_idle_time (guint interval)
-{
- guint i;
-
- g_atomic_int_set (&max_idle_time, interval);
-
- i = g_atomic_int_get (&unused_threads);
- if (i > 0)
- {
- g_atomic_int_inc (&wakeup_thread_serial);
- g_async_queue_lock (unused_thread_queue);
-
- do
- {
- g_async_queue_push_unlocked (unused_thread_queue,
- wakeup_thread_marker);
- }
- while (--i);
-
- g_async_queue_unlock (unused_thread_queue);
- }
-}
-
-/**
- * g_thread_pool_get_max_idle_time:
- *
- * This function will return the maximum @interval that a thread will
- * wait in the thread pool for new tasks before being stopped.
- *
- * If this function returns 0, threads waiting in the thread pool for
- * new work are not stopped.
- *
- * Return value: the maximum @interval to wait for new tasks in the
- * thread pool before stopping the thread (1/1000ths of a second).
- *
- * Since: 2.10
- **/
-guint
-g_thread_pool_get_max_idle_time (void)
-{
- return g_atomic_int_get (&max_idle_time);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-#include "glibconfig.h"
-
-#include <stdlib.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-
-#include <sys/time.h>
-#include <time.h>
-#ifndef G_OS_WIN32
-#include <errno.h>
-#endif /* G_OS_WIN32 */
-
-#ifdef G_OS_WIN32
-#include <windows.h>
-#endif /* G_OS_WIN32 */
-
-#include "gtimer.h"
-
-#include "gmem.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gthread.h"
-
-/**
- * SECTION: timers
- * @title: Timers
- * @short_description: keep track of elapsed time
- *
- * #GTimer records a start time, and counts microseconds elapsed since
- * that time. This is done somewhat differently on different platforms,
- * and can be tricky to get exactly right, so #GTimer provides a
- * portable/convenient interface.
- *
- * <note><para>
- * #GTimer uses a higher-quality clock when thread support is available.
- * Therefore, calling g_thread_init() while timers are running may lead to
- * unreliable results. It is best to call g_thread_init() before starting any
- * timers, if you are using threads at all.
- * </para></note>
- **/
-
-#define G_NSEC_PER_SEC 1000000000
-
-#define GETTIME(v) (v = g_thread_gettime ())
-
-/**
- * GTimer:
- *
- * Opaque datatype that records a start time.
- **/
-struct _GTimer
-{
- guint64 start;
- guint64 end;
-
- guint active : 1;
-};
-
-/**
- * g_timer_new:
- * @Returns: a new #GTimer.
- *
- * Creates a new timer, and starts timing (i.e. g_timer_start() is
- * implicitly called for you).
- **/
-GTimer*
-g_timer_new (void)
-{
- GTimer *timer;
-
- timer = g_new (GTimer, 1);
- timer->active = TRUE;
-
- GETTIME (timer->start);
-
- return timer;
-}
-
-/**
- * g_timer_destroy:
- * @timer: a #GTimer to destroy.
- *
- * Destroys a timer, freeing associated resources.
- **/
-void
-g_timer_destroy (GTimer *timer)
-{
- g_return_if_fail (timer != NULL);
-
- g_free (timer);
-}
-
-/**
- * g_timer_start:
- * @timer: a #GTimer.
- *
- * Marks a start time, so that future calls to g_timer_elapsed() will
- * report the time since g_timer_start() was called. g_timer_new()
- * automatically marks the start time, so no need to call
- * g_timer_start() immediately after creating the timer.
- **/
-void
-g_timer_start (GTimer *timer)
-{
- g_return_if_fail (timer != NULL);
-
- timer->active = TRUE;
-
- GETTIME (timer->start);
-}
-
-/**
- * g_timer_stop:
- * @timer: a #GTimer.
- *
- * Marks an end time, so calls to g_timer_elapsed() will return the
- * difference between this end time and the start time.
- **/
-void
-g_timer_stop (GTimer *timer)
-{
- g_return_if_fail (timer != NULL);
-
- timer->active = FALSE;
-
- GETTIME (timer->end);
-}
-
-/**
- * g_timer_reset:
- * @timer: a #GTimer.
- *
- * This function is useless; it's fine to call g_timer_start() on an
- * already-started timer to reset the start time, so g_timer_reset()
- * serves no purpose.
- **/
-void
-g_timer_reset (GTimer *timer)
-{
- g_return_if_fail (timer != NULL);
-
- GETTIME (timer->start);
-}
-
-/**
- * g_timer_continue:
- * @timer: a #GTimer.
- *
- * Resumes a timer that has previously been stopped with
- * g_timer_stop(). g_timer_stop() must be called before using this
- * function.
- *
- * Since: 2.4
- **/
-void
-g_timer_continue (GTimer *timer)
-{
- guint64 elapsed;
-
- g_return_if_fail (timer != NULL);
- g_return_if_fail (timer->active == FALSE);
-
- /* Get elapsed time and reset timer start time
- * to the current time minus the previously
- * elapsed interval.
- */
-
- elapsed = timer->end - timer->start;
-
- GETTIME (timer->start);
-
- timer->start -= elapsed;
-
- timer->active = TRUE;
-}
-
-/**
- * g_timer_elapsed:
- * @timer: a #GTimer.
- * @microseconds: return location for the fractional part of seconds
- * elapsed, in microseconds (that is, the total number
- * of microseconds elapsed, modulo 1000000), or %NULL
- * @Returns: seconds elapsed as a floating point value, including any
- * fractional part.
- *
- * If @timer has been started but not stopped, obtains the time since
- * the timer was started. If @timer has been stopped, obtains the
- * elapsed time between the time it was started and the time it was
- * stopped. The return value is the number of seconds elapsed,
- * including any fractional part. The @microseconds out parameter is
- * essentially useless.
- *
- * <warning><para>
- * Calling initialization functions, in particular g_thread_init(), while a
- * timer is running will cause invalid return values from this function.
- * </para></warning>
- **/
-gdouble
-g_timer_elapsed (GTimer *timer,
- gulong *microseconds)
-{
- gdouble total;
- gint64 elapsed;
-
- g_return_val_if_fail (timer != NULL, 0);
-
- if (timer->active)
- GETTIME (timer->end);
-
- elapsed = timer->end - timer->start;
-
- total = elapsed / 1e9;
-
- if (microseconds)
- *microseconds = (elapsed / 1000) % 1000000;
-
- return total;
-}
-
-void
-g_usleep (gulong microseconds)
-{
-#ifdef G_OS_WIN32
- Sleep (microseconds / 1000);
-#else /* !G_OS_WIN32 */
-# ifdef HAVE_NANOSLEEP
- struct timespec request, remaining;
- request.tv_sec = microseconds / G_USEC_PER_SEC;
- request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
- while (nanosleep (&request, &remaining) == -1 && errno == EINTR)
- request = remaining;
-# else /* !HAVE_NANOSLEEP */
-# ifdef HAVE_NSLEEP
- /* on AIX, nsleep is analogous to nanosleep */
- struct timespec request, remaining;
- request.tv_sec = microseconds / G_USEC_PER_SEC;
- request.tv_nsec = 1000 * (microseconds % G_USEC_PER_SEC);
- while (nsleep (&request, &remaining) == -1 && errno == EINTR)
- request = remaining;
-# else /* !HAVE_NSLEEP */
- if (g_thread_supported ())
- {
- static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
- static GCond* cond = NULL;
- GTimeVal end_time;
-
- g_get_current_time (&end_time);
- if (microseconds > G_MAXLONG)
- {
- microseconds -= G_MAXLONG;
- g_time_val_add (&end_time, G_MAXLONG);
- }
- g_time_val_add (&end_time, microseconds);
-
- g_static_mutex_lock (&mutex);
-
- if (!cond)
- cond = g_cond_new ();
-
- while (g_cond_timed_wait (cond, g_static_mutex_get_mutex (&mutex),
- &end_time))
- /* do nothing */;
-
- g_static_mutex_unlock (&mutex);
- }
- else
- {
- struct timeval tv;
- tv.tv_sec = microseconds / G_USEC_PER_SEC;
- tv.tv_usec = microseconds % G_USEC_PER_SEC;
- select(0, NULL, NULL, NULL, &tv);
- }
-# endif /* !HAVE_NSLEEP */
-# endif /* !HAVE_NANOSLEEP */
-#endif /* !G_OS_WIN32 */
-}
-
-/**
- * g_time_val_add:
- * @time_: a #GTimeVal
- * @microseconds: number of microseconds to add to @time
- *
- * Adds the given number of microseconds to @time_. @microseconds can
- * also be negative to decrease the value of @time_.
- **/
-void
-g_time_val_add (GTimeVal *time_, glong microseconds)
-{
- g_return_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC);
-
- if (microseconds >= 0)
- {
- time_->tv_usec += microseconds % G_USEC_PER_SEC;
- time_->tv_sec += microseconds / G_USEC_PER_SEC;
- if (time_->tv_usec >= G_USEC_PER_SEC)
- {
- time_->tv_usec -= G_USEC_PER_SEC;
- time_->tv_sec++;
- }
- }
- else
- {
- microseconds *= -1;
- time_->tv_usec -= microseconds % G_USEC_PER_SEC;
- time_->tv_sec -= microseconds / G_USEC_PER_SEC;
- if (time_->tv_usec < 0)
- {
- time_->tv_usec += G_USEC_PER_SEC;
- time_->tv_sec--;
- }
- }
-}
-
-/* converts a broken down date representation, relative to UTC, to
- * a timestamp; it uses timegm() if it's available.
- */
-static time_t
-mktime_utc (struct tm *tm)
-{
- time_t retval;
-
-#ifndef HAVE_TIMEGM
- static const gint days_before[] =
- {
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
- };
-#endif
-
-#ifndef HAVE_TIMEGM
- if (tm->tm_mon < 0 || tm->tm_mon > 11)
- return (time_t) -1;
-
- retval = (tm->tm_year - 70) * 365;
- retval += (tm->tm_year - 68) / 4;
- retval += days_before[tm->tm_mon] + tm->tm_mday - 1;
-
- if (tm->tm_year % 4 == 0 && tm->tm_mon < 2)
- retval -= 1;
-
- retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec;
-#else
- retval = timegm (tm);
-#endif /* !HAVE_TIMEGM */
-
- return retval;
-}
-
-/**
- * g_time_val_from_iso8601:
- * @iso_date: an ISO 8601 encoded date string
- * @time_: a #GTimeVal
- *
- * Converts a string containing an ISO 8601 encoded date and time
- * to a #GTimeVal and puts it into @time_.
- *
- * Return value: %TRUE if the conversion was successful.
- *
- * Since: 2.12
- */
-gboolean
-g_time_val_from_iso8601 (const gchar *iso_date,
- GTimeVal *time_)
-{
- struct tm tm = {0};
- long val;
-
- g_return_val_if_fail (iso_date != NULL, FALSE);
- g_return_val_if_fail (time_ != NULL, FALSE);
-
- /* Ensure that the first character is a digit,
- * the first digit of the date, otherwise we don't
- * have an ISO 8601 date */
- while (g_ascii_isspace (*iso_date))
- iso_date++;
-
- if (*iso_date == '\0')
- return FALSE;
-
- if (!g_ascii_isdigit (*iso_date) && *iso_date != '-' && *iso_date != '+')
- return FALSE;
-
- val = strtoul (iso_date, (char **)&iso_date, 10);
- if (*iso_date == '-')
- {
- /* YYYY-MM-DD */
- tm.tm_year = val - 1900;
- iso_date++;
- tm.tm_mon = strtoul (iso_date, (char **)&iso_date, 10) - 1;
-
- if (*iso_date++ != '-')
- return FALSE;
-
- tm.tm_mday = strtoul (iso_date, (char **)&iso_date, 10);
- }
- else
- {
- /* YYYYMMDD */
- tm.tm_mday = val % 100;
- tm.tm_mon = (val % 10000) / 100 - 1;
- tm.tm_year = val / 10000 - 1900;
- }
-
- if (*iso_date != 'T')
- {
- /* Date only */
- if (*iso_date == '\0')
- return TRUE;
- return FALSE;
- }
-
- iso_date++;
-
- /* If there is a 'T' then there has to be a time */
- if (!g_ascii_isdigit (*iso_date))
- return FALSE;
-
- val = strtoul (iso_date, (char **)&iso_date, 10);
- if (*iso_date == ':')
- {
- /* hh:mm:ss */
- tm.tm_hour = val;
- iso_date++;
- tm.tm_min = strtoul (iso_date, (char **)&iso_date, 10);
-
- if (*iso_date++ != ':')
- return FALSE;
-
- tm.tm_sec = strtoul (iso_date, (char **)&iso_date, 10);
- }
- else
- {
- /* hhmmss */
- tm.tm_sec = val % 100;
- tm.tm_min = (val % 10000) / 100;
- tm.tm_hour = val / 10000;
- }
-
- time_->tv_usec = 0;
-
- if (*iso_date == ',' || *iso_date == '.')
- {
- glong mul = 100000;
-
- while (g_ascii_isdigit (*++iso_date))
- {
- time_->tv_usec += (*iso_date - '0') * mul;
- mul /= 10;
- }
- }
-
- /* Now parse the offset and convert tm to a time_t */
- if (*iso_date == 'Z')
- {
- iso_date++;
- time_->tv_sec = mktime_utc (&tm);
- }
- else if (*iso_date == '+' || *iso_date == '-')
- {
- gint sign = (*iso_date == '+') ? -1 : 1;
-
- val = strtoul (iso_date + 1, (char **)&iso_date, 10);
-
- if (*iso_date == ':')
- val = 60 * val + strtoul (iso_date + 1, (char **)&iso_date, 10);
- else
- val = 60 * (val / 100) + (val % 100);
-
- time_->tv_sec = mktime_utc (&tm) + (time_t) (60 * val * sign);
- }
- else
- {
- /* No "Z" or offset, so local time */
- tm.tm_isdst = -1; /* locale selects DST */
- time_->tv_sec = mktime (&tm);
- }
-
- while (g_ascii_isspace (*iso_date))
- iso_date++;
-
- return *iso_date == '\0';
-}
-
-/**
- * g_time_val_to_iso8601:
- * @time_: a #GTimeVal
- *
- * Converts @time_ into an ISO 8601 encoded string, relative to the
- * Coordinated Universal Time (UTC).
- *
- * Return value: a newly allocated string containing an ISO 8601 date
- *
- * Since: 2.12
- */
-gchar *
-g_time_val_to_iso8601 (GTimeVal *time_)
-{
- gchar *retval;
- struct tm *tm;
-#ifdef HAVE_GMTIME_R
- struct tm tm_;
-#endif
- time_t secs;
-
- g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
-
- secs = time_->tv_sec;
-#ifdef _WIN32
- tm = gmtime (&secs);
-#else
-#ifdef HAVE_GMTIME_R
- tm = gmtime_r (&secs, &tm_);
-#else
- tm = gmtime (&secs);
-#endif
-#endif
-
- if (time_->tv_usec != 0)
- {
- /* ISO 8601 date and time format, with fractionary seconds:
- * YYYY-MM-DDTHH:MM:SS.MMMMMMZ
- */
- retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ",
- tm->tm_year + 1900,
- tm->tm_mon + 1,
- tm->tm_mday,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec,
- time_->tv_usec);
- }
- else
- {
- /* ISO 8601 date and time format:
- * YYYY-MM-DDTHH:MM:SSZ
- */
- retval = g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ",
- tm->tm_year + 1900,
- tm->tm_mon + 1,
- tm->tm_mday,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec);
- }
-
- return retval;
-}
+++ /dev/null
-/*
- * Copyright © 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-/* Prologue {{{1 */
-
-#include "gtimezoneprivate.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-
-#include "gmappedfile.h"
-#include "gtestutils.h"
-#include "gfileutils.h"
-#include "gstrfuncs.h"
-#include "ghash.h"
-#include "gthread.h"
-#include "gbuffer.h"
-
-/**
- * SECTION:timezone
- * @title: GTimeZone
- * @short_description: A structure representing a time zone
- * @see_also: #GDateTime
- *
- * #GTimeZone is a structure that represents a time zone, at no
- * particular point in time. It is refcounted and immutable.
- *
- * #GTimeZone is available since GLib 2.26.
- */
-
-/**
- * GTimeZone:
- *
- * #GDateTime is an opaque structure whose members cannot be accessed
- * directly.
- *
- * Since: 2.26
- **/
-
-/* zoneinfo file format {{{1 */
-
-/* unaligned */
-typedef struct { gchar bytes[8]; } gint64_be;
-typedef struct { gchar bytes[4]; } gint32_be;
-typedef struct { gchar bytes[4]; } guint32_be;
-
-static inline gint64 gint64_from_be (const gint64_be be) {
- gint64 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT64_FROM_BE (tmp);
-}
-
-static inline gint32 gint32_from_be (const gint32_be be) {
- gint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GINT32_FROM_BE (tmp);
-}
-
-static inline guint32 guint32_from_be (const guint32_be be) {
- guint32 tmp; memcpy (&tmp, &be, sizeof tmp); return GUINT32_FROM_BE (tmp);
-}
-
-struct tzhead
-{
- gchar tzh_magic[4];
- gchar tzh_version;
- guchar tzh_reserved[15];
-
- guint32_be tzh_ttisgmtcnt;
- guint32_be tzh_ttisstdcnt;
- guint32_be tzh_leapcnt;
- guint32_be tzh_timecnt;
- guint32_be tzh_typecnt;
- guint32_be tzh_charcnt;
-};
-
-struct ttinfo
-{
- gint32_be tt_gmtoff;
- guint8 tt_isdst;
- guint8 tt_abbrind;
-};
-
-/* GTimeZone structure and lifecycle {{{1 */
-struct _GTimeZone
-{
- gchar *name;
-
- GBuffer *zoneinfo;
-
- const struct tzhead *header;
- const struct ttinfo *infos;
- const gint64_be *trans;
- const guint8 *indices;
- const gchar *abbrs;
- gint timecnt;
-
- gint ref_count;
-};
-
-G_LOCK_DEFINE_STATIC (time_zones);
-static GHashTable/*<string?, GTimeZone>*/ *time_zones;
-
-static guint
-g_str_hash0 (gconstpointer data)
-{
- return data ? g_str_hash (data) : 0;
-}
-
-static gboolean
-g_str_equal0 (gconstpointer a,
- gconstpointer b)
-{
- if (a == b)
- return TRUE;
-
- if (!a || !b)
- return FALSE;
-
- return g_str_equal (a, b);
-}
-
-/**
- * g_time_zone_unref:
- * @tz: a #GTimeZone
- *
- * Decreases the reference count on @tz.
- *
- * Since: 2.26
- **/
-void
-g_time_zone_unref (GTimeZone *tz)
-{
- g_assert (tz->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&tz->ref_count))
- {
- G_LOCK(time_zones);
- g_hash_table_remove (time_zones, tz->name);
- G_UNLOCK(time_zones);
-
- if (tz->zoneinfo)
- g_buffer_unref (tz->zoneinfo);
-
- g_free (tz->name);
-
- g_slice_free (GTimeZone, tz);
- }
-}
-
-/**
- * g_time_zone_ref:
- * @tz: a #GTimeZone
- *
- * Increases the reference count on @tz.
- *
- * Returns: a new reference to @tz.
- *
- * Since: 2.26
- **/
-GTimeZone *
-g_time_zone_ref (GTimeZone *tz)
-{
- g_assert (tz->ref_count > 0);
-
- g_atomic_int_inc (&tz->ref_count);
-
- return tz;
-}
-
-/* fake zoneinfo creation (for RFC3339/ISO 8601 timezones) {{{1 */
-/*
- * parses strings of the form 'hh' 'hhmm' or 'hh:mm' where:
- * - hh is 00 to 23
- * - mm is 00 to 59
- */
-gboolean
-parse_time (const gchar *time,
- gint32 *offset)
-{
- if (*time < '0' || '2' < *time)
- return FALSE;
-
- *offset = 10 * 60 * 60 * (*time++ - '0');
-
- if (*time < '0' || '9' < *time)
- return FALSE;
-
- *offset += 60 * 60 * (*time++ - '0');
-
- if (*offset > 23 * 60 * 60)
- return FALSE;
-
- if (*time == '\0')
- return TRUE;
-
- if (*time == ':')
- time++;
-
- if (*time < '0' || '5' < *time)
- return FALSE;
-
- *offset += 10 * 60 * (*time++ - '0');
-
- if (*time < '0' || '9' < *time)
- return FALSE;
-
- *offset += 60 * (*time++ - '0');
-
- return *time == '\0';
-}
-
-gboolean
-parse_constant_offset (const gchar *name,
- gint32 *offset)
-{
- switch (*name++)
- {
- case 'Z':
- *offset = 0;
- return !*name;
-
- case '+':
- return parse_time (name, offset);
-
- case '-':
- if (parse_time (name, offset))
- {
- *offset = -*offset;
- return TRUE;
- }
-
- default:
- return FALSE;
- }
-}
-
-static GBuffer *
-zone_for_constant_offset (const gchar *name)
-{
- const gchar fake_zoneinfo_headers[] =
- "TZif" "2..." "...." "...." "...."
- "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0"
- "TZif" "2..." "...." "...." "...."
- "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\1" "\0\0\0\7";
- struct {
- struct tzhead headers[2];
- struct ttinfo info;
- gchar abbr[8];
- } *fake;
- gint32 offset;
-
- if (name == NULL || !parse_constant_offset (name, &offset))
- return NULL;
-
- offset = GINT32_TO_BE (offset);
-
- fake = g_malloc (sizeof *fake);
- memcpy (fake, fake_zoneinfo_headers, sizeof fake_zoneinfo_headers);
- memcpy (&fake->info.tt_gmtoff, &offset, sizeof offset);
- fake->info.tt_isdst = FALSE;
- fake->info.tt_abbrind = 0;
- strcpy (fake->abbr, name);
-
- return g_buffer_new_take_data (fake, sizeof *fake);
-}
-
-/* Construction {{{1 */
-/**
- * g_time_zone_new:
- * @identifier: (allow-none): a timezone identifier
- *
- * Creates a #GTimeZone corresponding to @identifier.
- *
- * @identifier can either be an RFC3339/ISO 8601 time offset or
- * something that would pass as a valid value for the
- * <varname>TZ</varname> environment variable (including %NULL).
- *
- * Valid RFC3339 time offsets are <literal>"Z"</literal> (for UTC) or
- * <literal>"±hh:mm"</literal>. ISO 8601 additionally specifies
- * <literal>"±hhmm"</literal> and <literal>"±hh"</literal>.
- *
- * The <varname>TZ</varname> environment variable typically corresponds
- * to the name of a file in the zoneinfo database, but there are many
- * other possibilities. Note that those other possibilities are not
- * currently implemented, but are planned.
- *
- * g_time_zone_new_local() calls this function with the value of the
- * <varname>TZ</varname> environment variable. This function itself is
- * independent of the value of <varname>TZ</varname>, but if @identifier
- * is %NULL then <filename>/etc/localtime</filename> will be consulted
- * to discover the correct timezone.
- *
- * See <ulink
- * url='http://tools.ietf.org/html/rfc3339#section-5.6'>RFC3339
- * §5.6</ulink> for a precise definition of valid RFC3339 time offsets
- * (the <varname>time-offset</varname> expansion) and ISO 8601 for the
- * full list of valid time offsets. See <ulink
- * url='http://www.gnu.org/s/libc/manual/html_node/TZ-Variable.html'>The
- * GNU C Library manual</ulink> for an explanation of the possible
- * values of the <varname>TZ</varname> environment variable.
- *
- * You should release the return value by calling g_time_zone_unref()
- * when you are done with it.
- *
- * Returns: the requested timezone
- *
- * Since: 2.26
- **/
-GTimeZone *
-g_time_zone_new (const gchar *identifier)
-{
- GTimeZone *tz;
-
- G_LOCK (time_zones);
- if (time_zones == NULL)
- time_zones = g_hash_table_new (g_str_hash0,
- g_str_equal0);
-
- tz = g_hash_table_lookup (time_zones, identifier);
- if (tz == NULL)
- {
- tz = g_slice_new0 (GTimeZone);
- tz->name = g_strdup (identifier);
- tz->ref_count = 0;
-
- tz->zoneinfo = zone_for_constant_offset (identifier);
-
- if (tz->zoneinfo == NULL)
- {
- gchar *filename;
-
- if (identifier != NULL)
- {
- const gchar *tzdir;
-
- tzdir = getenv ("TZDIR");
- if (tzdir == NULL)
- tzdir = "/usr/share/zoneinfo";
-
- filename = g_build_filename (tzdir, identifier, NULL);
- }
- else
- filename = g_strdup ("/etc/localtime");
-
- tz->zoneinfo = (GBuffer *) g_mapped_file_new (filename, FALSE, NULL);
- g_free (filename);
- }
-
- if (tz->zoneinfo != NULL)
- {
- const struct tzhead *header = tz->zoneinfo->data;
- gsize size = tz->zoneinfo->size;
-
- /* we only bother to support version 2 */
- if (size < sizeof (struct tzhead) || memcmp (header, "TZif2", 5))
- {
- g_buffer_unref (tz->zoneinfo);
- tz->zoneinfo = NULL;
- }
- else
- {
- gint typecnt;
-
- /* we trust the file completely. */
- tz->header = (const struct tzhead *)
- (((const gchar *) (header + 1)) +
- guint32_from_be(header->tzh_ttisgmtcnt) +
- guint32_from_be(header->tzh_ttisstdcnt) +
- 8 * guint32_from_be(header->tzh_leapcnt) +
- 5 * guint32_from_be(header->tzh_timecnt) +
- 6 * guint32_from_be(header->tzh_typecnt) +
- guint32_from_be(header->tzh_charcnt));
-
- typecnt = guint32_from_be (tz->header->tzh_typecnt);
- tz->timecnt = guint32_from_be (tz->header->tzh_timecnt);
- tz->trans = (gconstpointer) (tz->header + 1);
- tz->indices = (gconstpointer) (tz->trans + tz->timecnt);
- tz->infos = (gconstpointer) (tz->indices + tz->timecnt);
- tz->abbrs = (gconstpointer) (tz->infos + typecnt);
- }
- }
-
- g_hash_table_insert (time_zones, tz->name, tz);
- }
- g_atomic_int_inc (&tz->ref_count);
- G_UNLOCK (time_zones);
-
- return tz;
-}
-
-/**
- * g_time_zone_new_utc:
- *
- * Creates a #GTimeZone corresponding to UTC.
- *
- * This is equivalent to calling g_time_zone_new() with a value like
- * "Z", "UTC", "+00", etc.
- *
- * You should release the return value by calling g_time_zone_unref()
- * when you are done with it.
- *
- * Returns: the universal timezone
- *
- * Since: 2.26
- **/
-GTimeZone *
-g_time_zone_new_utc (void)
-{
- return g_time_zone_new ("UTC");
-}
-
-/**
- * g_time_zone_new_local:
- *
- * Creates a #GTimeZone corresponding to local time.
- *
- * This is equivalent to calling g_time_zone_new() with the value of the
- * <varname>TZ</varname> environment variable (including the possibility
- * of %NULL). Changes made to <varname>TZ</varname> after the first
- * call to this function may or may not be noticed by future calls.
- *
- * You should release the return value by calling g_time_zone_unref()
- * when you are done with it.
- *
- * Returns: the local timezone
- *
- * Since: 2.26
- **/
-GTimeZone *
-g_time_zone_new_local (void)
-{
- return g_time_zone_new (getenv ("TZ"));
-}
-
-/* Internal helpers {{{1 */
-inline static const struct ttinfo *
-interval_info (GTimeZone *tz,
- gint interval)
-{
- if (interval)
- return tz->infos + tz->indices[interval - 1];
-
- return tz->infos;
-}
-
-inline static gint64
-interval_start (GTimeZone *tz,
- gint interval)
-{
- if (interval)
- return gint64_from_be (tz->trans[interval - 1]);
-
- return G_MININT64;
-}
-
-inline static gint64
-interval_end (GTimeZone *tz,
- gint interval)
-{
- if (interval < tz->timecnt)
- return gint64_from_be (tz->trans[interval]) - 1;
-
- return G_MAXINT64;
-}
-
-inline static gint32
-interval_offset (GTimeZone *tz,
- gint interval)
-{
- return gint32_from_be (interval_info (tz, interval)->tt_gmtoff);
-}
-
-inline static gboolean
-interval_isdst (GTimeZone *tz,
- gint interval)
-{
- return interval_info (tz, interval)->tt_isdst;
-}
-
-inline static guint8
-interval_abbrind (GTimeZone *tz,
- gint interval)
-{
- return interval_info (tz, interval)->tt_abbrind;
-}
-
-inline static gint64
-interval_local_start (GTimeZone *tz,
- gint interval)
-{
- if (interval)
- return interval_start (tz, interval) + interval_offset (tz, interval);
-
- return G_MININT64;
-}
-
-inline static gint64
-interval_local_end (GTimeZone *tz,
- gint interval)
-{
- if (interval < tz->timecnt)
- return interval_end (tz, interval) + interval_offset (tz, interval);
-
- return G_MAXINT64;
-}
-
-static gboolean
-interval_valid (GTimeZone *tz,
- gint interval)
-{
- return interval <= tz->timecnt;
-}
-
-/* g_time_zone_find_interval() {{{1 */
-
-/**
- * g_time_zone_adjust_time:
- * @tz: a #GTimeZone
- * @type: the #GTimeType of @time
- * @time: a pointer to a number of seconds since January 1, 1970
- *
- * Finds an interval within @tz that corresponds to the given @time,
- * possibly adjusting @time if required to fit into an interval.
- * The meaning of @time depends on @type.
- *
- * This function is similar to g_time_zone_find_interval(), with the
- * difference that it always succeeds (by making the adjustments
- * described below).
- *
- * In any of the cases where g_time_zone_find_interval() succeeds then
- * this function returns the same value, without modifying @time.
- *
- * This function may, however, modify @time in order to deal with
- * non-existent times. If the non-existent local @time of 02:30 were
- * requested on March 13th 2010 in Toronto then this function would
- * adjust @time to be 03:00 and return the interval containing the
- * adjusted time.
- *
- * Returns: the interval containing @time, never -1
- *
- * Since: 2.26
- **/
-gint
-g_time_zone_adjust_time (GTimeZone *tz,
- GTimeType type,
- gint64 *time)
-{
- gint i;
-
- if (tz->zoneinfo == NULL)
- return 0;
-
- /* find the interval containing *time UTC
- * TODO: this could be binary searched (or better) */
- for (i = 0; i < tz->timecnt; i++)
- if (*time <= interval_end (tz, i))
- break;
-
- g_assert (interval_start (tz, i) <= *time && *time <= interval_end (tz, i));
-
- if (type != G_TIME_TYPE_UNIVERSAL)
- {
- if (*time < interval_local_start (tz, i))
- /* if time came before the start of this interval... */
- {
- i--;
-
- /* if it's not in the previous interval... */
- if (*time > interval_local_end (tz, i))
- {
- /* it doesn't exist. fast-forward it. */
- i++;
- *time = interval_local_start (tz, i);
- }
- }
-
- else if (*time > interval_local_end (tz, i))
- /* if time came after the end of this interval... */
- {
- i++;
-
- /* if it's not in the next interval... */
- if (*time < interval_local_start (tz, i))
- /* it doesn't exist. fast-forward it. */
- *time = interval_local_start (tz, i);
- }
-
- else if (interval_isdst (tz, i) != type)
- /* it's in this interval, but dst flag doesn't match.
- * check neighbours for a better fit. */
- {
- if (i && *time <= interval_local_end (tz, i - 1))
- i--;
-
- else if (i < tz->timecnt &&
- *time >= interval_local_start (tz, i + 1))
- i++;
- }
- }
-
- return i;
-}
-
-/**
- * g_time_zone_find_interval:
- * @tz: a #GTimeZone
- * @type: the #GTimeType of @time
- * @time: a number of seconds since January 1, 1970
- *
- * Finds an the interval within @tz that corresponds to the given @time.
- * The meaning of @time depends on @type.
- *
- * If @type is %G_TIME_TYPE_UNIVERSAL then this function will always
- * succeed (since universal time is monotonic and continuous).
- *
- * Otherwise @time is treated is local time. The distinction between
- * %G_TIME_TYPE_STANDARD and %G_TIME_TYPE_DAYLIGHT is ignored except in
- * the case that the given @time is ambiguous. In Toronto, for example,
- * 01:30 on November 7th 2010 occured twice (once inside of daylight
- * savings time and the next, an hour later, outside of daylight savings
- * time). In this case, the different value of @type would result in a
- * different interval being returned.
- *
- * It is still possible for this function to fail. In Toronto, for
- * example, 02:00 on March 14th 2010 does not exist (due to the leap
- * forward to begin daylight savings time). -1 is returned in that
- * case.
- *
- * Returns: the interval containing @time, or -1 in case of failure
- *
- * Since: 2.26
- */
-gint
-g_time_zone_find_interval (GTimeZone *tz,
- GTimeType type,
- gint64 time)
-{
- gint i;
-
- if (tz->zoneinfo == NULL)
- return 0;
-
- for (i = 0; i < tz->timecnt; i++)
- if (time <= interval_end (tz, i))
- break;
-
- if (type == G_TIME_TYPE_UNIVERSAL)
- return i;
-
- if (time < interval_local_start (tz, i))
- {
- if (time > interval_local_end (tz, --i))
- return -1;
- }
-
- else if (time > interval_local_end (tz, i))
- {
- if (time < interval_local_start (tz, ++i))
- return -1;
- }
-
- else if (interval_isdst (tz, i) != type)
- {
- if (i && time <= interval_local_end (tz, i - 1))
- i--;
-
- else if (i < tz->timecnt && time >= interval_local_start (tz, i + 1))
- i++;
- }
-
- return i;
-}
-
-/* Public API accessors {{{1 */
-
-/**
- * g_time_zone_get_abbreviation:
- * @tz: a #GTimeZone
- * @interval: an interval within the timezone
- *
- * Determines the time zone abbreviation to be used during a particular
- * @interval of time in the time zone @tz.
- *
- * For example, in Toronto this is currently "EST" during the winter
- * months and "EDT" during the summer months when daylight savings time
- * is in effect.
- *
- * Returns: the time zone abbreviation, which belongs to @tz
- *
- * Since: 2.26
- **/
-const gchar *
-g_time_zone_get_abbreviation (GTimeZone *tz,
- gint interval)
-{
- g_return_val_if_fail (interval_valid (tz, interval), NULL);
-
- if (tz->header == NULL)
- return "UTC";
-
- return tz->abbrs + interval_abbrind (tz, interval);
-}
-
-/**
- * g_time_zone_get_offset:
- * @tz: a #GTimeZone
- * @interval: an interval within the timezone
- *
- * Determines the offset to UTC in effect during a particular @interval
- * of time in the time zone @tz.
- *
- * The offset is the number of seconds that you add to UTC time to
- * arrive at local time for @tz (ie: negative numbers for time zones
- * west of GMT, positive numbers for east).
- *
- * Returns: the number of seconds that should be added to UTC to get the
- * local time in @tz
- *
- * Since: 2.26
- **/
-gint32
-g_time_zone_get_offset (GTimeZone *tz,
- gint interval)
-{
- g_return_val_if_fail (interval_valid (tz, interval), 0);
-
- if (tz->header == NULL)
- return 0;
-
- return interval_offset (tz, interval);
-}
-
-/**
- * g_time_zone_is_dst:
- * @tz: a #GTimeZone
- * @interval: an interval within the timezone
- *
- * Determines if daylight savings time is in effect during a particular
- * @interval of time in the time zone @tz.
- *
- * Returns: %TRUE if daylight savings time is in effect
- *
- * Since: 2.26
- **/
-gboolean
-g_time_zone_is_dst (GTimeZone *tz,
- gint interval)
-{
- g_return_val_if_fail (interval_valid (tz, interval), FALSE);
-
- if (tz->header == NULL)
- return FALSE;
-
- return interval_isdst (tz, interval);
-}
-
-/* Epilogue {{{1 */
-/* vim:set foldmethod=marker: */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "gtree.h"
-
-#include "gatomic.h"
-#include "gtestutils.h"
-
-/**
- * SECTION: trees-binary
- * @title: Balanced Binary Trees
- * @short_description: a sorted collection of key/value pairs optimized
- * for searching and traversing in order
- *
- * The #GTree structure and its associated functions provide a sorted
- * collection of key/value pairs optimized for searching and traversing
- * in order.
- *
- * To create a new #GTree use g_tree_new().
- *
- * To insert a key/value pair into a #GTree use g_tree_insert().
- *
- * To lookup the value corresponding to a given key, use
- * g_tree_lookup() and g_tree_lookup_extended().
- *
- * To find out the number of nodes in a #GTree, use g_tree_nnodes(). To
- * get the height of a #GTree, use g_tree_height().
- *
- * To traverse a #GTree, calling a function for each node visited in
- * the traversal, use g_tree_foreach().
- *
- * To remove a key/value pair use g_tree_remove().
- *
- * To destroy a #GTree, use g_tree_destroy().
- **/
-
-#undef G_TREE_DEBUG
-
-#define MAX_GTREE_HEIGHT 40
-
-typedef struct _GTreeNode GTreeNode;
-
-/**
- * GTree:
- *
- * The <structname>GTree</structname> struct is an opaque data
- * structure representing a <link
- * linkend="glib-Balanced-Binary-Trees">Balanced Binary Tree</link>. It
- * should be accessed only by using the following functions.
- **/
-struct _GTree
-{
- GTreeNode *root;
- GCompareDataFunc key_compare;
- GDestroyNotify key_destroy_func;
- GDestroyNotify value_destroy_func;
- gpointer key_compare_data;
- guint nnodes;
- gint ref_count;
-};
-
-struct _GTreeNode
-{
- gpointer key; /* key for this node */
- gpointer value; /* value stored at this node */
- GTreeNode *left; /* left subtree */
- GTreeNode *right; /* right subtree */
- gint8 balance; /* height (left) - height (right) */
- guint8 left_child;
- guint8 right_child;
-};
-
-
-static GTreeNode* g_tree_node_new (gpointer key,
- gpointer value);
-static void g_tree_insert_internal (GTree *tree,
- gpointer key,
- gpointer value,
- gboolean replace);
-static gboolean g_tree_remove_internal (GTree *tree,
- gconstpointer key,
- gboolean steal);
-static GTreeNode* g_tree_node_balance (GTreeNode *node);
-static GTreeNode *g_tree_find_node (GTree *tree,
- gconstpointer key);
-static gint g_tree_node_pre_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data);
-static gint g_tree_node_in_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data);
-static gint g_tree_node_post_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data);
-static gpointer g_tree_node_search (GTreeNode *node,
- GCompareFunc search_func,
- gconstpointer data);
-static GTreeNode* g_tree_node_rotate_left (GTreeNode *node);
-static GTreeNode* g_tree_node_rotate_right (GTreeNode *node);
-#ifdef G_TREE_DEBUG
-static void g_tree_node_check (GTreeNode *node);
-#endif
-
-
-static GTreeNode*
-g_tree_node_new (gpointer key,
- gpointer value)
-{
- GTreeNode *node = g_slice_new (GTreeNode);
-
- node->balance = 0;
- node->left = NULL;
- node->right = NULL;
- node->left_child = FALSE;
- node->right_child = FALSE;
- node->key = key;
- node->value = value;
-
- return node;
-}
-
-/**
- * g_tree_new:
- * @key_compare_func: the function used to order the nodes in the #GTree.
- * It should return values similar to the standard strcmp() function -
- * 0 if the two arguments are equal, a negative value if the first argument
- * comes before the second, or a positive value if the first argument comes
- * after the second.
- *
- * Creates a new #GTree.
- *
- * Return value: a new #GTree.
- **/
-GTree*
-g_tree_new (GCompareFunc key_compare_func)
-{
- g_return_val_if_fail (key_compare_func != NULL, NULL);
-
- return g_tree_new_full ((GCompareDataFunc) key_compare_func, NULL,
- NULL, NULL);
-}
-
-/**
- * g_tree_new_with_data:
- * @key_compare_func: qsort()-style comparison function.
- * @key_compare_data: data to pass to comparison function.
- *
- * Creates a new #GTree with a comparison function that accepts user data.
- * See g_tree_new() for more details.
- *
- * Return value: a new #GTree.
- **/
-GTree*
-g_tree_new_with_data (GCompareDataFunc key_compare_func,
- gpointer key_compare_data)
-{
- g_return_val_if_fail (key_compare_func != NULL, NULL);
-
- return g_tree_new_full (key_compare_func, key_compare_data,
- NULL, NULL);
-}
-
-/**
- * g_tree_new_full:
- * @key_compare_func: qsort()-style comparison function.
- * @key_compare_data: data to pass to comparison function.
- * @key_destroy_func: a function to free the memory allocated for the key
- * used when removing the entry from the #GTree or %NULL if you don't
- * want to supply such a function.
- * @value_destroy_func: a function to free the memory allocated for the
- * value used when removing the entry from the #GTree or %NULL if you
- * don't want to supply such a function.
- *
- * Creates a new #GTree like g_tree_new() and allows to specify functions
- * to free the memory allocated for the key and value that get called when
- * removing the entry from the #GTree.
- *
- * Return value: a new #GTree.
- **/
-GTree*
-g_tree_new_full (GCompareDataFunc key_compare_func,
- gpointer key_compare_data,
- GDestroyNotify key_destroy_func,
- GDestroyNotify value_destroy_func)
-{
- GTree *tree;
-
- g_return_val_if_fail (key_compare_func != NULL, NULL);
-
- tree = g_slice_new (GTree);
- tree->root = NULL;
- tree->key_compare = key_compare_func;
- tree->key_destroy_func = key_destroy_func;
- tree->value_destroy_func = value_destroy_func;
- tree->key_compare_data = key_compare_data;
- tree->nnodes = 0;
- tree->ref_count = 1;
-
- return tree;
-}
-
-static inline GTreeNode *
-g_tree_first_node (GTree *tree)
-{
- GTreeNode *tmp;
-
- if (!tree->root)
- return NULL;
-
- tmp = tree->root;
-
- while (tmp->left_child)
- tmp = tmp->left;
-
- return tmp;
-}
-
-static inline GTreeNode *
-g_tree_node_previous (GTreeNode *node)
-{
- GTreeNode *tmp;
-
- tmp = node->left;
-
- if (node->left_child)
- while (tmp->right_child)
- tmp = tmp->right;
-
- return tmp;
-}
-
-static inline GTreeNode *
-g_tree_node_next (GTreeNode *node)
-{
- GTreeNode *tmp;
-
- tmp = node->right;
-
- if (node->right_child)
- while (tmp->left_child)
- tmp = tmp->left;
-
- return tmp;
-}
-
-static void
-g_tree_remove_all (GTree *tree)
-{
- GTreeNode *node;
- GTreeNode *next;
-
- g_return_if_fail (tree != NULL);
-
- node = g_tree_first_node (tree);
-
- while (node)
- {
- next = g_tree_node_next (node);
-
- if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
- if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
- g_slice_free (GTreeNode, node);
-
- node = next;
- }
-
- tree->root = NULL;
- tree->nnodes = 0;
-}
-
-/**
- * g_tree_ref:
- * @tree: a #GTree.
- *
- * Increments the reference count of @tree by one. It is safe to call
- * this function from any thread.
- *
- * Return value: the passed in #GTree.
- *
- * Since: 2.22
- **/
-GTree *
-g_tree_ref (GTree *tree)
-{
- g_return_val_if_fail (tree != NULL, NULL);
-
- g_atomic_int_inc (&tree->ref_count);
-
- return tree;
-}
-
-/**
- * g_tree_unref:
- * @tree: a #GTree.
- *
- * Decrements the reference count of @tree by one. If the reference count
- * drops to 0, all keys and values will be destroyed (if destroy
- * functions were specified) and all memory allocated by @tree will be
- * released.
- *
- * It is safe to call this function from any thread.
- *
- * Since: 2.22
- **/
-void
-g_tree_unref (GTree *tree)
-{
- g_return_if_fail (tree != NULL);
-
- if (g_atomic_int_dec_and_test (&tree->ref_count))
- {
- g_tree_remove_all (tree);
- g_slice_free (GTree, tree);
- }
-}
-
-/**
- * g_tree_destroy:
- * @tree: a #GTree.
- *
- * Removes all keys and values from the #GTree and decreases its
- * reference count by one. If keys and/or values are dynamically
- * allocated, you should either free them first or create the #GTree
- * using g_tree_new_full(). In the latter case the destroy functions
- * you supplied will be called on all keys and values before destroying
- * the #GTree.
- **/
-void
-g_tree_destroy (GTree *tree)
-{
- g_return_if_fail (tree != NULL);
-
- g_tree_remove_all (tree);
- g_tree_unref (tree);
-}
-
-/**
- * g_tree_insert:
- * @tree: a #GTree.
- * @key: the key to insert.
- * @value: the value corresponding to the key.
- *
- * Inserts a key/value pair into a #GTree. If the given key already exists
- * in the #GTree its corresponding value is set to the new value. If you
- * supplied a value_destroy_func when creating the #GTree, the old value is
- * freed using that function. If you supplied a @key_destroy_func when
- * creating the #GTree, the passed key is freed using that function.
- *
- * The tree is automatically 'balanced' as new key/value pairs are added,
- * so that the distance from the root to every leaf is as small as possible.
- **/
-void
-g_tree_insert (GTree *tree,
- gpointer key,
- gpointer value)
-{
- g_return_if_fail (tree != NULL);
-
- g_tree_insert_internal (tree, key, value, FALSE);
-
-#ifdef G_TREE_DEBUG
- g_tree_node_check (tree->root);
-#endif
-}
-
-/**
- * g_tree_replace:
- * @tree: a #GTree.
- * @key: the key to insert.
- * @value: the value corresponding to the key.
- *
- * Inserts a new key and value into a #GTree similar to g_tree_insert().
- * The difference is that if the key already exists in the #GTree, it gets
- * replaced by the new key. If you supplied a @value_destroy_func when
- * creating the #GTree, the old value is freed using that function. If you
- * supplied a @key_destroy_func when creating the #GTree, the old key is
- * freed using that function.
- *
- * The tree is automatically 'balanced' as new key/value pairs are added,
- * so that the distance from the root to every leaf is as small as possible.
- **/
-void
-g_tree_replace (GTree *tree,
- gpointer key,
- gpointer value)
-{
- g_return_if_fail (tree != NULL);
-
- g_tree_insert_internal (tree, key, value, TRUE);
-
-#ifdef G_TREE_DEBUG
- g_tree_node_check (tree->root);
-#endif
-}
-
-/* internal insert routine */
-static void
-g_tree_insert_internal (GTree *tree,
- gpointer key,
- gpointer value,
- gboolean replace)
-{
- GTreeNode *node;
- GTreeNode *path[MAX_GTREE_HEIGHT];
- int idx;
-
- g_return_if_fail (tree != NULL);
-
- if (!tree->root)
- {
- tree->root = g_tree_node_new (key, value);
- tree->nnodes++;
- return;
- }
-
- idx = 0;
- path[idx++] = NULL;
- node = tree->root;
-
- while (1)
- {
- int cmp = tree->key_compare (key, node->key, tree->key_compare_data);
-
- if (cmp == 0)
- {
- if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
-
- node->value = value;
-
- if (replace)
- {
- if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
-
- node->key = key;
- }
- else
- {
- /* free the passed key */
- if (tree->key_destroy_func)
- tree->key_destroy_func (key);
- }
-
- return;
- }
- else if (cmp < 0)
- {
- if (node->left_child)
- {
- path[idx++] = node;
- node = node->left;
- }
- else
- {
- GTreeNode *child = g_tree_node_new (key, value);
-
- child->left = node->left;
- child->right = node;
- node->left = child;
- node->left_child = TRUE;
- node->balance -= 1;
-
- tree->nnodes++;
-
- break;
- }
- }
- else
- {
- if (node->right_child)
- {
- path[idx++] = node;
- node = node->right;
- }
- else
- {
- GTreeNode *child = g_tree_node_new (key, value);
-
- child->right = node->right;
- child->left = node;
- node->right = child;
- node->right_child = TRUE;
- node->balance += 1;
-
- tree->nnodes++;
-
- break;
- }
- }
- }
-
- /* restore balance. This is the goodness of a non-recursive
- implementation, when we are done with balancing we 'break'
- the loop and we are done. */
- while (1)
- {
- GTreeNode *bparent = path[--idx];
- gboolean left_node = (bparent && node == bparent->left);
- g_assert (!bparent || bparent->left == node || bparent->right == node);
-
- if (node->balance < -1 || node->balance > 1)
- {
- node = g_tree_node_balance (node);
- if (bparent == NULL)
- tree->root = node;
- else if (left_node)
- bparent->left = node;
- else
- bparent->right = node;
- }
-
- if (node->balance == 0 || bparent == NULL)
- break;
-
- if (left_node)
- bparent->balance -= 1;
- else
- bparent->balance += 1;
-
- node = bparent;
- }
-}
-
-/**
- * g_tree_remove:
- * @tree: a #GTree.
- * @key: the key to remove.
- *
- * Removes a key/value pair from a #GTree.
- *
- * If the #GTree was created using g_tree_new_full(), the key and value
- * are freed using the supplied destroy functions, otherwise you have to
- * make sure that any dynamically allocated values are freed yourself.
- * If the key does not exist in the #GTree, the function does nothing.
- *
- * Returns: %TRUE if the key was found (prior to 2.8, this function returned
- * nothing)
- **/
-gboolean
-g_tree_remove (GTree *tree,
- gconstpointer key)
-{
- gboolean removed;
-
- g_return_val_if_fail (tree != NULL, FALSE);
-
- removed = g_tree_remove_internal (tree, key, FALSE);
-
-#ifdef G_TREE_DEBUG
- g_tree_node_check (tree->root);
-#endif
-
- return removed;
-}
-
-/**
- * g_tree_steal:
- * @tree: a #GTree.
- * @key: the key to remove.
- *
- * Removes a key and its associated value from a #GTree without calling
- * the key and value destroy functions.
- *
- * If the key does not exist in the #GTree, the function does nothing.
- *
- * Returns: %TRUE if the key was found (prior to 2.8, this function returned
- * nothing)
- **/
-gboolean
-g_tree_steal (GTree *tree,
- gconstpointer key)
-{
- gboolean removed;
-
- g_return_val_if_fail (tree != NULL, FALSE);
-
- removed = g_tree_remove_internal (tree, key, TRUE);
-
-#ifdef G_TREE_DEBUG
- g_tree_node_check (tree->root);
-#endif
-
- return removed;
-}
-
-/* internal remove routine */
-static gboolean
-g_tree_remove_internal (GTree *tree,
- gconstpointer key,
- gboolean steal)
-{
- GTreeNode *node, *parent, *balance;
- GTreeNode *path[MAX_GTREE_HEIGHT];
- int idx;
- gboolean left_node;
-
- g_return_val_if_fail (tree != NULL, FALSE);
-
- if (!tree->root)
- return FALSE;
-
- idx = 0;
- path[idx++] = NULL;
- node = tree->root;
-
- while (1)
- {
- int cmp = tree->key_compare (key, node->key, tree->key_compare_data);
-
- if (cmp == 0)
- break;
- else if (cmp < 0)
- {
- if (!node->left_child)
- return FALSE;
-
- path[idx++] = node;
- node = node->left;
- }
- else
- {
- if (!node->right_child)
- return FALSE;
-
- path[idx++] = node;
- node = node->right;
- }
- }
-
- /* the following code is almost equal to g_tree_remove_node,
- except that we do not have to call g_tree_node_parent. */
- balance = parent = path[--idx];
- g_assert (!parent || parent->left == node || parent->right == node);
- left_node = (parent && node == parent->left);
-
- if (!node->left_child)
- {
- if (!node->right_child)
- {
- if (!parent)
- tree->root = NULL;
- else if (left_node)
- {
- parent->left_child = FALSE;
- parent->left = node->left;
- parent->balance += 1;
- }
- else
- {
- parent->right_child = FALSE;
- parent->right = node->right;
- parent->balance -= 1;
- }
- }
- else /* node has a right child */
- {
- GTreeNode *tmp = g_tree_node_next (node);
- tmp->left = node->left;
-
- if (!parent)
- tree->root = node->right;
- else if (left_node)
- {
- parent->left = node->right;
- parent->balance += 1;
- }
- else
- {
- parent->right = node->right;
- parent->balance -= 1;
- }
- }
- }
- else /* node has a left child */
- {
- if (!node->right_child)
- {
- GTreeNode *tmp = g_tree_node_previous (node);
- tmp->right = node->right;
-
- if (parent == NULL)
- tree->root = node->left;
- else if (left_node)
- {
- parent->left = node->left;
- parent->balance += 1;
- }
- else
- {
- parent->right = node->left;
- parent->balance -= 1;
- }
- }
- else /* node has a both children (pant, pant!) */
- {
- GTreeNode *prev = node->left;
- GTreeNode *next = node->right;
- GTreeNode *nextp = node;
- int old_idx = idx + 1;
- idx++;
-
- /* path[idx] == parent */
- /* find the immediately next node (and its parent) */
- while (next->left_child)
- {
- path[++idx] = nextp = next;
- next = next->left;
- }
-
- path[old_idx] = next;
- balance = path[idx];
-
- /* remove 'next' from the tree */
- if (nextp != node)
- {
- if (next->right_child)
- nextp->left = next->right;
- else
- nextp->left_child = FALSE;
- nextp->balance += 1;
-
- next->right_child = TRUE;
- next->right = node->right;
- }
- else
- node->balance -= 1;
-
- /* set the prev to point to the right place */
- while (prev->right_child)
- prev = prev->right;
- prev->right = next;
-
- /* prepare 'next' to replace 'node' */
- next->left_child = TRUE;
- next->left = node->left;
- next->balance = node->balance;
-
- if (!parent)
- tree->root = next;
- else if (left_node)
- parent->left = next;
- else
- parent->right = next;
- }
- }
-
- /* restore balance */
- if (balance)
- while (1)
- {
- GTreeNode *bparent = path[--idx];
- g_assert (!bparent || bparent->left == balance || bparent->right == balance);
- left_node = (bparent && balance == bparent->left);
-
- if(balance->balance < -1 || balance->balance > 1)
- {
- balance = g_tree_node_balance (balance);
- if (!bparent)
- tree->root = balance;
- else if (left_node)
- bparent->left = balance;
- else
- bparent->right = balance;
- }
-
- if (balance->balance != 0 || !bparent)
- break;
-
- if (left_node)
- bparent->balance += 1;
- else
- bparent->balance -= 1;
-
- balance = bparent;
- }
-
- if (!steal)
- {
- if (tree->key_destroy_func)
- tree->key_destroy_func (node->key);
- if (tree->value_destroy_func)
- tree->value_destroy_func (node->value);
- }
-
- g_slice_free (GTreeNode, node);
-
- tree->nnodes--;
-
- return TRUE;
-}
-
-/**
- * g_tree_lookup:
- * @tree: a #GTree.
- * @key: the key to look up.
- *
- * Gets the value corresponding to the given key. Since a #GTree is
- * automatically balanced as key/value pairs are added, key lookup is very
- * fast.
- *
- * Return value: the value corresponding to the key, or %NULL if the key was
- * not found.
- **/
-gpointer
-g_tree_lookup (GTree *tree,
- gconstpointer key)
-{
- GTreeNode *node;
-
- g_return_val_if_fail (tree != NULL, NULL);
-
- node = g_tree_find_node (tree, key);
-
- return node ? node->value : NULL;
-}
-
-/**
- * g_tree_lookup_extended:
- * @tree: a #GTree.
- * @lookup_key: the key to look up.
- * @orig_key: returns the original key.
- * @value: returns the value associated with the key.
- *
- * Looks up a key in the #GTree, returning the original key and the
- * associated value and a #gboolean which is %TRUE if the key was found. This
- * is useful if you need to free the memory allocated for the original key,
- * for example before calling g_tree_remove().
- *
- * Return value: %TRUE if the key was found in the #GTree.
- **/
-gboolean
-g_tree_lookup_extended (GTree *tree,
- gconstpointer lookup_key,
- gpointer *orig_key,
- gpointer *value)
-{
- GTreeNode *node;
-
- g_return_val_if_fail (tree != NULL, FALSE);
-
- node = g_tree_find_node (tree, lookup_key);
-
- if (node)
- {
- if (orig_key)
- *orig_key = node->key;
- if (value)
- *value = node->value;
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**
- * g_tree_foreach:
- * @tree: a #GTree.
- * @func: the function to call for each node visited. If this function
- * returns %TRUE, the traversal is stopped.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each of the key/value pairs in the #GTree.
- * The function is passed the key and value of each pair, and the given
- * @data parameter. The tree is traversed in sorted order.
- *
- * The tree may not be modified while iterating over it (you can't
- * add/remove items). To remove all items matching a predicate, you need
- * to add each item to a list in your #GTraverseFunc as you walk over
- * the tree, then walk the list and remove each item.
- **/
-void
-g_tree_foreach (GTree *tree,
- GTraverseFunc func,
- gpointer user_data)
-{
- GTreeNode *node;
-
- g_return_if_fail (tree != NULL);
-
- if (!tree->root)
- return;
-
- node = g_tree_first_node (tree);
-
- while (node)
- {
- if ((*func) (node->key, node->value, user_data))
- break;
-
- node = g_tree_node_next (node);
- }
-}
-
-/**
- * g_tree_traverse:
- * @tree: a #GTree.
- * @traverse_func: the function to call for each node visited. If this
- * function returns %TRUE, the traversal is stopped.
- * @traverse_type: the order in which nodes are visited, one of %G_IN_ORDER,
- * %G_PRE_ORDER and %G_POST_ORDER.
- * @user_data: user data to pass to the function.
- *
- * Calls the given function for each node in the #GTree.
- *
- * Deprecated:2.2: The order of a balanced tree is somewhat arbitrary. If you
- * just want to visit all nodes in sorted order, use g_tree_foreach()
- * instead. If you really need to visit nodes in a different order, consider
- * using an <link linkend="glib-N-ary-Trees">N-ary Tree</link>.
- **/
-/**
- * GTraverseFunc:
- * @key: a key of a #GTree node.
- * @value: the value corresponding to the key.
- * @data: user data passed to g_tree_traverse().
- * @Returns: %TRUE to stop the traversal.
- *
- * Specifies the type of function passed to g_tree_traverse(). It is
- * passed the key and value of each node, together with the @user_data
- * parameter passed to g_tree_traverse(). If the function returns
- * %TRUE, the traversal is stopped.
- **/
-/**
- * GTraverseType:
- * @G_IN_ORDER: vists a node's left child first, then the node itself,
- * then its right child. This is the one to use if you
- * want the output sorted according to the compare
- * function.
- * @G_PRE_ORDER: visits a node, then its children.
- * @G_POST_ORDER: visits the node's children, then the node itself.
- * @G_LEVEL_ORDER: is not implemented for <link
- * linkend="glib-Balanced-Binary-Trees">Balanced Binary
- * Trees</link>. For <link
- * linkend="glib-N-ary-Trees">N-ary Trees</link>, it
- * vists the root node first, then its children, then
- * its grandchildren, and so on. Note that this is less
- * efficient than the other orders.
- *
- * Specifies the type of traveral performed by g_tree_traverse(),
- * g_node_traverse() and g_node_find().
- **/
-void
-g_tree_traverse (GTree *tree,
- GTraverseFunc traverse_func,
- GTraverseType traverse_type,
- gpointer user_data)
-{
- g_return_if_fail (tree != NULL);
-
- if (!tree->root)
- return;
-
- switch (traverse_type)
- {
- case G_PRE_ORDER:
- g_tree_node_pre_order (tree->root, traverse_func, user_data);
- break;
-
- case G_IN_ORDER:
- g_tree_node_in_order (tree->root, traverse_func, user_data);
- break;
-
- case G_POST_ORDER:
- g_tree_node_post_order (tree->root, traverse_func, user_data);
- break;
-
- case G_LEVEL_ORDER:
- g_warning ("g_tree_traverse(): traverse type G_LEVEL_ORDER isn't implemented.");
- break;
- }
-}
-
-/**
- * g_tree_search:
- * @tree: a #GTree.
- * @search_func: a function used to search the #GTree.
- * @user_data: the data passed as the second argument to the @search_func
- * function.
- *
- * Searches a #GTree using @search_func.
- *
- * The @search_func is called with a pointer to the key of a key/value pair in
- * the tree, and the passed in @user_data. If @search_func returns 0 for a
- * key/value pair, then g_tree_search_func() will return the value of that
- * pair. If @search_func returns -1, searching will proceed among the
- * key/value pairs that have a smaller key; if @search_func returns 1,
- * searching will proceed among the key/value pairs that have a larger key.
- *
- * Return value: the value corresponding to the found key, or %NULL if the key
- * was not found.
- **/
-gpointer
-g_tree_search (GTree *tree,
- GCompareFunc search_func,
- gconstpointer user_data)
-{
- g_return_val_if_fail (tree != NULL, NULL);
-
- if (tree->root)
- return g_tree_node_search (tree->root, search_func, user_data);
- else
- return NULL;
-}
-
-/**
- * g_tree_height:
- * @tree: a #GTree.
- *
- * Gets the height of a #GTree.
- *
- * If the #GTree contains no nodes, the height is 0.
- * If the #GTree contains only one root node the height is 1.
- * If the root node has children the height is 2, etc.
- *
- * Return value: the height of the #GTree.
- **/
-gint
-g_tree_height (GTree *tree)
-{
- GTreeNode *node;
- gint height;
-
- g_return_val_if_fail (tree != NULL, 0);
-
- if (!tree->root)
- return 0;
-
- height = 0;
- node = tree->root;
-
- while (1)
- {
- height += 1 + MAX(node->balance, 0);
-
- if (!node->left_child)
- return height;
-
- node = node->left;
- }
-}
-
-/**
- * g_tree_nnodes:
- * @tree: a #GTree.
- *
- * Gets the number of nodes in a #GTree.
- *
- * Return value: the number of nodes in the #GTree.
- **/
-gint
-g_tree_nnodes (GTree *tree)
-{
- g_return_val_if_fail (tree != NULL, 0);
-
- return tree->nnodes;
-}
-
-static GTreeNode*
-g_tree_node_balance (GTreeNode *node)
-{
- if (node->balance < -1)
- {
- if (node->left->balance > 0)
- node->left = g_tree_node_rotate_left (node->left);
- node = g_tree_node_rotate_right (node);
- }
- else if (node->balance > 1)
- {
- if (node->right->balance < 0)
- node->right = g_tree_node_rotate_right (node->right);
- node = g_tree_node_rotate_left (node);
- }
-
- return node;
-}
-
-static GTreeNode *
-g_tree_find_node (GTree *tree,
- gconstpointer key)
-{
- GTreeNode *node;
- gint cmp;
-
- node = tree->root;
- if (!node)
- return NULL;
-
- while (1)
- {
- cmp = tree->key_compare (key, node->key, tree->key_compare_data);
- if (cmp == 0)
- return node;
- else if (cmp < 0)
- {
- if (!node->left_child)
- return NULL;
-
- node = node->left;
- }
- else
- {
- if (!node->right_child)
- return NULL;
-
- node = node->right;
- }
- }
-}
-
-static gint
-g_tree_node_pre_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data)
-{
- if ((*traverse_func) (node->key, node->value, data))
- return TRUE;
-
- if (node->left_child)
- {
- if (g_tree_node_pre_order (node->left, traverse_func, data))
- return TRUE;
- }
-
- if (node->right_child)
- {
- if (g_tree_node_pre_order (node->right, traverse_func, data))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gint
-g_tree_node_in_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data)
-{
- if (node->left_child)
- {
- if (g_tree_node_in_order (node->left, traverse_func, data))
- return TRUE;
- }
-
- if ((*traverse_func) (node->key, node->value, data))
- return TRUE;
-
- if (node->right_child)
- {
- if (g_tree_node_in_order (node->right, traverse_func, data))
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gint
-g_tree_node_post_order (GTreeNode *node,
- GTraverseFunc traverse_func,
- gpointer data)
-{
- if (node->left_child)
- {
- if (g_tree_node_post_order (node->left, traverse_func, data))
- return TRUE;
- }
-
- if (node->right_child)
- {
- if (g_tree_node_post_order (node->right, traverse_func, data))
- return TRUE;
- }
-
- if ((*traverse_func) (node->key, node->value, data))
- return TRUE;
-
- return FALSE;
-}
-
-static gpointer
-g_tree_node_search (GTreeNode *node,
- GCompareFunc search_func,
- gconstpointer data)
-{
- gint dir;
-
- if (!node)
- return NULL;
-
- while (1)
- {
- dir = (* search_func) (node->key, data);
- if (dir == 0)
- return node->value;
- else if (dir < 0)
- {
- if (!node->left_child)
- return NULL;
-
- node = node->left;
- }
- else
- {
- if (!node->right_child)
- return NULL;
-
- node = node->right;
- }
- }
-}
-
-static GTreeNode*
-g_tree_node_rotate_left (GTreeNode *node)
-{
- GTreeNode *right;
- gint a_bal;
- gint b_bal;
-
- right = node->right;
-
- if (right->left_child)
- node->right = right->left;
- else
- {
- node->right_child = FALSE;
- node->right = right;
- right->left_child = TRUE;
- }
- right->left = node;
-
- a_bal = node->balance;
- b_bal = right->balance;
-
- if (b_bal <= 0)
- {
- if (a_bal >= 1)
- right->balance = b_bal - 1;
- else
- right->balance = a_bal + b_bal - 2;
- node->balance = a_bal - 1;
- }
- else
- {
- if (a_bal <= b_bal)
- right->balance = a_bal - 2;
- else
- right->balance = b_bal - 1;
- node->balance = a_bal - b_bal - 1;
- }
-
- return right;
-}
-
-static GTreeNode*
-g_tree_node_rotate_right (GTreeNode *node)
-{
- GTreeNode *left;
- gint a_bal;
- gint b_bal;
-
- left = node->left;
-
- if (left->right_child)
- node->left = left->right;
- else
- {
- node->left_child = FALSE;
- node->left = left;
- left->right_child = TRUE;
- }
- left->right = node;
-
- a_bal = node->balance;
- b_bal = left->balance;
-
- if (b_bal <= 0)
- {
- if (b_bal > a_bal)
- left->balance = b_bal + 1;
- else
- left->balance = a_bal + 2;
- node->balance = a_bal - b_bal + 1;
- }
- else
- {
- if (a_bal <= -1)
- left->balance = b_bal + 1;
- else
- left->balance = a_bal + b_bal + 2;
- node->balance = a_bal + 1;
- }
-
- return left;
-}
-
-#ifdef G_TREE_DEBUG
-static gint
-g_tree_node_height (GTreeNode *node)
-{
- gint left_height;
- gint right_height;
-
- if (node)
- {
- left_height = 0;
- right_height = 0;
-
- if (node->left_child)
- left_height = g_tree_node_height (node->left);
-
- if (node->right_child)
- right_height = g_tree_node_height (node->right);
-
- return MAX (left_height, right_height) + 1;
- }
-
- return 0;
-}
-
-static void
-g_tree_node_check (GTreeNode *node)
-{
- gint left_height;
- gint right_height;
- gint balance;
- GTreeNode *tmp;
-
- if (node)
- {
- if (node->left_child)
- {
- tmp = g_tree_node_previous (node);
- g_assert (tmp->right == node);
- }
-
- if (node->right_child)
- {
- tmp = g_tree_node_next (node);
- g_assert (tmp->left == node);
- }
-
- left_height = 0;
- right_height = 0;
-
- if (node->left_child)
- left_height = g_tree_node_height (node->left);
- if (node->right_child)
- right_height = g_tree_node_height (node->right);
-
- balance = right_height - left_height;
- g_assert (balance == node->balance);
-
- if (node->left_child)
- g_tree_node_check (node->left);
- if (node->right_child)
- g_tree_node_check (node->right);
- }
-}
-
-static void
-g_tree_node_dump (GTreeNode *node,
- gint indent)
-{
- g_print ("%*s%c\n", indent, "", *(char *)node->key);
-
- if (node->left_child)
- g_tree_node_dump (node->left, indent + 2);
- else if (node->left)
- g_print ("%*s<%c\n", indent + 2, "", *(char *)node->left->key);
-
- if (node->right_child)
- g_tree_node_dump (node->right, indent + 2);
- else if (node->right)
- g_print ("%*s>%c\n", indent + 2, "", *(char *)node->right->key);
-}
-
-
-void
-g_tree_dump (GTree *tree)
-{
- if (tree->root)
- g_tree_node_dump (tree->root, 0);
-}
-#endif
+++ /dev/null
-/* gunibreak.c - line break properties
- *
- * Copyright 2000 Red Hat, Inc.
- *
- * The Gnome 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 of the
- * License, or (at your option) any later version.
- *
- * The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "gunibreak.h"
-
-#define TPROP_PART1(Page, Char) \
- ((break_property_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (break_property_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (break_property_data[break_property_table_part1[Page]][Char]))
-
-#define TPROP_PART2(Page, Char) \
- ((break_property_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (break_property_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (break_property_data[break_property_table_part2[Page]][Char]))
-
-#define PROP(Char) \
- (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
- ? TPROP_PART1 ((Char) >> 8, (Char) & 0xff) \
- : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
- ? TPROP_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
- : G_UNICODE_BREAK_UNKNOWN))
-
-/**
- * g_unichar_break_type:
- * @c: a Unicode character
- *
- * Determines the break type of @c. @c should be a Unicode character
- * (to derive a character from UTF-8 encoded text, use
- * g_utf8_get_char()). The break type is used to find word and line
- * breaks ("text boundaries"), Pango implements the Unicode boundary
- * resolution algorithms and normally you would use a function such
- * as pango_break() instead of caring about break types yourself.
- *
- * Return value: the break type of @c
- **/
-GUnicodeBreakType
-g_unichar_break_type (gunichar c)
-{
- return PROP (c);
-}
+++ /dev/null
-/* gunicollate.c - Collation
- *
- * Copyright 2001,2005 Red Hat, Inc.
- *
- * The Gnome 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 of the
- * License, or (at your option) any later version.
- *
- * The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <locale.h>
-#include <string.h>
-#ifdef __STDC_ISO_10646__
-#include <wchar.h>
-#endif
-
-#ifdef HAVE_CARBON
-#include <CoreServices/CoreServices.h>
-#endif
-
-#include "gmem.h"
-#include "gunicode.h"
-#include "gunicodeprivate.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#ifndef __STDC_ISO_10646__
-#include "gconvert.h"
-#endif
-
-
-#ifdef _MSC_VER
-/* Workaround for bug in MSVCR80.DLL */
-static gsize
-msc_strxfrm_wrapper (char *string1,
- const char *string2,
- gsize count)
-{
- if (!string1 || count <= 0)
- {
- char tmp;
-
- return strxfrm (&tmp, string2, 1);
- }
- return strxfrm (string1, string2, count);
-}
-#define strxfrm msc_strxfrm_wrapper
-#endif
-
-/**
- * g_utf8_collate:
- * @str1: a UTF-8 encoded string
- * @str2: a UTF-8 encoded string
- *
- * Compares two strings for ordering using the linguistically
- * correct rules for the <link linkend="setlocale">current locale</link>.
- * When sorting a large number of strings, it will be significantly
- * faster to obtain collation keys with g_utf8_collate_key() and
- * compare the keys with strcmp() when sorting instead of sorting
- * the original strings.
- *
- * Return value: < 0 if @str1 compares before @str2,
- * 0 if they compare equal, > 0 if @str1 compares after @str2.
- **/
-gint
-g_utf8_collate (const gchar *str1,
- const gchar *str2)
-{
- gint result;
-
-#ifdef HAVE_CARBON
-
- UniChar *str1_utf16;
- UniChar *str2_utf16;
- glong len1;
- glong len2;
- SInt32 retval = 0;
-
- g_return_val_if_fail (str1 != NULL, 0);
- g_return_val_if_fail (str2 != NULL, 0);
-
- str1_utf16 = g_utf8_to_utf16 (str1, -1, NULL, &len1, NULL);
- str2_utf16 = g_utf8_to_utf16 (str2, -1, NULL, &len2, NULL);
-
- UCCompareTextDefault (kUCCollateStandardOptions,
- str1_utf16, len1, str2_utf16, len2,
- NULL, &retval);
- result = retval;
-
- g_free (str2_utf16);
- g_free (str1_utf16);
-
-#elif defined(__STDC_ISO_10646__)
-
- gunichar *str1_norm;
- gunichar *str2_norm;
-
- g_return_val_if_fail (str1 != NULL, 0);
- g_return_val_if_fail (str2 != NULL, 0);
-
- str1_norm = _g_utf8_normalize_wc (str1, -1, G_NORMALIZE_ALL_COMPOSE);
- str2_norm = _g_utf8_normalize_wc (str2, -1, G_NORMALIZE_ALL_COMPOSE);
-
- result = wcscoll ((wchar_t *)str1_norm, (wchar_t *)str2_norm);
-
- g_free (str1_norm);
- g_free (str2_norm);
-
-#else /* !__STDC_ISO_10646__ */
-
- const gchar *charset;
- gchar *str1_norm;
- gchar *str2_norm;
-
- g_return_val_if_fail (str1 != NULL, 0);
- g_return_val_if_fail (str2 != NULL, 0);
-
- str1_norm = g_utf8_normalize (str1, -1, G_NORMALIZE_ALL_COMPOSE);
- str2_norm = g_utf8_normalize (str2, -1, G_NORMALIZE_ALL_COMPOSE);
-
- if (g_get_charset (&charset))
- {
- result = strcoll (str1_norm, str2_norm);
- }
- else
- {
- gchar *str1_locale = g_convert (str1_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
- gchar *str2_locale = g_convert (str2_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
-
- if (str1_locale && str2_locale)
- result = strcoll (str1_locale, str2_locale);
- else if (str1_locale)
- result = -1;
- else if (str2_locale)
- result = 1;
- else
- result = strcmp (str1_norm, str2_norm);
-
- g_free (str1_locale);
- g_free (str2_locale);
- }
-
- g_free (str1_norm);
- g_free (str2_norm);
-
-#endif /* __STDC_ISO_10646__ */
-
- return result;
-}
-
-#if defined(__STDC_ISO_10646__) || defined(HAVE_CARBON)
-/* We need UTF-8 encoding of numbers to encode the weights if
- * we are using wcsxfrm. However, we aren't encoding Unicode
- * characters, so we can't simply use g_unichar_to_utf8.
- *
- * The following routine is taken (with modification) from GNU
- * libc's strxfrm routine:
- *
- * Copyright (C) 1995-1999,2000,2001 Free Software Foundation, Inc.
- * Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
- */
-static inline int
-utf8_encode (char *buf, wchar_t val)
-{
- int retval;
-
- if (val < 0x80)
- {
- if (buf)
- *buf++ = (char) val;
- retval = 1;
- }
- else
- {
- int step;
-
- for (step = 2; step < 6; ++step)
- if ((val & (~(guint32)0 << (5 * step + 1))) == 0)
- break;
- retval = step;
-
- if (buf)
- {
- *buf = (unsigned char) (~0xff >> step);
- --step;
- do
- {
- buf[step] = 0x80 | (val & 0x3f);
- val >>= 6;
- }
- while (--step > 0);
- *buf |= val;
- }
- }
-
- return retval;
-}
-#endif /* __STDC_ISO_10646__ || HAVE_CARBON */
-
-#ifdef HAVE_CARBON
-
-static gchar *
-collate_key_to_string (UCCollationValue *key,
- gsize key_len)
-{
- gchar *result;
- gsize result_len;
- gsize i;
-
- /* Pretty smart algorithm here: ignore first eight bytes of the
- * collation key. It doesn't produce results equivalent to
- * UCCompareCollationKeys's, but the difference seems to be only
- * that UCCompareCollationKeys in some cases produces 0 where our
- * comparison gets -1 or 1. */
-
- if (key_len * sizeof (UCCollationValue) <= 8)
- return g_strdup ("");
-
- result_len = 0;
- for (i = 8; i < key_len * sizeof (UCCollationValue); i++)
- /* there may be nul bytes, encode byteval+1 */
- result_len += utf8_encode (NULL, *((guchar*)key + i) + 1);
-
- result = g_malloc (result_len + 1);
- result_len = 0;
- for (i = 8; i < key_len * sizeof (UCCollationValue); i++)
- result_len += utf8_encode (result + result_len, *((guchar*)key + i) + 1);
-
- result[result_len] = 0;
- return result;
-}
-
-static gchar *
-carbon_collate_key_with_collator (const gchar *str,
- gssize len,
- CollatorRef collator)
-{
- UniChar *str_utf16 = NULL;
- glong len_utf16;
- OSStatus ret;
- UCCollationValue staticbuf[512];
- UCCollationValue *freeme = NULL;
- UCCollationValue *buf;
- ItemCount buf_len;
- ItemCount key_len;
- ItemCount try_len;
- gchar *result = NULL;
-
- str_utf16 = g_utf8_to_utf16 (str, len, NULL, &len_utf16, NULL);
- try_len = len_utf16 * 5 + 2;
-
- if (try_len <= sizeof staticbuf)
- {
- buf = staticbuf;
- buf_len = sizeof staticbuf;
- }
- else
- {
- freeme = g_new (UCCollationValue, try_len);
- buf = freeme;
- buf_len = try_len;
- }
-
- ret = UCGetCollationKey (collator, str_utf16, len_utf16,
- buf_len, &key_len, buf);
-
- if (ret == kCollateBufferTooSmall)
- {
- freeme = g_renew (UCCollationValue, freeme, try_len * 2);
- buf = freeme;
- buf_len = try_len * 2;
- ret = UCGetCollationKey (collator, str_utf16, len_utf16,
- buf_len, &key_len, buf);
- }
-
- if (ret == 0)
- result = collate_key_to_string (buf, key_len);
- else
- result = g_strdup ("");
-
- g_free (freeme);
- g_free (str_utf16);
- return result;
-}
-
-static gchar *
-carbon_collate_key (const gchar *str,
- gssize len)
-{
- static CollatorRef collator;
-
- if (G_UNLIKELY (!collator))
- {
- UCCreateCollator (NULL, 0, kUCCollateStandardOptions, &collator);
-
- if (!collator)
- {
- static gboolean been_here;
- if (!been_here)
- g_warning ("%s: UCCreateCollator failed", G_STRLOC);
- been_here = TRUE;
- return g_strdup ("");
- }
- }
-
- return carbon_collate_key_with_collator (str, len, collator);
-}
-
-static gchar *
-carbon_collate_key_for_filename (const gchar *str,
- gssize len)
-{
- static CollatorRef collator;
-
- if (G_UNLIKELY (!collator))
- {
- /* http://developer.apple.com/qa/qa2004/qa1159.html */
- UCCreateCollator (NULL, 0,
- kUCCollateComposeInsensitiveMask
- | kUCCollateWidthInsensitiveMask
- | kUCCollateCaseInsensitiveMask
- | kUCCollateDigitsOverrideMask
- | kUCCollateDigitsAsNumberMask
- | kUCCollatePunctuationSignificantMask,
- &collator);
-
- if (!collator)
- {
- static gboolean been_here;
- if (!been_here)
- g_warning ("%s: UCCreateCollator failed", G_STRLOC);
- been_here = TRUE;
- return g_strdup ("");
- }
- }
-
- return carbon_collate_key_with_collator (str, len, collator);
-}
-
-#endif /* HAVE_CARBON */
-
-/**
- * g_utf8_collate_key:
- * @str: a UTF-8 encoded string.
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- *
- * Converts a string into a collation key that can be compared
- * with other collation keys produced by the same function using
- * strcmp().
- *
- * The results of comparing the collation keys of two strings
- * with strcmp() will always be the same as comparing the two
- * original keys with g_utf8_collate().
- *
- * Note that this function depends on the
- * <link linkend="setlocale">current locale</link>.
- *
- * Return value: a newly allocated string. This string should
- * be freed with g_free() when you are done with it.
- **/
-gchar *
-g_utf8_collate_key (const gchar *str,
- gssize len)
-{
- gchar *result;
-
-#ifdef HAVE_CARBON
-
- g_return_val_if_fail (str != NULL, NULL);
- result = carbon_collate_key (str, len);
-
-#elif defined(__STDC_ISO_10646__)
-
- gsize xfrm_len;
- gunichar *str_norm;
- wchar_t *result_wc;
- gsize i;
- gsize result_len = 0;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- str_norm = _g_utf8_normalize_wc (str, len, G_NORMALIZE_ALL_COMPOSE);
-
- xfrm_len = wcsxfrm (NULL, (wchar_t *)str_norm, 0);
- result_wc = g_new (wchar_t, xfrm_len + 1);
- wcsxfrm (result_wc, (wchar_t *)str_norm, xfrm_len + 1);
-
- for (i=0; i < xfrm_len; i++)
- result_len += utf8_encode (NULL, result_wc[i]);
-
- result = g_malloc (result_len + 1);
- result_len = 0;
- for (i=0; i < xfrm_len; i++)
- result_len += utf8_encode (result + result_len, result_wc[i]);
-
- result[result_len] = '\0';
-
- g_free (result_wc);
- g_free (str_norm);
-
- return result;
-#else /* !__STDC_ISO_10646__ */
-
- gsize xfrm_len;
- const gchar *charset;
- gchar *str_norm;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- str_norm = g_utf8_normalize (str, len, G_NORMALIZE_ALL_COMPOSE);
-
- result = NULL;
-
- if (g_get_charset (&charset))
- {
- xfrm_len = strxfrm (NULL, str_norm, 0);
- if (xfrm_len >= 0 && xfrm_len < G_MAXINT - 2)
- {
- result = g_malloc (xfrm_len + 1);
- strxfrm (result, str_norm, xfrm_len + 1);
- }
- }
- else
- {
- gchar *str_locale = g_convert (str_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
-
- if (str_locale)
- {
- xfrm_len = strxfrm (NULL, str_locale, 0);
- if (xfrm_len < 0 || xfrm_len >= G_MAXINT - 2)
- {
- g_free (str_locale);
- str_locale = NULL;
- }
- }
- if (str_locale)
- {
- result = g_malloc (xfrm_len + 2);
- result[0] = 'A';
- strxfrm (result + 1, str_locale, xfrm_len + 1);
-
- g_free (str_locale);
- }
- }
-
- if (!result)
- {
- xfrm_len = strlen (str_norm);
- result = g_malloc (xfrm_len + 2);
- result[0] = 'B';
- memcpy (result + 1, str_norm, xfrm_len);
- result[xfrm_len+1] = '\0';
- }
-
- g_free (str_norm);
-#endif /* __STDC_ISO_10646__ */
-
- return result;
-}
-
-/* This is a collation key that is very very likely to sort before any
- collation key that libc strxfrm generates. We use this before any
- special case (dot or number) to make sure that its sorted before
- anything else.
- */
-#define COLLATION_SENTINEL "\1\1\1"
-
-/**
- * g_utf8_collate_key_for_filename:
- * @str: a UTF-8 encoded string.
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- *
- * Converts a string into a collation key that can be compared
- * with other collation keys produced by the same function using strcmp().
- *
- * In order to sort filenames correctly, this function treats the dot '.'
- * as a special case. Most dictionary orderings seem to consider it
- * insignificant, thus producing the ordering "event.c" "eventgenerator.c"
- * "event.h" instead of "event.c" "event.h" "eventgenerator.c". Also, we
- * would like to treat numbers intelligently so that "file1" "file10" "file5"
- * is sorted as "file1" "file5" "file10".
- *
- * Note that this function depends on the
- * <link linkend="setlocale">current locale</link>.
- *
- * Return value: a newly allocated string. This string should
- * be freed with g_free() when you are done with it.
- *
- * Since: 2.8
- */
-gchar*
-g_utf8_collate_key_for_filename (const gchar *str,
- gssize len)
-{
-#ifndef HAVE_CARBON
- GString *result;
- GString *append;
- const gchar *p;
- const gchar *prev;
- const gchar *end;
- gchar *collate_key;
- gint digits;
- gint leading_zeros;
-
- /*
- * How it works:
- *
- * Split the filename into collatable substrings which do
- * not contain [.0-9] and special-cased substrings. The collatable
- * substrings are run through the normal g_utf8_collate_key() and the
- * resulting keys are concatenated with keys generated from the
- * special-cased substrings.
- *
- * Special cases: Dots are handled by replacing them with '\1' which
- * implies that short dot-delimited substrings are before long ones,
- * e.g.
- *
- * a\1a (a.a)
- * a-\1a (a-.a)
- * aa\1a (aa.a)
- *
- * Numbers are handled by prepending to each number d-1 superdigits
- * where d = number of digits in the number and SUPERDIGIT is a
- * character with an integer value higher than any digit (for instance
- * ':'). This ensures that single-digit numbers are sorted before
- * double-digit numbers which in turn are sorted separately from
- * triple-digit numbers, etc. To avoid strange side-effects when
- * sorting strings that already contain SUPERDIGITs, a '\2'
- * is also prepended, like this
- *
- * file\21 (file1)
- * file\25 (file5)
- * file\2:10 (file10)
- * file\2:26 (file26)
- * file\2::100 (file100)
- * file:foo (file:foo)
- *
- * This has the side-effect of sorting numbers before everything else (except
- * dots), but this is probably OK.
- *
- * Leading digits are ignored when doing the above. To discriminate
- * numbers which differ only in the number of leading digits, we append
- * the number of leading digits as a byte at the very end of the collation
- * key.
- *
- * To try avoid conflict with any collation key sequence generated by libc we
- * start each switch to a special cased part with a sentinel that hopefully
- * will sort before anything libc will generate.
- */
-
- if (len < 0)
- len = strlen (str);
-
- result = g_string_sized_new (len * 2);
- append = g_string_sized_new (0);
-
- end = str + len;
-
- /* No need to use utf8 functions, since we're only looking for ascii chars */
- for (prev = p = str; p < end; p++)
- {
- switch (*p)
- {
- case '.':
- if (prev != p)
- {
- collate_key = g_utf8_collate_key (prev, p - prev);
- g_string_append (result, collate_key);
- g_free (collate_key);
- }
-
- g_string_append (result, COLLATION_SENTINEL "\1");
-
- /* skip the dot */
- prev = p + 1;
- break;
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (prev != p)
- {
- collate_key = g_utf8_collate_key (prev, p - prev);
- g_string_append (result, collate_key);
- g_free (collate_key);
- }
-
- g_string_append (result, COLLATION_SENTINEL "\2");
-
- prev = p;
-
- /* write d-1 colons */
- if (*p == '0')
- {
- leading_zeros = 1;
- digits = 0;
- }
- else
- {
- leading_zeros = 0;
- digits = 1;
- }
-
- while (++p < end)
- {
- if (*p == '0' && !digits)
- ++leading_zeros;
- else if (g_ascii_isdigit(*p))
- ++digits;
- else
- {
- /* count an all-zero sequence as
- * one digit plus leading zeros
- */
- if (!digits)
- {
- ++digits;
- --leading_zeros;
- }
- break;
- }
- }
-
- while (digits > 1)
- {
- g_string_append_c (result, ':');
- --digits;
- }
-
- if (leading_zeros > 0)
- {
- g_string_append_c (append, (char)leading_zeros);
- prev += leading_zeros;
- }
-
- /* write the number itself */
- g_string_append_len (result, prev, p - prev);
-
- prev = p;
- --p; /* go one step back to avoid disturbing outer loop */
- break;
-
- default:
- /* other characters just accumulate */
- break;
- }
- }
-
- if (prev != p)
- {
- collate_key = g_utf8_collate_key (prev, p - prev);
- g_string_append (result, collate_key);
- g_free (collate_key);
- }
-
- g_string_append (result, append->str);
- g_string_free (append, TRUE);
-
- return g_string_free (result, FALSE);
-#else /* HAVE_CARBON */
- return carbon_collate_key_for_filename (str, len);
-#endif
-}
+++ /dev/null
-/* decomp.c - Character decomposition.
- *
- * Copyright (C) 1999, 2000 Tom Tromey
- * Copyright 2000 Red Hat, Inc.
- *
- * The Gnome 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 of the
- * License, or (at your option) any later version.
- *
- * The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "gunicode.h"
-#include "gunidecomp.h"
-#include "gmem.h"
-#include "gunicomp.h"
-#include "gunicodeprivate.h"
-
-
-#define CC_PART1(Page, Char) \
- ((combining_class_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (combining_class_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (cclass_data[combining_class_table_part1[Page]][Char]))
-
-#define CC_PART2(Page, Char) \
- ((combining_class_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (combining_class_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (cclass_data[combining_class_table_part2[Page]][Char]))
-
-#define COMBINING_CLASS(Char) \
- (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
- ? CC_PART1 ((Char) >> 8, (Char) & 0xff) \
- : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
- ? CC_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
- : 0))
-
-/**
- * g_unichar_combining_class:
- * @uc: a Unicode character
- *
- * Determines the canonical combining class of a Unicode character.
- *
- * Return value: the combining class of the character
- *
- * Since: 2.14
- **/
-gint
-g_unichar_combining_class (gunichar uc)
-{
- return COMBINING_CLASS (uc);
-}
-
-/* constants for hangul syllable [de]composition */
-#define SBase 0xAC00
-#define LBase 0x1100
-#define VBase 0x1161
-#define TBase 0x11A7
-#define LCount 19
-#define VCount 21
-#define TCount 28
-#define NCount (VCount * TCount)
-#define SCount (LCount * NCount)
-
-/**
- * g_unicode_canonical_ordering:
- * @string: a UCS-4 encoded string.
- * @len: the maximum length of @string to use.
- *
- * Computes the canonical ordering of a string in-place.
- * This rearranges decomposed characters in the string
- * according to their combining classes. See the Unicode
- * manual for more information.
- **/
-void
-g_unicode_canonical_ordering (gunichar *string,
- gsize len)
-{
- gsize i;
- int swap = 1;
-
- while (swap)
- {
- int last;
- swap = 0;
- last = COMBINING_CLASS (string[0]);
- for (i = 0; i < len - 1; ++i)
- {
- int next = COMBINING_CLASS (string[i + 1]);
- if (next != 0 && last > next)
- {
- gsize j;
- /* Percolate item leftward through string. */
- for (j = i + 1; j > 0; --j)
- {
- gunichar t;
- if (COMBINING_CLASS (string[j - 1]) <= next)
- break;
- t = string[j];
- string[j] = string[j - 1];
- string[j - 1] = t;
- swap = 1;
- }
- /* We're re-entering the loop looking at the old
- character again. */
- next = last;
- }
- last = next;
- }
- }
-}
-
-/* http://www.unicode.org/unicode/reports/tr15/#Hangul
- * r should be null or have sufficient space. Calling with r == NULL will
- * only calculate the result_len; however, a buffer with space for three
- * characters will always be big enough. */
-static void
-decompose_hangul (gunichar s,
- gunichar *r,
- gsize *result_len)
-{
- gint SIndex = s - SBase;
-
- /* not a hangul syllable */
- if (SIndex < 0 || SIndex >= SCount)
- {
- if (r)
- r[0] = s;
- *result_len = 1;
- }
- else
- {
- gunichar L = LBase + SIndex / NCount;
- gunichar V = VBase + (SIndex % NCount) / TCount;
- gunichar T = TBase + SIndex % TCount;
-
- if (r)
- {
- r[0] = L;
- r[1] = V;
- }
-
- if (T != TBase)
- {
- if (r)
- r[2] = T;
- *result_len = 3;
- }
- else
- *result_len = 2;
- }
-}
-
-/* returns a pointer to a null-terminated UTF-8 string */
-static const gchar *
-find_decomposition (gunichar ch,
- gboolean compat)
-{
- int start = 0;
- int end = G_N_ELEMENTS (decomp_table);
-
- if (ch >= decomp_table[start].ch &&
- ch <= decomp_table[end - 1].ch)
- {
- while (TRUE)
- {
- int half = (start + end) / 2;
- if (ch == decomp_table[half].ch)
- {
- int offset;
-
- if (compat)
- {
- offset = decomp_table[half].compat_offset;
- if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
- offset = decomp_table[half].canon_offset;
- }
- else
- {
- offset = decomp_table[half].canon_offset;
- if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
- return NULL;
- }
-
- return &(decomp_expansion_string[offset]);
- }
- else if (half == start)
- break;
- else if (ch > decomp_table[half].ch)
- start = half;
- else
- end = half;
- }
- }
-
- return NULL;
-}
-
-/**
- * g_unicode_canonical_decomposition:
- * @ch: a Unicode character.
- * @result_len: location to store the length of the return value.
- *
- * Computes the canonical decomposition of a Unicode character.
- *
- * Return value: a newly allocated string of Unicode characters.
- * @result_len is set to the resulting length of the string.
- **/
-gunichar *
-g_unicode_canonical_decomposition (gunichar ch,
- gsize *result_len)
-{
- const gchar *decomp;
- const gchar *p;
- gunichar *r;
-
- /* Hangul syllable */
- if (ch >= 0xac00 && ch <= 0xd7a3)
- {
- decompose_hangul (ch, NULL, result_len);
- r = g_malloc (*result_len * sizeof (gunichar));
- decompose_hangul (ch, r, result_len);
- }
- else if ((decomp = find_decomposition (ch, FALSE)) != NULL)
- {
- /* Found it. */
- int i;
-
- *result_len = g_utf8_strlen (decomp, -1);
- r = g_malloc (*result_len * sizeof (gunichar));
-
- for (p = decomp, i = 0; *p != '\0'; p = g_utf8_next_char (p), i++)
- r[i] = g_utf8_get_char (p);
- }
- else
- {
- /* Not in our table. */
- r = g_malloc (sizeof (gunichar));
- *r = ch;
- *result_len = 1;
- }
-
- /* Supposedly following the Unicode 2.1.9 table means that the
- decompositions come out in canonical order. I haven't tested
- this, but we rely on it here. */
- return r;
-}
-
-/* L,V => LV and LV,T => LVT */
-static gboolean
-combine_hangul (gunichar a,
- gunichar b,
- gunichar *result)
-{
- gint LIndex = a - LBase;
- gint SIndex = a - SBase;
-
- gint VIndex = b - VBase;
- gint TIndex = b - TBase;
-
- if (0 <= LIndex && LIndex < LCount
- && 0 <= VIndex && VIndex < VCount)
- {
- *result = SBase + (LIndex * VCount + VIndex) * TCount;
- return TRUE;
- }
- else if (0 <= SIndex && SIndex < SCount && (SIndex % TCount) == 0
- && 0 < TIndex && TIndex < TCount)
- {
- *result = a + TIndex;
- return TRUE;
- }
-
- return FALSE;
-}
-
-#define CI(Page, Char) \
- ((compose_table[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (compose_table[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (compose_data[compose_table[Page]][Char]))
-
-#define COMPOSE_INDEX(Char) \
- (((Char >> 8) > (COMPOSE_TABLE_LAST)) ? 0 : CI((Char) >> 8, (Char) & 0xff))
-
-static gboolean
-combine (gunichar a,
- gunichar b,
- gunichar *result)
-{
- gushort index_a, index_b;
-
- if (combine_hangul (a, b, result))
- return TRUE;
-
- index_a = COMPOSE_INDEX(a);
-
- if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
- {
- if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
- {
- *result = compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
- return TRUE;
- }
- else
- return FALSE;
- }
-
- index_b = COMPOSE_INDEX(b);
-
- if (index_b >= COMPOSE_SECOND_SINGLE_START)
- {
- if (a == compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
- {
- *result = compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
- return TRUE;
- }
- else
- return FALSE;
- }
-
- if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START &&
- index_b >= COMPOSE_SECOND_START && index_b < COMPOSE_SECOND_SINGLE_START)
- {
- gunichar res = compose_array[index_a - COMPOSE_FIRST_START][index_b - COMPOSE_SECOND_START];
-
- if (res)
- {
- *result = res;
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-gunichar *
-_g_utf8_normalize_wc (const gchar *str,
- gssize max_len,
- GNormalizeMode mode)
-{
- gsize n_wc;
- gunichar *wc_buffer;
- const char *p;
- gsize last_start;
- gboolean do_compat = (mode == G_NORMALIZE_NFKC ||
- mode == G_NORMALIZE_NFKD);
- gboolean do_compose = (mode == G_NORMALIZE_NFC ||
- mode == G_NORMALIZE_NFKC);
-
- n_wc = 0;
- p = str;
- while ((max_len < 0 || p < str + max_len) && *p)
- {
- const gchar *decomp;
- gunichar wc = g_utf8_get_char (p);
-
- if (wc >= 0xac00 && wc <= 0xd7a3)
- {
- gsize result_len;
- decompose_hangul (wc, NULL, &result_len);
- n_wc += result_len;
- }
- else
- {
- decomp = find_decomposition (wc, do_compat);
-
- if (decomp)
- n_wc += g_utf8_strlen (decomp, -1);
- else
- n_wc++;
- }
-
- p = g_utf8_next_char (p);
- }
-
- wc_buffer = g_new (gunichar, n_wc + 1);
-
- last_start = 0;
- n_wc = 0;
- p = str;
- while ((max_len < 0 || p < str + max_len) && *p)
- {
- gunichar wc = g_utf8_get_char (p);
- const gchar *decomp;
- int cc;
- gsize old_n_wc = n_wc;
-
- if (wc >= 0xac00 && wc <= 0xd7a3)
- {
- gsize result_len;
- decompose_hangul (wc, wc_buffer + n_wc, &result_len);
- n_wc += result_len;
- }
- else
- {
- decomp = find_decomposition (wc, do_compat);
-
- if (decomp)
- {
- const char *pd;
- for (pd = decomp; *pd != '\0'; pd = g_utf8_next_char (pd))
- wc_buffer[n_wc++] = g_utf8_get_char (pd);
- }
- else
- wc_buffer[n_wc++] = wc;
- }
-
- if (n_wc > 0)
- {
- cc = COMBINING_CLASS (wc_buffer[old_n_wc]);
-
- if (cc == 0)
- {
- g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
- last_start = old_n_wc;
- }
- }
-
- p = g_utf8_next_char (p);
- }
-
- if (n_wc > 0)
- {
- g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
- last_start = n_wc;
- }
-
- wc_buffer[n_wc] = 0;
-
- /* All decomposed and reordered */
-
- if (do_compose && n_wc > 0)
- {
- gsize i, j;
- int last_cc = 0;
- last_start = 0;
-
- for (i = 0; i < n_wc; i++)
- {
- int cc = COMBINING_CLASS (wc_buffer[i]);
-
- if (i > 0 &&
- (last_cc == 0 || last_cc < cc) &&
- combine (wc_buffer[last_start], wc_buffer[i],
- &wc_buffer[last_start]))
- {
- for (j = i + 1; j < n_wc; j++)
- wc_buffer[j-1] = wc_buffer[j];
- n_wc--;
- i--;
-
- if (i == last_start)
- last_cc = 0;
- else
- last_cc = COMBINING_CLASS (wc_buffer[i-1]);
-
- continue;
- }
-
- if (cc == 0)
- last_start = i;
-
- last_cc = cc;
- }
- }
-
- wc_buffer[n_wc] = 0;
-
- return wc_buffer;
-}
-
-/**
- * g_utf8_normalize:
- * @str: a UTF-8 encoded string.
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- * @mode: the type of normalization to perform.
- *
- * Converts a string into canonical form, standardizing
- * such issues as whether a character with an accent
- * is represented as a base character and combining
- * accent or as a single precomposed character. The
- * string has to be valid UTF-8, otherwise %NULL is
- * returned. You should generally call g_utf8_normalize()
- * before comparing two Unicode strings.
- *
- * The normalization mode %G_NORMALIZE_DEFAULT only
- * standardizes differences that do not affect the
- * text content, such as the above-mentioned accent
- * representation. %G_NORMALIZE_ALL also standardizes
- * the "compatibility" characters in Unicode, such
- * as SUPERSCRIPT THREE to the standard forms
- * (in this case DIGIT THREE). Formatting information
- * may be lost but for most text operations such
- * characters should be considered the same.
- *
- * %G_NORMALIZE_DEFAULT_COMPOSE and %G_NORMALIZE_ALL_COMPOSE
- * are like %G_NORMALIZE_DEFAULT and %G_NORMALIZE_ALL,
- * but returned a result with composed forms rather
- * than a maximally decomposed form. This is often
- * useful if you intend to convert the string to
- * a legacy encoding or pass it to a system with
- * less capable Unicode handling.
- *
- * Return value: a newly allocated string, that is the
- * normalized form of @str, or %NULL if @str is not
- * valid UTF-8.
- **/
-gchar *
-g_utf8_normalize (const gchar *str,
- gssize len,
- GNormalizeMode mode)
-{
- gunichar *result_wc = _g_utf8_normalize_wc (str, len, mode);
- gchar *result;
-
- result = g_ucs4_to_utf8 (result_wc, -1, NULL, NULL, NULL);
- g_free (result_wc);
-
- return result;
-}
+++ /dev/null
-/* guniprop.c - Unicode character properties.
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * 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 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 "config.h"
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <locale.h>
-
-#include "gmem.h"
-#include "gstring.h"
-#include "gtestutils.h"
-#include "gtypes.h"
-#include "gunicode.h"
-#include "gunichartables.h"
-#include "gmirroringtable.h"
-#include "gscripttable.h"
-#include "gunicodeprivate.h"
-#ifdef G_OS_WIN32
-#include "gwin32.h"
-#endif
-
-#define ATTR_TABLE(Page) (((Page) <= G_UNICODE_LAST_PAGE_PART1) \
- ? attr_table_part1[Page] \
- : attr_table_part2[(Page) - 0xe00])
-
-#define ATTTABLE(Page, Char) \
- ((ATTR_TABLE(Page) == G_UNICODE_MAX_TABLE_INDEX) ? 0 : (attr_data[ATTR_TABLE(Page)][Char]))
-
-#define TTYPE_PART1(Page, Char) \
- ((type_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (type_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (type_data[type_table_part1[Page]][Char]))
-
-#define TTYPE_PART2(Page, Char) \
- ((type_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
- ? (type_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
- : (type_data[type_table_part2[Page]][Char]))
-
-#define TYPE(Char) \
- (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
- ? TTYPE_PART1 ((Char) >> 8, (Char) & 0xff) \
- : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
- ? TTYPE_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
- : G_UNICODE_UNASSIGNED))
-
-
-#define IS(Type, Class) (((guint)1 << (Type)) & (Class))
-#define OR(Type, Rest) (((guint)1 << (Type)) | (Rest))
-
-
-
-#define ISALPHA(Type) IS ((Type), \
- OR (G_UNICODE_LOWERCASE_LETTER, \
- OR (G_UNICODE_UPPERCASE_LETTER, \
- OR (G_UNICODE_TITLECASE_LETTER, \
- OR (G_UNICODE_MODIFIER_LETTER, \
- OR (G_UNICODE_OTHER_LETTER, 0))))))
-
-#define ISALDIGIT(Type) IS ((Type), \
- OR (G_UNICODE_DECIMAL_NUMBER, \
- OR (G_UNICODE_LETTER_NUMBER, \
- OR (G_UNICODE_OTHER_NUMBER, \
- OR (G_UNICODE_LOWERCASE_LETTER, \
- OR (G_UNICODE_UPPERCASE_LETTER, \
- OR (G_UNICODE_TITLECASE_LETTER, \
- OR (G_UNICODE_MODIFIER_LETTER, \
- OR (G_UNICODE_OTHER_LETTER, 0)))))))))
-
-#define ISMARK(Type) IS ((Type), \
- OR (G_UNICODE_NON_SPACING_MARK, \
- OR (G_UNICODE_COMBINING_MARK, \
- OR (G_UNICODE_ENCLOSING_MARK, 0))))
-
-#define ISZEROWIDTHTYPE(Type) IS ((Type), \
- OR (G_UNICODE_NON_SPACING_MARK, \
- OR (G_UNICODE_ENCLOSING_MARK, \
- OR (G_UNICODE_FORMAT, 0))))
-
-/**
- * g_unichar_isalnum:
- * @c: a Unicode character
- *
- * Determines whether a character is alphanumeric.
- * Given some UTF-8 text, obtain a character value
- * with g_utf8_get_char().
- *
- * Return value: %TRUE if @c is an alphanumeric character
- **/
-gboolean
-g_unichar_isalnum (gunichar c)
-{
- return ISALDIGIT (TYPE (c)) ? TRUE : FALSE;
-}
-
-/**
- * g_unichar_isalpha:
- * @c: a Unicode character
- *
- * Determines whether a character is alphabetic (i.e. a letter).
- * Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is an alphabetic character
- **/
-gboolean
-g_unichar_isalpha (gunichar c)
-{
- return ISALPHA (TYPE (c)) ? TRUE : FALSE;
-}
-
-
-/**
- * g_unichar_iscntrl:
- * @c: a Unicode character
- *
- * Determines whether a character is a control character.
- * Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is a control character
- **/
-gboolean
-g_unichar_iscntrl (gunichar c)
-{
- return TYPE (c) == G_UNICODE_CONTROL;
-}
-
-/**
- * g_unichar_isdigit:
- * @c: a Unicode character
- *
- * Determines whether a character is numeric (i.e. a digit). This
- * covers ASCII 0-9 and also digits in other languages/scripts. Given
- * some UTF-8 text, obtain a character value with g_utf8_get_char().
- *
- * Return value: %TRUE if @c is a digit
- **/
-gboolean
-g_unichar_isdigit (gunichar c)
-{
- return TYPE (c) == G_UNICODE_DECIMAL_NUMBER;
-}
-
-
-/**
- * g_unichar_isgraph:
- * @c: a Unicode character
- *
- * Determines whether a character is printable and not a space
- * (returns %FALSE for control characters, format characters, and
- * spaces). g_unichar_isprint() is similar, but returns %TRUE for
- * spaces. Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is printable unless it's a space
- **/
-gboolean
-g_unichar_isgraph (gunichar c)
-{
- return !IS (TYPE(c),
- OR (G_UNICODE_CONTROL,
- OR (G_UNICODE_FORMAT,
- OR (G_UNICODE_UNASSIGNED,
- OR (G_UNICODE_SURROGATE,
- OR (G_UNICODE_SPACE_SEPARATOR,
- 0))))));
-}
-
-/**
- * g_unichar_islower:
- * @c: a Unicode character
- *
- * Determines whether a character is a lowercase letter.
- * Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is a lowercase letter
- **/
-gboolean
-g_unichar_islower (gunichar c)
-{
- return TYPE (c) == G_UNICODE_LOWERCASE_LETTER;
-}
-
-
-/**
- * g_unichar_isprint:
- * @c: a Unicode character
- *
- * Determines whether a character is printable.
- * Unlike g_unichar_isgraph(), returns %TRUE for spaces.
- * Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is printable
- **/
-gboolean
-g_unichar_isprint (gunichar c)
-{
- return !IS (TYPE(c),
- OR (G_UNICODE_CONTROL,
- OR (G_UNICODE_FORMAT,
- OR (G_UNICODE_UNASSIGNED,
- OR (G_UNICODE_SURROGATE,
- 0)))));
-}
-
-/**
- * g_unichar_ispunct:
- * @c: a Unicode character
- *
- * Determines whether a character is punctuation or a symbol.
- * Given some UTF-8 text, obtain a character value with
- * g_utf8_get_char().
- *
- * Return value: %TRUE if @c is a punctuation or symbol character
- **/
-gboolean
-g_unichar_ispunct (gunichar c)
-{
- return IS (TYPE(c),
- OR (G_UNICODE_CONNECT_PUNCTUATION,
- OR (G_UNICODE_DASH_PUNCTUATION,
- OR (G_UNICODE_CLOSE_PUNCTUATION,
- OR (G_UNICODE_FINAL_PUNCTUATION,
- OR (G_UNICODE_INITIAL_PUNCTUATION,
- OR (G_UNICODE_OTHER_PUNCTUATION,
- OR (G_UNICODE_OPEN_PUNCTUATION,
- OR (G_UNICODE_CURRENCY_SYMBOL,
- OR (G_UNICODE_MODIFIER_SYMBOL,
- OR (G_UNICODE_MATH_SYMBOL,
- OR (G_UNICODE_OTHER_SYMBOL,
- 0)))))))))))) ? TRUE : FALSE;
-}
-
-/**
- * g_unichar_isspace:
- * @c: a Unicode character
- *
- * Determines whether a character is a space, tab, or line separator
- * (newline, carriage return, etc.). Given some UTF-8 text, obtain a
- * character value with g_utf8_get_char().
- *
- * (Note: don't use this to do word breaking; you have to use
- * Pango or equivalent to get word breaking right, the algorithm
- * is fairly complex.)
- *
- * Return value: %TRUE if @c is a space character
- **/
-gboolean
-g_unichar_isspace (gunichar c)
-{
- switch (c)
- {
- /* special-case these since Unicode thinks they are not spaces */
- case '\t':
- case '\n':
- case '\r':
- case '\f':
- return TRUE;
- break;
-
- default:
- {
- return IS (TYPE(c),
- OR (G_UNICODE_SPACE_SEPARATOR,
- OR (G_UNICODE_LINE_SEPARATOR,
- OR (G_UNICODE_PARAGRAPH_SEPARATOR,
- 0)))) ? TRUE : FALSE;
- }
- break;
- }
-}
-
-/**
- * g_unichar_ismark:
- * @c: a Unicode character
- *
- * Determines whether a character is a mark (non-spacing mark,
- * combining mark, or enclosing mark in Unicode speak).
- * Given some UTF-8 text, obtain a character value
- * with g_utf8_get_char().
- *
- * Note: in most cases where isalpha characters are allowed,
- * ismark characters should be allowed to as they are essential
- * for writing most European languages as well as many non-Latin
- * scripts.
- *
- * Return value: %TRUE if @c is a mark character
- *
- * Since: 2.14
- **/
-gboolean
-g_unichar_ismark (gunichar c)
-{
- return ISMARK (TYPE (c));
-}
-
-/**
- * g_unichar_isupper:
- * @c: a Unicode character
- *
- * Determines if a character is uppercase.
- *
- * Return value: %TRUE if @c is an uppercase character
- **/
-gboolean
-g_unichar_isupper (gunichar c)
-{
- return TYPE (c) == G_UNICODE_UPPERCASE_LETTER;
-}
-
-/**
- * g_unichar_istitle:
- * @c: a Unicode character
- *
- * Determines if a character is titlecase. Some characters in
- * Unicode which are composites, such as the DZ digraph
- * have three case variants instead of just two. The titlecase
- * form is used at the beginning of a word where only the
- * first letter is capitalized. The titlecase form of the DZ
- * digraph is U+01F2 LATIN CAPITAL LETTTER D WITH SMALL LETTER Z.
- *
- * Return value: %TRUE if the character is titlecase
- **/
-gboolean
-g_unichar_istitle (gunichar c)
-{
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- if (title_table[i][0] == c)
- return TRUE;
- return FALSE;
-}
-
-/**
- * g_unichar_isxdigit:
- * @c: a Unicode character.
- *
- * Determines if a character is a hexidecimal digit.
- *
- * Return value: %TRUE if the character is a hexadecimal digit
- **/
-gboolean
-g_unichar_isxdigit (gunichar c)
-{
- return ((c >= 'a' && c <= 'f')
- || (c >= 'A' && c <= 'F')
- || (TYPE (c) == G_UNICODE_DECIMAL_NUMBER));
-}
-
-/**
- * g_unichar_isdefined:
- * @c: a Unicode character
- *
- * Determines if a given character is assigned in the Unicode
- * standard.
- *
- * Return value: %TRUE if the character has an assigned value
- **/
-gboolean
-g_unichar_isdefined (gunichar c)
-{
- return !IS (TYPE(c),
- OR (G_UNICODE_UNASSIGNED,
- OR (G_UNICODE_SURROGATE,
- 0)));
-}
-
-/**
- * g_unichar_iszerowidth:
- * @c: a Unicode character
- *
- * Determines if a given character typically takes zero width when rendered.
- * The return value is %TRUE for all non-spacing and enclosing marks
- * (e.g., combining accents), format characters, zero-width
- * space, but not U+00AD SOFT HYPHEN.
- *
- * A typical use of this function is with one of g_unichar_iswide() or
- * g_unichar_iswide_cjk() to determine the number of cells a string occupies
- * when displayed on a grid display (terminals). However, note that not all
- * terminals support zero-width rendering of zero-width marks.
- *
- * Return value: %TRUE if the character has zero width
- *
- * Since: 2.14
- **/
-gboolean
-g_unichar_iszerowidth (gunichar c)
-{
- if (G_UNLIKELY (c == 0x00AD))
- return FALSE;
-
- if (G_UNLIKELY (ISZEROWIDTHTYPE (TYPE (c))))
- return TRUE;
-
- if (G_UNLIKELY ((c >= 0x1160 && c < 0x1200) ||
- c == 0x200B))
- return TRUE;
-
- return FALSE;
-}
-
-struct Interval
-{
- gunichar start, end;
-};
-
-static int
-interval_compare (const void *key, const void *elt)
-{
- gunichar c = GPOINTER_TO_UINT (key);
- struct Interval *interval = (struct Interval *)elt;
-
- if (c < interval->start)
- return -1;
- if (c > interval->end)
- return +1;
-
- return 0;
-}
-
-/**
- * g_unichar_iswide:
- * @c: a Unicode character
- *
- * Determines if a character is typically rendered in a double-width
- * cell.
- *
- * Return value: %TRUE if the character is wide
- **/
-gboolean
-g_unichar_iswide (gunichar c)
-{
- /* sorted list of intervals of East_Asian_Width = W and F characters
- * from Unicode 5.1.0. produced by mungling output of:
- * grep ';[FW]\>' EastAsianWidth.txt */
- static const struct Interval wide[] = {
- {0x1100, 0x1159}, {0x115F, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99},
- {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E},
- {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E},
- {0x3190, 0x31B7}, {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3243},
- {0x3250, 0x32FE}, {0x3300, 0x4DB5}, {0x4E00, 0x9FC3}, {0xA000, 0xA48C},
- {0xA490, 0xA4C6}, {0xAC00, 0xD7A3}, {0xF900, 0xFA2D}, {0xFA30, 0xFA6A},
- {0xFA70, 0xFAD9}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66},
- {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x20000, 0x2FFFD},
- {0x30000, 0x3FFFD}
- };
-
- if (bsearch (GUINT_TO_POINTER (c), wide, G_N_ELEMENTS (wide), sizeof wide[0],
- interval_compare))
- return TRUE;
-
- return FALSE;
-}
-
-
-/**
- * g_unichar_iswide_cjk:
- * @c: a Unicode character
- *
- * Determines if a character is typically rendered in a double-width
- * cell under legacy East Asian locales. If a character is wide according to
- * g_unichar_iswide(), then it is also reported wide with this function, but
- * the converse is not necessarily true. See the
- * <ulink url="http://www.unicode.org/reports/tr11/">Unicode Standard
- * Annex #11</ulink> for details.
- *
- * If a character passes the g_unichar_iswide() test then it will also pass
- * this test, but not the other way around. Note that some characters may
- * pas both this test and g_unichar_iszerowidth().
- *
- * Return value: %TRUE if the character is wide in legacy East Asian locales
- *
- * Since: 2.12
- */
-gboolean
-g_unichar_iswide_cjk (gunichar c)
-{
- /* sorted list of intervals of East_Asian_Width = A and F characters
- * from Unicode 5.1.0. produced by mungling output of:
- * grep ';[A]\>' EastAsianWidth.txt */
- static const struct Interval ambiguous[] = {
- {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA},
- {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF},
- {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
- {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, {0x00F0, 0x00F0},
- {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE},
- {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
- {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138},
- {0x013F, 0x0142}, {0x0144, 0x0144}, {0x0148, 0x014B}, {0x014D, 0x014D},
- {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
- {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6},
- {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251},
- {0x0261, 0x0261}, {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
- {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, {0x02DD, 0x02DD},
- {0x02DF, 0x02DF}, {0x0300, 0x036F}, {0x0391, 0x03A1}, {0x03A3, 0x03A9},
- {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F},
- {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019},
- {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027}, {0x2030, 0x2030},
- {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E},
- {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC},
- {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113},
- {0x2116, 0x2116}, {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B},
- {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179},
- {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4},
- {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208},
- {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215},
- {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225},
- {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, {0x223C, 0x223D},
- {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261},
- {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283},
- {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5},
- {0x22BF, 0x22BF}, {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B},
- {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1},
- {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, {0x25BC, 0x25BD},
- {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1},
- {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609},
- {0x260E, 0x260F}, {0x2614, 0x2615}, {0x261C, 0x261C}, {0x261E, 0x261E},
- {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665},
- {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, {0x273D, 0x273D},
- {0x2776, 0x277F}, {0xE000, 0xF8FF}, {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD},
- {0xE0100, 0xE01EF}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}
- };
-
- if (g_unichar_iswide (c))
- return TRUE;
-
- if (bsearch (GUINT_TO_POINTER (c), ambiguous, G_N_ELEMENTS (ambiguous), sizeof ambiguous[0],
- interval_compare))
- return TRUE;
-
- return FALSE;
-}
-
-
-/**
- * g_unichar_toupper:
- * @c: a Unicode character
- *
- * Converts a character to uppercase.
- *
- * Return value: the result of converting @c to uppercase.
- * If @c is not an lowercase or titlecase character,
- * or has no upper case equivalent @c is returned unchanged.
- **/
-gunichar
-g_unichar_toupper (gunichar c)
-{
- int t = TYPE (c);
- if (t == G_UNICODE_LOWERCASE_LETTER)
- {
- gunichar val = ATTTABLE (c >> 8, c & 0xff);
- if (val >= 0x1000000)
- {
- const gchar *p = special_case_table + val - 0x1000000;
- val = g_utf8_get_char (p);
- }
- /* Some lowercase letters, e.g., U+000AA, FEMININE ORDINAL INDICATOR,
- * do not have an uppercase equivalent, in which case val will be
- * zero.
- */
- return val ? val : c;
- }
- else if (t == G_UNICODE_TITLECASE_LETTER)
- {
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- {
- if (title_table[i][0] == c)
- return title_table[i][1];
- }
- }
- return c;
-}
-
-/**
- * g_unichar_tolower:
- * @c: a Unicode character.
- *
- * Converts a character to lower case.
- *
- * Return value: the result of converting @c to lower case.
- * If @c is not an upperlower or titlecase character,
- * or has no lowercase equivalent @c is returned unchanged.
- **/
-gunichar
-g_unichar_tolower (gunichar c)
-{
- int t = TYPE (c);
- if (t == G_UNICODE_UPPERCASE_LETTER)
- {
- gunichar val = ATTTABLE (c >> 8, c & 0xff);
- if (val >= 0x1000000)
- {
- const gchar *p = special_case_table + val - 0x1000000;
- return g_utf8_get_char (p);
- }
- else
- {
- /* Not all uppercase letters are guaranteed to have a lowercase
- * equivalent. If this is the case, val will be zero. */
- return val ? val : c;
- }
- }
- else if (t == G_UNICODE_TITLECASE_LETTER)
- {
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- {
- if (title_table[i][0] == c)
- return title_table[i][2];
- }
- }
- return c;
-}
-
-/**
- * g_unichar_totitle:
- * @c: a Unicode character
- *
- * Converts a character to the titlecase.
- *
- * Return value: the result of converting @c to titlecase.
- * If @c is not an uppercase or lowercase character,
- * @c is returned unchanged.
- **/
-gunichar
-g_unichar_totitle (gunichar c)
-{
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- {
- if (title_table[i][0] == c || title_table[i][1] == c
- || title_table[i][2] == c)
- return title_table[i][0];
- }
-
- if (TYPE (c) == G_UNICODE_LOWERCASE_LETTER)
- return g_unichar_toupper (c);
-
- return c;
-}
-
-/**
- * g_unichar_digit_value:
- * @c: a Unicode character
- *
- * Determines the numeric value of a character as a decimal
- * digit.
- *
- * Return value: If @c is a decimal digit (according to
- * g_unichar_isdigit()), its numeric value. Otherwise, -1.
- **/
-int
-g_unichar_digit_value (gunichar c)
-{
- if (TYPE (c) == G_UNICODE_DECIMAL_NUMBER)
- return ATTTABLE (c >> 8, c & 0xff);
- return -1;
-}
-
-/**
- * g_unichar_xdigit_value:
- * @c: a Unicode character
- *
- * Determines the numeric value of a character as a hexidecimal
- * digit.
- *
- * Return value: If @c is a hex digit (according to
- * g_unichar_isxdigit()), its numeric value. Otherwise, -1.
- **/
-int
-g_unichar_xdigit_value (gunichar c)
-{
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (TYPE (c) == G_UNICODE_DECIMAL_NUMBER)
- return ATTTABLE (c >> 8, c & 0xff);
- return -1;
-}
-
-/**
- * g_unichar_type:
- * @c: a Unicode character
- *
- * Classifies a Unicode character by type.
- *
- * Return value: the type of the character.
- **/
-GUnicodeType
-g_unichar_type (gunichar c)
-{
- return TYPE (c);
-}
-
-/*
- * Case mapping functions
- */
-
-typedef enum {
- LOCALE_NORMAL,
- LOCALE_TURKIC,
- LOCALE_LITHUANIAN
-} LocaleType;
-
-static LocaleType
-get_locale_type (void)
-{
-#ifdef G_OS_WIN32
- char *tem = g_win32_getlocale ();
- char locale[2];
-
- locale[0] = tem[0];
- locale[1] = tem[1];
- g_free (tem);
-#else
- const char *locale = setlocale (LC_CTYPE, NULL);
-#endif
-
- switch (locale[0])
- {
- case 'a':
- if (locale[1] == 'z')
- return LOCALE_TURKIC;
- break;
- case 'l':
- if (locale[1] == 't')
- return LOCALE_LITHUANIAN;
- break;
- case 't':
- if (locale[1] == 'r')
- return LOCALE_TURKIC;
- break;
- }
-
- return LOCALE_NORMAL;
-}
-
-static gint
-output_marks (const char **p_inout,
- char *out_buffer,
- gboolean remove_dot)
-{
- const char *p = *p_inout;
- gint len = 0;
-
- while (*p)
- {
- gunichar c = g_utf8_get_char (p);
-
- if (ISMARK (TYPE (c)))
- {
- if (!remove_dot || c != 0x307 /* COMBINING DOT ABOVE */)
- len += g_unichar_to_utf8 (c, out_buffer ? out_buffer + len : NULL);
- p = g_utf8_next_char (p);
- }
- else
- break;
- }
-
- *p_inout = p;
- return len;
-}
-
-static gint
-output_special_case (gchar *out_buffer,
- int offset,
- int type,
- int which)
-{
- const gchar *p = special_case_table + offset;
- gint len;
-
- if (type != G_UNICODE_TITLECASE_LETTER)
- p = g_utf8_next_char (p);
-
- if (which == 1)
- p += strlen (p) + 1;
-
- len = strlen (p);
- if (out_buffer)
- memcpy (out_buffer, p, len);
-
- return len;
-}
-
-static gsize
-real_toupper (const gchar *str,
- gssize max_len,
- gchar *out_buffer,
- LocaleType locale_type)
-{
- const gchar *p = str;
- const char *last = NULL;
- gsize len = 0;
- gboolean last_was_i = FALSE;
-
- while ((max_len < 0 || p < str + max_len) && *p)
- {
- gunichar c = g_utf8_get_char (p);
- int t = TYPE (c);
- gunichar val;
-
- last = p;
- p = g_utf8_next_char (p);
-
- if (locale_type == LOCALE_LITHUANIAN)
- {
- if (c == 'i')
- last_was_i = TRUE;
- else
- {
- if (last_was_i)
- {
- /* Nasty, need to remove any dot above. Though
- * I think only E WITH DOT ABOVE occurs in practice
- * which could simplify this considerably.
- */
- gsize decomp_len, i;
- gunichar *decomp;
-
- decomp = g_unicode_canonical_decomposition (c, &decomp_len);
- for (i=0; i < decomp_len; i++)
- {
- if (decomp[i] != 0x307 /* COMBINING DOT ABOVE */)
- len += g_unichar_to_utf8 (g_unichar_toupper (decomp[i]), out_buffer ? out_buffer + len : NULL);
- }
- g_free (decomp);
-
- len += output_marks (&p, out_buffer ? out_buffer + len : NULL, TRUE);
-
- continue;
- }
-
- if (!ISMARK (t))
- last_was_i = FALSE;
- }
- }
-
- if (locale_type == LOCALE_TURKIC && c == 'i')
- {
- /* i => LATIN CAPITAL LETTER I WITH DOT ABOVE */
- len += g_unichar_to_utf8 (0x130, out_buffer ? out_buffer + len : NULL);
- }
- else if (c == 0x0345) /* COMBINING GREEK YPOGEGRAMMENI */
- {
- /* Nasty, need to move it after other combining marks .. this would go away if
- * we normalized first.
- */
- len += output_marks (&p, out_buffer ? out_buffer + len : NULL, FALSE);
-
- /* And output as GREEK CAPITAL LETTER IOTA */
- len += g_unichar_to_utf8 (0x399, out_buffer ? out_buffer + len : NULL);
- }
- else if (IS (t,
- OR (G_UNICODE_LOWERCASE_LETTER,
- OR (G_UNICODE_TITLECASE_LETTER,
- 0))))
- {
- val = ATTTABLE (c >> 8, c & 0xff);
-
- if (val >= 0x1000000)
- {
- len += output_special_case (out_buffer ? out_buffer + len : NULL, val - 0x1000000, t,
- t == G_UNICODE_LOWERCASE_LETTER ? 0 : 1);
- }
- else
- {
- if (t == G_UNICODE_TITLECASE_LETTER)
- {
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- {
- if (title_table[i][0] == c)
- {
- val = title_table[i][1];
- break;
- }
- }
- }
-
- /* Some lowercase letters, e.g., U+000AA, FEMININE ORDINAL INDICATOR,
- * do not have an uppercase equivalent, in which case val will be
- * zero. */
- len += g_unichar_to_utf8 (val ? val : c, out_buffer ? out_buffer + len : NULL);
- }
- }
- else
- {
- gsize char_len = g_utf8_skip[*(guchar *)last];
-
- if (out_buffer)
- memcpy (out_buffer + len, last, char_len);
-
- len += char_len;
- }
-
- }
-
- return len;
-}
-
-/**
- * g_utf8_strup:
- * @str: a UTF-8 encoded string
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- *
- * Converts all Unicode characters in the string that have a case
- * to uppercase. The exact manner that this is done depends
- * on the current locale, and may result in the number of
- * characters in the string increasing. (For instance, the
- * German ess-zet will be changed to SS.)
- *
- * Return value: a newly allocated string, with all characters
- * converted to uppercase.
- **/
-gchar *
-g_utf8_strup (const gchar *str,
- gssize len)
-{
- gsize result_len;
- LocaleType locale_type;
- gchar *result;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- locale_type = get_locale_type ();
-
- /*
- * We use a two pass approach to keep memory management simple
- */
- result_len = real_toupper (str, len, NULL, locale_type);
- result = g_malloc (result_len + 1);
- real_toupper (str, len, result, locale_type);
- result[result_len] = '\0';
-
- return result;
-}
-
-/* traverses the string checking for characters with combining class == 230
- * until a base character is found */
-static gboolean
-has_more_above (const gchar *str)
-{
- const gchar *p = str;
- gint combining_class;
-
- while (*p)
- {
- combining_class = g_unichar_combining_class (g_utf8_get_char (p));
- if (combining_class == 230)
- return TRUE;
- else if (combining_class == 0)
- break;
-
- p = g_utf8_next_char (p);
- }
-
- return FALSE;
-}
-
-static gsize
-real_tolower (const gchar *str,
- gssize max_len,
- gchar *out_buffer,
- LocaleType locale_type)
-{
- const gchar *p = str;
- const char *last = NULL;
- gsize len = 0;
-
- while ((max_len < 0 || p < str + max_len) && *p)
- {
- gunichar c = g_utf8_get_char (p);
- int t = TYPE (c);
- gunichar val;
-
- last = p;
- p = g_utf8_next_char (p);
-
- if (locale_type == LOCALE_TURKIC && c == 'I')
- {
- if (g_utf8_get_char (p) == 0x0307)
- {
- /* I + COMBINING DOT ABOVE => i (U+0069) */
- len += g_unichar_to_utf8 (0x0069, out_buffer ? out_buffer + len : NULL);
- p = g_utf8_next_char (p);
- }
- else
- {
- /* I => LATIN SMALL LETTER DOTLESS I */
- len += g_unichar_to_utf8 (0x131, out_buffer ? out_buffer + len : NULL);
- }
- }
- /* Introduce an explicit dot above when lowercasing capital I's and J's
- * whenever there are more accents above. [SpecialCasing.txt] */
- else if (locale_type == LOCALE_LITHUANIAN &&
- (c == 0x00cc || c == 0x00cd || c == 0x0128))
- {
- len += g_unichar_to_utf8 (0x0069, out_buffer ? out_buffer + len : NULL);
- len += g_unichar_to_utf8 (0x0307, out_buffer ? out_buffer + len : NULL);
-
- switch (c)
- {
- case 0x00cc:
- len += g_unichar_to_utf8 (0x0300, out_buffer ? out_buffer + len : NULL);
- break;
- case 0x00cd:
- len += g_unichar_to_utf8 (0x0301, out_buffer ? out_buffer + len : NULL);
- break;
- case 0x0128:
- len += g_unichar_to_utf8 (0x0303, out_buffer ? out_buffer + len : NULL);
- break;
- }
- }
- else if (locale_type == LOCALE_LITHUANIAN &&
- (c == 'I' || c == 'J' || c == 0x012e) &&
- has_more_above (p))
- {
- len += g_unichar_to_utf8 (g_unichar_tolower (c), out_buffer ? out_buffer + len : NULL);
- len += g_unichar_to_utf8 (0x0307, out_buffer ? out_buffer + len : NULL);
- }
- else if (c == 0x03A3) /* GREEK CAPITAL LETTER SIGMA */
- {
- if ((max_len < 0 || p < str + max_len) && *p)
- {
- gunichar next_c = g_utf8_get_char (p);
- int next_type = TYPE(next_c);
-
- /* SIGMA mapps differently depending on whether it is
- * final or not. The following simplified test would
- * fail in the case of combining marks following the
- * sigma, but I don't think that occurs in real text.
- * The test here matches that in ICU.
- */
- if (ISALPHA (next_type)) /* Lu,Ll,Lt,Lm,Lo */
- val = 0x3c3; /* GREEK SMALL SIGMA */
- else
- val = 0x3c2; /* GREEK SMALL FINAL SIGMA */
- }
- else
- val = 0x3c2; /* GREEK SMALL FINAL SIGMA */
-
- len += g_unichar_to_utf8 (val, out_buffer ? out_buffer + len : NULL);
- }
- else if (IS (t,
- OR (G_UNICODE_UPPERCASE_LETTER,
- OR (G_UNICODE_TITLECASE_LETTER,
- 0))))
- {
- val = ATTTABLE (c >> 8, c & 0xff);
-
- if (val >= 0x1000000)
- {
- len += output_special_case (out_buffer ? out_buffer + len : NULL, val - 0x1000000, t, 0);
- }
- else
- {
- if (t == G_UNICODE_TITLECASE_LETTER)
- {
- unsigned int i;
- for (i = 0; i < G_N_ELEMENTS (title_table); ++i)
- {
- if (title_table[i][0] == c)
- {
- val = title_table[i][2];
- break;
- }
- }
- }
-
- /* Not all uppercase letters are guaranteed to have a lowercase
- * equivalent. If this is the case, val will be zero. */
- len += g_unichar_to_utf8 (val ? val : c, out_buffer ? out_buffer + len : NULL);
- }
- }
- else
- {
- gsize char_len = g_utf8_skip[*(guchar *)last];
-
- if (out_buffer)
- memcpy (out_buffer + len, last, char_len);
-
- len += char_len;
- }
-
- }
-
- return len;
-}
-
-/**
- * g_utf8_strdown:
- * @str: a UTF-8 encoded string
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- *
- * Converts all Unicode characters in the string that have a case
- * to lowercase. The exact manner that this is done depends
- * on the current locale, and may result in the number of
- * characters in the string changing.
- *
- * Return value: a newly allocated string, with all characters
- * converted to lowercase.
- **/
-gchar *
-g_utf8_strdown (const gchar *str,
- gssize len)
-{
- gsize result_len;
- LocaleType locale_type;
- gchar *result;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- locale_type = get_locale_type ();
-
- /*
- * We use a two pass approach to keep memory management simple
- */
- result_len = real_tolower (str, len, NULL, locale_type);
- result = g_malloc (result_len + 1);
- real_tolower (str, len, result, locale_type);
- result[result_len] = '\0';
-
- return result;
-}
-
-/**
- * g_utf8_casefold:
- * @str: a UTF-8 encoded string
- * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
- *
- * Converts a string into a form that is independent of case. The
- * result will not correspond to any particular case, but can be
- * compared for equality or ordered with the results of calling
- * g_utf8_casefold() on other strings.
- *
- * Note that calling g_utf8_casefold() followed by g_utf8_collate() is
- * only an approximation to the correct linguistic case insensitive
- * ordering, though it is a fairly good one. Getting this exactly
- * right would require a more sophisticated collation function that
- * takes case sensitivity into account. GLib does not currently
- * provide such a function.
- *
- * Return value: a newly allocated string, that is a
- * case independent form of @str.
- **/
-gchar *
-g_utf8_casefold (const gchar *str,
- gssize len)
-{
- GString *result;
- const char *p;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- result = g_string_new (NULL);
- p = str;
- while ((len < 0 || p < str + len) && *p)
- {
- gunichar ch = g_utf8_get_char (p);
-
- int start = 0;
- int end = G_N_ELEMENTS (casefold_table);
-
- if (ch >= casefold_table[start].ch &&
- ch <= casefold_table[end - 1].ch)
- {
- while (TRUE)
- {
- int half = (start + end) / 2;
- if (ch == casefold_table[half].ch)
- {
- g_string_append (result, casefold_table[half].data);
- goto next;
- }
- else if (half == start)
- break;
- else if (ch > casefold_table[half].ch)
- start = half;
- else
- end = half;
- }
- }
-
- g_string_append_unichar (result, g_unichar_tolower (ch));
-
- next:
- p = g_utf8_next_char (p);
- }
-
- return g_string_free (result, FALSE);
-}
-
-/**
- * g_unichar_get_mirror_char:
- * @ch: a Unicode character
- * @mirrored_ch: location to store the mirrored character
- *
- * In Unicode, some characters are <firstterm>mirrored</firstterm>. This
- * means that their images are mirrored horizontally in text that is laid
- * out from right to left. For instance, "(" would become its mirror image,
- * ")", in right-to-left text.
- *
- * If @ch has the Unicode mirrored property and there is another unicode
- * character that typically has a glyph that is the mirror image of @ch's
- * glyph and @mirrored_ch is set, it puts that character in the address
- * pointed to by @mirrored_ch. Otherwise the original character is put.
- *
- * Return value: %TRUE if @ch has a mirrored character, %FALSE otherwise
- *
- * Since: 2.4
- **/
-gboolean
-g_unichar_get_mirror_char (gunichar ch,
- gunichar *mirrored_ch)
-{
- gboolean found;
- gunichar mirrored;
-
- mirrored = GLIB_GET_MIRRORING(ch);
-
- found = ch != mirrored;
- if (mirrored_ch)
- *mirrored_ch = mirrored;
-
- return found;
-
-}
-
-#define G_SCRIPT_TABLE_MIDPOINT (G_N_ELEMENTS (g_script_table) / 2)
-
-static inline GUnicodeScript
-g_unichar_get_script_bsearch (gunichar ch)
-{
- int lower = 0;
- int upper = G_N_ELEMENTS (g_script_table) - 1;
- static int saved_mid = G_SCRIPT_TABLE_MIDPOINT;
- int mid = saved_mid;
-
-
- do
- {
- if (ch < g_script_table[mid].start)
- upper = mid - 1;
- else if (ch >= g_script_table[mid].start + g_script_table[mid].chars)
- lower = mid + 1;
- else
- return g_script_table[saved_mid = mid].script;
-
- mid = (lower + upper) / 2;
- }
- while (lower <= upper);
-
- return G_UNICODE_SCRIPT_UNKNOWN;
-}
-
-/**
- * g_unichar_get_script:
- * @ch: a Unicode character
- *
- * Looks up the #GUnicodeScript for a particular character (as defined
- * by Unicode Standard Annex #24). No check is made for @ch being a
- * valid Unicode character; if you pass in invalid character, the
- * result is undefined.
- *
- * This function is equivalent to pango_script_for_unichar() and the
- * two are interchangeable.
- *
- * Return value: the #GUnicodeScript for the character.
- *
- * Since: 2.14
- */
-GUnicodeScript
-g_unichar_get_script (gunichar ch)
-{
- if (ch < G_EASY_SCRIPTS_RANGE)
- return g_script_easy_table[ch];
- else
- return g_unichar_get_script_bsearch (ch);
-}
+++ /dev/null
-/* GIO - GLib Input, Output and Streaming Library
- *
- * Copyright (C) 2006-2007 Red Hat, Inc.
- *
- * 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 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.
- *
- * Author: Alexander Larsson <alexl@redhat.com>
- */
-
-#include "config.h"
-
-#include "gurifuncs.h"
-
-#include <glib/gstrfuncs.h>
-#include <glib/gmessages.h>
-#include <glib/gstring.h>
-#include <glib/gmem.h>
-
-#include <string.h>
-
-#include "config.h"
-
-/**
- * SECTION:gurifuncs
- * @short_description: URI Functions
- *
- * Functions for manipulating Universal Resource Identifiers (URIs) as
- * defined by <ulink url="http://www.ietf.org/rfc/rfc3986.txt">
- * RFC 3986</ulink>. It is highly recommended that you have read and
- * understand RFC 3986 for understanding this API.
- */
-
-static int
-unescape_character (const char *scanner)
-{
- int first_digit;
- int second_digit;
-
- first_digit = g_ascii_xdigit_value (*scanner++);
- if (first_digit < 0)
- return -1;
-
- second_digit = g_ascii_xdigit_value (*scanner++);
- if (second_digit < 0)
- return -1;
-
- return (first_digit << 4) | second_digit;
-}
-
-/**
- * g_uri_unescape_segment:
- * @escaped_string: a string.
- * @escaped_string_end: a string.
- * @illegal_characters: an optional string of illegal characters not to be allowed.
- *
- * Unescapes a segment of an escaped string.
- *
- * If any of the characters in @illegal_characters or the character zero appears
- * as an escaped character in @escaped_string then that is an error and %NULL
- * will be returned. This is useful it you want to avoid for instance having a
- * slash being expanded in an escaped path element, which might confuse pathname
- * handling.
- *
- * Returns: an unescaped version of @escaped_string or %NULL on error.
- * The returned string should be freed when no longer needed.
- *
- * Since: 2.16
- **/
-char *
-g_uri_unescape_segment (const char *escaped_string,
- const char *escaped_string_end,
- const char *illegal_characters)
-{
- const char *in;
- char *out, *result;
- gint character;
-
- if (escaped_string == NULL)
- return NULL;
-
- if (escaped_string_end == NULL)
- escaped_string_end = escaped_string + strlen (escaped_string);
-
- result = g_malloc (escaped_string_end - escaped_string + 1);
-
- out = result;
- for (in = escaped_string; in < escaped_string_end; in++)
- {
- character = *in;
-
- if (*in == '%')
- {
- in++;
-
- if (escaped_string_end - in < 2)
- {
- /* Invalid escaped char (to short) */
- g_free (result);
- return NULL;
- }
-
- character = unescape_character (in);
-
- /* Check for an illegal character. We consider '\0' illegal here. */
- if (character <= 0 ||
- (illegal_characters != NULL &&
- strchr (illegal_characters, (char)character) != NULL))
- {
- g_free (result);
- return NULL;
- }
-
- in++; /* The other char will be eaten in the loop header */
- }
- *out++ = (char)character;
- }
-
- *out = '\0';
-
- return result;
-}
-
-/**
- * g_uri_unescape_string:
- * @escaped_string: an escaped string to be unescaped.
- * @illegal_characters: an optional string of illegal characters not to be allowed.
- *
- * Unescapes a whole escaped string.
- *
- * If any of the characters in @illegal_characters or the character zero appears
- * as an escaped character in @escaped_string then that is an error and %NULL
- * will be returned. This is useful it you want to avoid for instance having a
- * slash being expanded in an escaped path element, which might confuse pathname
- * handling.
- *
- * Returns: an unescaped version of @escaped_string. The returned string
- * should be freed when no longer needed.
- *
- * Since: 2.16
- **/
-char *
-g_uri_unescape_string (const char *escaped_string,
- const char *illegal_characters)
-{
- return g_uri_unescape_segment (escaped_string, NULL, illegal_characters);
-}
-
-/**
- * g_uri_parse_scheme:
- * @uri: a valid URI.
- *
- * Gets the scheme portion of a URI string. RFC 3986 decodes the scheme as:
- * <programlisting>
- * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
- * </programlisting>
- * Common schemes include "file", "http", "svn+ssh", etc.
- *
- * Returns: The "Scheme" component of the URI, or %NULL on error.
- * The returned string should be freed when no longer needed.
- *
- * Since: 2.16
- **/
-char *
-g_uri_parse_scheme (const char *uri)
-{
- const char *p;
- char c;
-
- g_return_val_if_fail (uri != NULL, NULL);
-
- /* From RFC 3986 Decodes:
- * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
- */
-
- p = uri;
-
- /* Decode scheme:
- scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
- */
-
- if (!g_ascii_isalpha (*p))
- return NULL;
-
- while (1)
- {
- c = *p++;
-
- if (c == ':')
- break;
-
- if (!(g_ascii_isalnum(c) ||
- c == '+' ||
- c == '-' ||
- c == '.'))
- return NULL;
- }
-
- return g_strndup (uri, p - uri - 1);
-}
-
-/**
- * g_uri_escape_string:
- * @unescaped: the unescaped input string.
- * @reserved_chars_allowed: a string of reserved characters that are
- * allowed to be used, or %NULL.
- * @allow_utf8: %TRUE if the result can include UTF-8 characters.
- *
- * Escapes a string for use in a URI.
- *
- * Normally all characters that are not "unreserved" (i.e. ASCII alphanumerical
- * characters plus dash, dot, underscore and tilde) are escaped.
- * But if you specify characters in @reserved_chars_allowed they are not
- * escaped. This is useful for the "reserved" characters in the URI
- * specification, since those are allowed unescaped in some portions of
- * a URI.
- *
- * Returns: an escaped version of @unescaped. The returned string should be
- * freed when no longer needed.
- *
- * Since: 2.16
- **/
-char *
-g_uri_escape_string (const char *unescaped,
- const char *reserved_chars_allowed,
- gboolean allow_utf8)
-{
- GString *s;
-
- g_return_val_if_fail (unescaped != NULL, NULL);
-
- s = g_string_sized_new (strlen (unescaped) + 10);
-
- g_string_append_uri_escaped (s, unescaped, reserved_chars_allowed, allow_utf8);
-
- return g_string_free (s, FALSE);
-}
+++ /dev/null
-/* gutf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * 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 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 "config.h"
-
-#include <stdlib.h>
-#ifdef HAVE_CODESET
-#include <langinfo.h>
-#endif
-#include <string.h>
-
-#ifdef G_PLATFORM_WIN32
-#include <stdio.h>
-#define STRICT
-#include <windows.h>
-#undef STRICT
-#endif
-
-#include "libcharset/libcharset.h"
-
-#include "gconvert.h"
-#include "ghash.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gtypes.h"
-#include "gthread.h"
-#include "glibintl.h"
-
-#define UTF8_COMPUTE(Char, Mask, Len) \
- if (Char < 128) \
- { \
- Len = 1; \
- Mask = 0x7f; \
- } \
- else if ((Char & 0xe0) == 0xc0) \
- { \
- Len = 2; \
- Mask = 0x1f; \
- } \
- else if ((Char & 0xf0) == 0xe0) \
- { \
- Len = 3; \
- Mask = 0x0f; \
- } \
- else if ((Char & 0xf8) == 0xf0) \
- { \
- Len = 4; \
- Mask = 0x07; \
- } \
- else if ((Char & 0xfc) == 0xf8) \
- { \
- Len = 5; \
- Mask = 0x03; \
- } \
- else if ((Char & 0xfe) == 0xfc) \
- { \
- Len = 6; \
- Mask = 0x01; \
- } \
- else \
- Len = -1;
-
-#define UTF8_LENGTH(Char) \
- ((Char) < 0x80 ? 1 : \
- ((Char) < 0x800 ? 2 : \
- ((Char) < 0x10000 ? 3 : \
- ((Char) < 0x200000 ? 4 : \
- ((Char) < 0x4000000 ? 5 : 6)))))
-
-
-#define UTF8_GET(Result, Chars, Count, Mask, Len) \
- (Result) = (Chars)[0] & (Mask); \
- for ((Count) = 1; (Count) < (Len); ++(Count)) \
- { \
- if (((Chars)[(Count)] & 0xc0) != 0x80) \
- { \
- (Result) = -1; \
- break; \
- } \
- (Result) <<= 6; \
- (Result) |= ((Chars)[(Count)] & 0x3f); \
- }
-
-/*
- * Check whether a Unicode (5.2) char is in a valid range.
- *
- * The first check comes from the Unicode guarantee to never encode
- * a point above 0x0010ffff, since UTF-16 couldn't represent it.
- *
- * The second check covers surrogate pairs (category Cs).
- *
- * The last two checks cover "Noncharacter": defined as:
- * "A code point that is permanently reserved for
- * internal use, and that should never be interchanged. In
- * Unicode 3.1, these consist of the values U+nFFFE and U+nFFFF
- * (where n is from 0 to 10_16) and the values U+FDD0..U+FDEF."
- *
- * @param Char the character
- */
-#define UNICODE_VALID(Char) \
- ((Char) < 0x110000 && \
- (((Char) & 0xFFFFF800) != 0xD800) && \
- ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \
- ((Char) & 0xFFFE) != 0xFFFE)
-
-
-static const gchar utf8_skip_data[256] = {
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
-};
-
-const gchar * const g_utf8_skip = utf8_skip_data;
-
-/**
- * g_utf8_find_prev_char:
- * @str: pointer to the beginning of a UTF-8 encoded string
- * @p: pointer to some position within @str
- *
- * Given a position @p with a UTF-8 encoded string @str, find the start
- * of the previous UTF-8 character starting before @p. Returns %NULL if no
- * UTF-8 characters are present in @str before @p.
- *
- * @p does not have to be at the beginning of a UTF-8 character. No check
- * is made to see if the character found is actually valid other than
- * it starts with an appropriate byte.
- *
- * Return value: a pointer to the found character or %NULL.
- **/
-gchar *
-g_utf8_find_prev_char (const char *str,
- const char *p)
-{
- for (--p; p >= str; --p)
- {
- if ((*p & 0xc0) != 0x80)
- return (gchar *)p;
- }
- return NULL;
-}
-
-/**
- * g_utf8_find_next_char:
- * @p: a pointer to a position within a UTF-8 encoded string
- * @end: a pointer to the byte following the end of the string,
- * or %NULL to indicate that the string is nul-terminated.
- *
- * Finds the start of the next UTF-8 character in the string after @p.
- *
- * @p does not have to be at the beginning of a UTF-8 character. No check
- * is made to see if the character found is actually valid other than
- * it starts with an appropriate byte.
- *
- * Return value: a pointer to the found character or %NULL
- **/
-gchar *
-g_utf8_find_next_char (const gchar *p,
- const gchar *end)
-{
- if (*p)
- {
- if (end)
- for (++p; p < end && (*p & 0xc0) == 0x80; ++p)
- ;
- else
- for (++p; (*p & 0xc0) == 0x80; ++p)
- ;
- }
- return (p == end) ? NULL : (gchar *)p;
-}
-
-/**
- * g_utf8_prev_char:
- * @p: a pointer to a position within a UTF-8 encoded string
- *
- * Finds the previous UTF-8 character in the string before @p.
- *
- * @p does not have to be at the beginning of a UTF-8 character. No check
- * is made to see if the character found is actually valid other than
- * it starts with an appropriate byte. If @p might be the first
- * character of the string, you must use g_utf8_find_prev_char() instead.
- *
- * Return value: a pointer to the found character.
- **/
-gchar *
-g_utf8_prev_char (const gchar *p)
-{
- while (TRUE)
- {
- p--;
- if ((*p & 0xc0) != 0x80)
- return (gchar *)p;
- }
-}
-
-/**
- * g_utf8_strlen:
- * @p: pointer to the start of a UTF-8 encoded string
- * @max: the maximum number of bytes to examine. If @max
- * is less than 0, then the string is assumed to be
- * nul-terminated. If @max is 0, @p will not be examined and
- * may be %NULL.
- *
- * Computes the length of the string in characters, not including
- * the terminating nul character.
- *
- * Return value: the length of the string in characters
- **/
-glong
-g_utf8_strlen (const gchar *p,
- gssize max)
-{
- glong len = 0;
- const gchar *start = p;
- g_return_val_if_fail (p != NULL || max == 0, 0);
-
- if (max < 0)
- {
- while (*p)
- {
- p = g_utf8_next_char (p);
- ++len;
- }
- }
- else
- {
- if (max == 0 || !*p)
- return 0;
-
- p = g_utf8_next_char (p);
-
- while (p - start < max && *p)
- {
- ++len;
- p = g_utf8_next_char (p);
- }
-
- /* only do the last len increment if we got a complete
- * char (don't count partial chars)
- */
- if (p - start <= max)
- ++len;
- }
-
- return len;
-}
-
-/**
- * g_utf8_get_char:
- * @p: a pointer to Unicode character encoded as UTF-8
- *
- * Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
- * If @p does not point to a valid UTF-8 encoded character, results are
- * undefined. If you are not sure that the bytes are complete
- * valid Unicode characters, you should use g_utf8_get_char_validated()
- * instead.
- *
- * Return value: the resulting character
- **/
-gunichar
-g_utf8_get_char (const gchar *p)
-{
- int i, mask = 0, len;
- gunichar result;
- unsigned char c = (unsigned char) *p;
-
- UTF8_COMPUTE (c, mask, len);
- if (len == -1)
- return (gunichar)-1;
- UTF8_GET (result, p, i, mask, len);
-
- return result;
-}
-
-/**
- * g_utf8_offset_to_pointer:
- * @str: a UTF-8 encoded string
- * @offset: a character offset within @str
- *
- * Converts from an integer character offset to a pointer to a position
- * within the string.
- *
- * Since 2.10, this function allows to pass a negative @offset to
- * step backwards. It is usually worth stepping backwards from the end
- * instead of forwards if @offset is in the last fourth of the string,
- * since moving forward is about 3 times faster than moving backward.
- *
- * <note><para>
- * This function doesn't abort when reaching the end of @str. Therefore
- * you should be sure that @offset is within string boundaries before
- * calling that function. Call g_utf8_strlen() when unsure.
- *
- * This limitation exists as this function is called frequently during
- * text rendering and therefore has to be as fast as possible.
- * </para></note>
- *
- * Return value: the resulting pointer
- **/
-gchar *
-g_utf8_offset_to_pointer (const gchar *str,
- glong offset)
-{
- const gchar *s = str;
-
- if (offset > 0)
- while (offset--)
- s = g_utf8_next_char (s);
- else
- {
- const char *s1;
-
- /* This nice technique for fast backwards stepping
- * through a UTF-8 string was dubbed "stutter stepping"
- * by its inventor, Larry Ewing.
- */
- while (offset)
- {
- s1 = s;
- s += offset;
- while ((*s & 0xc0) == 0x80)
- s--;
-
- offset += g_utf8_pointer_to_offset (s, s1);
- }
- }
-
- return (gchar *)s;
-}
-
-/**
- * g_utf8_pointer_to_offset:
- * @str: a UTF-8 encoded string
- * @pos: a pointer to a position within @str
- *
- * Converts from a pointer to position within a string to a integer
- * character offset.
- *
- * Since 2.10, this function allows @pos to be before @str, and returns
- * a negative offset in this case.
- *
- * Return value: the resulting character offset
- **/
-glong
-g_utf8_pointer_to_offset (const gchar *str,
- const gchar *pos)
-{
- const gchar *s = str;
- glong offset = 0;
-
- if (pos < str)
- offset = - g_utf8_pointer_to_offset (pos, str);
- else
- while (s < pos)
- {
- s = g_utf8_next_char (s);
- offset++;
- }
-
- return offset;
-}
-
-
-/**
- * g_utf8_strncpy:
- * @dest: buffer to fill with characters from @src
- * @src: UTF-8 encoded string
- * @n: character count
- *
- * Like the standard C strncpy() function, but
- * copies a given number of characters instead of a given number of
- * bytes. The @src string must be valid UTF-8 encoded text.
- * (Use g_utf8_validate() on all text before trying to use UTF-8
- * utility functions with it.)
- *
- * Return value: @dest
- **/
-gchar *
-g_utf8_strncpy (gchar *dest,
- const gchar *src,
- gsize n)
-{
- const gchar *s = src;
- while (n && *s)
- {
- s = g_utf8_next_char(s);
- n--;
- }
- strncpy(dest, src, s - src);
- dest[s - src] = 0;
- return dest;
-}
-
-G_LOCK_DEFINE_STATIC (aliases);
-
-static GHashTable *
-get_alias_hash (void)
-{
- static GHashTable *alias_hash = NULL;
- const char *aliases;
-
- G_LOCK (aliases);
-
- if (!alias_hash)
- {
- alias_hash = g_hash_table_new (g_str_hash, g_str_equal);
-
- aliases = _g_locale_get_charset_aliases ();
- while (*aliases != '\0')
- {
- const char *canonical;
- const char *alias;
- const char **alias_array;
- int count = 0;
-
- alias = aliases;
- aliases += strlen (aliases) + 1;
- canonical = aliases;
- aliases += strlen (aliases) + 1;
-
- alias_array = g_hash_table_lookup (alias_hash, canonical);
- if (alias_array)
- {
- while (alias_array[count])
- count++;
- }
-
- alias_array = g_renew (const char *, alias_array, count + 2);
- alias_array[count] = alias;
- alias_array[count + 1] = NULL;
-
- g_hash_table_insert (alias_hash, (char *)canonical, alias_array);
- }
- }
-
- G_UNLOCK (aliases);
-
- return alias_hash;
-}
-
-/* As an abuse of the alias table, the following routines gets
- * the charsets that are aliases for the canonical name.
- */
-G_GNUC_INTERNAL const char **
-_g_charset_get_aliases (const char *canonical_name)
-{
- GHashTable *alias_hash = get_alias_hash ();
-
- return g_hash_table_lookup (alias_hash, canonical_name);
-}
-
-static gboolean
-g_utf8_get_charset_internal (const char *raw_data,
- const char **a)
-{
- const char *charset = getenv("CHARSET");
-
- if (charset && *charset)
- {
- *a = charset;
-
- if (charset && strstr (charset, "UTF-8"))
- return TRUE;
- else
- return FALSE;
- }
-
- /* The libcharset code tries to be thread-safe without
- * a lock, but has a memory leak and a missing memory
- * barrier, so we lock for it
- */
- G_LOCK (aliases);
- charset = _g_locale_charset_unalias (raw_data);
- G_UNLOCK (aliases);
-
- if (charset && *charset)
- {
- *a = charset;
-
- if (charset && strstr (charset, "UTF-8"))
- return TRUE;
- else
- return FALSE;
- }
-
- /* Assume this for compatibility at present. */
- *a = "US-ASCII";
-
- return FALSE;
-}
-
-typedef struct _GCharsetCache GCharsetCache;
-
-struct _GCharsetCache {
- gboolean is_utf8;
- gchar *raw;
- gchar *charset;
-};
-
-static void
-charset_cache_free (gpointer data)
-{
- GCharsetCache *cache = data;
- g_free (cache->raw);
- g_free (cache->charset);
- g_free (cache);
-}
-
-/**
- * g_get_charset:
- * @charset: return location for character set name
- *
- * Obtains the character set for the <link linkend="setlocale">current
- * locale</link>; you might use this character set as an argument to
- * g_convert(), to convert from the current locale's encoding to some
- * other encoding. (Frequently g_locale_to_utf8() and g_locale_from_utf8()
- * are nice shortcuts, though.)
- *
- * On Windows the character set returned by this function is the
- * so-called system default ANSI code-page. That is the character set
- * used by the "narrow" versions of C library and Win32 functions that
- * handle file names. It might be different from the character set
- * used by the C library's current locale.
- *
- * The return value is %TRUE if the locale's encoding is UTF-8, in that
- * case you can perhaps avoid calling g_convert().
- *
- * The string returned in @charset is not allocated, and should not be
- * freed.
- *
- * Return value: %TRUE if the returned charset is UTF-8
- **/
-gboolean
-g_get_charset (G_CONST_RETURN char **charset)
-{
- static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
- GCharsetCache *cache = g_static_private_get (&cache_private);
- const gchar *raw;
-
- if (!cache)
- {
- cache = g_new0 (GCharsetCache, 1);
- g_static_private_set (&cache_private, cache, charset_cache_free);
- }
-
- raw = _g_locale_charset_raw ();
-
- if (!(cache->raw && strcmp (cache->raw, raw) == 0))
- {
- const gchar *new_charset;
-
- g_free (cache->raw);
- g_free (cache->charset);
- cache->raw = g_strdup (raw);
- cache->is_utf8 = g_utf8_get_charset_internal (raw, &new_charset);
- cache->charset = g_strdup (new_charset);
- }
-
- if (charset)
- *charset = cache->charset;
-
- return cache->is_utf8;
-}
-
-/* unicode_strchr */
-
-/**
- * g_unichar_to_utf8:
- * @c: a Unicode character code
- * @outbuf: output buffer, must have at least 6 bytes of space.
- * If %NULL, the length will be computed and returned
- * and nothing will be written to @outbuf.
- *
- * Converts a single character to UTF-8.
- *
- * Return value: number of bytes written
- **/
-int
-g_unichar_to_utf8 (gunichar c,
- gchar *outbuf)
-{
- /* If this gets modified, also update the copy in g_string_insert_unichar() */
- guint len = 0;
- int first;
- int i;
-
- if (c < 0x80)
- {
- first = 0;
- len = 1;
- }
- else if (c < 0x800)
- {
- first = 0xc0;
- len = 2;
- }
- else if (c < 0x10000)
- {
- first = 0xe0;
- len = 3;
- }
- else if (c < 0x200000)
- {
- first = 0xf0;
- len = 4;
- }
- else if (c < 0x4000000)
- {
- first = 0xf8;
- len = 5;
- }
- else
- {
- first = 0xfc;
- len = 6;
- }
-
- if (outbuf)
- {
- for (i = len - 1; i > 0; --i)
- {
- outbuf[i] = (c & 0x3f) | 0x80;
- c >>= 6;
- }
- outbuf[0] = c | first;
- }
-
- return len;
-}
-
-/**
- * g_utf8_strchr:
- * @p: a nul-terminated UTF-8 encoded string
- * @len: the maximum length of @p
- * @c: a Unicode character
- *
- * Finds the leftmost occurrence of the given Unicode character
- * in a UTF-8 encoded string, while limiting the search to @len bytes.
- * If @len is -1, allow unbounded search.
- *
- * Return value: %NULL if the string does not contain the character,
- * otherwise, a pointer to the start of the leftmost occurrence of
- * the character in the string.
- **/
-gchar *
-g_utf8_strchr (const char *p,
- gssize len,
- gunichar c)
-{
- gchar ch[10];
-
- gint charlen = g_unichar_to_utf8 (c, ch);
- ch[charlen] = '\0';
-
- return g_strstr_len (p, len, ch);
-}
-
-
-/**
- * g_utf8_strrchr:
- * @p: a nul-terminated UTF-8 encoded string
- * @len: the maximum length of @p
- * @c: a Unicode character
- *
- * Find the rightmost occurrence of the given Unicode character
- * in a UTF-8 encoded string, while limiting the search to @len bytes.
- * If @len is -1, allow unbounded search.
- *
- * Return value: %NULL if the string does not contain the character,
- * otherwise, a pointer to the start of the rightmost occurrence of the
- * character in the string.
- **/
-gchar *
-g_utf8_strrchr (const char *p,
- gssize len,
- gunichar c)
-{
- gchar ch[10];
-
- gint charlen = g_unichar_to_utf8 (c, ch);
- ch[charlen] = '\0';
-
- return g_strrstr_len (p, len, ch);
-}
-
-
-/* Like g_utf8_get_char, but take a maximum length
- * and return (gunichar)-2 on incomplete trailing character;
- * also check for malformed or overlong sequences
- * and return (gunichar)-1 in this case.
- */
-static inline gunichar
-g_utf8_get_char_extended (const gchar *p,
- gssize max_len)
-{
- guint i, len;
- gunichar min_code;
- gunichar wc = (guchar) *p;
-
- if (wc < 0x80)
- {
- return wc;
- }
- else if (G_UNLIKELY (wc < 0xc0))
- {
- return (gunichar)-1;
- }
- else if (wc < 0xe0)
- {
- len = 2;
- wc &= 0x1f;
- min_code = 1 << 7;
- }
- else if (wc < 0xf0)
- {
- len = 3;
- wc &= 0x0f;
- min_code = 1 << 11;
- }
- else if (wc < 0xf8)
- {
- len = 4;
- wc &= 0x07;
- min_code = 1 << 16;
- }
- else if (wc < 0xfc)
- {
- len = 5;
- wc &= 0x03;
- min_code = 1 << 21;
- }
- else if (wc < 0xfe)
- {
- len = 6;
- wc &= 0x01;
- min_code = 1 << 26;
- }
- else
- {
- return (gunichar)-1;
- }
-
- if (G_UNLIKELY (max_len >= 0 && len > max_len))
- {
- for (i = 1; i < max_len; i++)
- {
- if ((((guchar *)p)[i] & 0xc0) != 0x80)
- return (gunichar)-1;
- }
- return (gunichar)-2;
- }
-
- for (i = 1; i < len; ++i)
- {
- gunichar ch = ((guchar *)p)[i];
-
- if (G_UNLIKELY ((ch & 0xc0) != 0x80))
- {
- if (ch)
- return (gunichar)-1;
- else
- return (gunichar)-2;
- }
-
- wc <<= 6;
- wc |= (ch & 0x3f);
- }
-
- if (G_UNLIKELY (wc < min_code))
- return (gunichar)-1;
-
- return wc;
-}
-
-/**
- * g_utf8_get_char_validated:
- * @p: a pointer to Unicode character encoded as UTF-8
- * @max_len: the maximum number of bytes to read, or -1, for no maximum or
- * if @p is nul-terminated
- *
- * Convert a sequence of bytes encoded as UTF-8 to a Unicode character.
- * This function checks for incomplete characters, for invalid characters
- * such as characters that are out of the range of Unicode, and for
- * overlong encodings of valid characters.
- *
- * Return value: the resulting character. If @p points to a partial
- * sequence at the end of a string that could begin a valid
- * character (or if @max_len is zero), returns (gunichar)-2;
- * otherwise, if @p does not point to a valid UTF-8 encoded
- * Unicode character, returns (gunichar)-1.
- **/
-gunichar
-g_utf8_get_char_validated (const gchar *p,
- gssize max_len)
-{
- gunichar result;
-
- if (max_len == 0)
- return (gunichar)-2;
-
- result = g_utf8_get_char_extended (p, max_len);
-
- if (result & 0x80000000)
- return result;
- else if (!UNICODE_VALID (result))
- return (gunichar)-1;
- else
- return result;
-}
-
-/**
- * g_utf8_to_ucs4_fast:
- * @str: a UTF-8 encoded string
- * @len: the maximum length of @str to use, in bytes. If @len < 0,
- * then the string is nul-terminated.
- * @items_written: location to store the number of characters in the
- * result, or %NULL.
- *
- * Convert a string from UTF-8 to a 32-bit fixed width
- * representation as UCS-4, assuming valid UTF-8 input.
- * This function is roughly twice as fast as g_utf8_to_ucs4()
- * but does no error checking on the input.
- *
- * Return value: a pointer to a newly allocated UCS-4 string.
- * This value must be freed with g_free().
- **/
-gunichar *
-g_utf8_to_ucs4_fast (const gchar *str,
- glong len,
- glong *items_written)
-{
- gint j, charlen;
- gunichar *result;
- gint n_chars, i;
- const gchar *p;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- p = str;
- n_chars = 0;
- if (len < 0)
- {
- while (*p)
- {
- p = g_utf8_next_char (p);
- ++n_chars;
- }
- }
- else
- {
- while (p < str + len && *p)
- {
- p = g_utf8_next_char (p);
- ++n_chars;
- }
- }
-
- result = g_new (gunichar, n_chars + 1);
-
- p = str;
- for (i=0; i < n_chars; i++)
- {
- gunichar wc = ((unsigned char *)p)[0];
-
- if (wc < 0x80)
- {
- result[i] = wc;
- p++;
- }
- else
- {
- if (wc < 0xe0)
- {
- charlen = 2;
- wc &= 0x1f;
- }
- else if (wc < 0xf0)
- {
- charlen = 3;
- wc &= 0x0f;
- }
- else if (wc < 0xf8)
- {
- charlen = 4;
- wc &= 0x07;
- }
- else if (wc < 0xfc)
- {
- charlen = 5;
- wc &= 0x03;
- }
- else
- {
- charlen = 6;
- wc &= 0x01;
- }
-
- for (j = 1; j < charlen; j++)
- {
- wc <<= 6;
- wc |= ((unsigned char *)p)[j] & 0x3f;
- }
-
- result[i] = wc;
- p += charlen;
- }
- }
- result[i] = 0;
-
- if (items_written)
- *items_written = i;
-
- return result;
-}
-
-/**
- * g_utf8_to_ucs4:
- * @str: a UTF-8 encoded string
- * @len: the maximum length of @str to use, in bytes. If @len < 0,
- * then the string is nul-terminated.
- * @items_read: location to store number of bytes read, or %NULL.
- * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
- * returned in case @str contains a trailing partial
- * character. If an error occurs then the index of the
- * invalid input is stored here.
- * @items_written: location to store number of characters written or %NULL.
- * The value here stored does not include the trailing 0
- * character.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from UTF-8 to a 32-bit fixed width
- * representation as UCS-4. A trailing 0 will be added to the
- * string after the converted text.
- *
- * Return value: a pointer to a newly allocated UCS-4 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set.
- **/
-gunichar *
-g_utf8_to_ucs4 (const gchar *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- gunichar *result = NULL;
- gint n_chars, i;
- const gchar *in;
-
- in = str;
- n_chars = 0;
- while ((len < 0 || str + len - in > 0) && *in)
- {
- gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
- if (wc & 0x80000000)
- {
- if (wc == (gunichar)-2)
- {
- if (items_read)
- break;
- else
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Partial character sequence at end of input"));
- }
- else
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
-
- goto err_out;
- }
-
- n_chars++;
-
- in = g_utf8_next_char (in);
- }
-
- result = g_new (gunichar, n_chars + 1);
-
- in = str;
- for (i=0; i < n_chars; i++)
- {
- result[i] = g_utf8_get_char (in);
- in = g_utf8_next_char (in);
- }
- result[i] = 0;
-
- if (items_written)
- *items_written = n_chars;
-
- err_out:
- if (items_read)
- *items_read = in - str;
-
- return result;
-}
-
-/**
- * g_ucs4_to_utf8:
- * @str: a UCS-4 encoded string
- * @len: the maximum length (number of characters) of @str to use.
- * If @len < 0, then the string is nul-terminated.
- * @items_read: location to store number of characters read, or %NULL.
- * @items_written: location to store number of bytes written or %NULL.
- * The value here stored does not include the trailing 0
- * byte.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from a 32-bit fixed width representation as UCS-4.
- * to UTF-8. The result will be terminated with a 0 byte.
- *
- * Return value: a pointer to a newly allocated UTF-8 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set. In that case, @items_read will be
- * set to the position of the first invalid input
- * character.
- **/
-gchar *
-g_ucs4_to_utf8 (const gunichar *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- gint result_length;
- gchar *result = NULL;
- gchar *p;
- gint i;
-
- result_length = 0;
- for (i = 0; len < 0 || i < len ; i++)
- {
- if (!str[i])
- break;
-
- if (str[i] >= 0x80000000)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Character out of range for UTF-8"));
- goto err_out;
- }
-
- result_length += UTF8_LENGTH (str[i]);
- }
-
- result = g_malloc (result_length + 1);
- p = result;
-
- i = 0;
- while (p < result + result_length)
- p += g_unichar_to_utf8 (str[i++], p);
-
- *p = '\0';
-
- if (items_written)
- *items_written = p - result;
-
- err_out:
- if (items_read)
- *items_read = i;
-
- return result;
-}
-
-#define SURROGATE_VALUE(h,l) (((h) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000)
-
-/**
- * g_utf16_to_utf8:
- * @str: a UTF-16 encoded string
- * @len: the maximum length (number of <type>gunichar2</type>) of @str to use.
- * If @len < 0, then the string is nul-terminated.
- * @items_read: location to store number of words read, or %NULL.
- * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
- * returned in case @str contains a trailing partial
- * character. If an error occurs then the index of the
- * invalid input is stored here.
- * @items_written: location to store number of bytes written, or %NULL.
- * The value stored here does not include the trailing
- * 0 byte.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from UTF-16 to UTF-8. The result will be
- * terminated with a 0 byte.
- *
- * Note that the input is expected to be already in native endianness,
- * an initial byte-order-mark character is not handled specially.
- * g_convert() can be used to convert a byte buffer of UTF-16 data of
- * ambiguous endianess.
- *
- * Further note that this function does not validate the result
- * string; it may e.g. include embedded NUL characters. The only
- * validation done by this function is to ensure that the input can
- * be correctly interpreted as UTF-16, i.e. it doesn't contain
- * things unpaired surrogates.
- *
- * Return value: a pointer to a newly allocated UTF-8 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set.
- **/
-gchar *
-g_utf16_to_utf8 (const gunichar2 *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- /* This function and g_utf16_to_ucs4 are almost exactly identical - The lines that differ
- * are marked.
- */
- const gunichar2 *in;
- gchar *out;
- gchar *result = NULL;
- gint n_bytes;
- gunichar high_surrogate;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- n_bytes = 0;
- in = str;
- high_surrogate = 0;
- while ((len < 0 || in - str < len) && *in)
- {
- gunichar2 c = *in;
- gunichar wc;
-
- if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
- {
- if (high_surrogate)
- {
- wc = SURROGATE_VALUE (high_surrogate, c);
- high_surrogate = 0;
- }
- else
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
- goto err_out;
- }
- }
- else
- {
- if (high_surrogate)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
- goto err_out;
- }
-
- if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
- {
- high_surrogate = c;
- goto next1;
- }
- else
- wc = c;
- }
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- n_bytes += UTF8_LENGTH (wc);
-
- next1:
- in++;
- }
-
- if (high_surrogate && !items_read)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Partial character sequence at end of input"));
- goto err_out;
- }
-
- /* At this point, everything is valid, and we just need to convert
- */
- /********** DIFFERENT for UTF8/UCS4 **********/
- result = g_malloc (n_bytes + 1);
-
- high_surrogate = 0;
- out = result;
- in = str;
- while (out < result + n_bytes)
- {
- gunichar2 c = *in;
- gunichar wc;
-
- if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
- {
- wc = SURROGATE_VALUE (high_surrogate, c);
- high_surrogate = 0;
- }
- else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
- {
- high_surrogate = c;
- goto next2;
- }
- else
- wc = c;
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- out += g_unichar_to_utf8 (wc, out);
-
- next2:
- in++;
- }
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- *out = '\0';
-
- if (items_written)
- /********** DIFFERENT for UTF8/UCS4 **********/
- *items_written = out - result;
-
- err_out:
- if (items_read)
- *items_read = in - str;
-
- return result;
-}
-
-/**
- * g_utf16_to_ucs4:
- * @str: a UTF-16 encoded string
- * @len: the maximum length (number of <type>gunichar2</type>) of @str to use.
- * If @len < 0, then the string is nul-terminated.
- * @items_read: location to store number of words read, or %NULL.
- * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
- * returned in case @str contains a trailing partial
- * character. If an error occurs then the index of the
- * invalid input is stored here.
- * @items_written: location to store number of characters written, or %NULL.
- * The value stored here does not include the trailing
- * 0 character.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from UTF-16 to UCS-4. The result will be
- * nul-terminated.
- *
- * Return value: a pointer to a newly allocated UCS-4 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set.
- **/
-gunichar *
-g_utf16_to_ucs4 (const gunichar2 *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- const gunichar2 *in;
- gchar *out;
- gchar *result = NULL;
- gint n_bytes;
- gunichar high_surrogate;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- n_bytes = 0;
- in = str;
- high_surrogate = 0;
- while ((len < 0 || in - str < len) && *in)
- {
- gunichar2 c = *in;
- gunichar wc;
-
- if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
- {
- if (high_surrogate)
- {
- wc = SURROGATE_VALUE (high_surrogate, c);
- high_surrogate = 0;
- }
- else
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
- goto err_out;
- }
- }
- else
- {
- if (high_surrogate)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
- goto err_out;
- }
-
- if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
- {
- high_surrogate = c;
- goto next1;
- }
- else
- wc = c;
- }
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- n_bytes += sizeof (gunichar);
-
- next1:
- in++;
- }
-
- if (high_surrogate && !items_read)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Partial character sequence at end of input"));
- goto err_out;
- }
-
- /* At this point, everything is valid, and we just need to convert
- */
- /********** DIFFERENT for UTF8/UCS4 **********/
- result = g_malloc (n_bytes + 4);
-
- high_surrogate = 0;
- out = result;
- in = str;
- while (out < result + n_bytes)
- {
- gunichar2 c = *in;
- gunichar wc;
-
- if (c >= 0xdc00 && c < 0xe000) /* low surrogate */
- {
- wc = SURROGATE_VALUE (high_surrogate, c);
- high_surrogate = 0;
- }
- else if (c >= 0xd800 && c < 0xdc00) /* high surrogate */
- {
- high_surrogate = c;
- goto next2;
- }
- else
- wc = c;
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- *(gunichar *)out = wc;
- out += sizeof (gunichar);
-
- next2:
- in++;
- }
-
- /********** DIFFERENT for UTF8/UCS4 **********/
- *(gunichar *)out = 0;
-
- if (items_written)
- /********** DIFFERENT for UTF8/UCS4 **********/
- *items_written = (out - result) / sizeof (gunichar);
-
- err_out:
- if (items_read)
- *items_read = in - str;
-
- return (gunichar *)result;
-}
-
-/**
- * g_utf8_to_utf16:
- * @str: a UTF-8 encoded string
- * @len: the maximum length (number of bytes) of @str to use.
- * If @len < 0, then the string is nul-terminated.
- * @items_read: location to store number of bytes read, or %NULL.
- * If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
- * returned in case @str contains a trailing partial
- * character. If an error occurs then the index of the
- * invalid input is stored here.
- * @items_written: location to store number of <type>gunichar2</type> written,
- * or %NULL.
- * The value stored here does not include the trailing 0.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from UTF-8 to UTF-16. A 0 character will be
- * added to the result after the converted text.
- *
- * Return value: a pointer to a newly allocated UTF-16 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set.
- **/
-gunichar2 *
-g_utf8_to_utf16 (const gchar *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- gunichar2 *result = NULL;
- gint n16;
- const gchar *in;
- gint i;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- in = str;
- n16 = 0;
- while ((len < 0 || str + len - in > 0) && *in)
- {
- gunichar wc = g_utf8_get_char_extended (in, len < 0 ? 6 : str + len - in);
- if (wc & 0x80000000)
- {
- if (wc == (gunichar)-2)
- {
- if (items_read)
- break;
- else
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
- _("Partial character sequence at end of input"));
- }
- else
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid byte sequence in conversion input"));
-
- goto err_out;
- }
-
- if (wc < 0xd800)
- n16 += 1;
- else if (wc < 0xe000)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
-
- goto err_out;
- }
- else if (wc < 0x10000)
- n16 += 1;
- else if (wc < 0x110000)
- n16 += 2;
- else
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Character out of range for UTF-16"));
-
- goto err_out;
- }
-
- in = g_utf8_next_char (in);
- }
-
- result = g_new (gunichar2, n16 + 1);
-
- in = str;
- for (i = 0; i < n16;)
- {
- gunichar wc = g_utf8_get_char (in);
-
- if (wc < 0x10000)
- {
- result[i++] = wc;
- }
- else
- {
- result[i++] = (wc - 0x10000) / 0x400 + 0xd800;
- result[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
- }
-
- in = g_utf8_next_char (in);
- }
-
- result[i] = 0;
-
- if (items_written)
- *items_written = n16;
-
- err_out:
- if (items_read)
- *items_read = in - str;
-
- return result;
-}
-
-/**
- * g_ucs4_to_utf16:
- * @str: a UCS-4 encoded string
- * @len: the maximum length (number of characters) of @str to use.
- * If @len < 0, then the string is nul-terminated.
- * @items_read: location to store number of bytes read, or %NULL.
- * If an error occurs then the index of the invalid input
- * is stored here.
- * @items_written: location to store number of <type>gunichar2</type>
- * written, or %NULL. The value stored here does not
- * include the trailing 0.
- * @error: location to store the error occuring, or %NULL to ignore
- * errors. Any of the errors in #GConvertError other than
- * %G_CONVERT_ERROR_NO_CONVERSION may occur.
- *
- * Convert a string from UCS-4 to UTF-16. A 0 character will be
- * added to the result after the converted text.
- *
- * Return value: a pointer to a newly allocated UTF-16 string.
- * This value must be freed with g_free(). If an
- * error occurs, %NULL will be returned and
- * @error set.
- **/
-gunichar2 *
-g_ucs4_to_utf16 (const gunichar *str,
- glong len,
- glong *items_read,
- glong *items_written,
- GError **error)
-{
- gunichar2 *result = NULL;
- gint n16;
- gint i, j;
-
- n16 = 0;
- i = 0;
- while ((len < 0 || i < len) && str[i])
- {
- gunichar wc = str[i];
-
- if (wc < 0xd800)
- n16 += 1;
- else if (wc < 0xe000)
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Invalid sequence in conversion input"));
-
- goto err_out;
- }
- else if (wc < 0x10000)
- n16 += 1;
- else if (wc < 0x110000)
- n16 += 2;
- else
- {
- g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
- _("Character out of range for UTF-16"));
-
- goto err_out;
- }
-
- i++;
- }
-
- result = g_new (gunichar2, n16 + 1);
-
- for (i = 0, j = 0; j < n16; i++)
- {
- gunichar wc = str[i];
-
- if (wc < 0x10000)
- {
- result[j++] = wc;
- }
- else
- {
- result[j++] = (wc - 0x10000) / 0x400 + 0xd800;
- result[j++] = (wc - 0x10000) % 0x400 + 0xdc00;
- }
- }
- result[j] = 0;
-
- if (items_written)
- *items_written = n16;
-
- err_out:
- if (items_read)
- *items_read = i;
-
- return result;
-}
-
-#define CONTINUATION_CHAR \
- G_STMT_START { \
- if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ \
- goto error; \
- val <<= 6; \
- val |= (*(guchar *)p) & 0x3f; \
- } G_STMT_END
-
-static const gchar *
-fast_validate (const char *str)
-
-{
- gunichar val = 0;
- gunichar min = 0;
- const gchar *p;
-
- for (p = str; *p; p++)
- {
- if (*(guchar *)p < 128)
- /* done */;
- else
- {
- const gchar *last;
-
- last = p;
- if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
- {
- if (G_UNLIKELY ((*(guchar *)p & 0x1e) == 0))
- goto error;
- p++;
- if (G_UNLIKELY ((*(guchar *)p & 0xc0) != 0x80)) /* 10xxxxxx */
- goto error;
- }
- else
- {
- if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
- {
- min = (1 << 11);
- val = *(guchar *)p & 0x0f;
- goto TWO_REMAINING;
- }
- else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
- {
- min = (1 << 16);
- val = *(guchar *)p & 0x07;
- }
- else
- goto error;
-
- p++;
- CONTINUATION_CHAR;
- TWO_REMAINING:
- p++;
- CONTINUATION_CHAR;
- p++;
- CONTINUATION_CHAR;
-
- if (G_UNLIKELY (val < min))
- goto error;
-
- if (G_UNLIKELY (!UNICODE_VALID(val)))
- goto error;
- }
-
- continue;
-
- error:
- return last;
- }
- }
-
- return p;
-}
-
-static const gchar *
-fast_validate_len (const char *str,
- gssize max_len)
-
-{
- gunichar val = 0;
- gunichar min = 0;
- const gchar *p;
-
- g_assert (max_len >= 0);
-
- for (p = str; ((p - str) < max_len) && *p; p++)
- {
- if (*(guchar *)p < 128)
- /* done */;
- else
- {
- const gchar *last;
-
- last = p;
- if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */
- {
- if (G_UNLIKELY (max_len - (p - str) < 2))
- goto error;
-
- if (G_UNLIKELY ((*(guchar *)p & 0x1e) == 0))
- goto error;
- p++;
- if (G_UNLIKELY ((*(guchar *)p & 0xc0) != 0x80)) /* 10xxxxxx */
- goto error;
- }
- else
- {
- if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */
- {
- if (G_UNLIKELY (max_len - (p - str) < 3))
- goto error;
-
- min = (1 << 11);
- val = *(guchar *)p & 0x0f;
- goto TWO_REMAINING;
- }
- else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */
- {
- if (G_UNLIKELY (max_len - (p - str) < 4))
- goto error;
-
- min = (1 << 16);
- val = *(guchar *)p & 0x07;
- }
- else
- goto error;
-
- p++;
- CONTINUATION_CHAR;
- TWO_REMAINING:
- p++;
- CONTINUATION_CHAR;
- p++;
- CONTINUATION_CHAR;
-
- if (G_UNLIKELY (val < min))
- goto error;
- if (G_UNLIKELY (!UNICODE_VALID(val)))
- goto error;
- }
-
- continue;
-
- error:
- return last;
- }
- }
-
- return p;
-}
-
-/**
- * g_utf8_validate:
- * @str: a pointer to character data
- * @max_len: max bytes to validate, or -1 to go until NUL
- * @end: return location for end of valid data
- *
- * Validates UTF-8 encoded text. @str is the text to validate;
- * if @str is nul-terminated, then @max_len can be -1, otherwise
- * @max_len should be the number of bytes to validate.
- * If @end is non-%NULL, then the end of the valid range
- * will be stored there (i.e. the start of the first invalid
- * character if some bytes were invalid, or the end of the text
- * being validated otherwise).
- *
- * Note that g_utf8_validate() returns %FALSE if @max_len is
- * positive and NUL is met before @max_len bytes have been read.
- *
- * Returns %TRUE if all of @str was valid. Many GLib and GTK+
- * routines <emphasis>require</emphasis> valid UTF-8 as input;
- * so data read from a file or the network should be checked
- * with g_utf8_validate() before doing anything else with it.
- *
- * Return value: %TRUE if the text was valid UTF-8
- **/
-gboolean
-g_utf8_validate (const char *str,
- gssize max_len,
- const gchar **end)
-
-{
- const gchar *p;
-
- if (max_len < 0)
- p = fast_validate (str);
- else
- p = fast_validate_len (str, max_len);
-
- if (end)
- *end = p;
-
- if ((max_len >= 0 && p != str + max_len) ||
- (max_len < 0 && *p != '\0'))
- return FALSE;
- else
- return TRUE;
-}
-
-/**
- * g_unichar_validate:
- * @ch: a Unicode character
- *
- * Checks whether @ch is a valid Unicode character. Some possible
- * integer values of @ch will not be valid. 0 is considered a valid
- * character, though it's normally a string terminator.
- *
- * Return value: %TRUE if @ch is a valid Unicode character
- **/
-gboolean
-g_unichar_validate (gunichar ch)
-{
- return UNICODE_VALID (ch);
-}
-
-/**
- * g_utf8_strreverse:
- * @str: a UTF-8 encoded string
- * @len: the maximum length of @str to use, in bytes. If @len < 0,
- * then the string is nul-terminated.
- *
- * Reverses a UTF-8 string. @str must be valid UTF-8 encoded text.
- * (Use g_utf8_validate() on all text before trying to use UTF-8
- * utility functions with it.)
- *
- * This function is intended for programmatic uses of reversed strings.
- * It pays no attention to decomposed characters, combining marks, byte
- * order marks, directional indicators (LRM, LRO, etc) and similar
- * characters which might need special handling when reversing a string
- * for display purposes.
- *
- * Note that unlike g_strreverse(), this function returns
- * newly-allocated memory, which should be freed with g_free() when
- * no longer needed.
- *
- * Returns: a newly-allocated string which is the reverse of @str.
- *
- * Since: 2.2
- */
-gchar *
-g_utf8_strreverse (const gchar *str,
- gssize len)
-{
- gchar *r, *result;
- const gchar *p;
-
- if (len < 0)
- len = strlen (str);
-
- result = g_new (gchar, len + 1);
- r = result + len;
- p = str;
- while (r > result)
- {
- gchar *m, skip = g_utf8_skip[*(guchar*) p];
- r -= skip;
- for (m = r; skip; skip--)
- *m++ = *p++;
- }
- result[len] = 0;
-
- return result;
-}
-
-
-gchar *
-_g_utf8_make_valid (const gchar *name)
-{
- GString *string;
- const gchar *remainder, *invalid;
- gint remaining_bytes, valid_bytes;
-
- g_return_val_if_fail (name != NULL, NULL);
-
- string = NULL;
- remainder = name;
- remaining_bytes = strlen (name);
-
- while (remaining_bytes != 0)
- {
- if (g_utf8_validate (remainder, remaining_bytes, &invalid))
- break;
- valid_bytes = invalid - remainder;
-
- if (string == NULL)
- string = g_string_sized_new (remaining_bytes);
-
- g_string_append_len (string, remainder, valid_bytes);
- /* append U+FFFD REPLACEMENT CHARACTER */
- g_string_append (string, "\357\277\275");
-
- remaining_bytes -= valid_bytes + 1;
- remainder = invalid + 1;
- }
-
- if (string == NULL)
- return g_strdup (name);
-
- g_string_append (string, remainder);
-
- g_assert (g_utf8_validate (string->str, -1, NULL));
-
- return g_string_free (string, FALSE);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
- */
-
-#include "config.h"
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdarg.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <locale.h>
-#include <string.h>
-#include <ctype.h> /* For tolower() */
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-#include <sys/types.h>
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#ifdef HAVE_CRT_EXTERNS_H
-#include <crt_externs.h> /* for _NSGetEnviron */
-#endif
-
-/* implement gutils's inline functions
- */
-#define G_IMPLEMENT_INLINES 1
-#define __G_UTILS_C__
-#include "gutils.h"
-
-#include "gfileutils.h"
-#include "ghash.h"
-#include "gslist.h"
-#include "gprintfint.h"
-#include "gthread.h"
-#include "gthreadprivate.h"
-#include "gtestutils.h"
-#include "gunicode.h"
-#include "gstrfuncs.h"
-#include "glibintl.h"
-
-#ifdef G_PLATFORM_WIN32
-#include "garray.h"
-#include "gconvert.h"
-#include "gwin32.h"
-#endif
-
-#ifdef MAXPATHLEN
-#define G_PATH_LENGTH MAXPATHLEN
-#elif defined (PATH_MAX)
-#define G_PATH_LENGTH PATH_MAX
-#elif defined (_PC_PATH_MAX)
-#define G_PATH_LENGTH sysconf(_PC_PATH_MAX)
-#else
-#define G_PATH_LENGTH 2048
-#endif
-
-#ifdef G_PLATFORM_WIN32
-# define STRICT /* Strict typing, please */
-# include <windows.h>
-# undef STRICT
-# ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
-# define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
-# define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
-# endif
-# include <lmcons.h> /* For UNLEN */
-#endif /* G_PLATFORM_WIN32 */
-
-#ifdef G_OS_WIN32
-# include <direct.h>
-# include <shlobj.h>
- /* older SDK (e.g. msvc 5.0) does not have these*/
-# ifndef CSIDL_MYMUSIC
-# define CSIDL_MYMUSIC 13
-# endif
-# ifndef CSIDL_MYVIDEO
-# define CSIDL_MYVIDEO 14
-# endif
-# ifndef CSIDL_INTERNET_CACHE
-# define CSIDL_INTERNET_CACHE 32
-# endif
-# ifndef CSIDL_COMMON_APPDATA
-# define CSIDL_COMMON_APPDATA 35
-# endif
-# ifndef CSIDL_MYPICTURES
-# define CSIDL_MYPICTURES 0x27
-# endif
-# ifndef CSIDL_COMMON_DOCUMENTS
-# define CSIDL_COMMON_DOCUMENTS 46
-# endif
-# ifndef CSIDL_PROFILE
-# define CSIDL_PROFILE 40
-# endif
-# include <process.h>
-#endif
-
-#ifdef HAVE_CARBON
-#include <CoreServices/CoreServices.h>
-#endif
-
-#ifdef HAVE_CODESET
-#include <langinfo.h>
-#endif
-
-const guint glib_major_version = GLIB_MAJOR_VERSION;
-const guint glib_minor_version = GLIB_MINOR_VERSION;
-const guint glib_micro_version = GLIB_MICRO_VERSION;
-const guint glib_interface_age = GLIB_INTERFACE_AGE;
-const guint glib_binary_age = GLIB_BINARY_AGE;
-
-#ifdef G_PLATFORM_WIN32
-
-static HMODULE glib_dll = NULL;
-
-#ifdef DLL_EXPORT
-
-BOOL WINAPI
-DllMain (HINSTANCE hinstDLL,
- DWORD fdwReason,
- LPVOID lpvReserved)
-{
- if (fdwReason == DLL_PROCESS_ATTACH)
- glib_dll = hinstDLL;
-
- return TRUE;
-}
-
-#endif
-
-gchar *
-_glib_get_dll_directory (void)
-{
- gchar *retval;
- gchar *p;
- wchar_t wc_fn[MAX_PATH];
-
-#ifdef DLL_EXPORT
- if (glib_dll == NULL)
- return NULL;
-#endif
-
- /* This code is different from that in
- * g_win32_get_package_installation_directory_of_module() in that
- * here we return the actual folder where the GLib DLL is. We don't
- * do the check for it being in a "bin" or "lib" subfolder and then
- * returning the parent of that.
- *
- * In a statically built GLib, glib_dll will be NULL and we will
- * thus look up the application's .exe file's location.
- */
- if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH))
- return NULL;
-
- retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
-
- p = strrchr (retval, G_DIR_SEPARATOR);
- if (p == NULL)
- {
- /* Wtf? */
- return NULL;
- }
- *p = '\0';
-
- return retval;
-}
-
-#endif
-
-/**
- * glib_check_version:
- * @required_major: the required major version.
- * @required_minor: the required minor version.
- * @required_micro: the required micro version.
- *
- * Checks that the GLib library in use is compatible with the
- * given version. Generally you would pass in the constants
- * #GLIB_MAJOR_VERSION, #GLIB_MINOR_VERSION, #GLIB_MICRO_VERSION
- * as the three arguments to this function; that produces
- * a check that the library in use is compatible with
- * the version of GLib the application or module was compiled
- * against.
- *
- * Compatibility is defined by two things: first the version
- * of the running library is newer than the version
- * @required_major.required_minor.@required_micro. Second
- * the running library must be binary compatible with the
- * version @required_major.required_minor.@required_micro
- * (same major version.)
- *
- * Return value: %NULL if the GLib library is compatible with the
- * given version, or a string describing the version mismatch.
- * The returned string is owned by GLib and must not be modified
- * or freed.
- *
- * Since: 2.6
- **/
-const gchar *
-glib_check_version (guint required_major,
- guint required_minor,
- guint required_micro)
-{
- gint glib_effective_micro = 100 * GLIB_MINOR_VERSION + GLIB_MICRO_VERSION;
- gint required_effective_micro = 100 * required_minor + required_micro;
-
- if (required_major > GLIB_MAJOR_VERSION)
- return "GLib version too old (major mismatch)";
- if (required_major < GLIB_MAJOR_VERSION)
- return "GLib version too new (major mismatch)";
- if (required_effective_micro < glib_effective_micro - GLIB_BINARY_AGE)
- return "GLib version too new (micro mismatch)";
- if (required_effective_micro > glib_effective_micro)
- return "GLib version too old (micro mismatch)";
- return NULL;
-}
-
-#if !defined (HAVE_MEMMOVE) && !defined (HAVE_WORKING_BCOPY)
-/**
- * g_memmove:
- * @dest: the destination address to copy the bytes to.
- * @src: the source address to copy the bytes from.
- * @len: the number of bytes to copy.
- *
- * Copies a block of memory @len bytes long, from @src to @dest.
- * The source and destination areas may overlap.
- *
- * In order to use this function, you must include
- * <filename>string.h</filename> yourself, because this macro will
- * typically simply resolve to memmove() and GLib does not include
- * <filename>string.h</filename> for you.
- */
-void
-g_memmove (gpointer dest,
- gconstpointer src,
- gulong len)
-{
- gchar* destptr = dest;
- const gchar* srcptr = src;
- if (src + len < dest || dest + len < src)
- {
- bcopy (src, dest, len);
- return;
- }
- else if (dest <= src)
- {
- while (len--)
- *(destptr++) = *(srcptr++);
- }
- else
- {
- destptr += len;
- srcptr += len;
- while (len--)
- *(--destptr) = *(--srcptr);
- }
-}
-#endif /* !HAVE_MEMMOVE && !HAVE_WORKING_BCOPY */
-
-#ifdef G_OS_WIN32
-#undef g_atexit
-#endif
-
-/**
- * g_atexit:
- * @func: the function to call on normal program termination.
- *
- * Specifies a function to be called at normal program termination.
- *
- * Since GLib 2.8.2, on Windows g_atexit() actually is a preprocessor
- * macro that maps to a call to the atexit() function in the C
- * library. This means that in case the code that calls g_atexit(),
- * i.e. atexit(), is in a DLL, the function will be called when the
- * DLL is detached from the program. This typically makes more sense
- * than that the function is called when the GLib DLL is detached,
- * which happened earlier when g_atexit() was a function in the GLib
- * DLL.
- *
- * The behaviour of atexit() in the context of dynamically loaded
- * modules is not formally specified and varies wildly.
- *
- * On POSIX systems, calling g_atexit() (or atexit()) in a dynamically
- * loaded module which is unloaded before the program terminates might
- * well cause a crash at program exit.
- *
- * Some POSIX systems implement atexit() like Windows, and have each
- * dynamically loaded module maintain an own atexit chain that is
- * called when the module is unloaded.
- *
- * On other POSIX systems, before a dynamically loaded module is
- * unloaded, the registered atexit functions (if any) residing in that
- * module are called, regardless where the code that registered them
- * resided. This is presumably the most robust approach.
- *
- * As can be seen from the above, for portability it's best to avoid
- * calling g_atexit() (or atexit()) except in the main executable of a
- * program.
- */
-void
-g_atexit (GVoidFunc func)
-{
- gint result;
- const gchar *error = NULL;
-
- /* keep this in sync with glib.h */
-
-#ifdef G_NATIVE_ATEXIT
- result = ATEXIT (func);
- if (result)
- error = g_strerror (errno);
-#elif defined (HAVE_ATEXIT)
-# ifdef NeXT /* @#%@! NeXTStep */
- result = !atexit ((void (*)(void)) func);
- if (result)
- error = g_strerror (errno);
-# else
- result = atexit ((void (*)(void)) func);
- if (result)
- error = g_strerror (errno);
-# endif /* NeXT */
-#elif defined (HAVE_ON_EXIT)
- result = on_exit ((void (*)(int, void *)) func, NULL);
- if (result)
- error = g_strerror (errno);
-#else
- result = 0;
- error = "no implementation";
-#endif /* G_NATIVE_ATEXIT */
-
- if (error)
- g_error ("Could not register atexit() function: %s", error);
-}
-
-/* Based on execvp() from GNU Libc.
- * Some of this code is cut-and-pasted into gspawn.c
- */
-
-static gchar*
-my_strchrnul (const gchar *str,
- gchar c)
-{
- gchar *p = (gchar*)str;
- while (*p && (*p != c))
- ++p;
-
- return p;
-}
-
-#ifdef G_OS_WIN32
-
-static gchar *inner_find_program_in_path (const gchar *program);
-
-gchar*
-g_find_program_in_path (const gchar *program)
-{
- const gchar *last_dot = strrchr (program, '.');
-
- if (last_dot == NULL ||
- strchr (last_dot, '\\') != NULL ||
- strchr (last_dot, '/') != NULL)
- {
- const gint program_length = strlen (program);
- gchar *pathext = g_build_path (";",
- ".exe;.cmd;.bat;.com",
- g_getenv ("PATHEXT"),
- NULL);
- gchar *p;
- gchar *decorated_program;
- gchar *retval;
-
- p = pathext;
- do
- {
- gchar *q = my_strchrnul (p, ';');
-
- decorated_program = g_malloc (program_length + (q-p) + 1);
- memcpy (decorated_program, program, program_length);
- memcpy (decorated_program+program_length, p, q-p);
- decorated_program [program_length + (q-p)] = '\0';
-
- retval = inner_find_program_in_path (decorated_program);
- g_free (decorated_program);
-
- if (retval != NULL)
- {
- g_free (pathext);
- return retval;
- }
- p = q;
- } while (*p++ != '\0');
- g_free (pathext);
- return NULL;
- }
- else
- return inner_find_program_in_path (program);
-}
-
-#endif
-
-/**
- * g_find_program_in_path:
- * @program: a program name in the GLib file name encoding
- *
- * Locates the first executable named @program in the user's path, in the
- * same way that execvp() would locate it. Returns an allocated string
- * with the absolute path name, or %NULL if the program is not found in
- * the path. If @program is already an absolute path, returns a copy of
- * @program if @program exists and is executable, and %NULL otherwise.
- *
- * On Windows, if @program does not have a file type suffix, tries
- * with the suffixes .exe, .cmd, .bat and .com, and the suffixes in
- * the <envar>PATHEXT</envar> environment variable.
- *
- * On Windows, it looks for the file in the same way as CreateProcess()
- * would. This means first in the directory where the executing
- * program was loaded from, then in the current directory, then in the
- * Windows 32-bit system directory, then in the Windows directory, and
- * finally in the directories in the <envar>PATH</envar> environment
- * variable. If the program is found, the return value contains the
- * full name including the type suffix.
- *
- * Return value: absolute path, or %NULL
- **/
-#ifdef G_OS_WIN32
-static gchar *
-inner_find_program_in_path (const gchar *program)
-#else
-gchar*
-g_find_program_in_path (const gchar *program)
-#endif
-{
- const gchar *path, *p;
- gchar *name, *freeme;
-#ifdef G_OS_WIN32
- const gchar *path_copy;
- gchar *filename = NULL, *appdir = NULL;
- gchar *sysdir = NULL, *windir = NULL;
- int n;
- wchar_t wfilename[MAXPATHLEN], wsysdir[MAXPATHLEN],
- wwindir[MAXPATHLEN];
-#endif
- gsize len;
- gsize pathlen;
-
- g_return_val_if_fail (program != NULL, NULL);
-
- /* If it is an absolute path, or a relative path including subdirectories,
- * don't look in PATH.
- */
- if (g_path_is_absolute (program)
- || strchr (program, G_DIR_SEPARATOR) != NULL
-#ifdef G_OS_WIN32
- || strchr (program, '/') != NULL
-#endif
- )
- {
- if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
- !g_file_test (program, G_FILE_TEST_IS_DIR))
- return g_strdup (program);
- else
- return NULL;
- }
-
- path = g_getenv ("PATH");
-#if defined(G_OS_UNIX) || defined(G_OS_BEOS)
- if (path == NULL)
- {
- /* There is no `PATH' in the environment. The default
- * search path in GNU libc is the current directory followed by
- * the path `confstr' returns for `_CS_PATH'.
- */
-
- /* In GLib we put . last, for security, and don't use the
- * unportable confstr(); UNIX98 does not actually specify
- * what to search if PATH is unset. POSIX may, dunno.
- */
-
- path = "/bin:/usr/bin:.";
- }
-#else
- n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
- if (n > 0 && n < MAXPATHLEN)
- filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
-
- n = GetSystemDirectoryW (wsysdir, MAXPATHLEN);
- if (n > 0 && n < MAXPATHLEN)
- sysdir = g_utf16_to_utf8 (wsysdir, -1, NULL, NULL, NULL);
-
- n = GetWindowsDirectoryW (wwindir, MAXPATHLEN);
- if (n > 0 && n < MAXPATHLEN)
- windir = g_utf16_to_utf8 (wwindir, -1, NULL, NULL, NULL);
-
- if (filename)
- {
- appdir = g_path_get_dirname (filename);
- g_free (filename);
- }
-
- path = g_strdup (path);
-
- if (windir)
- {
- const gchar *tem = path;
- path = g_strconcat (windir, ";", path, NULL);
- g_free ((gchar *) tem);
- g_free (windir);
- }
-
- if (sysdir)
- {
- const gchar *tem = path;
- path = g_strconcat (sysdir, ";", path, NULL);
- g_free ((gchar *) tem);
- g_free (sysdir);
- }
-
- {
- const gchar *tem = path;
- path = g_strconcat (".;", path, NULL);
- g_free ((gchar *) tem);
- }
-
- if (appdir)
- {
- const gchar *tem = path;
- path = g_strconcat (appdir, ";", path, NULL);
- g_free ((gchar *) tem);
- g_free (appdir);
- }
-
- path_copy = path;
-#endif
-
- len = strlen (program) + 1;
- pathlen = strlen (path);
- freeme = name = g_malloc (pathlen + len + 1);
-
- /* Copy the file name at the top, including '\0' */
- memcpy (name + pathlen + 1, program, len);
- name = name + pathlen;
- /* And add the slash before the filename */
- *name = G_DIR_SEPARATOR;
-
- p = path;
- do
- {
- char *startp;
-
- path = p;
- p = my_strchrnul (path, G_SEARCHPATH_SEPARATOR);
-
- if (p == path)
- /* Two adjacent colons, or a colon at the beginning or the end
- * of `PATH' means to search the current directory.
- */
- startp = name + 1;
- else
- startp = memcpy (name - (p - path), path, p - path);
-
- if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
- !g_file_test (startp, G_FILE_TEST_IS_DIR))
- {
- gchar *ret;
- ret = g_strdup (startp);
- g_free (freeme);
-#ifdef G_OS_WIN32
- g_free ((gchar *) path_copy);
-#endif
- return ret;
- }
- }
- while (*p++ != '\0');
-
- g_free (freeme);
-#ifdef G_OS_WIN32
- g_free ((gchar *) path_copy);
-#endif
-
- return NULL;
-}
-
-static gboolean
-debug_key_matches (const gchar *key,
- const gchar *token,
- guint length)
-{
- for (; length; length--, key++, token++)
- {
- char k = (*key == '_') ? '-' : tolower (*key );
- char t = (*token == '_') ? '-' : tolower (*token);
-
- if (k != t)
- return FALSE;
- }
-
- return *key == '\0';
-}
-
-/**
- * g_parse_debug_string:
- * @string: a list of debug options separated by colons, spaces, or
- * commas, or %NULL.
- * @keys: pointer to an array of #GDebugKey which associate
- * strings with bit flags.
- * @nkeys: the number of #GDebugKey<!-- -->s in the array.
- *
- * Parses a string containing debugging options
- * into a %guint containing bit flags. This is used
- * within GDK and GTK+ to parse the debug options passed on the
- * command line or through environment variables.
- *
- * If @string is equal to "all", all flags are set. If @string
- * is equal to "help", all the available keys in @keys are printed
- * out to standard error.
- *
- * Returns: the combined set of bit flags.
- */
-guint
-g_parse_debug_string (const gchar *string,
- const GDebugKey *keys,
- guint nkeys)
-{
- guint i;
- guint result = 0;
-
- if (string == NULL)
- return 0;
-
- /* this function is used by gmem.c/gslice.c initialization code,
- * so introducing malloc dependencies here would require adaptions
- * of those code portions.
- */
-
- if (!g_ascii_strcasecmp (string, "all"))
- {
- for (i=0; i<nkeys; i++)
- result |= keys[i].value;
- }
- else if (!g_ascii_strcasecmp (string, "help"))
- {
- /* using stdio directly for the reason stated above */
- fprintf (stderr, "Supported debug values: ");
- for (i=0; i<nkeys; i++)
- fprintf (stderr, " %s", keys[i].key);
- fprintf (stderr, "\n");
- }
- else
- {
- const gchar *p = string;
- const gchar *q;
-
- while (*p)
- {
- q = strpbrk (p, ":;, \t");
- if (!q)
- q = p + strlen(p);
-
- for (i = 0; i < nkeys; i++)
- if (debug_key_matches (keys[i].key, p, q - p))
- result |= keys[i].value;
-
- p = q;
- if (*p)
- p++;
- }
- }
-
- return result;
-}
-
-/**
- * g_basename:
- * @file_name: the name of the file.
- *
- * Gets the name of the file without any leading directory components.
- * It returns a pointer into the given file name string.
- *
- * Return value: the name of the file without any leading directory components.
- *
- * Deprecated:2.2: Use g_path_get_basename() instead, but notice that
- * g_path_get_basename() allocates new memory for the returned string, unlike
- * this function which returns a pointer into the argument.
- **/
-G_CONST_RETURN gchar*
-g_basename (const gchar *file_name)
-{
- register gchar *base;
-
- g_return_val_if_fail (file_name != NULL, NULL);
-
- base = strrchr (file_name, G_DIR_SEPARATOR);
-
-#ifdef G_OS_WIN32
- {
- gchar *q = strrchr (file_name, '/');
- if (base == NULL || (q != NULL && q > base))
- base = q;
- }
-#endif
-
- if (base)
- return base + 1;
-
-#ifdef G_OS_WIN32
- if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
- return (gchar*) file_name + 2;
-#endif /* G_OS_WIN32 */
-
- return (gchar*) file_name;
-}
-
-/**
- * g_path_get_basename:
- * @file_name: the name of the file.
- *
- * Gets the last component of the filename. If @file_name ends with a
- * directory separator it gets the component before the last slash. If
- * @file_name consists only of directory separators (and on Windows,
- * possibly a drive letter), a single separator is returned. If
- * @file_name is empty, it gets ".".
- *
- * Return value: a newly allocated string containing the last component of
- * the filename.
- */
-gchar*
-g_path_get_basename (const gchar *file_name)
-{
- register gssize base;
- register gssize last_nonslash;
- gsize len;
- gchar *retval;
-
- g_return_val_if_fail (file_name != NULL, NULL);
-
- if (file_name[0] == '\0')
- /* empty string */
- return g_strdup (".");
-
- last_nonslash = strlen (file_name) - 1;
-
- while (last_nonslash >= 0 && G_IS_DIR_SEPARATOR (file_name [last_nonslash]))
- last_nonslash--;
-
- if (last_nonslash == -1)
- /* string only containing slashes */
- return g_strdup (G_DIR_SEPARATOR_S);
-
-#ifdef G_OS_WIN32
- if (last_nonslash == 1 && g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
- /* string only containing slashes and a drive */
- return g_strdup (G_DIR_SEPARATOR_S);
-#endif /* G_OS_WIN32 */
-
- base = last_nonslash;
-
- while (base >=0 && !G_IS_DIR_SEPARATOR (file_name [base]))
- base--;
-
-#ifdef G_OS_WIN32
- if (base == -1 && g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
- base = 1;
-#endif /* G_OS_WIN32 */
-
- len = last_nonslash - base;
- retval = g_malloc (len + 1);
- memcpy (retval, file_name + base + 1, len);
- retval [len] = '\0';
- return retval;
-}
-
-/**
- * g_path_is_absolute:
- * @file_name: a file name.
- *
- * Returns %TRUE if the given @file_name is an absolute file name,
- * i.e. it contains a full path from the root directory such as "/usr/local"
- * on UNIX or "C:\windows" on Windows systems.
- *
- * Returns: %TRUE if @file_name is an absolute path.
- */
-gboolean
-g_path_is_absolute (const gchar *file_name)
-{
- g_return_val_if_fail (file_name != NULL, FALSE);
-
- if (G_IS_DIR_SEPARATOR (file_name[0]))
- return TRUE;
-
-#ifdef G_OS_WIN32
- /* Recognize drive letter on native Windows */
- if (g_ascii_isalpha (file_name[0]) &&
- file_name[1] == ':' && G_IS_DIR_SEPARATOR (file_name[2]))
- return TRUE;
-#endif /* G_OS_WIN32 */
-
- return FALSE;
-}
-
-/**
- * g_path_skip_root:
- * @file_name: a file name.
- *
- * Returns a pointer into @file_name after the root component, i.e. after
- * the "/" in UNIX or "C:\" under Windows. If @file_name is not an absolute
- * path it returns %NULL.
- *
- * Returns: a pointer into @file_name after the root component.
- */
-G_CONST_RETURN gchar*
-g_path_skip_root (const gchar *file_name)
-{
- g_return_val_if_fail (file_name != NULL, NULL);
-
-#ifdef G_PLATFORM_WIN32
- /* Skip \\server\share or //server/share */
- if (G_IS_DIR_SEPARATOR (file_name[0]) &&
- G_IS_DIR_SEPARATOR (file_name[1]) &&
- file_name[2] &&
- !G_IS_DIR_SEPARATOR (file_name[2]))
- {
- gchar *p;
-
- p = strchr (file_name + 2, G_DIR_SEPARATOR);
-#ifdef G_OS_WIN32
- {
- gchar *q = strchr (file_name + 2, '/');
- if (p == NULL || (q != NULL && q < p))
- p = q;
- }
-#endif
- if (p &&
- p > file_name + 2 &&
- p[1])
- {
- file_name = p + 1;
-
- while (file_name[0] && !G_IS_DIR_SEPARATOR (file_name[0]))
- file_name++;
-
- /* Possibly skip a backslash after the share name */
- if (G_IS_DIR_SEPARATOR (file_name[0]))
- file_name++;
-
- return (gchar *)file_name;
- }
- }
-#endif
-
- /* Skip initial slashes */
- if (G_IS_DIR_SEPARATOR (file_name[0]))
- {
- while (G_IS_DIR_SEPARATOR (file_name[0]))
- file_name++;
- return (gchar *)file_name;
- }
-
-#ifdef G_OS_WIN32
- /* Skip X:\ */
- if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':' && G_IS_DIR_SEPARATOR (file_name[2]))
- return (gchar *)file_name + 3;
-#endif
-
- return NULL;
-}
-
-/**
- * g_path_get_dirname:
- * @file_name: the name of the file.
- *
- * Gets the directory components of a file name. If the file name has no
- * directory components "." is returned. The returned string should be
- * freed when no longer needed.
- *
- * Returns: the directory components of the file.
- */
-gchar*
-g_path_get_dirname (const gchar *file_name)
-{
- register gchar *base;
- register gsize len;
-
- g_return_val_if_fail (file_name != NULL, NULL);
-
- base = strrchr (file_name, G_DIR_SEPARATOR);
-#ifdef G_OS_WIN32
- {
- gchar *q = strrchr (file_name, '/');
- if (base == NULL || (q != NULL && q > base))
- base = q;
- }
-#endif
- if (!base)
- {
-#ifdef G_OS_WIN32
- if (g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
- {
- gchar drive_colon_dot[4];
-
- drive_colon_dot[0] = file_name[0];
- drive_colon_dot[1] = ':';
- drive_colon_dot[2] = '.';
- drive_colon_dot[3] = '\0';
-
- return g_strdup (drive_colon_dot);
- }
-#endif
- return g_strdup (".");
- }
-
- while (base > file_name && G_IS_DIR_SEPARATOR (*base))
- base--;
-
-#ifdef G_OS_WIN32
- /* base points to the char before the last slash.
- *
- * In case file_name is the root of a drive (X:\) or a child of the
- * root of a drive (X:\foo), include the slash.
- *
- * In case file_name is the root share of an UNC path
- * (\\server\share), add a slash, returning \\server\share\ .
- *
- * In case file_name is a direct child of a share in an UNC path
- * (\\server\share\foo), include the slash after the share name,
- * returning \\server\share\ .
- */
- if (base == file_name + 1 && g_ascii_isalpha (file_name[0]) && file_name[1] == ':')
- base++;
- else if (G_IS_DIR_SEPARATOR (file_name[0]) &&
- G_IS_DIR_SEPARATOR (file_name[1]) &&
- file_name[2] &&
- !G_IS_DIR_SEPARATOR (file_name[2]) &&
- base >= file_name + 2)
- {
- const gchar *p = file_name + 2;
- while (*p && !G_IS_DIR_SEPARATOR (*p))
- p++;
- if (p == base + 1)
- {
- len = (guint) strlen (file_name) + 1;
- base = g_new (gchar, len + 1);
- strcpy (base, file_name);
- base[len-1] = G_DIR_SEPARATOR;
- base[len] = 0;
- return base;
- }
- if (G_IS_DIR_SEPARATOR (*p))
- {
- p++;
- while (*p && !G_IS_DIR_SEPARATOR (*p))
- p++;
- if (p == base + 1)
- base++;
- }
- }
-#endif
-
- len = (guint) 1 + base - file_name;
-
- base = g_new (gchar, len + 1);
- g_memmove (base, file_name, len);
- base[len] = 0;
-
- return base;
-}
-
-/**
- * g_get_current_dir:
- *
- * Gets the current directory.
- * The returned string should be freed when no longer needed. The encoding
- * of the returned string is system defined. On Windows, it is always UTF-8.
- *
- * Returns: the current directory.
- */
-gchar*
-g_get_current_dir (void)
-{
-#ifdef G_OS_WIN32
-
- gchar *dir = NULL;
- wchar_t dummy[2], *wdir;
- int len;
-
- len = GetCurrentDirectoryW (2, dummy);
- wdir = g_new (wchar_t, len);
-
- if (GetCurrentDirectoryW (len, wdir) == len - 1)
- dir = g_utf16_to_utf8 (wdir, -1, NULL, NULL, NULL);
-
- g_free (wdir);
-
- if (dir == NULL)
- dir = g_strdup ("\\");
-
- return dir;
-
-#else
-
- gchar *buffer = NULL;
- gchar *dir = NULL;
- static gulong max_len = 0;
-
- if (max_len == 0)
- max_len = (G_PATH_LENGTH == -1) ? 2048 : G_PATH_LENGTH;
-
- /* We don't use getcwd(3) on SUNOS, because, it does a popen("pwd")
- * and, if that wasn't bad enough, hangs in doing so.
- */
-#if (defined (sun) && !defined (__SVR4)) || !defined(HAVE_GETCWD)
- buffer = g_new (gchar, max_len + 1);
- *buffer = 0;
- dir = getwd (buffer);
-#else /* !sun || !HAVE_GETCWD */
- while (max_len < G_MAXULONG / 2)
- {
- g_free (buffer);
- buffer = g_new (gchar, max_len + 1);
- *buffer = 0;
- dir = getcwd (buffer, max_len);
-
- if (dir || errno != ERANGE)
- break;
-
- max_len *= 2;
- }
-#endif /* !sun || !HAVE_GETCWD */
-
- if (!dir || !*buffer)
- {
- /* hm, should we g_error() out here?
- * this can happen if e.g. "./" has mode \0000
- */
- buffer[0] = G_DIR_SEPARATOR;
- buffer[1] = 0;
- }
-
- dir = g_strdup (buffer);
- g_free (buffer);
-
- return dir;
-#endif /* !Win32 */
-}
-
-/**
- * g_getenv:
- * @variable: the environment variable to get, in the GLib file name encoding.
- *
- * Returns the value of an environment variable. The name and value
- * are in the GLib file name encoding. On UNIX, this means the actual
- * bytes which might or might not be in some consistent character set
- * and encoding. On Windows, it is in UTF-8. On Windows, in case the
- * environment variable's value contains references to other
- * environment variables, they are expanded.
- *
- * Return value: the value of the environment variable, or %NULL if
- * the environment variable is not found. The returned string may be
- * overwritten by the next call to g_getenv(), g_setenv() or
- * g_unsetenv().
- **/
-G_CONST_RETURN gchar*
-g_getenv (const gchar *variable)
-{
-#ifndef G_OS_WIN32
-
- g_return_val_if_fail (variable != NULL, NULL);
-
- return getenv (variable);
-
-#else /* G_OS_WIN32 */
-
- GQuark quark;
- gchar *value;
- wchar_t dummy[2], *wname, *wvalue;
- int len;
-
- g_return_val_if_fail (variable != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (variable, -1, NULL), NULL);
-
- /* On Windows NT, it is relatively typical that environment
- * variables contain references to other environment variables. If
- * so, use ExpandEnvironmentStrings(). (In an ideal world, such
- * environment variables would be stored in the Registry as
- * REG_EXPAND_SZ type values, and would then get automatically
- * expanded before a program sees them. But there is broken software
- * that stores environment variables as REG_SZ values even if they
- * contain references to other environment variables.)
- */
-
- wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
-
- len = GetEnvironmentVariableW (wname, dummy, 2);
-
- if (len == 0)
- {
- g_free (wname);
- return NULL;
- }
- else if (len == 1)
- len = 2;
-
- wvalue = g_new (wchar_t, len);
-
- if (GetEnvironmentVariableW (wname, wvalue, len) != len - 1)
- {
- g_free (wname);
- g_free (wvalue);
- return NULL;
- }
-
- if (wcschr (wvalue, L'%') != NULL)
- {
- wchar_t *tem = wvalue;
-
- len = ExpandEnvironmentStringsW (wvalue, dummy, 2);
-
- if (len > 0)
- {
- wvalue = g_new (wchar_t, len);
-
- if (ExpandEnvironmentStringsW (tem, wvalue, len) != len)
- {
- g_free (wvalue);
- wvalue = tem;
- }
- else
- g_free (tem);
- }
- }
-
- value = g_utf16_to_utf8 (wvalue, -1, NULL, NULL, NULL);
-
- g_free (wname);
- g_free (wvalue);
-
- quark = g_quark_from_string (value);
- g_free (value);
-
- return g_quark_to_string (quark);
-
-#endif /* G_OS_WIN32 */
-}
-
-/* _g_getenv_nomalloc
- * this function does a getenv() without doing any kind of allocation
- * through glib. it's suitable for chars <= 127 only (both, for the
- * variable name and the contents) and for contents < 1024 chars in
- * length. also, it aliases "" to a NULL return value.
- **/
-const gchar*
-_g_getenv_nomalloc (const gchar *variable,
- gchar buffer[1024])
-{
- const gchar *retval = getenv (variable);
- if (retval && retval[0])
- {
- gint l = strlen (retval);
- if (l < 1024)
- {
- strncpy (buffer, retval, l);
- buffer[l] = 0;
- return buffer;
- }
- }
- return NULL;
-}
-
-/**
- * g_setenv:
- * @variable: the environment variable to set, must not contain '='.
- * @value: the value for to set the variable to.
- * @overwrite: whether to change the variable if it already exists.
- *
- * Sets an environment variable. Both the variable's name and value
- * should be in the GLib file name encoding. On UNIX, this means that
- * they can be any sequence of bytes. On Windows, they should be in
- * UTF-8.
- *
- * Note that on some systems, when variables are overwritten, the memory
- * used for the previous variables and its value isn't reclaimed.
- *
- * Returns: %FALSE if the environment variable couldn't be set.
- *
- * Since: 2.4
- */
-gboolean
-g_setenv (const gchar *variable,
- const gchar *value,
- gboolean overwrite)
-{
-#ifndef G_OS_WIN32
-
- gint result;
-#ifndef HAVE_SETENV
- gchar *string;
-#endif
-
- g_return_val_if_fail (variable != NULL, FALSE);
- g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
-
-#ifdef HAVE_SETENV
- result = setenv (variable, value, overwrite);
-#else
- if (!overwrite && getenv (variable) != NULL)
- return TRUE;
-
- /* This results in a leak when you overwrite existing
- * settings. It would be fairly easy to fix this by keeping
- * our own parallel array or hash table.
- */
- string = g_strconcat (variable, "=", value, NULL);
- result = putenv (string);
-#endif
- return result == 0;
-
-#else /* G_OS_WIN32 */
-
- gboolean retval;
- wchar_t *wname, *wvalue, *wassignment;
- gchar *tem;
-
- g_return_val_if_fail (variable != NULL, FALSE);
- g_return_val_if_fail (strchr (variable, '=') == NULL, FALSE);
- g_return_val_if_fail (g_utf8_validate (variable, -1, NULL), FALSE);
- g_return_val_if_fail (g_utf8_validate (value, -1, NULL), FALSE);
-
- if (!overwrite && g_getenv (variable) != NULL)
- return TRUE;
-
- /* We want to (if possible) set both the environment variable copy
- * kept by the C runtime and the one kept by the system.
- *
- * We can't use only the C runtime's putenv or _wputenv() as that
- * won't work for arbitrary Unicode strings in a "non-Unicode" app
- * (with main() and not wmain()). In a "main()" app the C runtime
- * initializes the C runtime's environment table by converting the
- * real (wide char) environment variables to system codepage, thus
- * breaking those that aren't representable in the system codepage.
- *
- * As the C runtime's putenv() will also set the system copy, we do
- * the putenv() first, then call SetEnvironmentValueW ourselves.
- */
-
- wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
- wvalue = g_utf8_to_utf16 (value, -1, NULL, NULL, NULL);
- tem = g_strconcat (variable, "=", value, NULL);
- wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
-
- g_free (tem);
- _wputenv (wassignment);
- g_free (wassignment);
-
- retval = (SetEnvironmentVariableW (wname, wvalue) != 0);
-
- g_free (wname);
- g_free (wvalue);
-
- return retval;
-
-#endif /* G_OS_WIN32 */
-}
-
-#ifdef HAVE__NSGETENVIRON
-#define environ (*_NSGetEnviron())
-#elif !defined(G_OS_WIN32)
-
-/* According to the Single Unix Specification, environ is not in
- * any system header, although unistd.h often declares it.
- */
-extern char **environ;
-#endif
-
-/**
- * g_unsetenv:
- * @variable: the environment variable to remove, must not contain '='.
- *
- * Removes an environment variable from the environment.
- *
- * Note that on some systems, when variables are overwritten, the memory
- * used for the previous variables and its value isn't reclaimed.
- * Furthermore, this function can't be guaranteed to operate in a
- * threadsafe way.
- *
- * Since: 2.4
- **/
-void
-g_unsetenv (const gchar *variable)
-{
-#ifndef G_OS_WIN32
-
-#ifdef HAVE_UNSETENV
- g_return_if_fail (variable != NULL);
- g_return_if_fail (strchr (variable, '=') == NULL);
-
- unsetenv (variable);
-#else /* !HAVE_UNSETENV */
- int len;
- gchar **e, **f;
-
- g_return_if_fail (variable != NULL);
- g_return_if_fail (strchr (variable, '=') == NULL);
-
- len = strlen (variable);
-
- /* Mess directly with the environ array.
- * This seems to be the only portable way to do this.
- *
- * Note that we remove *all* environment entries for
- * the variable name, not just the first.
- */
- e = f = environ;
- while (*e != NULL)
- {
- if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
- {
- *f = *e;
- f++;
- }
- e++;
- }
- *f = NULL;
-#endif /* !HAVE_UNSETENV */
-
-#else /* G_OS_WIN32 */
-
- wchar_t *wname, *wassignment;
- gchar *tem;
-
- g_return_if_fail (variable != NULL);
- g_return_if_fail (strchr (variable, '=') == NULL);
- g_return_if_fail (g_utf8_validate (variable, -1, NULL));
-
- wname = g_utf8_to_utf16 (variable, -1, NULL, NULL, NULL);
- tem = g_strconcat (variable, "=", NULL);
- wassignment = g_utf8_to_utf16 (tem, -1, NULL, NULL, NULL);
-
- g_free (tem);
- _wputenv (wassignment);
- g_free (wassignment);
-
- SetEnvironmentVariableW (wname, NULL);
-
- g_free (wname);
-
-#endif /* G_OS_WIN32 */
-}
-
-/**
- * g_listenv:
- *
- * Gets the names of all variables set in the environment.
- *
- * Returns: a %NULL-terminated list of strings which must be freed
- * with g_strfreev().
- *
- * Programs that want to be portable to Windows should typically use
- * this function and g_getenv() instead of using the environ array
- * from the C library directly. On Windows, the strings in the environ
- * array are in system codepage encoding, while in most of the typical
- * use cases for environment variables in GLib-using programs you want
- * the UTF-8 encoding that this function and g_getenv() provide.
- *
- * Since: 2.8
- */
-gchar **
-g_listenv (void)
-{
-#ifndef G_OS_WIN32
- gchar **result, *eq;
- gint len, i, j;
-
- len = g_strv_length (environ);
- result = g_new0 (gchar *, len + 1);
-
- j = 0;
- for (i = 0; i < len; i++)
- {
- eq = strchr (environ[i], '=');
- if (eq)
- result[j++] = g_strndup (environ[i], eq - environ[i]);
- }
-
- result[j] = NULL;
-
- return result;
-#else
- gchar **result, *eq;
- gint len = 0, j;
- wchar_t *p, *q;
-
- p = (wchar_t *) GetEnvironmentStringsW ();
- if (p != NULL)
- {
- q = p;
- while (*q)
- {
- q += wcslen (q) + 1;
- len++;
- }
- }
- result = g_new0 (gchar *, len + 1);
-
- j = 0;
- q = p;
- while (*q)
- {
- result[j] = g_utf16_to_utf8 (q, -1, NULL, NULL, NULL);
- if (result[j] != NULL)
- {
- eq = strchr (result[j], '=');
- if (eq && eq > result[j])
- {
- *eq = '\0';
- j++;
- }
- else
- g_free (result[j]);
- }
- q += wcslen (q) + 1;
- }
- result[j] = NULL;
- FreeEnvironmentStringsW (p);
-
- return result;
-#endif
-}
-
-G_LOCK_DEFINE_STATIC (g_utils_global);
-
-static gchar *g_tmp_dir = NULL;
-static gchar *g_user_name = NULL;
-static gchar *g_real_name = NULL;
-static gchar *g_home_dir = NULL;
-static gchar *g_host_name = NULL;
-
-#ifdef G_OS_WIN32
-/* System codepage versions of the above, kept at file level so that they,
- * too, are produced only once.
- */
-static gchar *g_tmp_dir_cp = NULL;
-static gchar *g_user_name_cp = NULL;
-static gchar *g_real_name_cp = NULL;
-static gchar *g_home_dir_cp = NULL;
-#endif
-
-static gchar *g_user_data_dir = NULL;
-static gchar **g_system_data_dirs = NULL;
-static gchar *g_user_cache_dir = NULL;
-static gchar *g_user_config_dir = NULL;
-static gchar **g_system_config_dirs = NULL;
-
-static gchar **g_user_special_dirs = NULL;
-
-/* fifteen minutes of fame for everybody */
-#define G_USER_DIRS_EXPIRE 15 * 60
-
-#ifdef G_OS_WIN32
-
-static gchar *
-get_special_folder (int csidl)
-{
- wchar_t path[MAX_PATH+1];
- HRESULT hr;
- LPITEMIDLIST pidl = NULL;
- BOOL b;
- gchar *retval = NULL;
-
- hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
- if (hr == S_OK)
- {
- b = SHGetPathFromIDListW (pidl, path);
- if (b)
- retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
- CoTaskMemFree (pidl);
- }
- return retval;
-}
-
-static char *
-get_windows_directory_root (void)
-{
- wchar_t wwindowsdir[MAX_PATH];
-
- if (GetWindowsDirectoryW (wwindowsdir, G_N_ELEMENTS (wwindowsdir)))
- {
- /* Usually X:\Windows, but in terminal server environments
- * might be an UNC path, AFAIK.
- */
- char *windowsdir = g_utf16_to_utf8 (wwindowsdir, -1, NULL, NULL, NULL);
- char *p;
-
- if (windowsdir == NULL)
- return g_strdup ("C:\\");
-
- p = (char *) g_path_skip_root (windowsdir);
- if (G_IS_DIR_SEPARATOR (p[-1]) && p[-2] != ':')
- p--;
- *p = '\0';
- return windowsdir;
- }
- else
- return g_strdup ("C:\\");
-}
-
-#endif
-
-/* HOLDS: g_utils_global_lock */
-static void
-g_get_any_init_do (void)
-{
- gchar hostname[100];
-
- g_tmp_dir = g_strdup (g_getenv ("TMPDIR"));
- if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
- g_tmp_dir = g_strdup (g_getenv ("TMP"));
- if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
- g_tmp_dir = g_strdup (g_getenv ("TEMP"));
-
-#ifdef G_OS_WIN32
- if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
- g_tmp_dir = get_windows_directory_root ();
-#else
-#ifdef P_tmpdir
- if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
- {
- gsize k;
- g_tmp_dir = g_strdup (P_tmpdir);
- k = strlen (g_tmp_dir);
- if (k > 1 && G_IS_DIR_SEPARATOR (g_tmp_dir[k - 1]))
- g_tmp_dir[k - 1] = '\0';
- }
-#endif
-
- if (g_tmp_dir == NULL || *g_tmp_dir == '\0')
- {
- g_tmp_dir = g_strdup ("/tmp");
- }
-#endif /* !G_OS_WIN32 */
-
-#ifdef G_OS_WIN32
- /* We check $HOME first for Win32, though it is a last resort for Unix
- * where we prefer the results of getpwuid().
- */
- g_home_dir = g_strdup (g_getenv ("HOME"));
-
- /* Only believe HOME if it is an absolute path and exists */
- if (g_home_dir)
- {
- if (!(g_path_is_absolute (g_home_dir) &&
- g_file_test (g_home_dir, G_FILE_TEST_IS_DIR)))
- {
- g_free (g_home_dir);
- g_home_dir = NULL;
- }
- }
-
- /* In case HOME is Unix-style (it happens), convert it to
- * Windows style.
- */
- if (g_home_dir)
- {
- gchar *p;
- while ((p = strchr (g_home_dir, '/')) != NULL)
- *p = '\\';
- }
-
- if (!g_home_dir)
- {
- /* USERPROFILE is probably the closest equivalent to $HOME? */
- if (g_getenv ("USERPROFILE") != NULL)
- g_home_dir = g_strdup (g_getenv ("USERPROFILE"));
- }
-
- if (!g_home_dir)
- g_home_dir = get_special_folder (CSIDL_PROFILE);
-
- if (!g_home_dir)
- g_home_dir = get_windows_directory_root ();
-#endif /* G_OS_WIN32 */
-
-#ifdef HAVE_PWD_H
- {
- struct passwd *pw = NULL;
- gpointer buffer = NULL;
- gint error;
- gchar *logname;
-
-# if defined (HAVE_POSIX_GETPWUID_R) || defined (HAVE_NONPOSIX_GETPWUID_R)
- struct passwd pwd;
-# ifdef _SC_GETPW_R_SIZE_MAX
- /* This reurns the maximum length */
- glong bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
-
- if (bufsize < 0)
- bufsize = 64;
-# else /* _SC_GETPW_R_SIZE_MAX */
- glong bufsize = 64;
-# endif /* _SC_GETPW_R_SIZE_MAX */
-
- logname = (gchar *) g_getenv ("LOGNAME");
-
- do
- {
- g_free (buffer);
- /* we allocate 6 extra bytes to work around a bug in
- * Mac OS < 10.3. See #156446
- */
- buffer = g_malloc (bufsize + 6);
- errno = 0;
-
-# ifdef HAVE_POSIX_GETPWUID_R
- if (logname) {
- error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
- if (!pw || (pw->pw_uid != getuid ())) {
- /* LOGNAME is lying, fall back to looking up the uid */
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
- }
- } else {
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
- }
- error = error < 0 ? errno : error;
-# else /* HAVE_NONPOSIX_GETPWUID_R */
- /* HPUX 11 falls into the HAVE_POSIX_GETPWUID_R case */
-# if defined(_AIX) || defined(__hpux)
- error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- pw = error == 0 ? &pwd : NULL;
-# else /* !_AIX */
- if (logname) {
- pw = getpwnam_r (logname, &pwd, buffer, bufsize);
- if (!pw || (pw->pw_uid != getuid ())) {
- /* LOGNAME is lying, fall back to looking up the uid */
- pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- }
- } else {
- pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
- }
- error = pw ? 0 : errno;
-# endif /* !_AIX */
-# endif /* HAVE_NONPOSIX_GETPWUID_R */
-
- if (!pw)
- {
- /* we bail out prematurely if the user id can't be found
- * (should be pretty rare case actually), or if the buffer
- * should be sufficiently big and lookups are still not
- * successfull.
- */
- if (error == 0 || error == ENOENT)
- {
- g_warning ("getpwuid_r(): failed due to unknown user id (%lu)",
- (gulong) getuid ());
- break;
- }
- if (bufsize > 32 * 1024)
- {
- g_warning ("getpwuid_r(): failed due to: %s.",
- g_strerror (error));
- break;
- }
-
- bufsize *= 2;
- }
- }
- while (!pw);
-# endif /* HAVE_POSIX_GETPWUID_R || HAVE_NONPOSIX_GETPWUID_R */
-
- if (!pw)
- {
-#ifdef BUILD_WITH_ANDROID
- pw = getpwuid (getuid ());
-#else
- setpwent ();
- pw = getpwuid (getuid ());
- endpwent ();
-#endif
- }
- if (pw)
- {
- g_user_name = g_strdup (pw->pw_name);
-
-#ifdef HAVE_STRUCT_PASSWD_PW_GECO
- if (pw->pw_gecos && *pw->pw_gecos != '\0')
- {
- gchar **gecos_fields;
- gchar **name_parts;
-
- /* split the gecos field and substitute '&' */
- gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
- name_parts = g_strsplit (gecos_fields[0], "&", 0);
- pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
- g_real_name = g_strjoinv (pw->pw_name, name_parts);
- g_strfreev (gecos_fields);
- g_strfreev (name_parts);
- }
-#else
- g_real_name = g_strdup (g_user_name);
-#endif
-
- if (!g_home_dir)
- g_home_dir = g_strdup (pw->pw_dir);
- }
- g_free (buffer);
- }
-
-#else /* !HAVE_PWD_H */
-
-#ifdef G_OS_WIN32
- {
- guint len = UNLEN+1;
- wchar_t buffer[UNLEN+1];
-
- if (GetUserNameW (buffer, (LPDWORD) &len))
- {
- g_user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
- g_real_name = g_strdup (g_user_name);
- }
- }
-#endif /* G_OS_WIN32 */
-
-#endif /* !HAVE_PWD_H */
-
-#ifndef G_OS_WIN32
- if (!g_home_dir)
- g_home_dir = g_strdup (g_getenv ("HOME"));
-#endif
-
-#ifdef __EMX__
- /* change '\\' in %HOME% to '/' */
- g_strdelimit (g_home_dir, "\\",'/');
-#endif
- if (!g_user_name)
- g_user_name = g_strdup ("somebody");
- if (!g_real_name)
- g_real_name = g_strdup ("Unknown");
-
- {
-#ifndef G_OS_WIN32
- gboolean hostname_fail = (gethostname (hostname, sizeof (hostname)) == -1);
-#else
- DWORD size = sizeof (hostname);
- gboolean hostname_fail = (!GetComputerName (hostname, &size));
-#endif
- g_host_name = g_strdup (hostname_fail ? "localhost" : hostname);
- }
-
-#ifdef G_OS_WIN32
- g_tmp_dir_cp = g_locale_from_utf8 (g_tmp_dir, -1, NULL, NULL, NULL);
- g_user_name_cp = g_locale_from_utf8 (g_user_name, -1, NULL, NULL, NULL);
- g_real_name_cp = g_locale_from_utf8 (g_real_name, -1, NULL, NULL, NULL);
-
- if (!g_tmp_dir_cp)
- g_tmp_dir_cp = g_strdup ("\\");
- if (!g_user_name_cp)
- g_user_name_cp = g_strdup ("somebody");
- if (!g_real_name_cp)
- g_real_name_cp = g_strdup ("Unknown");
-
- /* home_dir might be NULL, unlike tmp_dir, user_name and
- * real_name.
- */
- if (g_home_dir)
- g_home_dir_cp = g_locale_from_utf8 (g_home_dir, -1, NULL, NULL, NULL);
- else
- g_home_dir_cp = NULL;
-#endif /* G_OS_WIN32 */
-}
-
-static inline void
-g_get_any_init (void)
-{
- if (!g_tmp_dir)
- g_get_any_init_do ();
-}
-
-static inline void
-g_get_any_init_locked (void)
-{
- G_LOCK (g_utils_global);
- g_get_any_init ();
- G_UNLOCK (g_utils_global);
-}
-
-
-/**
- * g_get_user_name:
- *
- * Gets the user name of the current user. The encoding of the returned
- * string is system-defined. On UNIX, it might be the preferred file name
- * encoding, or something else, and there is no guarantee that it is even
- * consistent on a machine. On Windows, it is always UTF-8.
- *
- * Returns: the user name of the current user.
- */
-G_CONST_RETURN gchar*
-g_get_user_name (void)
-{
- g_get_any_init_locked ();
- return g_user_name;
-}
-
-/**
- * g_get_real_name:
- *
- * Gets the real name of the user. This usually comes from the user's entry
- * in the <filename>passwd</filename> file. The encoding of the returned
- * string is system-defined. (On Windows, it is, however, always UTF-8.)
- * If the real user name cannot be determined, the string "Unknown" is
- * returned.
- *
- * Returns: the user's real name.
- */
-G_CONST_RETURN gchar*
-g_get_real_name (void)
-{
- g_get_any_init_locked ();
- return g_real_name;
-}
-
-/**
- * g_get_home_dir:
- *
- * Gets the current user's home directory as defined in the
- * password database.
- *
- * Note that in contrast to traditional UNIX tools, this function
- * prefers <filename>passwd</filename> entries over the <envar>HOME</envar>
- * environment variable.
- *
- * One of the reasons for this decision is that applications in many
- * cases need special handling to deal with the case where
- * <envar>HOME</envar> is
- * <simplelist>
- * <member>Not owned by the user</member>
- * <member>Not writeable</member>
- * <member>Not even readable</member>
- * </simplelist>
- * Since applications are in general <emphasis>not</emphasis> written
- * to deal with these situations it was considered better to make
- * g_get_home_dir() not pay attention to <envar>HOME</envar> and to
- * return the real home directory for the user. If applications
- * want to pay attention to <envar>HOME</envar>, they can do:
- * |[
- * const char *homedir = g_getenv ("HOME");
- * if (!homedir)
- * homedir = g_get_home_dir (<!-- -->);
- * ]|
- *
- * Returns: the current user's home directory
- */
-G_CONST_RETURN gchar*
-g_get_home_dir (void)
-{
- g_get_any_init_locked ();
- return g_home_dir;
-}
-
-/**
- * g_get_tmp_dir:
- *
- * Gets the directory to use for temporary files. This is found from
- * inspecting the environment variables <envar>TMPDIR</envar>,
- * <envar>TMP</envar>, and <envar>TEMP</envar> in that order. If none
- * of those are defined "/tmp" is returned on UNIX and "C:\" on Windows.
- * The encoding of the returned string is system-defined. On Windows,
- * it is always UTF-8. The return value is never %NULL or the empty string.
- *
- * Returns: the directory to use for temporary files.
- */
-G_CONST_RETURN gchar*
-g_get_tmp_dir (void)
-{
- g_get_any_init_locked ();
- return g_tmp_dir;
-}
-
-/**
- * g_get_host_name:
- *
- * Return a name for the machine.
- *
- * The returned name is not necessarily a fully-qualified domain name,
- * or even present in DNS or some other name service at all. It need
- * not even be unique on your local network or site, but usually it
- * is. Callers should not rely on the return value having any specific
- * properties like uniqueness for security purposes. Even if the name
- * of the machine is changed while an application is running, the
- * return value from this function does not change. The returned
- * string is owned by GLib and should not be modified or freed. If no
- * name can be determined, a default fixed string "localhost" is
- * returned.
- *
- * Returns: the host name of the machine.
- *
- * Since: 2.8
- */
-const gchar *
-g_get_host_name (void)
-{
- g_get_any_init_locked ();
- return g_host_name;
-}
-
-G_LOCK_DEFINE_STATIC (g_prgname);
-static gchar *g_prgname = NULL;
-
-/**
- * g_get_prgname:
- *
- * Gets the name of the program. This name should <emphasis>not</emphasis>
- * be localized, contrast with g_get_application_name().
- * (If you are using GDK or GTK+ the program name is set in gdk_init(),
- * which is called by gtk_init(). The program name is found by taking
- * the last component of <literal>argv[0]</literal>.)
- *
- * Returns: the name of the program. The returned string belongs
- * to GLib and must not be modified or freed.
- */
-gchar*
-g_get_prgname (void)
-{
- gchar* retval;
-
- G_LOCK (g_prgname);
-#ifdef G_OS_WIN32
- if (g_prgname == NULL)
- {
- static gboolean beenhere = FALSE;
-
- if (!beenhere)
- {
- gchar *utf8_buf = NULL;
- wchar_t buf[MAX_PATH+1];
-
- beenhere = TRUE;
- if (GetModuleFileNameW (GetModuleHandle (NULL),
- buf, G_N_ELEMENTS (buf)) > 0)
- utf8_buf = g_utf16_to_utf8 (buf, -1, NULL, NULL, NULL);
-
- if (utf8_buf)
- {
- g_prgname = g_path_get_basename (utf8_buf);
- g_free (utf8_buf);
- }
- }
- }
-#endif
- retval = g_prgname;
- G_UNLOCK (g_prgname);
-
- return retval;
-}
-
-/**
- * g_set_prgname:
- * @prgname: the name of the program.
- *
- * Sets the name of the program. This name should <emphasis>not</emphasis>
- * be localized, contrast with g_set_application_name(). Note that for
- * thread-safety reasons this function can only be called once.
- */
-void
-g_set_prgname (const gchar *prgname)
-{
- G_LOCK (g_prgname);
- g_free (g_prgname);
- g_prgname = g_strdup (prgname);
- G_UNLOCK (g_prgname);
-}
-
-G_LOCK_DEFINE_STATIC (g_application_name);
-static gchar *g_application_name = NULL;
-
-/**
- * g_get_application_name:
- *
- * Gets a human-readable name for the application, as set by
- * g_set_application_name(). This name should be localized if
- * possible, and is intended for display to the user. Contrast with
- * g_get_prgname(), which gets a non-localized name. If
- * g_set_application_name() has not been called, returns the result of
- * g_get_prgname() (which may be %NULL if g_set_prgname() has also not
- * been called).
- *
- * Return value: human-readable application name. may return %NULL
- *
- * Since: 2.2
- **/
-G_CONST_RETURN gchar*
-g_get_application_name (void)
-{
- gchar* retval;
-
- G_LOCK (g_application_name);
- retval = g_application_name;
- G_UNLOCK (g_application_name);
-
- if (retval == NULL)
- return g_get_prgname ();
-
- return retval;
-}
-
-/**
- * g_set_application_name:
- * @application_name: localized name of the application
- *
- * Sets a human-readable name for the application. This name should be
- * localized if possible, and is intended for display to the user.
- * Contrast with g_set_prgname(), which sets a non-localized name.
- * g_set_prgname() will be called automatically by gtk_init(),
- * but g_set_application_name() will not.
- *
- * Note that for thread safety reasons, this function can only
- * be called once.
- *
- * The application name will be used in contexts such as error messages,
- * or when displaying an application's name in the task list.
- *
- * Since: 2.2
- **/
-void
-g_set_application_name (const gchar *application_name)
-{
- gboolean already_set = FALSE;
-
- G_LOCK (g_application_name);
- if (g_application_name)
- already_set = TRUE;
- else
- g_application_name = g_strdup (application_name);
- G_UNLOCK (g_application_name);
-
- if (already_set)
- g_warning ("g_set_application_name() called multiple times");
-}
-
-/**
- * g_get_user_data_dir:
- *
- * Returns a base directory in which to access application data such
- * as icons that is customized for a particular user.
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>.
- * In this case the directory retrieved will be XDG_DATA_HOME.
- *
- * On Windows is the virtual folder that represents the My Documents
- * desktop item. See documentation for CSIDL_PERSONAL.
- *
- * Return value: a string owned by GLib that must not be modified
- * or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar*
-g_get_user_data_dir (void)
-{
- gchar *data_dir;
-
- G_LOCK (g_utils_global);
-
- if (!g_user_data_dir)
- {
-#ifdef G_OS_WIN32
- data_dir = get_special_folder (CSIDL_PERSONAL);
-#else
- data_dir = (gchar *) g_getenv ("XDG_DATA_HOME");
-
- if (data_dir && data_dir[0])
- data_dir = g_strdup (data_dir);
-#endif
- if (!data_dir || !data_dir[0])
- {
- g_get_any_init ();
-
- if (g_home_dir)
- data_dir = g_build_filename (g_home_dir, ".local",
- "share", NULL);
- else
- data_dir = g_build_filename (g_tmp_dir, g_user_name, ".local",
- "share", NULL);
- }
-
- g_user_data_dir = data_dir;
- }
- else
- data_dir = g_user_data_dir;
-
- G_UNLOCK (g_utils_global);
-
- return data_dir;
-}
-
-static void
-g_init_user_config_dir (void)
-{
- gchar *config_dir;
-
- if (!g_user_config_dir)
- {
-#ifdef G_OS_WIN32
- config_dir = get_special_folder (CSIDL_APPDATA);
-#else
- config_dir = (gchar *) g_getenv ("XDG_CONFIG_HOME");
-
- if (config_dir && config_dir[0])
- config_dir = g_strdup (config_dir);
-#endif
- if (!config_dir || !config_dir[0])
- {
- g_get_any_init ();
-
- if (g_home_dir)
- config_dir = g_build_filename (g_home_dir, ".config", NULL);
- else
- config_dir = g_build_filename (g_tmp_dir, g_user_name, ".config", NULL);
- }
-
- g_user_config_dir = config_dir;
- }
-}
-
-/**
- * g_get_user_config_dir:
- *
- * Returns a base directory in which to store user-specific application
- * configuration information such as user preferences and settings.
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>.
- * In this case the directory retrieved will be XDG_CONFIG_HOME.
- *
- * On Windows is the directory that serves as a common repository for
- * application-specific data. A typical path is
- * C:\Documents and Settings\username\Application. See documentation for
- * CSIDL_APPDATA.
- *
- * Return value: a string owned by GLib that must not be modified
- * or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar*
-g_get_user_config_dir (void)
-{
- G_LOCK (g_utils_global);
-
- g_init_user_config_dir ();
-
- G_UNLOCK (g_utils_global);
-
- return g_user_config_dir;
-}
-
-/**
- * g_get_user_cache_dir:
- *
- * Returns a base directory in which to store non-essential, cached
- * data specific to particular user.
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>.
- * In this case the directory retrieved will be XDG_CACHE_HOME.
- *
- * On Windows is the directory that serves as a common repository for
- * temporary Internet files. A typical path is
- * C:\Documents and Settings\username\Local Settings\Temporary Internet Files.
- * See documentation for CSIDL_INTERNET_CACHE.
- *
- * Return value: a string owned by GLib that must not be modified
- * or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar*
-g_get_user_cache_dir (void)
-{
- gchar *cache_dir;
-
- G_LOCK (g_utils_global);
-
- if (!g_user_cache_dir)
- {
-#ifdef G_OS_WIN32
- cache_dir = get_special_folder (CSIDL_INTERNET_CACHE); /* XXX correct? */
-#else
- cache_dir = (gchar *) g_getenv ("XDG_CACHE_HOME");
-
- if (cache_dir && cache_dir[0])
- cache_dir = g_strdup (cache_dir);
-#endif
- if (!cache_dir || !cache_dir[0])
- {
- g_get_any_init ();
-
- if (g_home_dir)
- cache_dir = g_build_filename (g_home_dir, ".cache", NULL);
- else
- cache_dir = g_build_filename (g_tmp_dir, g_user_name, ".cache", NULL);
- }
- g_user_cache_dir = cache_dir;
- }
- else
- cache_dir = g_user_cache_dir;
-
- G_UNLOCK (g_utils_global);
-
- return cache_dir;
-}
-
-#ifdef HAVE_CARBON
-
-static gchar *
-find_folder (OSType type)
-{
- gchar *filename = NULL;
- FSRef found;
-
- if (FSFindFolder (kUserDomain, type, kDontCreateFolder, &found) == noErr)
- {
- CFURLRef url = CFURLCreateFromFSRef (kCFAllocatorSystemDefault, &found);
-
- if (url)
- {
- CFStringRef path = CFURLCopyFileSystemPath (url, kCFURLPOSIXPathStyle);
-
- if (path)
- {
- filename = g_strdup (CFStringGetCStringPtr (path, kCFStringEncodingUTF8));
-
- if (! filename)
- {
- filename = g_new0 (gchar, CFStringGetLength (path) * 3 + 1);
-
- CFStringGetCString (path, filename,
- CFStringGetLength (path) * 3 + 1,
- kCFStringEncodingUTF8);
- }
-
- CFRelease (path);
- }
-
- CFRelease (url);
- }
- }
-
- return filename;
-}
-
-static void
-load_user_special_dirs (void)
-{
- g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = find_folder (kDesktopFolderType);
- g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = find_folder (kDocumentsFolderType);
- g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = find_folder (kDesktopFolderType); /* XXX correct ? */
- g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = find_folder (kMusicDocumentsFolderType);
- g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = find_folder (kPictureDocumentsFolderType);
- g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = NULL;
- g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = NULL;
- g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = find_folder (kMovieDocumentsFolderType);
-}
-
-#endif /* HAVE_CARBON */
-
-#if defined(G_OS_WIN32)
-static void
-load_user_special_dirs (void)
-{
- typedef HRESULT (WINAPI *t_SHGetKnownFolderPath) (const GUID *rfid,
- DWORD dwFlags,
- HANDLE hToken,
- PWSTR *ppszPath);
- t_SHGetKnownFolderPath p_SHGetKnownFolderPath;
-
- static const GUID FOLDERID_Downloads =
- { 0x374de290, 0x123f, 0x4565, { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b } };
- static const GUID FOLDERID_Public =
- { 0xDFDF76A2, 0xC82A, 0x4D63, { 0x90, 0x6A, 0x56, 0x44, 0xAC, 0x45, 0x73, 0x85 } };
-
- wchar_t *wcp;
-
- p_SHGetKnownFolderPath = (t_SHGetKnownFolderPath) GetProcAddress (GetModuleHandle ("shell32.dll"),
- "SHGetKnownFolderPath");
-
- g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
- g_user_special_dirs[G_USER_DIRECTORY_DOCUMENTS] = get_special_folder (CSIDL_PERSONAL);
-
- if (p_SHGetKnownFolderPath == NULL)
- {
- g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
- }
- else
- {
- wcp = NULL;
- (*p_SHGetKnownFolderPath) (&FOLDERID_Downloads, 0, NULL, &wcp);
- g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = g_utf16_to_utf8 (wcp, -1, NULL, NULL, NULL);
- if (g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] == NULL)
- g_user_special_dirs[G_USER_DIRECTORY_DOWNLOAD] = get_special_folder (CSIDL_DESKTOPDIRECTORY);
- CoTaskMemFree (wcp);
- }
-
- g_user_special_dirs[G_USER_DIRECTORY_MUSIC] = get_special_folder (CSIDL_MYMUSIC);
- g_user_special_dirs[G_USER_DIRECTORY_PICTURES] = get_special_folder (CSIDL_MYPICTURES);
-
- if (p_SHGetKnownFolderPath == NULL)
- {
- /* XXX */
- g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);
- }
- else
- {
- wcp = NULL;
- (*p_SHGetKnownFolderPath) (&FOLDERID_Public, 0, NULL, &wcp);
- g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = g_utf16_to_utf8 (wcp, -1, NULL, NULL, NULL);
- if (g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] == NULL)
- g_user_special_dirs[G_USER_DIRECTORY_PUBLIC_SHARE] = get_special_folder (CSIDL_COMMON_DOCUMENTS);
- CoTaskMemFree (wcp);
- }
-
- g_user_special_dirs[G_USER_DIRECTORY_TEMPLATES] = get_special_folder (CSIDL_TEMPLATES);
- g_user_special_dirs[G_USER_DIRECTORY_VIDEOS] = get_special_folder (CSIDL_MYVIDEO);
-}
-#endif /* G_OS_WIN32 */
-
-static void g_init_user_config_dir (void);
-
-#if defined(G_OS_UNIX) && !defined(HAVE_CARBON)
-
-/* adapted from xdg-user-dir-lookup.c
- *
- * Copyright (C) 2007 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-static void
-load_user_special_dirs (void)
-{
- gchar *config_file;
- gchar *data;
- gchar **lines;
- gint n_lines, i;
-
- g_init_user_config_dir ();
- config_file = g_build_filename (g_user_config_dir,
- "user-dirs.dirs",
- NULL);
-
- if (!g_file_get_contents (config_file, &data, NULL, NULL))
- {
- g_free (config_file);
- return;
- }
-
- lines = g_strsplit (data, "\n", -1);
- n_lines = g_strv_length (lines);
- g_free (data);
-
- for (i = 0; i < n_lines; i++)
- {
- gchar *buffer = lines[i];
- gchar *d, *p;
- gint len;
- gboolean is_relative = FALSE;
- GUserDirectory directory;
-
- /* Remove newline at end */
- len = strlen (buffer);
- if (len > 0 && buffer[len - 1] == '\n')
- buffer[len - 1] = 0;
-
- p = buffer;
- while (*p == ' ' || *p == '\t')
- p++;
-
- if (strncmp (p, "XDG_DESKTOP_DIR", strlen ("XDG_DESKTOP_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_DESKTOP;
- p += strlen ("XDG_DESKTOP_DIR");
- }
- else if (strncmp (p, "XDG_DOCUMENTS_DIR", strlen ("XDG_DOCUMENTS_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_DOCUMENTS;
- p += strlen ("XDG_DOCUMENTS_DIR");
- }
- else if (strncmp (p, "XDG_DOWNLOAD_DIR", strlen ("XDG_DOWNLOAD_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_DOWNLOAD;
- p += strlen ("XDG_DOWNLOAD_DIR");
- }
- else if (strncmp (p, "XDG_MUSIC_DIR", strlen ("XDG_MUSIC_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_MUSIC;
- p += strlen ("XDG_MUSIC_DIR");
- }
- else if (strncmp (p, "XDG_PICTURES_DIR", strlen ("XDG_PICTURES_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_PICTURES;
- p += strlen ("XDG_PICTURES_DIR");
- }
- else if (strncmp (p, "XDG_PUBLICSHARE_DIR", strlen ("XDG_PUBLICSHARE_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_PUBLIC_SHARE;
- p += strlen ("XDG_PUBLICSHARE_DIR");
- }
- else if (strncmp (p, "XDG_TEMPLATES_DIR", strlen ("XDG_TEMPLATES_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_TEMPLATES;
- p += strlen ("XDG_TEMPLATES_DIR");
- }
- else if (strncmp (p, "XDG_VIDEOS_DIR", strlen ("XDG_VIDEOS_DIR")) == 0)
- {
- directory = G_USER_DIRECTORY_VIDEOS;
- p += strlen ("XDG_VIDEOS_DIR");
- }
- else
- continue;
-
- while (*p == ' ' || *p == '\t')
- p++;
-
- if (*p != '=')
- continue;
- p++;
-
- while (*p == ' ' || *p == '\t')
- p++;
-
- if (*p != '"')
- continue;
- p++;
-
- if (strncmp (p, "$HOME", 5) == 0)
- {
- p += 5;
- is_relative = TRUE;
- }
- else if (*p != '/')
- continue;
-
- d = strrchr (p, '"');
- if (!d)
- continue;
- *d = 0;
-
- d = p;
-
- /* remove trailing slashes */
- len = strlen (d);
- if (d[len - 1] == '/')
- d[len - 1] = 0;
-
- if (is_relative)
- {
- g_get_any_init ();
- g_user_special_dirs[directory] = g_build_filename (g_home_dir, d, NULL);
- }
- else
- g_user_special_dirs[directory] = g_strdup (d);
- }
-
- g_strfreev (lines);
- g_free (config_file);
-}
-
-#endif /* G_OS_UNIX && !HAVE_CARBON */
-
-
-/**
- * g_reload_user_special_dirs_cache:
- *
- * Resets the cache used for g_get_user_special_dir(), so
- * that the latest on-disk version is used. Call this only
- * if you just changed the data on disk yourself.
- *
- * Due to threadsafety issues this may cause leaking of strings
- * that were previously returned from g_get_user_special_dir()
- * that can't be freed. We ensure to only leak the data for
- * the directories that actually changed value though.
- *
- * Since: 2.22
- */
-void
-g_reload_user_special_dirs_cache (void)
-{
- int i;
-
- G_LOCK (g_utils_global);
-
- if (g_user_special_dirs != NULL)
- {
- /* save a copy of the pointer, to check if some memory can be preserved */
- char **old_g_user_special_dirs = g_user_special_dirs;
- char *old_val;
-
- /* recreate and reload our cache */
- g_user_special_dirs = g_new0 (gchar *, G_USER_N_DIRECTORIES);
- load_user_special_dirs ();
-
- /* only leak changed directories */
- for (i = 0; i < G_USER_N_DIRECTORIES; i++)
- {
- old_val = old_g_user_special_dirs[i];
- if (g_strcmp0 (old_val, g_user_special_dirs[i]) == 0)
- {
- /* don't leak */
- g_free (g_user_special_dirs[i]);
- g_user_special_dirs[i] = old_val;
- }
- else
- g_free (old_val);
- }
-
- /* free the old array */
- g_free (old_g_user_special_dirs);
- }
-
- G_UNLOCK (g_utils_global);
-}
-
-/**
- * g_get_user_special_dir:
- * @directory: the logical id of special directory
- *
- * Returns the full path of a special directory using its logical id.
- *
- * On Unix this is done using the XDG special user directories.
- * For compatibility with existing practise, %G_USER_DIRECTORY_DESKTOP
- * falls back to <filename>$HOME/Desktop</filename> when XDG special
- * user directories have not been set up.
- *
- * Depending on the platform, the user might be able to change the path
- * of the special directory without requiring the session to restart; GLib
- * will not reflect any change once the special directories are loaded.
- *
- * Return value: the path to the specified special directory, or %NULL
- * if the logical id was not found. The returned string is owned by
- * GLib and should not be modified or freed.
- *
- * Since: 2.14
- */
-G_CONST_RETURN gchar *
-g_get_user_special_dir (GUserDirectory directory)
-{
- g_return_val_if_fail (directory >= G_USER_DIRECTORY_DESKTOP &&
- directory < G_USER_N_DIRECTORIES, NULL);
-
- G_LOCK (g_utils_global);
-
- if (G_UNLIKELY (g_user_special_dirs == NULL))
- {
- g_user_special_dirs = g_new0 (gchar *, G_USER_N_DIRECTORIES);
-
- load_user_special_dirs ();
-
- /* Special-case desktop for historical compatibility */
- if (g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] == NULL)
- {
- g_get_any_init ();
-
- g_user_special_dirs[G_USER_DIRECTORY_DESKTOP] =
- g_build_filename (g_home_dir, "Desktop", NULL);
- }
- }
-
- G_UNLOCK (g_utils_global);
-
- return g_user_special_dirs[directory];
-}
-
-#ifdef G_OS_WIN32
-
-#undef g_get_system_data_dirs
-
-static HMODULE
-get_module_for_address (gconstpointer address)
-{
- /* Holds the g_utils_global lock */
-
- static gboolean beenhere = FALSE;
- typedef BOOL (WINAPI *t_GetModuleHandleExA) (DWORD, LPCTSTR, HMODULE *);
- static t_GetModuleHandleExA p_GetModuleHandleExA = NULL;
- HMODULE hmodule = NULL;
-
- if (!address)
- return NULL;
-
- if (!beenhere)
- {
- p_GetModuleHandleExA =
- (t_GetModuleHandleExA) GetProcAddress (GetModuleHandle ("kernel32.dll"),
- "GetModuleHandleExA");
- beenhere = TRUE;
- }
-
- if (p_GetModuleHandleExA == NULL ||
- !(*p_GetModuleHandleExA) (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT |
- GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
- address, &hmodule))
- {
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery (address, &mbi, sizeof (mbi));
- hmodule = (HMODULE) mbi.AllocationBase;
- }
-
- return hmodule;
-}
-
-static gchar *
-get_module_share_dir (gconstpointer address)
-{
- HMODULE hmodule;
- gchar *filename;
- gchar *retval;
-
- hmodule = get_module_for_address (address);
- if (hmodule == NULL)
- return NULL;
-
- filename = g_win32_get_package_installation_directory_of_module (hmodule);
- retval = g_build_filename (filename, "share", NULL);
- g_free (filename);
-
- return retval;
-}
-
-G_CONST_RETURN gchar * G_CONST_RETURN *
-g_win32_get_system_data_dirs_for_module (void (*address_of_function)())
-{
- GArray *data_dirs;
- HMODULE hmodule;
- static GHashTable *per_module_data_dirs = NULL;
- gchar **retval;
- gchar *p;
- gchar *exe_root;
-
- if (address_of_function)
- {
- G_LOCK (g_utils_global);
- hmodule = get_module_for_address (address_of_function);
- if (hmodule != NULL)
- {
- if (per_module_data_dirs == NULL)
- per_module_data_dirs = g_hash_table_new (NULL, NULL);
- else
- {
- retval = g_hash_table_lookup (per_module_data_dirs, hmodule);
-
- if (retval != NULL)
- {
- G_UNLOCK (g_utils_global);
- return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval;
- }
- }
- }
- }
-
- data_dirs = g_array_new (TRUE, TRUE, sizeof (char *));
-
- /* Documents and Settings\All Users\Application Data */
- p = get_special_folder (CSIDL_COMMON_APPDATA);
- if (p)
- g_array_append_val (data_dirs, p);
-
- /* Documents and Settings\All Users\Documents */
- p = get_special_folder (CSIDL_COMMON_DOCUMENTS);
- if (p)
- g_array_append_val (data_dirs, p);
-
- /* Using the above subfolders of Documents and Settings perhaps
- * makes sense from a Windows perspective.
- *
- * But looking at the actual use cases of this function in GTK+
- * and GNOME software, what we really want is the "share"
- * subdirectory of the installation directory for the package
- * our caller is a part of.
- *
- * The address_of_function parameter, if non-NULL, points to a
- * function in the calling module. Use that to determine that
- * module's installation folder, and use its "share" subfolder.
- *
- * Additionally, also use the "share" subfolder of the installation
- * locations of GLib and the .exe file being run.
- *
- * To guard against none of the above being what is really wanted,
- * callers of this function should have Win32-specific code to look
- * up their installation folder themselves, and handle a subfolder
- * "share" of it in the same way as the folders returned from this
- * function.
- */
-
- p = get_module_share_dir (address_of_function);
- if (p)
- g_array_append_val (data_dirs, p);
-
- if (glib_dll != NULL)
- {
- gchar *glib_root = g_win32_get_package_installation_directory_of_module (glib_dll);
- p = g_build_filename (glib_root, "share", NULL);
- if (p)
- g_array_append_val (data_dirs, p);
- g_free (glib_root);
- }
-
- exe_root = g_win32_get_package_installation_directory_of_module (NULL);
- p = g_build_filename (exe_root, "share", NULL);
- if (p)
- g_array_append_val (data_dirs, p);
- g_free (exe_root);
-
- retval = (gchar **) g_array_free (data_dirs, FALSE);
-
- if (address_of_function)
- {
- if (hmodule != NULL)
- g_hash_table_insert (per_module_data_dirs, hmodule, retval);
- G_UNLOCK (g_utils_global);
- }
-
- return (G_CONST_RETURN gchar * G_CONST_RETURN *) retval;
-}
-
-#endif
-
-/**
- * g_get_system_data_dirs:
- *
- * Returns an ordered list of base directories in which to access
- * system-wide application data.
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>
- * In this case the list of directories retrieved will be XDG_DATA_DIRS.
- *
- * On Windows the first elements in the list are the Application Data
- * and Documents folders for All Users. (These can be determined only
- * on Windows 2000 or later and are not present in the list on other
- * Windows versions.) See documentation for CSIDL_COMMON_APPDATA and
- * CSIDL_COMMON_DOCUMENTS.
- *
- * Then follows the "share" subfolder in the installation folder for
- * the package containing the DLL that calls this function, if it can
- * be determined.
- *
- * Finally the list contains the "share" subfolder in the installation
- * folder for GLib, and in the installation folder for the package the
- * application's .exe file belongs to.
- *
- * The installation folders above are determined by looking up the
- * folder where the module (DLL or EXE) in question is located. If the
- * folder's name is "bin", its parent is used, otherwise the folder
- * itself.
- *
- * Note that on Windows the returned list can vary depending on where
- * this function is called.
- *
- * Return value: a %NULL-terminated array of strings owned by GLib that must
- * not be modified or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar * G_CONST_RETURN *
-g_get_system_data_dirs (void)
-{
- gchar **data_dir_vector;
-
- G_LOCK (g_utils_global);
-
- if (!g_system_data_dirs)
- {
-#ifdef G_OS_WIN32
- data_dir_vector = (gchar **) g_win32_get_system_data_dirs_for_module (NULL);
-#else
- gchar *data_dirs = (gchar *) g_getenv ("XDG_DATA_DIRS");
-
- if (!data_dirs || !data_dirs[0])
- data_dirs = "/usr/local/share/:/usr/share/";
-
- data_dir_vector = g_strsplit (data_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
-#endif
-
- g_system_data_dirs = data_dir_vector;
- }
- else
- data_dir_vector = g_system_data_dirs;
-
- G_UNLOCK (g_utils_global);
-
- return (G_CONST_RETURN gchar * G_CONST_RETURN *) data_dir_vector;
-}
-
-/**
- * g_get_system_config_dirs:
- *
- * Returns an ordered list of base directories in which to access
- * system-wide configuration information.
- *
- * On UNIX platforms this is determined using the mechanisms described in
- * the <ulink url="http://www.freedesktop.org/Standards/basedir-spec">
- * XDG Base Directory Specification</ulink>.
- * In this case the list of directories retrieved will be XDG_CONFIG_DIRS.
- *
- * On Windows is the directory that contains application data for all users.
- * A typical path is C:\Documents and Settings\All Users\Application Data.
- * This folder is used for application data that is not user specific.
- * For example, an application can store a spell-check dictionary, a database
- * of clip art, or a log file in the CSIDL_COMMON_APPDATA folder.
- * This information will not roam and is available to anyone using the computer.
- *
- * Return value: a %NULL-terminated array of strings owned by GLib that must
- * not be modified or freed.
- * Since: 2.6
- **/
-G_CONST_RETURN gchar * G_CONST_RETURN *
-g_get_system_config_dirs (void)
-{
- gchar *conf_dirs, **conf_dir_vector;
-
- G_LOCK (g_utils_global);
-
- if (!g_system_config_dirs)
- {
-#ifdef G_OS_WIN32
- conf_dirs = get_special_folder (CSIDL_COMMON_APPDATA);
- if (conf_dirs)
- {
- conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
- g_free (conf_dirs);
- }
- else
- {
- /* Return empty list */
- conf_dir_vector = g_strsplit ("", G_SEARCHPATH_SEPARATOR_S, 0);
- }
-#else
- conf_dirs = (gchar *) g_getenv ("XDG_CONFIG_DIRS");
-
- if (!conf_dirs || !conf_dirs[0])
- conf_dirs = "/etc/xdg";
-
- conf_dir_vector = g_strsplit (conf_dirs, G_SEARCHPATH_SEPARATOR_S, 0);
-#endif
-
- g_system_config_dirs = conf_dir_vector;
- }
- else
- conf_dir_vector = g_system_config_dirs;
- G_UNLOCK (g_utils_global);
-
- return (G_CONST_RETURN gchar * G_CONST_RETURN *) conf_dir_vector;
-}
-
-#ifndef G_OS_WIN32
-
-static GHashTable *alias_table = NULL;
-
-/* read an alias file for the locales */
-static void
-read_aliases (gchar *file)
-{
- FILE *fp;
- char buf[256];
-
- if (!alias_table)
- alias_table = g_hash_table_new (g_str_hash, g_str_equal);
- fp = fopen (file,"r");
- if (!fp)
- return;
- while (fgets (buf, 256, fp))
- {
- char *p, *q;
-
- g_strstrip (buf);
-
- /* Line is a comment */
- if ((buf[0] == '#') || (buf[0] == '\0'))
- continue;
-
- /* Reads first column */
- for (p = buf, q = NULL; *p; p++) {
- if ((*p == '\t') || (*p == ' ') || (*p == ':')) {
- *p = '\0';
- q = p+1;
- while ((*q == '\t') || (*q == ' ')) {
- q++;
- }
- break;
- }
- }
- /* The line only had one column */
- if (!q || *q == '\0')
- continue;
-
- /* Read second column */
- for (p = q; *p; p++) {
- if ((*p == '\t') || (*p == ' ')) {
- *p = '\0';
- break;
- }
- }
-
- /* Add to alias table if necessary */
- if (!g_hash_table_lookup (alias_table, buf)) {
- g_hash_table_insert (alias_table, g_strdup (buf), g_strdup (q));
- }
- }
- fclose (fp);
-}
-
-#endif
-
-static char *
-unalias_lang (char *lang)
-{
-#ifndef G_OS_WIN32
- char *p;
- int i;
-
- if (!alias_table)
- read_aliases ("/usr/share/locale/locale.alias");
-
- i = 0;
- while ((p = g_hash_table_lookup (alias_table, lang)) && (strcmp (p, lang) != 0))
- {
- lang = p;
- if (i++ == 30)
- {
- static gboolean said_before = FALSE;
- if (!said_before)
- g_warning ("Too many alias levels for a locale, "
- "may indicate a loop");
- said_before = TRUE;
- return lang;
- }
- }
-#endif
- return lang;
-}
-
-/* Mask for components of locale spec. The ordering here is from
- * least significant to most significant
- */
-enum
-{
- COMPONENT_CODESET = 1 << 0,
- COMPONENT_TERRITORY = 1 << 1,
- COMPONENT_MODIFIER = 1 << 2
-};
-
-/* Break an X/Open style locale specification into components
- */
-static guint
-explode_locale (const gchar *locale,
- gchar **language,
- gchar **territory,
- gchar **codeset,
- gchar **modifier)
-{
- const gchar *uscore_pos;
- const gchar *at_pos;
- const gchar *dot_pos;
-
- guint mask = 0;
-
- uscore_pos = strchr (locale, '_');
- dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
- at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
-
- if (at_pos)
- {
- mask |= COMPONENT_MODIFIER;
- *modifier = g_strdup (at_pos);
- }
- else
- at_pos = locale + strlen (locale);
-
- if (dot_pos)
- {
- mask |= COMPONENT_CODESET;
- *codeset = g_strndup (dot_pos, at_pos - dot_pos);
- }
- else
- dot_pos = at_pos;
-
- if (uscore_pos)
- {
- mask |= COMPONENT_TERRITORY;
- *territory = g_strndup (uscore_pos, dot_pos - uscore_pos);
- }
- else
- uscore_pos = dot_pos;
-
- *language = g_strndup (locale, uscore_pos - locale);
-
- return mask;
-}
-
-/*
- * Compute all interesting variants for a given locale name -
- * by stripping off different components of the value.
- *
- * For simplicity, we assume that the locale is in
- * X/Open format: language[_territory][.codeset][@modifier]
- *
- * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
- * as well. We could just copy the code from glibc wholesale
- * but it is big, ugly, and complicated, so I'm reluctant
- * to do so when this should handle 99% of the time...
- */
-GSList *
-_g_compute_locale_variants (const gchar *locale)
-{
- GSList *retval = NULL;
-
- gchar *language = NULL;
- gchar *territory = NULL;
- gchar *codeset = NULL;
- gchar *modifier = NULL;
-
- guint mask;
- guint i;
-
- g_return_val_if_fail (locale != NULL, NULL);
-
- mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
-
- /* Iterate through all possible combinations, from least attractive
- * to most attractive.
- */
- for (i = 0; i <= mask; i++)
- if ((i & ~mask) == 0)
- {
- gchar *val = g_strconcat (language,
- (i & COMPONENT_TERRITORY) ? territory : "",
- (i & COMPONENT_CODESET) ? codeset : "",
- (i & COMPONENT_MODIFIER) ? modifier : "",
- NULL);
- retval = g_slist_prepend (retval, val);
- }
-
- g_free (language);
- if (mask & COMPONENT_CODESET)
- g_free (codeset);
- if (mask & COMPONENT_TERRITORY)
- g_free (territory);
- if (mask & COMPONENT_MODIFIER)
- g_free (modifier);
-
- return retval;
-}
-
-/* The following is (partly) taken from the gettext package.
- Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */
-
-static const gchar *
-guess_category_value (const gchar *category_name)
-{
- const gchar *retval;
-
- /* The highest priority value is the `LANGUAGE' environment
- variable. This is a GNU extension. */
- retval = g_getenv ("LANGUAGE");
- if ((retval != NULL) && (retval[0] != '\0'))
- return retval;
-
- /* `LANGUAGE' is not set. So we have to proceed with the POSIX
- methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
- systems this can be done by the `setlocale' function itself. */
-
- /* Setting of LC_ALL overwrites all other. */
- retval = g_getenv ("LC_ALL");
- if ((retval != NULL) && (retval[0] != '\0'))
- return retval;
-
- /* Next comes the name of the desired category. */
- retval = g_getenv (category_name);
- if ((retval != NULL) && (retval[0] != '\0'))
- return retval;
-
- /* Last possibility is the LANG environment variable. */
- retval = g_getenv ("LANG");
- if ((retval != NULL) && (retval[0] != '\0'))
- return retval;
-
-#ifdef G_PLATFORM_WIN32
- /* g_win32_getlocale() first checks for LC_ALL, LC_MESSAGES and
- * LANG, which we already did above. Oh well. The main point of
- * calling g_win32_getlocale() is to get the thread's locale as used
- * by Windows and the Microsoft C runtime (in the "English_United
- * States" format) translated into the Unixish format.
- */
- {
- char *locale = g_win32_getlocale ();
- retval = g_intern_string (locale);
- g_free (locale);
- return retval;
- }
-#endif
-
- return NULL;
-}
-
-typedef struct _GLanguageNamesCache GLanguageNamesCache;
-
-struct _GLanguageNamesCache {
- gchar *languages;
- gchar **language_names;
-};
-
-static void
-language_names_cache_free (gpointer data)
-{
- GLanguageNamesCache *cache = data;
- g_free (cache->languages);
- g_strfreev (cache->language_names);
- g_free (cache);
-}
-
-/**
- * g_get_language_names:
- *
- * Computes a list of applicable locale names, which can be used to
- * e.g. construct locale-dependent filenames or search paths. The returned
- * list is sorted from most desirable to least desirable and always contains
- * the default locale "C".
- *
- * For example, if LANGUAGE=de:en_US, then the returned list is
- * "de", "en_US", "en", "C".
- *
- * This function consults the environment variables <envar>LANGUAGE</envar>,
- * <envar>LC_ALL</envar>, <envar>LC_MESSAGES</envar> and <envar>LANG</envar>
- * to find the list of locales specified by the user.
- *
- * Return value: a %NULL-terminated array of strings owned by GLib
- * that must not be modified or freed.
- *
- * Since: 2.6
- **/
-G_CONST_RETURN gchar * G_CONST_RETURN *
-g_get_language_names (void)
-{
- static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
- GLanguageNamesCache *cache = g_static_private_get (&cache_private);
- const gchar *value;
-
- if (!cache)
- {
- cache = g_new0 (GLanguageNamesCache, 1);
- g_static_private_set (&cache_private, cache, language_names_cache_free);
- }
-
- value = guess_category_value ("LC_MESSAGES");
- if (!value)
- value = "C";
-
- if (!(cache->languages && strcmp (cache->languages, value) == 0))
- {
- gchar **languages;
- gchar **alist, **a;
- GSList *list, *l;
- gint i;
-
- g_free (cache->languages);
- g_strfreev (cache->language_names);
- cache->languages = g_strdup (value);
-
- alist = g_strsplit (value, ":", 0);
- list = NULL;
- for (a = alist; *a; a++)
- {
- gchar *b = unalias_lang (*a);
- list = g_slist_concat (list, _g_compute_locale_variants (b));
- }
- g_strfreev (alist);
- list = g_slist_append (list, g_strdup ("C"));
-
- cache->language_names = languages = g_new (gchar *, g_slist_length (list) + 1);
- for (l = list, i = 0; l; l = l->next, i++)
- languages[i] = l->data;
- languages[i] = NULL;
-
- g_slist_free (list);
- }
-
- return (G_CONST_RETURN gchar * G_CONST_RETURN *) cache->language_names;
-}
-
-/**
- * g_direct_hash:
- * @v: a #gpointer key
- *
- * Converts a gpointer to a hash value.
- * It can be passed to g_hash_table_new() as the @hash_func parameter,
- * when using pointers as keys in a #GHashTable.
- *
- * Returns: a hash value corresponding to the key.
- */
-guint
-g_direct_hash (gconstpointer v)
-{
- return GPOINTER_TO_UINT (v);
-}
-
-/**
- * g_direct_equal:
- * @v1: a key.
- * @v2: a key to compare with @v1.
- *
- * Compares two #gpointer arguments and returns %TRUE if they are equal.
- * It can be passed to g_hash_table_new() as the @key_equal_func
- * parameter, when using pointers as keys in a #GHashTable.
- *
- * Returns: %TRUE if the two keys match.
- */
-gboolean
-g_direct_equal (gconstpointer v1,
- gconstpointer v2)
-{
- return v1 == v2;
-}
-
-/**
- * g_int_equal:
- * @v1: a pointer to a #gint key.
- * @v2: a pointer to a #gint key to compare with @v1.
- *
- * Compares the two #gint values being pointed to and returns
- * %TRUE if they are equal.
- * It can be passed to g_hash_table_new() as the @key_equal_func
- * parameter, when using pointers to integers as keys in a #GHashTable.
- *
- * Returns: %TRUE if the two keys match.
- */
-gboolean
-g_int_equal (gconstpointer v1,
- gconstpointer v2)
-{
- return *((const gint*) v1) == *((const gint*) v2);
-}
-
-/**
- * g_int_hash:
- * @v: a pointer to a #gint key
- *
- * Converts a pointer to a #gint to a hash value.
- * It can be passed to g_hash_table_new() as the @hash_func parameter,
- * when using pointers to integers values as keys in a #GHashTable.
- *
- * Returns: a hash value corresponding to the key.
- */
-guint
-g_int_hash (gconstpointer v)
-{
- return *(const gint*) v;
-}
-
-/**
- * g_int64_equal:
- * @v1: a pointer to a #gint64 key.
- * @v2: a pointer to a #gint64 key to compare with @v1.
- *
- * Compares the two #gint64 values being pointed to and returns
- * %TRUE if they are equal.
- * It can be passed to g_hash_table_new() as the @key_equal_func
- * parameter, when using pointers to 64-bit integers as keys in a #GHashTable.
- *
- * Returns: %TRUE if the two keys match.
- *
- * Since: 2.22
- */
-gboolean
-g_int64_equal (gconstpointer v1,
- gconstpointer v2)
-{
- return *((const gint64*) v1) == *((const gint64*) v2);
-}
-
-/**
- * g_int64_hash:
- * @v: a pointer to a #gint64 key
- *
- * Converts a pointer to a #gint64 to a hash value.
- * It can be passed to g_hash_table_new() as the @hash_func parameter,
- * when using pointers to 64-bit integers values as keys in a #GHashTable.
- *
- * Returns: a hash value corresponding to the key.
- *
- * Since: 2.22
- */
-guint
-g_int64_hash (gconstpointer v)
-{
- return (guint) *(const gint64*) v;
-}
-
-/**
- * g_double_equal:
- * @v1: a pointer to a #gdouble key.
- * @v2: a pointer to a #gdouble key to compare with @v1.
- *
- * Compares the two #gdouble values being pointed to and returns
- * %TRUE if they are equal.
- * It can be passed to g_hash_table_new() as the @key_equal_func
- * parameter, when using pointers to doubles as keys in a #GHashTable.
- *
- * Returns: %TRUE if the two keys match.
- *
- * Since: 2.22
- */
-gboolean
-g_double_equal (gconstpointer v1,
- gconstpointer v2)
-{
- return *((const gdouble*) v1) == *((const gdouble*) v2);
-}
-
-/**
- * g_double_hash:
- * @v: a pointer to a #gdouble key
- *
- * Converts a pointer to a #gdouble to a hash value.
- * It can be passed to g_hash_table_new() as the @hash_func parameter,
- * when using pointers to doubles as keys in a #GHashTable.
- *
- * Returns: a hash value corresponding to the key.
- *
- * Since: 2.22
- */
-guint
-g_double_hash (gconstpointer v)
-{
- return (guint) *(const gdouble*) v;
-}
-
-/**
- * g_nullify_pointer:
- * @nullify_location: the memory address of the pointer.
- *
- * Set the pointer at the specified location to %NULL.
- **/
-void
-g_nullify_pointer (gpointer *nullify_location)
-{
- g_return_if_fail (nullify_location != NULL);
-
- *nullify_location = NULL;
-}
-
-/**
- * g_get_codeset:
- *
- * Get the codeset for the current locale.
- *
- * Return value: a newly allocated string containing the name
- * of the codeset. This string must be freed with g_free().
- **/
-gchar *
-g_get_codeset (void)
-{
- const gchar *charset;
-
- g_get_charset (&charset);
-
- return g_strdup (charset);
-}
-
-/* This is called from g_thread_init(). It's used to
- * initialize some static data in a threadsafe way.
- */
-void
-_g_utils_thread_init (void)
-{
- g_get_language_names ();
-}
-
-#ifdef G_OS_WIN32
-
-/**
- * _glib_get_locale_dir:
- *
- * Return the path to the share\locale or lib\locale subfolder of the
- * GLib installation folder. The path is in the system codepage. We
- * have to use system codepage as bindtextdomain() doesn't have a
- * UTF-8 interface.
- */
-static gchar *
-_glib_get_locale_dir (void)
-{
- gchar *install_dir = NULL, *locale_dir;
- gchar *retval = NULL;
-
- if (glib_dll != NULL)
- install_dir = g_win32_get_package_installation_directory_of_module (glib_dll);
-
- if (install_dir)
- {
- /*
- * Append "/share/locale" or "/lib/locale" depending on whether
- * autoconfigury detected GNU gettext or not.
- */
- const char *p = GLIB_LOCALE_DIR + strlen (GLIB_LOCALE_DIR);
- while (*--p != '/')
- ;
- while (*--p != '/')
- ;
-
- locale_dir = g_build_filename (install_dir, p, NULL);
-
- retval = g_win32_locale_filename_from_utf8 (locale_dir);
-
- g_free (install_dir);
- g_free (locale_dir);
- }
-
- if (retval)
- return retval;
- else
- return g_strdup ("");
-}
-
-#undef GLIB_LOCALE_DIR
-
-#endif /* G_OS_WIN32 */
-
-/**
- * glib_gettext:
- * @str: The string to be translated
- *
- * Returns the translated string from the glib translations.
- * This is an internal function and should only be used by
- * the internals of glib (such as libgio).
- *
- * Returns: the transation of @str to the current locale
- */
-G_CONST_RETURN gchar *
-glib_gettext (const gchar *str)
-{
- static gboolean _glib_gettext_initialized = FALSE;
-
- if (!_glib_gettext_initialized)
- {
-#ifdef G_OS_WIN32
- gchar *tmp = _glib_get_locale_dir ();
- bindtextdomain (GETTEXT_PACKAGE, tmp);
- g_free (tmp);
-#else
- bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
-#endif
-# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-# endif
- _glib_gettext_initialized = TRUE;
- }
-
- return g_dgettext (GETTEXT_PACKAGE, str);
-}
-
-#if defined (G_OS_WIN32) && !defined (_WIN64)
-
-/* Binary compatibility versions. Not for newly compiled code. */
-
-#undef g_find_program_in_path
-
-gchar*
-g_find_program_in_path (const gchar *program)
-{
- gchar *utf8_program = g_locale_to_utf8 (program, -1, NULL, NULL, NULL);
- gchar *utf8_retval = g_find_program_in_path_utf8 (utf8_program);
- gchar *retval;
-
- g_free (utf8_program);
- if (utf8_retval == NULL)
- return NULL;
- retval = g_locale_from_utf8 (utf8_retval, -1, NULL, NULL, NULL);
- g_free (utf8_retval);
-
- return retval;
-}
-
-#undef g_get_current_dir
-
-gchar*
-g_get_current_dir (void)
-{
- gchar *utf8_dir = g_get_current_dir_utf8 ();
- gchar *dir = g_locale_from_utf8 (utf8_dir, -1, NULL, NULL, NULL);
- g_free (utf8_dir);
- return dir;
-}
-
-#undef g_getenv
-
-G_CONST_RETURN gchar*
-g_getenv (const gchar *variable)
-{
- gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
- const gchar *utf8_value = g_getenv_utf8 (utf8_variable);
- gchar *value;
- GQuark quark;
-
- g_free (utf8_variable);
- if (!utf8_value)
- return NULL;
- value = g_locale_from_utf8 (utf8_value, -1, NULL, NULL, NULL);
- quark = g_quark_from_string (value);
- g_free (value);
-
- return g_quark_to_string (quark);
-}
-
-#undef g_setenv
-
-gboolean
-g_setenv (const gchar *variable,
- const gchar *value,
- gboolean overwrite)
-{
- gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
- gchar *utf8_value = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
- gboolean retval = g_setenv_utf8 (utf8_variable, utf8_value, overwrite);
-
- g_free (utf8_variable);
- g_free (utf8_value);
-
- return retval;
-}
-
-#undef g_unsetenv
-
-void
-g_unsetenv (const gchar *variable)
-{
- gchar *utf8_variable = g_locale_to_utf8 (variable, -1, NULL, NULL, NULL);
-
- g_unsetenv_utf8 (utf8_variable);
-
- g_free (utf8_variable);
-}
-
-#undef g_get_user_name
-
-G_CONST_RETURN gchar*
-g_get_user_name (void)
-{
- g_get_any_init_locked ();
- return g_user_name_cp;
-}
-
-#undef g_get_real_name
-
-G_CONST_RETURN gchar*
-g_get_real_name (void)
-{
- g_get_any_init_locked ();
- return g_real_name_cp;
-}
-
-#undef g_get_home_dir
-
-G_CONST_RETURN gchar*
-g_get_home_dir (void)
-{
- g_get_any_init_locked ();
- return g_home_dir_cp;
-}
-
-#undef g_get_tmp_dir
-
-G_CONST_RETURN gchar*
-g_get_tmp_dir (void)
-{
- g_get_any_init_locked ();
- return g_tmp_dir_cp;
-}
-
-#endif
+++ /dev/null
-/*
- * Copyright © 2007, 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * 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 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 <glib/gvariant-core.h>
-
-#include <glib/gvariant-serialiser.h>
-#include <glib/gtestutils.h>
-#include <glib/gbitlock.h>
-#include <glib/gatomic.h>
-#include <glib/gbuffer.h>
-#include <glib/gslice.h>
-#include <glib/gmem.h>
-#include <string.h>
-
-
-/*
- * This file includes the structure definition for GVariant and a small
- * set of functions that are allowed to access the structure directly.
- *
- * This minimises the amount of code that can possibly touch a GVariant
- * structure directly to a few simple fundamental operations. These few
- * operations are written to be completely threadsafe with respect to
- * all possible outside access. This means that we only need to be
- * concerned about thread safety issues in this one small file.
- *
- * Most GVariant API functions are in gvariant.c.
- */
-
-/**
- * GVariant:
- *
- * #GVariant is an opaque data structure and can only be accessed
- * using the following functions.
- *
- * Since: 2.24
- **/
-struct _GVariant
-/* see below for field member documentation */
-{
- GVariantTypeInfo *type_info;
- gsize size;
-
- union
- {
- struct
- {
- GBuffer *buffer;
- gconstpointer data;
- } serialised;
-
- struct
- {
- GVariant **children;
- gsize n_children;
- } tree;
- } contents;
-
- gint state;
- gint ref_count;
-};
-
-/* struct GVariant:
- *
- * There are two primary forms of GVariant instances: "serialised form"
- * and "tree form".
- *
- * "serialised form": A serialised GVariant instance stores its value in
- * the GVariant serialisation format. All
- * basic-typed instances (ie: non-containers) are in
- * serialised format, as are some containers.
- *
- * "tree form": Some containers are in "tree form". In this case,
- * instead of containing the serialised data for the
- * container, the instance contains an array of pointers to
- * the child values of the container (thus forming a tree).
- *
- * It is possible for an instance to transition from tree form to
- * serialised form. This happens, implicitly, if the serialised data is
- * requested (eg: via g_variant_get_data()). Serialised form instances
- * never transition into tree form.
- *
- *
- * The fields of the structure are documented here:
- *
- * type_info: this is a reference to a GVariantTypeInfo describing the
- * type of the instance. When the instance is freed, this
- * reference must be released with g_variant_type_info_unref().
- *
- * The type_info field never changes during the life of the
- * instance, so it can be accessed without a lock.
- *
- * size: this is the size of the serialised form for the instance, if it
- * is known. If the instance is in serialised form then it is, by
- * definition, known. If the instance is in tree form then it may
- * be unknown (in which case it is -1). It is possible for the
- * size to be known when in tree form if, for example, the user
- * has called g_variant_get_size() without calling
- * g_variant_get_data(). Additionally, even when the user calls
- * g_variant_get_data() the size of the data must first be
- * determined so that a large enough buffer can be allocated for
- * the data.
- *
- * Once the size is known, it can never become unknown again.
- * g_variant_ensure_size() is used to ensure that the size is in
- * the known state -- it calculates the size if needed. After
- * that, the size field can be accessed without a lock.
- *
- * contents: a union containing either the information associated with
- * holding a value in serialised form or holding a value in
- * tree form.
- *
- * .serialised: Only valid when the instance is in serialised form.
- *
- * Since an instance can never transition away from
- * serialised form, once these fields are set, they will
- * never be changed. It is therefore valid to access
- * them without holding a lock.
- *
- * .buffer: the #GBuffer that contains the memory pointed to by
- * .data, or %NULL if .data is %NULL. In the event that
- * the instance was deserialised from another instance,
- * then the buffer will be shared by both of them. When
- * the instance is freed, this reference must be released
- * with g_buffer_unref().
- *
- * .data: the serialised data (of size 'size') of the instance.
- * This pointer should not be freed or modified in any way.
- * #GBuffer is responsible for memory management.
- *
- * This pointer may be %NULL in two cases:
- *
- * - if the serialised size of the instance is 0
- *
- * - if the instance is of a fixed-sized type and was
- * deserialised out of a corrupted container such that
- * the container contains too few bytes to point to the
- * entire proper fixed-size of this instance. In this
- * case, 'size' will still be equal to the proper fixed
- * size, but this pointer will be %NULL. This is exactly
- * the reason that g_variant_get_data() sometimes returns
- * %NULL. For all other calls, the effect should be as
- * if .data pointed to the appropriate number of nul
- * bytes.
- *
- * .tree: Only valid when the instance is in tree form.
- *
- * Note that accesses from other threads could result in
- * conversion of the instance from tree form to serialised form
- * at any time. For this reason, the instance lock must always
- * be held while performing any operations on 'contents.tree'.
- *
- * .children: the array of the child instances of this instance.
- * When the instance is freed (or converted to serialised
- * form) then each child must have g_variant_unref()
- * called on it and the array must be freed using
- * g_free().
- *
- * .n_children: the number of items in the .children array.
- *
- * state: a bitfield describing the state of the instance. It is a
- * bitwise-or of the following STATE_* constants:
- *
- * STATE_LOCKED: the instance lock is held. This is the bit used by
- * g_bit_lock().
- *
- * STATE_SERIALISED: the instance is in serialised form. If this
- * flag is not set then the instance is in tree
- * form.
- *
- * STATE_TRUSTED: for serialised form instances, this means that the
- * serialised data is known to be in normal form (ie:
- * not corrupted).
- *
- * For tree form instances, this means that all of the
- * child instances in the contents.tree.children array
- * are trusted. This means that if the container is
- * serialised then the resulting data will be in
- * normal form.
- *
- * If this flag is unset it does not imply that the
- * data is corrupted. It merely means that we're not
- * sure that it's valid. See g_variant_is_trusted().
- *
- * STATE_FLOATING: if this flag is set then the object has a floating
- * reference. See g_variant_ref_sink().
- *
- * ref_count: the reference count of the instance
- */
-#define STATE_LOCKED 1
-#define STATE_SERIALISED 2
-#define STATE_TRUSTED 4
-#define STATE_FLOATING 8
-
-/* -- private -- */
-/* < private >
- * g_variant_lock:
- * @value: a #GVariant
- *
- * Locks @value for performing sensitive operations.
- */
-static void
-g_variant_lock (GVariant *value)
-{
- g_bit_lock (&value->state, 0);
-}
-
-/* < private >
- * g_variant_unlock:
- * @value: a #GVariant
- *
- * Unlocks @value after performing sensitive operations.
- */
-static void
-g_variant_unlock (GVariant *value)
-{
- g_bit_unlock (&value->state, 0);
-}
-
-/* < private >
- * g_variant_release_children:
- * @value: a #GVariant
- *
- * Releases the reference held on each child in the 'children' array of
- * @value and frees the array itself. @value must be in tree form.
- *
- * This is done when freeing a tree-form instance or converting it to
- * serialised form.
- *
- * The current thread must hold the lock on @value.
- */
-static void
-g_variant_release_children (GVariant *value)
-{
- gsize i;
-
- g_assert (value->state & STATE_LOCKED);
- g_assert (~value->state & STATE_SERIALISED);
-
- for (i = 0; i < value->contents.tree.n_children; i++)
- g_variant_unref (value->contents.tree.children[i]);
-
- g_free (value->contents.tree.children);
-}
-
-/* This begins the main body of the recursive serialiser.
- *
- * There are 3 functions here that work as a team with the serialiser to
- * get things done. g_variant_store() has a trivial role, but as a
- * public API function, it has its definition elsewhere.
- *
- * Note that "serialisation" of an instance does not mean that the
- * instance is converted to serialised form -- it means that the
- * serialised form of an instance is written to an external buffer.
- * g_variant_ensure_serialised() (which is not part of this set of
- * functions) is the function that is responsible for converting an
- * instance to serialised form.
- *
- * We are only concerned here with container types since non-container
- * instances are always in serialised form. For these instances,
- * storing their serialised form merely involves a memcpy().
- *
- * Serialisation is a two-step process. First, the size of the
- * serialised data must be calculated so that an appropriately-sized
- * buffer can be allocated. Second, the data is written into the
- * buffer.
- *
- * Determining the size:
- * The process of determining the size is triggered by a call to
- * g_variant_ensure_size() on a container. This invokes the
- * serialiser code to determine the size. The serialiser is passed
- * g_variant_fill_gvs() as a callback.
- *
- * g_variant_fill_gvs() is called by the serialiser on each child of
- * the container which, in turn, calls g_variant_ensure_size() on
- * itself and fills in the result of its own size calculation.
- *
- * The serialiser uses the size information from the children to
- * calculate the size needed for the entire container.
- *
- * Writing the data:
- * After the buffer has been allocated, g_variant_serialise() is
- * called on the container. This invokes the serialiser code to write
- * the bytes to the container. The serialiser is, again, passed
- * g_variant_fill_gvs() as a callback.
- *
- * This time, when g_variant_fill_gvs() is called for each child, the
- * child is given a pointer to a sub-region of the allocated buffer
- * where it should write its data. This is done by calling
- * g_variant_store(). In the event that the instance is in serialised
- * form this means a memcpy() of the serialised data into the
- * allocated buffer. In the event that the instance is in tree form
- * this means a recursive call back into g_variant_serialise().
- *
- *
- * The forward declaration here allows corecursion via callback:
- */
-static void g_variant_fill_gvs (GVariantSerialised *, gpointer);
-
-/* < private >
- * g_variant_ensure_size:
- * @value: a #GVariant
- *
- * Ensures that the ->size field of @value is filled in properly. This
- * must be done as a precursor to any serialisation of the value in
- * order to know how large of a buffer is needed to store the data.
- *
- * The current thread must hold the lock on @value.
- */
-static void
-g_variant_ensure_size (GVariant *value)
-{
- g_assert (value->state & STATE_LOCKED);
-
- if (value->size == (gssize) -1)
- {
- gpointer *children;
- gsize n_children;
-
- children = (gpointer *) value->contents.tree.children;
- n_children = value->contents.tree.n_children;
- value->size = g_variant_serialiser_needed_size (value->type_info,
- g_variant_fill_gvs,
- children, n_children);
- }
-}
-
-/* < private >
- * g_variant_serialise:
- * @value: a #GVariant
- * @data: an appropriately-sized buffer
- *
- * Serialises @value into @data. @value must be in tree form.
- *
- * No change is made to @value.
- *
- * The current thread must hold the lock on @value.
- */
-static void
-g_variant_serialise (GVariant *value,
- gpointer data)
-{
- GVariantSerialised serialised = { 0, };
- gpointer *children;
- gsize n_children;
-
- g_assert (~value->state & STATE_SERIALISED);
- g_assert (value->state & STATE_LOCKED);
-
- serialised.type_info = value->type_info;
- serialised.size = value->size;
- serialised.data = data;
-
- children = (gpointer *) value->contents.tree.children;
- n_children = value->contents.tree.n_children;
-
- g_variant_serialiser_serialise (serialised, g_variant_fill_gvs,
- children, n_children);
-}
-
-/* < private >
- * g_variant_fill_gvs:
- * @serialised: a pointer to a #GVariantSerialised
- * @data: a #GVariant instance
- *
- * This is the callback that is passed by a tree-form container instance
- * to the serialiser. This callback gets called on each child of the
- * container. Each child is responsible for performing the following
- * actions:
- *
- * - reporting its type
- *
- * - reporting its serialised size (requires knowing the size first)
- *
- * - possibly storing its serialised form into the provided buffer
- */
-static void
-g_variant_fill_gvs (GVariantSerialised *serialised,
- gpointer data)
-{
- GVariant *value = data;
-
- g_variant_lock (value);
- g_variant_ensure_size (value);
- g_variant_unlock (value);
-
- if (serialised->type_info == NULL)
- serialised->type_info = value->type_info;
- g_assert (serialised->type_info == value->type_info);
-
- if (serialised->size == 0)
- serialised->size = value->size;
- g_assert (serialised->size == value->size);
-
- if (serialised->data)
- /* g_variant_store() is a public API, so it
- * it will reacquire the lock if it needs to.
- */
- g_variant_store (value, serialised->data);
-}
-
-/* this ends the main body of the recursive serialiser */
-
-/* < private >
- * g_variant_ensure_serialised:
- * @value: a #GVariant
- *
- * Ensures that @value is in serialised form.
- *
- * If @value is in tree form then this function ensures that the
- * serialised size is known and then allocates a buffer of that size and
- * serialises the instance into the buffer. The 'children' array is
- * then released and the instance is set to serialised form based on the
- * contents of the buffer.
- *
- * The current thread must hold the lock on @value.
- */
-static void
-g_variant_ensure_serialised (GVariant *value)
-{
- g_assert (value->state & STATE_LOCKED);
-
- if (~value->state & STATE_SERIALISED)
- {
- GBuffer *buffer;
- gpointer data;
-
- g_variant_ensure_size (value);
- data = g_malloc (value->size);
- g_variant_serialise (value, data);
-
- g_variant_release_children (value);
-
- buffer = g_buffer_new_take_data (data, value->size);
- value->contents.serialised.data = buffer->data;
- value->contents.serialised.buffer = buffer;
- value->state |= STATE_SERIALISED;
- }
-}
-
-/* < private >
- * g_variant_alloc:
- * @type: the type of the new instance
- * @serialised: if the instance will be in serialised form
- * @trusted: if the instance will be trusted
- * @returns: a new #GVariant with a floating reference
- *
- * Allocates a #GVariant instance and does some common work (such as
- * looking up and filling in the type info), setting the state field,
- * and setting the ref_count to 1.
- */
-static GVariant *
-g_variant_alloc (const GVariantType *type,
- gboolean serialised,
- gboolean trusted)
-{
- GVariant *value;
-
- value = g_slice_new (GVariant);
- value->type_info = g_variant_type_info_get (type);
- value->state = (serialised ? STATE_SERIALISED : 0) |
- (trusted ? STATE_TRUSTED : 0) |
- STATE_FLOATING;
- value->size = (gssize) -1;
- value->ref_count = 1;
-
- return value;
-}
-
-/* -- internal -- */
-/* < internal >
- * g_variant_new_from_buffer:
- * @type: a #GVariantType
- * @buffer: a #GBuffer
- * @trusted: if the contents of @buffer are trusted
- * @returns: a new #GVariant with a floating reference
- *
- * Constructs a new serialised-mode #GVariant instance. This is the
- * inner interface for creation of new serialised values that gets
- * called from various functions in gvariant.c.
- *
- * A reference is taken on @buffer.
- */
-GVariant *
-g_variant_new_from_buffer (const GVariantType *type,
- GBuffer *buffer,
- gboolean trusted)
-{
- GVariant *value;
- guint alignment;
- gsize size;
-
- value = g_variant_alloc (type, TRUE, trusted);
-
- value->contents.serialised.buffer = g_buffer_ref (buffer);
-
- g_variant_type_info_query (value->type_info,
- &alignment, &size);
-
- if (size && buffer->size != size)
- {
- /* Creating a fixed-sized GVariant with a buffer of the wrong
- * size.
- *
- * We should do the equivalent of pulling a fixed-sized child out
- * of a brozen container (ie: data is NULL size is equal to the correct
- * fixed size).
- */
- value->contents.serialised.data = NULL;
- value->size = size;
- }
- else
- {
- value->contents.serialised.data = buffer->data;
- value->size = buffer->size;
- }
-
- return value;
-}
-
-/* < internal >
- * g_variant_new_from_children:
- * @type: a #GVariantType
- * @children: an array of #GVariant pointers. Consumed.
- * @n_children: the length of @children
- * @trusted: %TRUE if every child in @children in trusted
- * @returns: a new #GVariant with a floating reference
- *
- * Constructs a new tree-mode #GVariant instance. This is the inner
- * interface for creation of new serialised values that gets called from
- * various functions in gvariant.c.
- *
- * @children is consumed by this function. g_free() will be called on
- * it some time later.
- */
-GVariant *
-g_variant_new_from_children (const GVariantType *type,
- GVariant **children,
- gsize n_children,
- gboolean trusted)
-{
- GVariant *value;
-
- value = g_variant_alloc (type, FALSE, trusted);
- value->contents.tree.children = children;
- value->contents.tree.n_children = n_children;
-
- return value;
-}
-
-/* < internal >
- * g_variant_get_type_info:
- * @value: a #GVariant
- * @returns: the #GVariantTypeInfo for @value
- *
- * Returns the #GVariantTypeInfo corresponding to the type of @value. A
- * reference is not added, so the return value is only good for the
- * duration of the life of @value.
- */
-GVariantTypeInfo *
-g_variant_get_type_info (GVariant *value)
-{
- return value->type_info;
-}
-
-/* < internal >
- * g_variant_is_trusted:
- * @value: a #GVariant
- * @returns: if @value is trusted
- *
- * Determines if @value is trusted by #GVariant to contain only
- * fully-valid data. All values constructed solely via #GVariant APIs
- * are trusted, but values containing data read in from other sources
- * are usually not trusted.
- *
- * The main advantage of trusted data is that certain checks can be
- * skipped. For example, we don't need to check that a string is
- * properly nul-terminated or that an object path is actually a
- * properly-formatted object path.
- */
-gboolean
-g_variant_is_trusted (GVariant *value)
-{
- return (value->state & STATE_TRUSTED) != 0;
-}
-
-/* -- public -- */
-
-/**
- * g_variant_unref:
- * @value: a #GVariant
- *
- * Decreases the reference count of @value. When its reference count
- * drops to 0, the memory used by the variant is freed.
- *
- * Since: 2.24
- **/
-void
-g_variant_unref (GVariant *value)
-{
- if (g_atomic_int_dec_and_test (&value->ref_count))
- {
- if G_UNLIKELY (value->state & STATE_LOCKED)
- g_critical ("attempting to free a locked GVariant instance. "
- "This should never happen.");
-
- value->state |= STATE_LOCKED;
-
- g_variant_type_info_unref (value->type_info);
-
- if (value->state & STATE_SERIALISED)
- g_buffer_unref (value->contents.serialised.buffer);
- else
- g_variant_release_children (value);
-
- g_slice_free (GVariant, value);
- }
-}
-
-/**
- * g_variant_ref:
- * @value: a #GVariant
- * @returns: the same @value
- *
- * Increases the reference count of @value.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_ref (GVariant *value)
-{
- g_atomic_int_inc (&value->ref_count);
-
- return value;
-}
-
-/**
- * g_variant_ref_sink:
- * @value: a #GVariant
- * @returns: the same @value
- *
- * #GVariant uses a floating reference count system. All functions with
- * names starting with <literal>g_variant_new_</literal> return floating
- * references.
- *
- * Calling g_variant_ref_sink() on a #GVariant with a floating reference
- * will convert the floating reference into a full reference. Calling
- * g_variant_ref_sink() on a non-floating #GVariant results in an
- * additional normal reference being added.
- *
- * In other words, if the @value is floating, then this call "assumes
- * ownership" of the floating reference, converting it to a normal
- * reference. If the @value is not floating, then this call adds a
- * new normal reference increasing the reference count by one.
- *
- * All calls that result in a #GVariant instance being inserted into a
- * container will call g_variant_ref_sink() on the instance. This means
- * that if the value was just created (and has only its floating
- * reference) then the container will assume sole ownership of the value
- * at that point and the caller will not need to unreference it. This
- * makes certain common styles of programming much easier while still
- * maintaining normal refcounting semantics in situations where values
- * are not floating.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_ref_sink (GVariant *value)
-{
- g_variant_lock (value);
-
- if (~value->state & STATE_FLOATING)
- g_variant_ref (value);
- else
- value->state &= ~STATE_FLOATING;
-
- g_variant_unlock (value);
-
- return value;
-}
-
-/**
- * g_variant_is_floating:
- * @value: a #GVariant
- * @returns: whether @value is floating
- *
- * Checks whether @value has a floating reference count.
- *
- * This function should only ever be used to assert that a given variant
- * is or is not floating, or for debug purposes. To acquire a reference
- * to a variant that might be floating, always use g_variant_ref_sink().
- *
- * See g_variant_ref_sink() for more information about floating reference
- * counts.
- *
- * Since: 2.26
- **/
-gboolean
-g_variant_is_floating (GVariant *value)
-{
- g_return_val_if_fail (value != NULL, FALSE);
-
- return (value->state & STATE_FLOATING) != 0;
-}
-
-/**
- * g_variant_get_size:
- * @value: a #GVariant instance
- * @returns: the serialised size of @value
- *
- * Determines the number of bytes that would be required to store @value
- * with g_variant_store().
- *
- * If @value has a fixed-sized type then this function always returned
- * that fixed size.
- *
- * In the case that @value is already in serialised form or the size has
- * already been calculated (ie: this function has been called before)
- * then this function is O(1). Otherwise, the size is calculated, an
- * operation which is approximately O(n) in the number of values
- * involved.
- *
- * Since: 2.24
- **/
-gsize
-g_variant_get_size (GVariant *value)
-{
- g_variant_lock (value);
- g_variant_ensure_size (value);
- g_variant_unlock (value);
-
- return value->size;
-}
-
-/**
- * g_variant_get_data:
- * @value: a #GVariant instance
- * @returns: the serialised form of @value, or %NULL
- *
- * Returns a pointer to the serialised form of a #GVariant instance.
- * The returned data may not be in fully-normalised form if read from an
- * untrusted source. The returned data must not be freed; it remains
- * valid for as long as @value exists.
- *
- * If @value is a fixed-sized value that was deserialised from a
- * corrupted serialised container then %NULL may be returned. In this
- * case, the proper thing to do is typically to use the appropriate
- * number of nul bytes in place of @value. If @value is not fixed-sized
- * then %NULL is never returned.
- *
- * In the case that @value is already in serialised form, this function
- * is O(1). If the value is not already in serialised form,
- * serialisation occurs implicitly and is approximately O(n) in the size
- * of the result.
- *
- * Since: 2.24
- **/
-gconstpointer
-g_variant_get_data (GVariant *value)
-{
- g_variant_lock (value);
- g_variant_ensure_serialised (value);
- g_variant_unlock (value);
-
- return value->contents.serialised.data;
-}
-
-/**
- * g_variant_n_children:
- * @value: a container #GVariant
- * @returns: the number of children in the container
- *
- * Determines the number of children in a container #GVariant instance.
- * This includes variants, maybes, arrays, tuples and dictionary
- * entries. It is an error to call this function on any other type of
- * #GVariant.
- *
- * For variants, the return value is always 1. For values with maybe
- * types, it is always zero or one. For arrays, it is the length of the
- * array. For tuples it is the number of tuple items (which depends
- * only on the type). For dictionary entries, it is always 2
- *
- * This function is O(1).
- *
- * Since: 2.24
- **/
-gsize
-g_variant_n_children (GVariant *value)
-{
- gsize n_children;
-
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
- {
- GVariantSerialised serialised = {
- value->type_info,
- (gpointer) value->contents.serialised.data,
- value->size
- };
-
- n_children = g_variant_serialised_n_children (serialised);
- }
- else
- n_children = value->contents.tree.n_children;
-
- g_variant_unlock (value);
-
- return n_children;
-}
-
-/**
- * g_variant_get_child_value:
- * @value: a container #GVariant
- * @index_: the index of the child to fetch
- * @returns: the child at the specified index
- *
- * Reads a child item out of a container #GVariant instance. This
- * includes variants, maybes, arrays, tuples and dictionary
- * entries. It is an error to call this function on any other type of
- * #GVariant.
- *
- * It is an error if @index_ is greater than the number of child items
- * in the container. See g_variant_n_children().
- *
- * This function is O(1).
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_get_child_value (GVariant *value,
- gsize index_)
-{
- GVariant *child = NULL;
-
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
- {
- GVariantSerialised serialised = {
- value->type_info,
- (gpointer) value->contents.serialised.data,
- value->size
- };
- GVariantSerialised s_child;
-
- /* get the serialiser to extract the serialised data for the child
- * from the serialised data for the container
- */
- s_child = g_variant_serialised_get_child (serialised, index_);
-
- /* create a new serialised instance out of it */
- child = g_slice_new (GVariant);
- child->type_info = s_child.type_info;
- child->state = (value->state & STATE_TRUSTED) |
- STATE_SERIALISED;
- child->size = s_child.size;
- child->ref_count = 1;
- child->contents.serialised.buffer =
- g_buffer_ref (value->contents.serialised.buffer);
- child->contents.serialised.data = s_child.data;
- }
- else
- child = g_variant_ref (value->contents.tree.children[index_]);
-
- g_variant_unlock (value);
-
- return child;
-}
-
-/**
- * g_variant_store:
- * @value: the #GVariant to store
- * @data: the location to store the serialised data at
- *
- * Stores the serialised form of @value at @data. @data should be
- * large enough. See g_variant_get_size().
- *
- * The stored data is in machine native byte order but may not be in
- * fully-normalised form if read from an untrusted source. See
- * g_variant_normalise() for a solution.
- *
- * This function is approximately O(n) in the size of @data.
- *
- * Since: 2.24
- **/
-void
-g_variant_store (GVariant *value,
- gpointer data)
-{
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
- {
- if (value->contents.serialised.data != NULL)
- memcpy (data, value->contents.serialised.data, value->size);
- else
- memset (data, 0, value->size);
- }
- else
- g_variant_serialise (value, data);
-
- g_variant_unlock (value);
-}
-
-/**
- * g_variant_is_normal_form:
- * @value: a #GVariant instance
- * @returns: %TRUE if @value is in normal form
- *
- * Checks if @value is in normal form.
- *
- * The main reason to do this is to detect if a given chunk of
- * serialised data is in normal form: load the data into a #GVariant
- * using g_variant_create_from_data() and then use this function to
- * check.
- *
- * If @value is found to be in normal form then it will be marked as
- * being trusted. If the value was already marked as being trusted then
- * this function will immediately return %TRUE.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_is_normal_form (GVariant *value)
-{
- if (value->state & STATE_TRUSTED)
- return TRUE;
-
- g_variant_lock (value);
-
- if (value->state & STATE_SERIALISED)
- {
- GVariantSerialised serialised = {
- value->type_info,
- (gpointer) value->contents.serialised.data,
- value->size
- };
-
- if (g_variant_serialised_is_normal (serialised))
- value->state |= STATE_TRUSTED;
- }
- else
- {
- gboolean normal = TRUE;
- gsize i;
-
- for (i = 0; i < value->contents.tree.n_children; i++)
- normal &= g_variant_is_normal_form (value->contents.tree.children[i]);
-
- if (normal)
- value->state |= STATE_TRUSTED;
- }
-
- g_variant_unlock (value);
-
- return (value->state & STATE_TRUSTED) != 0;
-}
+++ /dev/null
-/*
- * Copyright © 2009, 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "gerror.h"
-#include "gquark.h"
-#include "gstring.h"
-#include "gstrfuncs.h"
-#include "gtestutils.h"
-#include "gvariant.h"
-#include "gvarianttype.h"
-
-/*
- * two-pass algorithm
- * designed by ryan lortie and william hua
- * designed in itb-229 and at ghazi's, 2009.
- */
-
-/**
- * G_VARIANT_PARSE_ERROR:
- *
- * Error domain for GVariant text format parsing. Specific error codes
- * are not currently defined for this domain. See #GError for
- * information on error domains.
- **/
-/**
- * GVariantParseError:
- * @G_VARIANT_PARSE_ERROR_FAILED: generic error
- *
- * Error codes returned by parsing text-format GVariants. Currently the
- * parser makes no distinction between different types of error.
- **/
-GQuark
-g_variant_parser_get_error_quark (void)
-{
- static GQuark the_quark;
-
- if (the_quark == 0)
- the_quark = g_quark_from_static_string ("g-variant-parse-error-quark");
-
- return the_quark;
-}
-
-typedef struct
-{
- gint start, end;
-} SourceRef;
-
-static void
-parser_set_error_va (GError **error,
- SourceRef *location,
- SourceRef *other,
- const gchar *format,
- va_list ap)
-{
- GString *msg = g_string_new (NULL);
-
- if (location->start == location->end)
- g_string_append_printf (msg, "%d", location->start);
- else
- g_string_append_printf (msg, "%d-%d", location->start, location->end);
-
- if (other != NULL)
- {
- g_assert (other->start != other->end);
- g_string_append_printf (msg, ",%d-%d", other->start, other->end);
- }
- g_string_append_c (msg, ':');
-
- g_string_append_vprintf (msg, format, ap);
- g_set_error_literal (error, G_VARIANT_PARSE_ERROR, 0, msg->str);
- g_string_free (msg, TRUE);
-}
-
-static void
-parser_set_error (GError **error,
- SourceRef *location,
- SourceRef *other,
- const gchar *format,
- ...)
-{
- va_list ap;
-
- va_start (ap, format);
- parser_set_error_va (error, location, other, format, ap);
- va_end (ap);
-}
-
-typedef struct
-{
- const gchar *start;
- const gchar *stream;
- const gchar *end;
-
- const gchar *this;
-} TokenStream;
-
-
-static void
-token_stream_set_error (TokenStream *stream,
- GError **error,
- gboolean this_token,
- const gchar *format,
- ...)
-{
- SourceRef ref;
- va_list ap;
-
- ref.start = stream->this - stream->start;
-
- if (this_token)
- ref.end = stream->stream - stream->start;
- else
- ref.end = ref.start;
-
- va_start (ap, format);
- parser_set_error_va (error, &ref, NULL, format, ap);
- va_end (ap);
-}
-
-static void
-token_stream_prepare (TokenStream *stream)
-{
- gint brackets = 0;
- const gchar *end;
-
- if (stream->this != NULL)
- return;
-
- while (stream->stream != stream->end && g_ascii_isspace (*stream->stream))
- stream->stream++;
-
- if (stream->stream == stream->end || *stream->stream == '\0')
- {
- stream->this = stream->stream;
- return;
- }
-
- switch (stream->stream[0])
- {
- case '-': case '+': case '.': case '0': case '1': case '2':
- case '3': case '4': case '5': case '6': case '7': case '8':
- case '9':
- for (end = stream->stream; end != stream->end; end++)
- if (!g_ascii_isalnum (*end) &&
- *end != '-' && *end != '+' && *end != '.')
- break;
- break;
-
- case 'b':
- if (stream->stream[1] == '\'' || stream->stream[1] == '"')
- {
- for (end = stream->stream + 2; end != stream->end; end++)
- if (*end == stream->stream[1] || *end == '\0' ||
- (*end == '\\' && (++end == stream->end || *end == '\0')))
- break;
-
- if (end != stream->end && *end)
- end++;
- break;
- }
-
- else /* ↓↓↓ */;
-
- case 'a': /* 'b' */ case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- for (end = stream->stream; end != stream->end; end++)
- if (!g_ascii_isalnum (*end))
- break;
- break;
-
- case '\'': case '"':
- for (end = stream->stream + 1; end != stream->end; end++)
- if (*end == stream->stream[0] || *end == '\0' ||
- (*end == '\\' && (++end == stream->end || *end == '\0')))
- break;
-
- if (end != stream->end && *end)
- end++;
- break;
-
- case '@': case '%':
- /* stop at the first space, comma, colon or unmatched bracket.
- * deals nicely with cases like (%i, %i) or {%i: %i}.
- */
- for (end = stream->stream + 1;
- end != stream->end && *end != ',' &&
- *end != ':' && *end != '>' && !g_ascii_isspace (*end);
- end++)
-
- if (*end == '(' || *end == '{')
- brackets++;
-
- else if ((*end == ')' || *end == '}') && !brackets--)
- break;
-
- break;
-
- default:
- end = stream->stream + 1;
- break;
- }
-
- stream->this = stream->stream;
- stream->stream = end;
-}
-
-static void
-token_stream_next (TokenStream *stream)
-{
- stream->this = NULL;
-}
-
-static gboolean
-token_stream_peek (TokenStream *stream,
- gchar first_char)
-{
- token_stream_prepare (stream);
-
- return stream->this[0] == first_char;
-}
-
-static gboolean
-token_stream_peek2 (TokenStream *stream,
- gchar first_char,
- gchar second_char)
-{
- token_stream_prepare (stream);
-
- return stream->this[0] == first_char &&
- stream->this[1] == second_char;
-}
-
-static gboolean
-token_stream_is_keyword (TokenStream *stream)
-{
- token_stream_prepare (stream);
-
- return g_ascii_isalpha (stream->this[0]) &&
- g_ascii_isalpha (stream->this[1]);
-}
-
-static gboolean
-token_stream_is_numeric (TokenStream *stream)
-{
- token_stream_prepare (stream);
-
- return (g_ascii_isdigit (stream->this[0]) ||
- stream->this[0] == '-' ||
- stream->this[0] == '+' ||
- stream->this[0] == '.');
-}
-
-static gboolean
-token_stream_consume (TokenStream *stream,
- const gchar *token)
-{
- gint length = strlen (token);
-
- token_stream_prepare (stream);
-
- if (stream->stream - stream->this == length &&
- memcmp (stream->this, token, length) == 0)
- {
- token_stream_next (stream);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static gboolean
-token_stream_require (TokenStream *stream,
- const gchar *token,
- const gchar *purpose,
- GError **error)
-{
-
- if (!token_stream_consume (stream, token))
- {
- token_stream_set_error (stream, error, FALSE,
- "expected `%s'%s", token, purpose);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-token_stream_assert (TokenStream *stream,
- const gchar *token)
-{
- gboolean correct_token;
-
- correct_token = token_stream_consume (stream, token);
- g_assert (correct_token);
-}
-
-static gchar *
-token_stream_get (TokenStream *stream)
-{
- gchar *result;
-
- token_stream_prepare (stream);
-
- result = g_strndup (stream->this, stream->stream - stream->this);
-
- return result;
-}
-
-static void
-token_stream_start_ref (TokenStream *stream,
- SourceRef *ref)
-{
- token_stream_prepare (stream);
- ref->start = stream->this - stream->start;
-}
-
-static void
-token_stream_end_ref (TokenStream *stream,
- SourceRef *ref)
-{
- ref->end = stream->stream - stream->start;
-}
-
-void
-pattern_copy (gchar **out,
- const gchar **in)
-{
- gint brackets = 0;
-
- while (**in == 'a' || **in == 'm' || **in == 'M')
- *(*out)++ = *(*in)++;
-
- do
- {
- if (**in == '(' || **in == '{')
- brackets++;
-
- else if (**in == ')' || **in == '}')
- brackets--;
-
- *(*out)++ = *(*in)++;
- }
- while (brackets);
-}
-
-static gchar *
-pattern_coalesce (const gchar *left,
- const gchar *right)
-{
- gchar *result;
- gchar *out;
-
- /* the length of the output is loosely bound by the sum of the input
- * lengths, not simply the greater of the two lengths.
- *
- * (*(iii)) + ((iii)*) ((iii)(iii))
- *
- * 8 + 8 = 12
- */
- out = result = g_malloc (strlen (left) + strlen (right));
-
- while (*left && *right)
- {
- if (*left == *right)
- {
- *out++ = *left++;
- right++;
- }
-
- else
- {
- const gchar **one = &left, **the_other = &right;
-
- again:
- if (**one == '*' && **the_other != ')')
- {
- pattern_copy (&out, the_other);
- (*one)++;
- }
-
- else if (**one == 'M' && **the_other == 'm')
- {
- *out++ = *(*the_other)++;
- }
-
- else if (**one == 'M' && **the_other != 'm')
- {
- (*one)++;
- }
-
- else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
- {
- *out++ = *(*the_other)++;
- (*one)++;
- }
-
- else if (**one == 'S' && strchr ("sog", **the_other))
- {
- *out++ = *(*the_other)++;
- (*one)++;
- }
-
- else if (one == &left)
- {
- one = &right, the_other = &left;
- goto again;
- }
-
- else
- break;
- }
- }
-
- if (*left || *right)
- {
- g_free (result);
- result = NULL;
- }
- else
- *out++ = '\0';
-
- return result;
-}
-
-typedef struct _AST AST;
-typedef gchar * (*get_pattern_func) (AST *ast,
- GError **error);
-typedef GVariant * (*get_value_func) (AST *ast,
- const GVariantType *type,
- GError **error);
-typedef GVariant * (*get_base_value_func) (AST *ast,
- const GVariantType *type,
- GError **error);
-typedef void (*free_func) (AST *ast);
-
-typedef struct
-{
- gchar * (* get_pattern) (AST *ast,
- GError **error);
- GVariant * (* get_value) (AST *ast,
- const GVariantType *type,
- GError **error);
- GVariant * (* get_base_value) (AST *ast,
- const GVariantType *type,
- GError **error);
- void (* free) (AST *ast);
-} ASTClass;
-
-struct _AST
-{
- const ASTClass *class;
- SourceRef source_ref;
-};
-
-static gchar *
-ast_get_pattern (AST *ast,
- GError **error)
-{
- return ast->class->get_pattern (ast, error);
-}
-
-static GVariant *
-ast_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- return ast->class->get_value (ast, type, error);
-}
-
-static void
-ast_free (AST *ast)
-{
- ast->class->free (ast);
-}
-
-static void
-ast_set_error (AST *ast,
- GError **error,
- AST *other_ast,
- const gchar *format,
- ...)
-{
- va_list ap;
-
- va_start (ap, format);
- parser_set_error_va (error, &ast->source_ref,
- other_ast ? & other_ast->source_ref : NULL,
- format, ap);
- va_end (ap);
-}
-
-static GVariant *
-ast_type_error (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- gchar *typestr;
-
- typestr = g_variant_type_dup_string (type);
- ast_set_error (ast, error, NULL,
- "can not parse as value of type `%s'",
- typestr);
- g_free (typestr);
-
- return NULL;
-}
-
-static GVariant *
-ast_resolve (AST *ast,
- GError **error)
-{
- GVariant *value;
- gchar *pattern;
- gint i, j = 0;
-
- pattern = ast_get_pattern (ast, error);
-
- if (pattern == NULL)
- return NULL;
-
- /* choose reasonable defaults
- *
- * 1) favour non-maybe values where possible
- * 2) default type for strings is 's'
- * 3) default type for integers is 'i'
- */
- for (i = 0; pattern[i]; i++)
- switch (pattern[i])
- {
- case '*':
- ast_set_error (ast, error, NULL, "unable to infer type");
- g_free (pattern);
- return NULL;
-
- case 'M':
- break;
-
- case 'S':
- pattern[j++] = 's';
- break;
-
- case 'N':
- pattern[j++] = 'i';
- break;
-
- default:
- pattern[j++] = pattern[i];
- break;
- }
- pattern[j++] = '\0';
-
- value = ast_get_value (ast, G_VARIANT_TYPE (pattern), error);
- g_free (pattern);
-
- return value;
-}
-
-
-static AST *parse (TokenStream *stream,
- va_list *app,
- GError **error);
-
-static void
-ast_array_append (AST ***array,
- gint *n_items,
- AST *ast)
-{
- if ((*n_items & (*n_items - 1)) == 0)
- *array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
-
- (*array)[(*n_items)++] = ast;
-}
-
-static void
-ast_array_free (AST **array,
- gint n_items)
-{
- gint i;
-
- for (i = 0; i < n_items; i++)
- ast_free (array[i]);
- g_free (array);
-}
-
-static gchar *
-ast_array_get_pattern (AST **array,
- gint n_items,
- GError **error)
-{
- gchar *pattern;
- gint i;
-
- pattern = ast_get_pattern (array[0], error);
-
- if (pattern == NULL)
- return NULL;
-
- for (i = 1; i < n_items; i++)
- {
- gchar *tmp, *merged;
-
- tmp = ast_get_pattern (array[i], error);
-
- if (tmp == NULL)
- {
- g_free (pattern);
- return NULL;
- }
-
- merged = pattern_coalesce (pattern, tmp);
- g_free (pattern);
- pattern = merged;
-
- if (merged == NULL)
- /* set coalescence implies pairwise coalescence (i think).
- * we should therefore be able to trace the failure to a single
- * pair of values.
- */
- {
- int j = 0;
-
- while (TRUE)
- {
- gchar *tmp2;
- gchar *m;
-
- /* if 'j' reaches 'i' then we failed to find the pair */
- g_assert (j < i);
-
- tmp2 = ast_get_pattern (array[j], NULL);
- g_assert (tmp2 != NULL);
-
- m = pattern_coalesce (tmp, tmp2);
- g_free (tmp2);
- g_free (m);
-
- if (m == NULL)
- {
- /* we found a conflict between 'i' and 'j'.
- *
- * report the error. note: 'j' is first.
- */
- ast_set_error (array[j], error, array[i],
- "unable to find a common type");
- g_free (tmp);
- return NULL;
- }
-
- j++;
- }
-
- }
-
- g_free (tmp);
- }
-
- return pattern;
-}
-
-typedef struct
-{
- AST ast;
-
- AST *child;
-} Maybe;
-
-static gchar *
-maybe_get_pattern (AST *ast,
- GError **error)
-{
- Maybe *maybe = (Maybe *) ast;
-
- if (maybe->child != NULL)
- {
- gchar *child_pattern;
- gchar *pattern;
-
- child_pattern = ast_get_pattern (maybe->child, error);
-
- if (child_pattern == NULL)
- return NULL;
-
- pattern = g_strdup_printf ("m%s", child_pattern);
- g_free (child_pattern);
-
- return pattern;
- }
-
- return g_strdup ("m*");
-}
-
-static GVariant *
-maybe_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Maybe *maybe = (Maybe *) ast;
- GVariant *value;
-
- if (!g_variant_type_is_maybe (type))
- return ast_type_error (ast, type, error);
-
- type = g_variant_type_element (type);
-
- if (maybe->child)
- {
- value = ast_get_value (maybe->child, type, error);
-
- if (value == NULL)
- return NULL;
- }
- else
- value = NULL;
-
- return g_variant_new_maybe (type, value);
-}
-
-static void
-maybe_free (AST *ast)
-{
- Maybe *maybe = (Maybe *) ast;
-
- if (maybe->child != NULL)
- ast_free (maybe->child);
-
- g_slice_free (Maybe, maybe);
-}
-
-static AST *
-maybe_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass maybe_class = {
- maybe_get_pattern,
- maybe_get_value, NULL,
- maybe_free
- };
- AST *child = NULL;
- Maybe *maybe;
-
- if (token_stream_consume (stream, "just"))
- {
- child = parse (stream, app, error);
- if (child == NULL)
- return NULL;
- }
-
- else if (!token_stream_consume (stream, "nothing"))
- {
- token_stream_set_error (stream, error, TRUE, "unknown keyword");
- return NULL;
- }
-
- maybe = g_slice_new (Maybe);
- maybe->ast.class = &maybe_class;
- maybe->child = child;
-
- return (AST *) maybe;
-}
-
-static GVariant *
-maybe_wrapper (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- const GVariantType *t;
- GVariant *value;
- int depth;
-
- for (depth = 0, t = type;
- g_variant_type_is_maybe (t);
- depth++, t = g_variant_type_element (t));
-
- value = ast->class->get_base_value (ast, t, error);
-
- if (value == NULL)
- return NULL;
-
- while (depth--)
- value = g_variant_new_maybe (NULL, value);
-
- return value;
-}
-
-typedef struct
-{
- AST ast;
-
- AST **children;
- gint n_children;
-} Array;
-
-static gchar *
-array_get_pattern (AST *ast,
- GError **error)
-{
- Array *array = (Array *) ast;
- gchar *pattern;
- gchar *result;
-
- if (array->n_children == 0)
- return g_strdup ("Ma*");
-
- pattern = ast_array_get_pattern (array->children, array->n_children, error);
-
- if (pattern == NULL)
- return NULL;
-
- result = g_strdup_printf ("Ma%s", pattern);
- g_free (pattern);
-
- return result;
-}
-
-static GVariant *
-array_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Array *array = (Array *) ast;
- const GVariantType *childtype;
- GVariantBuilder builder;
- gint i;
-
- if (!g_variant_type_is_array (type))
- return ast_type_error (ast, type, error);
-
- g_variant_builder_init (&builder, type);
- childtype = g_variant_type_element (type);
-
- for (i = 0; i < array->n_children; i++)
- {
- GVariant *child;
-
- if (!(child = ast_get_value (array->children[i], childtype, error)))
- {
- g_variant_builder_clear (&builder);
- return NULL;
- }
-
- g_variant_builder_add_value (&builder, child);
- }
-
- return g_variant_builder_end (&builder);
-}
-
-static void
-array_free (AST *ast)
-{
- Array *array = (Array *) ast;
-
- ast_array_free (array->children, array->n_children);
- g_slice_free (Array, array);
-}
-
-static AST *
-array_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass array_class = {
- array_get_pattern,
- maybe_wrapper, array_get_value,
- array_free
- };
- gboolean need_comma = FALSE;
- Array *array;
-
- array = g_slice_new (Array);
- array->ast.class = &array_class;
- array->children = NULL;
- array->n_children = 0;
-
- token_stream_assert (stream, "[");
- while (!token_stream_consume (stream, "]"))
- {
- AST *child;
-
- if (need_comma &&
- !token_stream_require (stream, ",",
- " or `]' to follow array element",
- error))
- goto error;
-
- child = parse (stream, app, error);
-
- if (!child)
- goto error;
-
- ast_array_append (&array->children, &array->n_children, child);
- need_comma = TRUE;
- }
-
- return (AST *) array;
-
- error:
- ast_array_free (array->children, array->n_children);
- g_slice_free (Array, array);
-
- return NULL;
-}
-
-typedef struct
-{
- AST ast;
-
- AST **children;
- gint n_children;
-} Tuple;
-
-static gchar *
-tuple_get_pattern (AST *ast,
- GError **error)
-{
- Tuple *tuple = (Tuple *) ast;
- gchar *result = NULL;
- gchar **parts;
- gint i;
-
- parts = g_new (gchar *, tuple->n_children + 4);
- parts[tuple->n_children + 1] = (gchar *) ")";
- parts[tuple->n_children + 2] = NULL;
- parts[0] = (gchar *) "M(";
-
- for (i = 0; i < tuple->n_children; i++)
- if (!(parts[i + 1] = ast_get_pattern (tuple->children[i], error)))
- break;
-
- if (i == tuple->n_children)
- result = g_strjoinv ("", parts);
-
- /* parts[0] should not be freed */
- while (i)
- g_free (parts[i--]);
- g_free (parts);
-
- return result;
-}
-
-static GVariant *
-tuple_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Tuple *tuple = (Tuple *) ast;
- const GVariantType *childtype;
- GVariantBuilder builder;
- gint i;
-
- if (!g_variant_type_is_tuple (type))
- return ast_type_error (ast, type, error);
-
- g_variant_builder_init (&builder, type);
- childtype = g_variant_type_first (type);
-
- for (i = 0; i < tuple->n_children; i++)
- {
- GVariant *child;
-
- if (!(child = ast_get_value (tuple->children[i], childtype, error)))
- {
- g_variant_builder_clear (&builder);
- return FALSE;
- }
-
- g_variant_builder_add_value (&builder, child);
- childtype = g_variant_type_next (childtype);
- }
-
- return g_variant_builder_end (&builder);
-}
-
-static void
-tuple_free (AST *ast)
-{
- Tuple *tuple = (Tuple *) ast;
-
- ast_array_free (tuple->children, tuple->n_children);
- g_slice_free (Tuple, tuple);
-}
-
-static AST *
-tuple_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass tuple_class = {
- tuple_get_pattern,
- maybe_wrapper, tuple_get_value,
- tuple_free
- };
- gboolean need_comma = FALSE;
- gboolean first = TRUE;
- Tuple *tuple;
-
- tuple = g_slice_new (Tuple);
- tuple->ast.class = &tuple_class;
- tuple->children = NULL;
- tuple->n_children = 0;
-
- token_stream_assert (stream, "(");
- while (!token_stream_consume (stream, ")"))
- {
- AST *child;
-
- if (need_comma &&
- !token_stream_require (stream, ",",
- " or `)' to follow tuple element",
- error))
- goto error;
-
- child = parse (stream, app, error);
-
- if (!child)
- goto error;
-
- ast_array_append (&tuple->children, &tuple->n_children, child);
-
- /* the first time, we absolutely require a comma, so grab it here
- * and leave need_comma = FALSE so that the code above doesn't
- * require a second comma.
- *
- * the second and remaining times, we set need_comma = TRUE.
- */
- if (first)
- {
- if (!token_stream_require (stream, ",",
- " after first tuple element", error))
- goto error;
-
- first = FALSE;
- }
- else
- need_comma = TRUE;
- }
-
- return (AST *) tuple;
-
- error:
- ast_array_free (tuple->children, tuple->n_children);
- g_slice_free (Tuple, tuple);
-
- return NULL;
-}
-
-typedef struct
-{
- AST ast;
-
- AST *value;
-} Variant;
-
-static gchar *
-variant_get_pattern (AST *ast,
- GError **error)
-{
- return g_strdup ("Mv");
-}
-
-static GVariant *
-variant_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Variant *variant = (Variant *) ast;
- GVariant *child;
-
- g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_VARIANT));
- child = ast_resolve (variant->value, error);
-
- if (child == NULL)
- return NULL;
-
- return g_variant_new_variant (child);
-}
-
-static void
-variant_free (AST *ast)
-{
- Variant *variant = (Variant *) ast;
-
- ast_free (variant->value);
- g_slice_free (Variant, variant);
-}
-
-static AST *
-variant_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass variant_class = {
- variant_get_pattern,
- maybe_wrapper, variant_get_value,
- variant_free
- };
- Variant *variant;
- AST *value;
-
- token_stream_assert (stream, "<");
- value = parse (stream, app, error);
-
- if (!value)
- return NULL;
-
- if (!token_stream_require (stream, ">", " to follow variant value", error))
- {
- ast_free (value);
- return NULL;
- }
-
- variant = g_slice_new (Variant);
- variant->ast.class = &variant_class;
- variant->value = value;
-
- return (AST *) variant;
-}
-
-typedef struct
-{
- AST ast;
-
- AST **keys;
- AST **values;
- gint n_children;
-} Dictionary;
-
-static gchar *
-dictionary_get_pattern (AST *ast,
- GError **error)
-{
- Dictionary *dict = (Dictionary *) ast;
- gchar *value_pattern;
- gchar *key_pattern;
- gchar key_char;
- gchar *result;
-
- if (dict->n_children == 0)
- return g_strdup ("Ma{**}");
-
- key_pattern = ast_array_get_pattern (dict->keys,
- abs (dict->n_children),
- error);
-
- if (key_pattern == NULL)
- return NULL;
-
- /* we can not have maybe keys */
- if (key_pattern[0] == 'M')
- key_char = key_pattern[1];
- else
- key_char = key_pattern[0];
-
- g_free (key_pattern);
-
- /* the basic types,
- * plus undetermined number type and undetermined string type.
- */
- if (!strchr ("bynqiuxthdsogNS", key_char))
- {
- ast_set_error (ast, error, NULL,
- "dictionary keys must have basic types");
- return NULL;
- }
-
- value_pattern = ast_get_pattern (dict->values[0], error);
-
- if (value_pattern == NULL)
- return NULL;
-
- result = g_strdup_printf ("M%s{%c%s}",
- dict->n_children > 0 ? "a" : "",
- key_char, value_pattern);
- g_free (value_pattern);
-
- return result;
-}
-
-static GVariant *
-dictionary_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Dictionary *dict = (Dictionary *) ast;
-
- if (dict->n_children == -1)
- {
- const GVariantType *subtype;
- GVariantBuilder builder;
- GVariant *subvalue;
-
- if (!g_variant_type_is_dict_entry (type))
- return ast_type_error (ast, type, error);
-
- g_variant_builder_init (&builder, type);
-
- subtype = g_variant_type_key (type);
- if (!(subvalue = ast_get_value (dict->keys[0], subtype, error)))
- {
- g_variant_builder_clear (&builder);
- return NULL;
- }
- g_variant_builder_add_value (&builder, subvalue);
-
- subtype = g_variant_type_value (type);
- if (!(subvalue = ast_get_value (dict->values[0], subtype, error)))
- {
- g_variant_builder_clear (&builder);
- return NULL;
- }
- g_variant_builder_add_value (&builder, subvalue);
-
- return g_variant_builder_end (&builder);
- }
- else
- {
- const GVariantType *entry, *key, *val;
- GVariantBuilder builder;
- gint i;
-
- if (!g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_DICTIONARY))
- return ast_type_error (ast, type, error);
-
- entry = g_variant_type_element (type);
- key = g_variant_type_key (entry);
- val = g_variant_type_value (entry);
-
- g_variant_builder_init (&builder, type);
-
- for (i = 0; i < dict->n_children; i++)
- {
- GVariant *subvalue;
-
- g_variant_builder_open (&builder, entry);
-
- if (!(subvalue = ast_get_value (dict->keys[i], key, error)))
- {
- g_variant_builder_clear (&builder);
- return NULL;
- }
- g_variant_builder_add_value (&builder, subvalue);
-
- if (!(subvalue = ast_get_value (dict->values[i], val, error)))
- {
- g_variant_builder_clear (&builder);
- return NULL;
- }
- g_variant_builder_add_value (&builder, subvalue);
- g_variant_builder_close (&builder);
- }
-
- return g_variant_builder_end (&builder);
- }
-}
-
-static void
-dictionary_free (AST *ast)
-{
- Dictionary *dict = (Dictionary *) ast;
- gint n_children;
-
- if (dict->n_children > -1)
- n_children = dict->n_children;
- else
- n_children = 1;
-
- ast_array_free (dict->keys, n_children);
- ast_array_free (dict->values, n_children);
- g_slice_free (Dictionary, dict);
-}
-
-static AST *
-dictionary_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass dictionary_class = {
- dictionary_get_pattern,
- maybe_wrapper, dictionary_get_value,
- dictionary_free
- };
- gint n_keys, n_values;
- gboolean only_one;
- Dictionary *dict;
- AST *first;
-
- dict = g_slice_new (Dictionary);
- dict->ast.class = &dictionary_class;
- dict->keys = NULL;
- dict->values = NULL;
- n_keys = n_values = 0;
-
- token_stream_assert (stream, "{");
-
- if (token_stream_consume (stream, "}"))
- {
- dict->n_children = 0;
- return (AST *) dict;
- }
-
- if ((first = parse (stream, app, error)) == NULL)
- goto error;
-
- ast_array_append (&dict->keys, &n_keys, first);
-
- only_one = token_stream_consume (stream, ",");
- if (!only_one &&
- !token_stream_require (stream, ":",
- " or `,' to follow dictionary entry key",
- error))
- goto error;
-
- if ((first = parse (stream, app, error)) == NULL)
- goto error;
-
- ast_array_append (&dict->values, &n_values, first);
-
- if (only_one)
- {
- if (!token_stream_require (stream, "}", " at end of dictionary entry",
- error))
- goto error;
-
- g_assert (n_keys == 1 && n_values == 1);
- dict->n_children = -1;
-
- return (AST *) dict;
- }
-
- while (!token_stream_consume (stream, "}"))
- {
- AST *child;
-
- if (!token_stream_require (stream, ",",
- " or `}' to follow dictionary entry", error))
- goto error;
-
- child = parse (stream, app, error);
-
- if (!child)
- goto error;
-
- ast_array_append (&dict->keys, &n_keys, child);
-
- if (!token_stream_require (stream, ":",
- " to follow dictionary entry key", error))
- goto error;
-
- child = parse (stream, app, error);
-
- if (!child)
- goto error;
-
- ast_array_append (&dict->values, &n_values, child);
- }
-
- g_assert (n_keys == n_values);
- dict->n_children = n_keys;
-
- return (AST *) dict;
-
- error:
- ast_array_free (dict->keys, n_keys);
- ast_array_free (dict->values, n_values);
- g_slice_free (Dictionary, dict);
-
- return NULL;
-}
-
-typedef struct
-{
- AST ast;
- gchar *string;
-} String;
-
-static gchar *
-string_get_pattern (AST *ast,
- GError **error)
-{
- return g_strdup ("MS");
-}
-
-static GVariant *
-string_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- String *string = (String *) ast;
-
- if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
- return g_variant_new_string (string->string);
-
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH))
- {
- if (!g_variant_is_object_path (string->string))
- {
- ast_set_error (ast, error, NULL, "not a valid object path");
- return NULL;
- }
-
- return g_variant_new_object_path (string->string);
- }
-
- else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE))
- {
- if (!g_variant_is_signature (string->string))
- {
- ast_set_error (ast, error, NULL, "not a valid signature");
- return NULL;
- }
-
- return g_variant_new_signature (string->string);
- }
-
- else
- return ast_type_error (ast, type, error);
-}
-
-static void
-string_free (AST *ast)
-{
- String *string = (String *) ast;
-
- g_free (string->string);
- g_slice_free (String, string);
-}
-
-static gboolean
-unicode_unescape (const gchar *src,
- gint *src_ofs,
- gchar *dest,
- gint *dest_ofs,
- gint length,
- SourceRef *ref,
- GError **error)
-{
- gchar buffer[9];
- guint64 value;
- gchar *end;
-
- (*src_ofs)++;
-
- g_assert (length < sizeof (buffer));
- strncpy (buffer, src + *src_ofs, length);
- buffer[length] = '\0';
-
- value = g_ascii_strtoull (buffer, &end, 0x10);
-
- if (value == 0 || end != buffer + length)
- {
- parser_set_error (error, ref, NULL,
- "invalid %d-character unicode escape", length);
- return FALSE;
- }
-
- g_assert (value <= G_MAXUINT32);
-
- *dest_ofs += g_unichar_to_utf8 (value, dest + *dest_ofs);
- *src_ofs += length;
-
- return TRUE;
-}
-
-static AST *
-string_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass string_class = {
- string_get_pattern,
- maybe_wrapper, string_get_value,
- string_free
- };
- String *string;
- SourceRef ref;
- gchar *token;
- gsize length;
- gchar quote;
- gchar *str;
- gint i, j;
-
- token_stream_start_ref (stream, &ref);
- token = token_stream_get (stream);
- token_stream_end_ref (stream, &ref);
- length = strlen (token);
- quote = token[0];
-
- str = g_malloc (length);
- g_assert (quote == '"' || quote == '\'');
- j = 0;
- i = 1;
- while (token[i] != quote)
- switch (token[i])
- {
- case '\0':
- parser_set_error (error, &ref, NULL,
- "unterminated string constant");
- g_free (token);
- g_free (str);
- return NULL;
-
- case '\\':
- switch (token[++i])
- {
- case '\0':
- parser_set_error (error, &ref, NULL,
- "unterminated string constant");
- g_free (token);
- g_free (str);
- return NULL;
-
- case 'u':
- if (!unicode_unescape (token, &i, str, &j, 4, &ref, error))
- {
- g_free (token);
- g_free (str);
- return NULL;
- }
- continue;
-
- case 'U':
- if (!unicode_unescape (token, &i, str, &j, 8, &ref, error))
- {
- g_free (token);
- g_free (str);
- return NULL;
- }
- continue;
-
- case 'a': str[j++] = '\a'; i++; continue;
- case 'b': str[j++] = '\b'; i++; continue;
- case 'f': str[j++] = '\f'; i++; continue;
- case 'n': str[j++] = '\n'; i++; continue;
- case 'r': str[j++] = '\r'; i++; continue;
- case 't': str[j++] = '\t'; i++; continue;
- case 'v': str[j++] = '\v'; i++; continue;
- case '\n': i++; continue;
- }
-
- default:
- str[j++] = token[i++];
- }
- str[j++] = '\0';
- g_free (token);
-
- string = g_slice_new (String);
- string->ast.class = &string_class;
- string->string = str;
-
- token_stream_next (stream);
-
- return (AST *) string;
-}
-
-typedef struct
-{
- AST ast;
- gchar *string;
-} ByteString;
-
-static gchar *
-bytestring_get_pattern (AST *ast,
- GError **error)
-{
- return g_strdup ("May");
-}
-
-static GVariant *
-bytestring_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- ByteString *string = (ByteString *) ast;
-
- g_assert (g_variant_type_equal (type, G_VARIANT_TYPE_BYTESTRING));
-
- return g_variant_new_bytestring (string->string);
-}
-
-static void
-bytestring_free (AST *ast)
-{
- ByteString *string = (ByteString *) ast;
-
- g_free (string->string);
- g_slice_free (ByteString, string);
-}
-
-static AST *
-bytestring_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass bytestring_class = {
- bytestring_get_pattern,
- maybe_wrapper, bytestring_get_value,
- bytestring_free
- };
- ByteString *string;
- SourceRef ref;
- gchar *token;
- gsize length;
- gchar quote;
- gchar *str;
- gint i, j;
-
- token_stream_start_ref (stream, &ref);
- token = token_stream_get (stream);
- token_stream_end_ref (stream, &ref);
- g_assert (token[0] == 'b');
- length = strlen (token);
- quote = token[1];
-
- str = g_malloc (length);
- g_assert (quote == '"' || quote == '\'');
- j = 0;
- i = 2;
- while (token[i] != quote)
- switch (token[i])
- {
- case '\0':
- parser_set_error (error, &ref, NULL,
- "unterminated string constant");
- g_free (token);
- return NULL;
-
- case '\\':
- switch (token[++i])
- {
- case '\0':
- parser_set_error (error, &ref, NULL,
- "unterminated string constant");
- g_free (token);
- return NULL;
-
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
- {
- /* up to 3 characters */
- guchar val = token[i++] - '0';
-
- if ('0' <= token[i] && token[i] < '8')
- val = (val << 3) | (token[i++] - '0');
-
- if ('0' <= token[i] && token[i] < '8')
- val = (val << 3) | (token[i++] - '0');
-
- str[j++] = val;
- }
- continue;
-
- case 'a': str[j++] = '\a'; i++; continue;
- case 'b': str[j++] = '\b'; i++; continue;
- case 'f': str[j++] = '\f'; i++; continue;
- case 'n': str[j++] = '\n'; i++; continue;
- case 'r': str[j++] = '\r'; i++; continue;
- case 't': str[j++] = '\t'; i++; continue;
- case 'v': str[j++] = '\v'; i++; continue;
- case '\n': i++; continue;
- }
-
- default:
- str[j++] = token[i++];
- }
- str[j++] = '\0';
- g_free (token);
-
- string = g_slice_new (ByteString);
- string->ast.class = &bytestring_class;
- string->string = str;
-
- token_stream_next (stream);
-
- return (AST *) string;
-}
-
-typedef struct
-{
- AST ast;
-
- gchar *token;
-} Number;
-
-static gchar *
-number_get_pattern (AST *ast,
- GError **error)
-{
- Number *number = (Number *) ast;
-
- if (strchr (number->token, '.') ||
- (!g_str_has_prefix (number->token, "0x") &&
- strchr (number->token, 'e')))
- return g_strdup ("Md");
-
- return g_strdup ("MN");
-}
-
-static GVariant *
-number_overflow (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- ast_set_error (ast, error, NULL, "number out of range for type `%c'",
- g_variant_type_peek_string (type)[0]);
- return NULL;
-}
-
-static GVariant *
-number_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Number *number = (Number *) ast;
- const gchar *token;
- gboolean negative;
- gboolean floating;
- guint64 abs_val;
- gdouble dbl_val;
- gchar *end;
-
- token = number->token;
-
- if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
- {
- floating = TRUE;
-
- errno = 0;
- dbl_val = g_ascii_strtod (token, &end);
- if (dbl_val != 0.0 && errno == ERANGE)
- {
- ast_set_error (ast, error, NULL, "number too big for any type");
- return NULL;
- }
-
- /* silence uninitialised warnings... */
- negative = FALSE;
- abs_val = 0;
- }
- else
- {
- floating = FALSE;
- negative = token[0] == '-';
- if (token[0] == '-')
- token++;
-
- errno = 0;
- abs_val = g_ascii_strtoull (token, &end, 0);
- if (abs_val == G_MAXUINT64 && errno == ERANGE)
- {
- ast_set_error (ast, error, NULL, "integer too big for any type");
- return NULL;
- }
-
- if (abs_val == 0)
- negative = FALSE;
-
- /* silence uninitialised warning... */
- dbl_val = 0.0;
- }
-
- if (*end != '\0')
- {
- SourceRef ref;
-
- ref = ast->source_ref;
- ref.start += end - number->token;
- ref.end = ref.start + 1;
-
- parser_set_error (error, &ref, NULL,
- "invalid character in number");
- return NULL;
- }
-
- if (floating)
- return g_variant_new_double (dbl_val);
-
- switch (*g_variant_type_peek_string (type))
- {
- case 'y':
- if (negative || abs_val > G_MAXUINT8)
- return number_overflow (ast, type, error);
- return g_variant_new_byte (abs_val);
-
- case 'n':
- if (abs_val - negative > G_MAXINT16)
- return number_overflow (ast, type, error);
- return g_variant_new_int16 (negative ? -abs_val : abs_val);
-
- case 'q':
- if (negative || abs_val > G_MAXUINT16)
- return number_overflow (ast, type, error);
- return g_variant_new_uint16 (negative ? -abs_val : abs_val);
-
- case 'i':
- if (abs_val - negative > G_MAXINT32)
- return number_overflow (ast, type, error);
- return g_variant_new_int32 (negative ? -abs_val : abs_val);
-
- case 'u':
- if (negative || abs_val > G_MAXUINT32)
- return number_overflow (ast, type, error);
- return g_variant_new_uint32 (negative ? -abs_val : abs_val);
-
- case 'x':
- if (abs_val - negative > G_MAXINT64)
- return number_overflow (ast, type, error);
- return g_variant_new_int64 (negative ? -abs_val : abs_val);
-
- case 't':
- if (negative)
- return number_overflow (ast, type, error);
- return g_variant_new_uint64 (negative ? -abs_val : abs_val);
-
- case 'h':
- if (abs_val - negative > G_MAXINT32)
- return number_overflow (ast, type, error);
- return g_variant_new_handle (negative ? -abs_val : abs_val);
-
- default:
- return ast_type_error (ast, type, error);
- }
-}
-
-static void
-number_free (AST *ast)
-{
- Number *number = (Number *) ast;
-
- g_free (number->token);
- g_slice_free (Number, number);
-}
-
-static AST *
-number_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass number_class = {
- number_get_pattern,
- maybe_wrapper, number_get_value,
- number_free
- };
- Number *number;
-
- number = g_slice_new (Number);
- number->ast.class = &number_class;
- number->token = token_stream_get (stream);
- token_stream_next (stream);
-
- return (AST *) number;
-}
-
-typedef struct
-{
- AST ast;
- gboolean value;
-} Boolean;
-
-static gchar *
-boolean_get_pattern (AST *ast,
- GError **error)
-{
- return g_strdup ("Mb");
-}
-
-static GVariant *
-boolean_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Boolean *boolean = (Boolean *) ast;
-
- if (!g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
- return ast_type_error (ast, type, error);
-
- return g_variant_new_boolean (boolean->value);
-}
-
-static void
-boolean_free (AST *ast)
-{
- Boolean *boolean = (Boolean *) ast;
-
- g_slice_free (Boolean, boolean);
-}
-
-static AST *
-boolean_new (gboolean value)
-{
- static const ASTClass boolean_class = {
- boolean_get_pattern,
- maybe_wrapper, boolean_get_value,
- boolean_free
- };
- Boolean *boolean;
-
- boolean = g_slice_new (Boolean);
- boolean->ast.class = &boolean_class;
- boolean->value = value;
-
- return (AST *) boolean;
-}
-
-typedef struct
-{
- AST ast;
-
- GVariant *value;
-} Positional;
-
-static gchar *
-positional_get_pattern (AST *ast,
- GError **error)
-{
- Positional *positional = (Positional *) ast;
-
- return g_strdup (g_variant_get_type_string (positional->value));
-}
-
-static GVariant *
-positional_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- Positional *positional = (Positional *) ast;
- GVariant *value;
-
- g_assert (positional->value != NULL);
-
- if G_UNLIKELY (!g_variant_is_of_type (positional->value, type))
- return ast_type_error (ast, type, error);
-
- /* NOTE: if _get is called more than once then
- * things get messed up with respect to floating refs.
- *
- * fortunately, this function should only ever get called once.
- */
- g_assert (positional->value != NULL);
- value = positional->value;
- positional->value = NULL;
-
- return value;
-}
-
-static void
-positional_free (AST *ast)
-{
- Positional *positional = (Positional *) ast;
-
- /* if positional->value is set, just leave it.
- * memory management doesn't matter in case of programmer error.
- */
- g_slice_free (Positional, positional);
-}
-
-static AST *
-positional_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass positional_class = {
- positional_get_pattern,
- positional_get_value, NULL,
- positional_free
- };
- Positional *positional;
- const gchar *endptr;
- gchar *token;
-
- token = token_stream_get (stream);
- g_assert (token[0] == '%');
-
- positional = g_slice_new (Positional);
- positional->ast.class = &positional_class;
- positional->value = g_variant_new_va (token + 1, &endptr, app);
-
- if (*endptr || positional->value == NULL)
- {
- token_stream_set_error (stream, error, TRUE,
- "invalid GVariant format string");
- /* memory management doesn't matter in case of programmer error. */
- return NULL;
- }
-
- token_stream_next (stream);
- g_free (token);
-
- return (AST *) positional;
-}
-
-typedef struct
-{
- AST ast;
-
- GVariantType *type;
- AST *child;
-} TypeDecl;
-
-static gchar *
-typedecl_get_pattern (AST *ast,
- GError **error)
-{
- TypeDecl *decl = (TypeDecl *) ast;
-
- return g_variant_type_dup_string (decl->type);
-}
-
-static GVariant *
-typedecl_get_value (AST *ast,
- const GVariantType *type,
- GError **error)
-{
- TypeDecl *decl = (TypeDecl *) ast;
-
- return ast_get_value (decl->child, type, error);
-}
-
-static void
-typedecl_free (AST *ast)
-{
- TypeDecl *decl = (TypeDecl *) ast;
-
- ast_free (decl->child);
- g_variant_type_free (decl->type);
- g_slice_free (TypeDecl, decl);
-}
-
-static AST *
-typedecl_parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- static const ASTClass typedecl_class = {
- typedecl_get_pattern,
- typedecl_get_value, NULL,
- typedecl_free
- };
- GVariantType *type;
- TypeDecl *decl;
- AST *child;
-
- if (token_stream_peek (stream, '@'))
- {
- gchar *token;
-
- token = token_stream_get (stream);
-
- if (!g_variant_type_string_is_valid (token + 1))
- {
- token_stream_set_error (stream, error, TRUE,
- "invalid type declaration");
- g_free (token);
-
- return NULL;
- }
-
- type = g_variant_type_new (token + 1);
-
- if (!g_variant_type_is_definite (type))
- {
- token_stream_set_error (stream, error, TRUE,
- "type declarations must be definite");
- g_variant_type_free (type);
- g_free (token);
-
- return NULL;
- }
-
- token_stream_next (stream);
- g_free (token);
- }
- else
- {
- if (token_stream_consume (stream, "boolean"))
- type = g_variant_type_copy (G_VARIANT_TYPE_BOOLEAN);
-
- else if (token_stream_consume (stream, "byte"))
- type = g_variant_type_copy (G_VARIANT_TYPE_BYTE);
-
- else if (token_stream_consume (stream, "int16"))
- type = g_variant_type_copy (G_VARIANT_TYPE_INT16);
-
- else if (token_stream_consume (stream, "uint16"))
- type = g_variant_type_copy (G_VARIANT_TYPE_UINT16);
-
- else if (token_stream_consume (stream, "int32"))
- type = g_variant_type_copy (G_VARIANT_TYPE_INT32);
-
- else if (token_stream_consume (stream, "handle"))
- type = g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
-
- else if (token_stream_consume (stream, "uint32"))
- type = g_variant_type_copy (G_VARIANT_TYPE_UINT32);
-
- else if (token_stream_consume (stream, "int64"))
- type = g_variant_type_copy (G_VARIANT_TYPE_INT64);
-
- else if (token_stream_consume (stream, "uint64"))
- type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
-
- else if (token_stream_consume (stream, "double"))
- type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
-
- else if (token_stream_consume (stream, "string"))
- type = g_variant_type_copy (G_VARIANT_TYPE_STRING);
-
- else if (token_stream_consume (stream, "objectpath"))
- type = g_variant_type_copy (G_VARIANT_TYPE_OBJECT_PATH);
-
- else if (token_stream_consume (stream, "signature"))
- type = g_variant_type_copy (G_VARIANT_TYPE_SIGNATURE);
-
- else
- {
- token_stream_set_error (stream, error, TRUE, "unknown keyword");
- return NULL;
- }
- }
-
- if ((child = parse (stream, app, error)) == NULL)
- {
- g_variant_type_free (type);
- return NULL;
- }
-
- decl = g_slice_new (TypeDecl);
- decl->ast.class = &typedecl_class;
- decl->type = type;
- decl->child = child;
-
- return (AST *) decl;
-}
-
-static AST *
-parse (TokenStream *stream,
- va_list *app,
- GError **error)
-{
- SourceRef source_ref;
- AST *result;
-
- token_stream_prepare (stream);
- token_stream_start_ref (stream, &source_ref);
-
- if (token_stream_peek (stream, '['))
- result = array_parse (stream, app, error);
-
- else if (token_stream_peek (stream, '('))
- result = tuple_parse (stream, app, error);
-
- else if (token_stream_peek (stream, '<'))
- result = variant_parse (stream, app, error);
-
- else if (token_stream_peek (stream, '{'))
- result = dictionary_parse (stream, app, error);
-
- else if (app && token_stream_peek (stream, '%'))
- result = positional_parse (stream, app, error);
-
- else if (token_stream_consume (stream, "true"))
- result = boolean_new (TRUE);
-
- else if (token_stream_consume (stream, "false"))
- result = boolean_new (FALSE);
-
- else if (token_stream_peek (stream, 'n') ||
- token_stream_peek (stream, 'j'))
- result = maybe_parse (stream, app, error);
-
- else if (token_stream_peek (stream, '@') ||
- token_stream_is_keyword (stream))
- result = typedecl_parse (stream, app, error);
-
- else if (token_stream_is_numeric (stream))
- result = number_parse (stream, app, error);
-
- else if (token_stream_peek (stream, '\'') ||
- token_stream_peek (stream, '"'))
- result = string_parse (stream, app, error);
-
- else if (token_stream_peek2 (stream, 'b', '\'') ||
- token_stream_peek2 (stream, 'b', '"'))
- result = bytestring_parse (stream, app, error);
-
- else
- {
- token_stream_set_error (stream, error, FALSE, "expected value");
- return NULL;
- }
-
- if (result != NULL)
- {
- token_stream_end_ref (stream, &source_ref);
- result->source_ref = source_ref;
- }
-
- return result;
-}
-
-/**
- * g_variant_parse:
- * @type: a #GVariantType, or %NULL
- * @text: a string containing a GVariant in text form
- * @limit: a pointer to the end of @text, or %NULL
- * @endptr: a location to store the end pointer, or %NULL
- * @error: a pointer to a %NULL #GError pointer, or %NULL
- * @Returns: a reference to a #GVariant, or %NULL
- *
- * Parses a #GVariant from a text representation.
- *
- * A single #GVariant is parsed from the content of @text.
- *
- * The memory at @limit will never be accessed and the parser behaves as
- * if the character at @limit is the nul terminator. This has the
- * effect of bounding @text.
- *
- * If @endptr is non-%NULL then @text is permitted to contain data
- * following the value that this function parses and @endptr will be
- * updated to point to the first character past the end of the text
- * parsed by this function. If @endptr is %NULL and there is extra data
- * then an error is returned.
- *
- * If @type is non-%NULL then the value will be parsed to have that
- * type. This may result in additional parse errors (in the case that
- * the parsed value doesn't fit the type) but may also result in fewer
- * errors (in the case that the type would have been ambiguous, such as
- * with empty arrays).
- *
- * In the event that the parsing is successful, the resulting #GVariant
- * is returned.
- *
- * In case of any error, %NULL will be returned. If @error is non-%NULL
- * then it will be set to reflect the error that occured.
- *
- * Officially, the language understood by the parser is "any string
- * produced by g_variant_print()".
- **/
-GVariant *
-g_variant_parse (const GVariantType *type,
- const gchar *text,
- const gchar *limit,
- const gchar **endptr,
- GError **error)
-{
- TokenStream stream = { 0, };
- GVariant *result = NULL;
- AST *ast;
-
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (text == limit || text != NULL, NULL);
-
- stream.start = text;
- stream.stream = text;
- stream.end = limit;
-
- if ((ast = parse (&stream, NULL, error)))
- {
- if (type == NULL)
- result = ast_resolve (ast, error);
- else
- result = ast_get_value (ast, type, error);
-
- if (result != NULL)
- {
- g_variant_ref_sink (result);
-
- if (endptr == NULL)
- {
- while (stream.stream != limit &&
- g_ascii_isspace (*stream.stream))
- stream.stream++;
-
- if (stream.stream != limit && *stream.stream != '\0')
- {
- SourceRef ref = { stream.stream - text,
- stream.stream - text };
-
- parser_set_error (error, &ref, NULL,
- "expected end of input");
- g_variant_unref (result);
-
- result = NULL;
- }
- }
- else
- *endptr = stream.stream;
- }
-
- ast_free (ast);
- }
-
- return result;
-}
-
-/**
- * g_variant_new_parsed_va:
- * @format: a text format #GVariant
- * @app: a pointer to a #va_list
- * @returns: a new, usually floating, #GVariant
- *
- * Parses @format and returns the result.
- *
- * This is the version of g_variant_new_parsed() intended to be used
- * from libraries.
- *
- * The return value will be floating if it was a newly created GVariant
- * instance. In the case that @format simply specified the collection
- * of a #GVariant pointer (eg: @format was "%*") then the collected
- * #GVariant pointer will be returned unmodified, without adding any
- * additional references.
- *
- * In order to behave correctly in all cases it is necessary for the
- * calling function to g_variant_ref_sink() the return result before
- * returning control to the user that originally provided the pointer.
- * At this point, the caller will have their own full reference to the
- * result. This can also be done by adding the result to a container,
- * or by passing it to another g_variant_new() call.
- **/
-GVariant *
-g_variant_new_parsed_va (const gchar *format,
- va_list *app)
-{
- TokenStream stream = { 0, };
- GVariant *result = NULL;
- GError *error = NULL;
- AST *ast;
-
- g_return_val_if_fail (format != NULL, NULL);
- g_return_val_if_fail (app != NULL, NULL);
-
- stream.start = format;
- stream.stream = format;
- stream.end = NULL;
-
- if ((ast = parse (&stream, app, &error)))
- {
- result = ast_resolve (ast, &error);
- ast_free (ast);
- }
-
- if (result == NULL)
- g_error ("g_variant_new_parsed: %s", error->message);
-
- if (*stream.stream)
- g_error ("g_variant_new_parsed: trailing text after value");
-
- return result;
-}
-
-/**
- * g_variant_new_parsed:
- * @format: a text format #GVariant
- * @...: arguments as per @format
- * @returns: a new floating #GVariant instance
- *
- * Parses @format and returns the result.
- *
- * @format must be a text format #GVariant with one extension: at any
- * point that a value may appear in the text, a '%' character followed
- * by a GVariant format string (as per g_variant_new()) may appear. In
- * that case, the same arguments are collected from the argument list as
- * g_variant_new() would have collected.
- *
- * Consider this simple example:
- *
- * <informalexample><programlisting>
- * g_variant_new_parsed ("[('one', 1), ('two', %i), (%s, 3)]", 2, "three");
- * </programlisting></informalexample>
- *
- * In the example, the variable argument parameters are collected and
- * filled in as if they were part of the original string to produce the
- * result of <code>[('one', 1), ('two', 2), ('three', 3)]</code>.
- *
- * This function is intended only to be used with @format as a string
- * literal. Any parse error is fatal to the calling process. If you
- * want to parse data from untrusted sources, use g_variant_parse().
- *
- * You may not use this function to return, unmodified, a single
- * #GVariant pointer from the argument list. ie: @format may not solely
- * be anything along the lines of "%*", "%?", "%r", or anything starting
- * with "%@".
- **/
-GVariant *
-g_variant_new_parsed (const gchar *format,
- ...)
-{
- GVariant *result;
- va_list ap;
-
- va_start (ap, format);
- result = g_variant_new_parsed_va (format, &ap);
- va_end (ap);
-
- return result;
-}
-
-/**
- * g_variant_builder_add_parsed:
- * @builder: a #GVariantBuilder
- * @format: a text format #GVariant
- * @...: arguments as per @format
- *
- * Adds to a #GVariantBuilder.
- *
- * This call is a convenience wrapper that is exactly equivalent to
- * calling g_variant_new_parsed() followed by
- * g_variant_builder_add_value().
- *
- * This function might be used as follows:
- *
- * <programlisting>
- * GVariant *
- * make_pointless_dictionary (void)
- * {
- * GVariantBuilder *builder;
- * int i;
- *
- * builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
- * g_variant_builder_add_parsed (builder, "{'width', <%i>}", 600);
- * g_variant_builder_add_parsed (builder, "{'title', <%s>}", "foo");
- * g_variant_builder_add_parsed (builder, "{'transparency', <0.5>}");
- * return g_variant_builder_end (builder);
- * }
- * </programlisting>
- *
- * Since: 2.26
- **/
-void
-g_variant_builder_add_parsed (GVariantBuilder *builder,
- const gchar *format,
- ...)
-{
- va_list ap;
-
- va_start (ap, format);
- g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap));
- va_end (ap);
-}
+++ /dev/null
-/*
- * Copyright © 2007, 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * 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 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-/* Prologue {{{1 */
-#include "config.h"
-
-#include "gvariant-serialiser.h"
-
-#include <glib/gtestutils.h>
-#include <glib/gstrfuncs.h>
-#include <glib/gtypes.h>
-
-#include <string.h>
-
-
-/* GVariantSerialiser
- *
- * After this prologue section, this file has roughly 2 parts.
- *
- * The first part is split up into sections according to various
- * container types. Maybe, Array, Tuple, Variant. The Maybe and Array
- * sections are subdivided for element types being fixed or
- * variable-sized types.
- *
- * Each section documents the format of that particular type of
- * container and implements 5 functions for dealing with it:
- *
- * n_children:
- * - determines (according to serialised data) how many child values
- * are inside a particular container value.
- *
- * get_child:
- * - gets the type of and the serialised data corresponding to a
- * given child value within the container value.
- *
- * needed_size:
- * - determines how much space would be required to serialise a
- * container of this type, containing the given children so that
- * buffers can be preallocated before serialising.
- *
- * serialise:
- * - write the serialised data for a container of this type,
- * containing the given children, to a buffer.
- *
- * is_normal:
- * - check the given data to ensure that it is in normal form. For a
- * given set of child values, there is exactly one normal form for
- * the serialised data of a container. Other forms are possible
- * while maintaining the same children (for example, by inserting
- * something other than zero bytes as padding) but only one form is
- * the normal form.
- *
- * The second part contains the main entry point for each of the above 5
- * functions and logic to dispatch it to the handler for the appropriate
- * container type code.
- *
- * The second part also contains a routine to byteswap serialised
- * values. This code makes use of the n_children() and get_child()
- * functions above to do its work so no extra support is needed on a
- * per-container-type basis.
- *
- * There is also additional code for checking for normal form. All
- * numeric types are always in normal form since the full range of
- * values is permitted (eg: 0 to 255 is a valid byte). Special checks
- * need to be performed for booleans (only 0 or 1 allowed), strings
- * (properly nul-terminated) and object paths and signature strings
- * (meeting the DBus specification requirements).
- */
-
-/* < private >
- * GVariantSerialised:
- * @type_info: the #GVariantTypeInfo of this value
- * @data: the serialised data of this value, or %NULL
- * @size: the size of this value
- *
- * A structure representing a GVariant in serialised form. This
- * structure is used with #GVariantSerialisedFiller functions and as the
- * primary interface to the serialiser. See #GVariantSerialisedFiller
- * for a description of its use there.
- *
- * When used with the serialiser API functions, the following invariants
- * apply to all #GVariantTypeSerialised structures passed to and
- * returned from the serialiser.
- *
- * @type_info must be non-%NULL.
- *
- * @data must be properly aligned for the type described by @type_info.
- *
- * If @type_info describes a fixed-sized type then @size must always be
- * equal to the fixed size of that type.
- *
- * For fixed-sized types (and only fixed-sized types), @data may be
- * %NULL even if @size is non-zero. This happens when a framing error
- * occurs while attempting to extract a fixed-sized value out of a
- * variable-sized container. There is no data to return for the
- * fixed-sized type, yet @size must be non-zero. The effect of this
- * combination should be as if @data were a pointer to an
- * appropriately-sized zero-filled region.
- */
-
-/* < private >
- * g_variant_serialised_check:
- * @serialised: a #GVariantSerialised struct
- *
- * Checks @serialised for validity according to the invariants described
- * above.
- */
-static void
-g_variant_serialised_check (GVariantSerialised serialised)
-{
- gsize fixed_size;
- guint alignment;
-
- g_assert (serialised.type_info != NULL);
- g_variant_type_info_query (serialised.type_info, &alignment, &fixed_size);
-
- if (fixed_size)
- g_assert_cmpint (serialised.size, ==, fixed_size);
- else
- g_assert (serialised.size == 0 || serialised.data != NULL);
-
- /* Depending on the native alignment requirements of the machine, the
- * compiler will insert either 3 or 7 padding bytes after the char.
- * This will result in the sizeof() the struct being 12 or 16.
- * Subtract 9 to get 3 or 7 which is a nice bitmask to apply to get
- * the alignment bits that we "care about" being zero: in the
- * 4-aligned case, we care about 2 bits, and in the 8-aligned case, we
- * care about 3 bits.
- */
- alignment &= sizeof (struct {
- char a;
- union {
- guint64 x;
- void *y;
- gdouble z;
- } b;
- }
- ) - 9;
-
- /* Some OSes (FreeBSD is a known example) have a malloc() that returns
- * unaligned memory if you request small sizes. 'malloc (1);', for
- * example, has been seen to return pointers aligned to 6 mod 16.
- *
- * Check if this is a small allocation and return without enforcing
- * the alignment assertion if this is the case.
- */
- if (serialised.size <= alignment)
- return;
-
- g_assert_cmpint (alignment & (gsize) serialised.data, ==, 0);
-}
-
-/* < private >
- * GVariantSerialisedFiller:
- * @serialised: a #GVariantSerialised instance to fill
- * @data: data from the children array
- *
- * This function is called back from g_variant_serialiser_needed_size()
- * and g_variant_serialiser_serialise(). It fills in missing details
- * from a partially-complete #GVariantSerialised.
- *
- * The @data parameter passed back to the function is one of the items
- * that was passed to the serialiser in the @children array. It
- * represents a single child item of the container that is being
- * serialised. The information filled in to @serialised is the
- * information for this child.
- *
- * If the @type_info field of @serialised is %NULL then the callback
- * function must set it to the type information corresponding to the
- * type of the child. No reference should be added. If it is non-%NULL
- * then the callback should assert that it is equal to the actual type
- * of the child.
- *
- * If the @size field is zero then the callback must fill it in with the
- * required amount of space to store the serialised form of the child.
- * If it is non-zero then the callback should assert that it is equal to
- * the needed size of the child.
- *
- * If @data is non-%NULL then it points to a space that is properly
- * aligned for and large enough to store the serialised data of the
- * child. The callback must store the serialised form of the child at
- * @data.
- *
- * If the child value is another container then the callback will likely
- * recurse back into the serialiser by calling
- * g_variant_serialiser_needed_size() to determine @size and
- * g_variant_serialiser_serialise() to write to @data.
- */
-
-/* PART 1: Container types {{{1
- *
- * This section contains the serialiser implementation functions for
- * each container type.
- */
-
-/* Maybe {{{2
- *
- * Maybe types are handled depending on if the element type of the maybe
- * type is a fixed-sized or variable-sized type. Although all maybe
- * types themselves are variable-sized types, herein, a maybe value with
- * a fixed-sized element type is called a "fixed-sized maybe" for
- * convenience and a maybe value with a variable-sized element type is
- * called a "variable-sized maybe".
- */
-
-/* Fixed-sized Maybe {{{3
- *
- * The size of a maybe value with a fixed-sized element type is either 0
- * or equal to the fixed size of its element type. The case where the
- * size of the maybe value is zero corresponds to the "Nothing" case and
- * the case where the size of the maybe value is equal to the fixed size
- * of the element type corresponds to the "Just" case; in that case, the
- * serialised data of the child value forms the entire serialised data
- * of the maybe value.
- *
- * In the event that a fixed-sized maybe value is presented with a size
- * that is not equal to the fixed size of the element type then the
- * value must be taken to be "Nothing".
- */
-
-static gsize
-gvs_fixed_sized_maybe_n_children (GVariantSerialised value)
-{
- gsize element_fixed_size;
-
- g_variant_type_info_query_element (value.type_info, NULL,
- &element_fixed_size);
-
- return (element_fixed_size == value.size) ? 1 : 0;
-}
-
-static GVariantSerialised
-gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
- gsize index_)
-{
- /* the child has the same bounds as the
- * container, so just update the type.
- */
- value.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_ref (value.type_info);
-
- return value;
-}
-
-static gsize
-gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- if (n_children)
- {
- gsize element_fixed_size;
-
- g_variant_type_info_query_element (type_info, NULL,
- &element_fixed_size);
-
- return element_fixed_size;
- }
- else
- return 0;
-}
-
-static void
-gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- if (n_children)
- {
- GVariantSerialised child = { NULL, value.data, value.size };
-
- gvs_filler (&child, children[0]);
- }
-}
-
-static gboolean
-gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
-{
- if (value.size > 0)
- {
- gsize element_fixed_size;
-
- g_variant_type_info_query_element (value.type_info,
- NULL, &element_fixed_size);
-
- if (value.size != element_fixed_size)
- return FALSE;
-
- /* proper element size: "Just". recurse to the child. */
- value.type_info = g_variant_type_info_element (value.type_info);
-
- return g_variant_serialised_is_normal (value);
- }
-
- /* size of 0: "Nothing" */
- return TRUE;
-}
-
-/* Variable-sized Maybe
- *
- * The size of a maybe value with a variable-sized element type is
- * either 0 or strictly greater than 0. The case where the size of the
- * maybe value is zero corresponds to the "Nothing" case and the case
- * where the size of the maybe value is greater than zero corresponds to
- * the "Just" case; in that case, the serialised data of the child value
- * forms the first part of the serialised data of the maybe value and is
- * followed by a single zero byte. This zero byte is always appended,
- * regardless of any zero bytes that may already be at the end of the
- * serialised ata of the child value.
- */
-
-static gsize
-gvs_variable_sized_maybe_n_children (GVariantSerialised value)
-{
- return (value.size > 0) ? 1 : 0;
-}
-
-static GVariantSerialised
-gvs_variable_sized_maybe_get_child (GVariantSerialised value,
- gsize index_)
-{
- /* remove the padding byte and update the type. */
- value.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_ref (value.type_info);
- value.size--;
-
- /* if it's zero-sized then it may as well be NULL */
- if (value.size == 0)
- value.data = NULL;
-
- return value;
-}
-
-static gsize
-gvs_variable_sized_maybe_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- if (n_children)
- {
- GVariantSerialised child = { 0, };
-
- gvs_filler (&child, children[0]);
-
- return child.size + 1;
- }
- else
- return 0;
-}
-
-static void
-gvs_variable_sized_maybe_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- if (n_children)
- {
- GVariantSerialised child = { NULL, value.data, value.size - 1 };
-
- /* write the data for the child. */
- gvs_filler (&child, children[0]);
- value.data[child.size] = '\0';
- }
-}
-
-static gboolean
-gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
-{
- if (value.size == 0)
- return TRUE;
-
- if (value.data[value.size - 1] != '\0')
- return FALSE;
-
- value.type_info = g_variant_type_info_element (value.type_info);
- value.size--;
-
- return g_variant_serialised_is_normal (value);
-}
-
-/* Arrays {{{2
- *
- * Just as with maybe types, array types are handled depending on if the
- * element type of the array type is a fixed-sized or variable-sized
- * type. Similar to maybe types, for convenience, an array value with a
- * fixed-sized element type is called a "fixed-sized array" and an array
- * value with a variable-sized element type is called a "variable sized
- * array".
- */
-
-/* Fixed-sized Array {{{3
- *
- * For fixed sized arrays, the serialised data is simply a concatenation
- * of the serialised data of each element, in order. Since fixed-sized
- * values always have a fixed size that is a multiple of their alignment
- * requirement no extra padding is required.
- *
- * In the event that a fixed-sized array is presented with a size that
- * is not an integer multiple of the element size then the value of the
- * array must be taken as being empty.
- */
-
-static gsize
-gvs_fixed_sized_array_n_children (GVariantSerialised value)
-{
- gsize element_fixed_size;
-
- g_variant_type_info_query_element (value.type_info, NULL,
- &element_fixed_size);
-
- if (value.size % element_fixed_size == 0)
- return value.size / element_fixed_size;
-
- return 0;
-}
-
-static GVariantSerialised
-gvs_fixed_sized_array_get_child (GVariantSerialised value,
- gsize index_)
-{
- GVariantSerialised child = { 0, };
-
- child.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_query (child.type_info, NULL, &child.size);
- child.data = value.data + (child.size * index_);
- g_variant_type_info_ref (child.type_info);
-
- return child;
-}
-
-static gsize
-gvs_fixed_sized_array_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- gsize element_fixed_size;
-
- g_variant_type_info_query_element (type_info, NULL, &element_fixed_size);
-
- return element_fixed_size * n_children;
-}
-
-static void
-gvs_fixed_sized_array_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- GVariantSerialised child = { 0, };
- gsize i;
-
- child.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_query (child.type_info, NULL, &child.size);
- child.data = value.data;
-
- for (i = 0; i < n_children; i++)
- {
- gvs_filler (&child, children[i]);
- child.data += child.size;
- }
-}
-
-static gboolean
-gvs_fixed_sized_array_is_normal (GVariantSerialised value)
-{
- GVariantSerialised child = { 0, };
-
- child.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_query (child.type_info, NULL, &child.size);
-
- if (value.size % child.size != 0)
- return FALSE;
-
- for (child.data = value.data;
- child.data < value.data + value.size;
- child.data += child.size)
- {
- if (!g_variant_serialised_is_normal (child))
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* Variable-sized Array {{{3
- *
- * Variable sized arrays, containing variable-sized elements, must be
- * able to determine the boundaries between the elements. The items
- * cannot simply be concatenated. Additionally, we are faced with the
- * fact that non-fixed-sized values do not neccessarily have a size that
- * is a multiple of their alignment requirement, so we may need to
- * insert zero-filled padding.
- *
- * While it is possible to find the start of an item by starting from
- * the end of the item before it and padding for alignment, it is not
- * generally possible to do the reverse operation. For this reason, we
- * record the end point of each element in the array.
- *
- * GVariant works in terms of "offsets". An offset is a pointer to a
- * boundary between two bytes. In 4 bytes of serialised data, there
- * would be 5 possible offsets: one at the start ('0'), one between each
- * pair of adjacent bytes ('1', '2', '3') and one at the end ('4').
- *
- * The numeric value of an offset is an unsigned integer given relative
- * to the start of the serialised data of the array. Offsets are always
- * stored in little endian byte order and are always only as big as they
- * need to be. For example, in 255 bytes of serialised data, there are
- * 256 offsets. All possibilities can be stored in an 8 bit unsigned
- * integer. In 256 bytes of serialised data, however, there are 257
- * possible offsets so 16 bit integers must be used. The size of an
- * offset is always a power of 2.
- *
- * The offsets are stored at the end of the serialised data of the
- * array. They are simply concatenated on without any particular
- * alignment. The size of the offsets is included in the size of the
- * serialised data for purposes of determining the size of the offsets.
- * This presents a possibly ambiguity; in certain cases, a particular
- * value of array could have two different serialised forms.
- *
- * Imagine an array containing a single string of 253 bytes in length
- * (so, 254 bytes including the nul terminator). Now the offset must be
- * written. If an 8 bit offset is written, it will bring the size of
- * the array's serialised data to 255 -- which means that the use of an
- * 8 bit offset was valid. If a 16 bit offset is used then the total
- * size of the array will be 256 -- which means that the use of a 16 bit
- * offset was valid. Although both of these will be accepted by the
- * deserialiser, only the smaller of the two is considered to be in
- * normal form and that is the one that the serialiser must produce.
- */
-
-static inline gsize
-gvs_read_unaligned_le (guchar *bytes,
- guint size)
-{
- union
- {
- guchar bytes[GLIB_SIZEOF_SIZE_T];
- gsize integer;
- } tmpvalue;
-
- tmpvalue.integer = 0;
- memcpy (&tmpvalue.bytes, bytes, size);
-
- return GSIZE_FROM_LE (tmpvalue.integer);
-}
-
-static inline void
-gvs_write_unaligned_le (guchar *bytes,
- gsize value,
- guint size)
-{
- union
- {
- guchar bytes[GLIB_SIZEOF_SIZE_T];
- gsize integer;
- } tmpvalue;
-
- tmpvalue.integer = GSIZE_TO_LE (value);
- memcpy (bytes, &tmpvalue.bytes, size);
-}
-
-static guint
-gvs_get_offset_size (gsize size)
-{
- if (size > G_MAXUINT32)
- return 8;
-
- else if (size > G_MAXUINT16)
- return 4;
-
- else if (size > G_MAXUINT8)
- return 2;
-
- else if (size > 0)
- return 1;
-
- return 0;
-}
-
-static gsize
-gvs_calculate_total_size (gsize body_size,
- gsize offsets)
-{
- if (body_size + 1 * offsets <= G_MAXUINT8)
- return body_size + 1 * offsets;
-
- if (body_size + 2 * offsets <= G_MAXUINT16)
- return body_size + 2 * offsets;
-
- if (body_size + 4 * offsets <= G_MAXUINT32)
- return body_size + 4 * offsets;
-
- return body_size + 8 * offsets;
-}
-
-static gsize
-gvs_variable_sized_array_n_children (GVariantSerialised value)
-{
- gsize offsets_array_size;
- gsize offset_size;
- gsize last_end;
-
- if (value.size == 0)
- return 0;
-
- offset_size = gvs_get_offset_size (value.size);
-
- last_end = gvs_read_unaligned_le (value.data + value.size -
- offset_size, offset_size);
-
- if (last_end > value.size)
- return 0;
-
- offsets_array_size = value.size - last_end;
-
- if (offsets_array_size % offset_size)
- return 0;
-
- return offsets_array_size / offset_size;
-}
-
-static GVariantSerialised
-gvs_variable_sized_array_get_child (GVariantSerialised value,
- gsize index_)
-{
- GVariantSerialised child = { 0, };
- gsize offset_size;
- gsize last_end;
- gsize start;
- gsize end;
-
- child.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_ref (child.type_info);
-
- offset_size = gvs_get_offset_size (value.size);
-
- last_end = gvs_read_unaligned_le (value.data + value.size -
- offset_size, offset_size);
-
- if (index_ > 0)
- {
- guint alignment;
-
- start = gvs_read_unaligned_le (value.data + last_end +
- (offset_size * (index_ - 1)),
- offset_size);
-
- g_variant_type_info_query (child.type_info, &alignment, NULL);
- start += (-start) & alignment;
- }
- else
- start = 0;
-
- end = gvs_read_unaligned_le (value.data + last_end +
- (offset_size * index_),
- offset_size);
-
- if (start < end && end <= value.size)
- {
- child.data = value.data + start;
- child.size = end - start;
- }
-
- return child;
-}
-
-static gsize
-gvs_variable_sized_array_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- guint alignment;
- gsize offset;
- gsize i;
-
- g_variant_type_info_query (type_info, &alignment, NULL);
- offset = 0;
-
- for (i = 0; i < n_children; i++)
- {
- GVariantSerialised child = { 0, };
-
- offset += (-offset) & alignment;
- gvs_filler (&child, children[i]);
- offset += child.size;
- }
-
- return gvs_calculate_total_size (offset, n_children);
-}
-
-static void
-gvs_variable_sized_array_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- guchar *offset_ptr;
- gsize offset_size;
- guint alignment;
- gsize offset;
- gsize i;
-
- g_variant_type_info_query (value.type_info, &alignment, NULL);
- offset_size = gvs_get_offset_size (value.size);
- offset = 0;
-
- offset_ptr = value.data + value.size - offset_size * n_children;
-
- for (i = 0; i < n_children; i++)
- {
- GVariantSerialised child = { 0, };
-
- while (offset & alignment)
- value.data[offset++] = '\0';
-
- child.data = value.data + offset;
- gvs_filler (&child, children[i]);
- offset += child.size;
-
- gvs_write_unaligned_le (offset_ptr, offset, offset_size);
- offset_ptr += offset_size;
- }
-}
-
-static gboolean
-gvs_variable_sized_array_is_normal (GVariantSerialised value)
-{
- GVariantSerialised child = { 0, };
- gsize offsets_array_size;
- guchar *offsets_array;
- guint offset_size;
- guint alignment;
- gsize last_end;
- gsize length;
- gsize offset;
- gsize i;
-
- if (value.size == 0)
- return TRUE;
-
- offset_size = gvs_get_offset_size (value.size);
- last_end = gvs_read_unaligned_le (value.data + value.size -
- offset_size, offset_size);
-
- if (last_end > value.size)
- return FALSE;
-
- offsets_array_size = value.size - last_end;
-
- if (offsets_array_size % offset_size)
- return FALSE;
-
- offsets_array = value.data + value.size - offsets_array_size;
- length = offsets_array_size / offset_size;
-
- if (length == 0)
- return FALSE;
-
- child.type_info = g_variant_type_info_element (value.type_info);
- g_variant_type_info_query (child.type_info, &alignment, NULL);
- offset = 0;
-
- for (i = 0; i < length; i++)
- {
- gsize this_end;
-
- this_end = gvs_read_unaligned_le (offsets_array + offset_size * i,
- offset_size);
-
- if (this_end < offset || this_end > last_end)
- return FALSE;
-
- while (offset & alignment)
- {
- if (!(offset < this_end && value.data[offset] == '\0'))
- return FALSE;
- offset++;
- }
-
- child.data = value.data + offset;
- child.size = this_end - offset;
-
- if (child.size == 0)
- child.data = NULL;
-
- if (!g_variant_serialised_is_normal (child))
- return FALSE;
-
- offset = this_end;
- }
-
- g_assert (offset == last_end);
-
- return TRUE;
-}
-
-/* Tuples {{{2
- *
- * Since tuples can contain a mix of variable- and fixed-sized items,
- * they are, in terms of serialisation, a hybrid of variable-sized and
- * fixed-sized arrays.
- *
- * Offsets are only stored for variable-sized items. Also, since the
- * number of items in a tuple is known from its type, we are able to
- * know exactly how many offsets to expect in the serialised data (and
- * therefore how much space is taken up by the offset array). This
- * means that we know where the end of the serialised data for the last
- * item is -- we can just subtract the size of the offset array from the
- * total size of the tuple. For this reason, the last item in the tuple
- * doesn't need an offset stored.
- *
- * Tuple offsets are stored in reverse. This design choice allows
- * iterator-based deserialisers to be more efficient.
- *
- * Most of the "heavy lifting" here is handled by the GVariantTypeInfo
- * for the tuple. See the notes in gvarianttypeinfo.h.
- */
-
-static gsize
-gvs_tuple_n_children (GVariantSerialised value)
-{
- return g_variant_type_info_n_members (value.type_info);
-}
-
-static GVariantSerialised
-gvs_tuple_get_child (GVariantSerialised value,
- gsize index_)
-{
- const GVariantMemberInfo *member_info;
- GVariantSerialised child = { 0, };
- gsize offset_size;
- gsize start, end;
-
- member_info = g_variant_type_info_member_info (value.type_info, index_);
- child.type_info = g_variant_type_info_ref (member_info->type_info);
- offset_size = gvs_get_offset_size (value.size);
-
- /* tuples are the only (potentially) fixed-sized containers, so the
- * only ones that have to deal with the possibility of having %NULL
- * data with a non-zero %size if errors occured elsewhere.
- */
- if G_UNLIKELY (value.data == NULL && value.size != 0)
- {
- g_variant_type_info_query (child.type_info, NULL, &child.size);
-
- /* this can only happen in fixed-sized tuples,
- * so the child must also be fixed sized.
- */
- g_assert (child.size != 0);
- child.data = NULL;
-
- return child;
- }
-
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
- {
- if (offset_size * (member_info->i + 2) > value.size)
- return child;
- }
- else
- {
- if (offset_size * (member_info->i + 1) > value.size)
- {
- /* if the child is fixed size, return its size.
- * if child is not fixed-sized, return size = 0.
- */
- g_variant_type_info_query (child.type_info, NULL, &child.size);
-
- return child;
- }
- }
-
- if (member_info->i + 1)
- start = gvs_read_unaligned_le (value.data + value.size -
- offset_size * (member_info->i + 1),
- offset_size);
- else
- start = 0;
-
- start += member_info->a;
- start &= member_info->b;
- start |= member_info->c;
-
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
- end = value.size - offset_size * (member_info->i + 1);
-
- else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
- {
- gsize fixed_size;
-
- g_variant_type_info_query (child.type_info, NULL, &fixed_size);
- end = start + fixed_size;
- child.size = fixed_size;
- }
-
- else /* G_VARIANT_MEMEBER_ENDING_OFFSET */
- end = gvs_read_unaligned_le (value.data + value.size -
- offset_size * (member_info->i + 2),
- offset_size);
-
- if (start < end && end <= value.size)
- {
- child.data = value.data + start;
- child.size = end - start;
- }
-
- return child;
-}
-
-static gsize
-gvs_tuple_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- const GVariantMemberInfo *member_info = NULL;
- gsize fixed_size;
- gsize offset;
- gsize i;
-
- g_variant_type_info_query (type_info, NULL, &fixed_size);
-
- if (fixed_size)
- return fixed_size;
-
- offset = 0;
-
- for (i = 0; i < n_children; i++)
- {
- guint alignment;
-
- member_info = g_variant_type_info_member_info (type_info, i);
- g_variant_type_info_query (member_info->type_info,
- &alignment, &fixed_size);
- offset += (-offset) & alignment;
-
- if (fixed_size)
- offset += fixed_size;
- else
- {
- GVariantSerialised child = { 0, };
-
- gvs_filler (&child, children[i]);
- offset += child.size;
- }
- }
-
- return gvs_calculate_total_size (offset, member_info->i + 1);
-}
-
-static void
-gvs_tuple_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- gsize offset_size;
- gsize offset;
- gsize i;
-
- offset_size = gvs_get_offset_size (value.size);
- offset = 0;
-
- for (i = 0; i < n_children; i++)
- {
- const GVariantMemberInfo *member_info;
- GVariantSerialised child = { 0, };
- guint alignment;
-
- member_info = g_variant_type_info_member_info (value.type_info, i);
- g_variant_type_info_query (member_info->type_info, &alignment, NULL);
-
- while (offset & alignment)
- value.data[offset++] = '\0';
-
- child.data = value.data + offset;
- gvs_filler (&child, children[i]);
- offset += child.size;
-
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
- {
- value.size -= offset_size;
- gvs_write_unaligned_le (value.data + value.size,
- offset, offset_size);
- }
- }
-
- while (offset < value.size)
- value.data[offset++] = '\0';
-}
-
-static gboolean
-gvs_tuple_is_normal (GVariantSerialised value)
-{
- guint offset_size;
- gsize offset_ptr;
- gsize length;
- gsize offset;
- gsize i;
-
- offset_size = gvs_get_offset_size (value.size);
- length = g_variant_type_info_n_members (value.type_info);
- offset_ptr = value.size;
- offset = 0;
-
- for (i = 0; i < length; i++)
- {
- const GVariantMemberInfo *member_info;
- GVariantSerialised child;
- gsize fixed_size;
- guint alignment;
- gsize end;
-
- member_info = g_variant_type_info_member_info (value.type_info, i);
- child.type_info = member_info->type_info;
-
- g_variant_type_info_query (child.type_info, &alignment, &fixed_size);
-
- while (offset & alignment)
- {
- if (offset > value.size || value.data[offset] != '\0')
- return FALSE;
- offset++;
- }
-
- child.data = value.data + offset;
-
- switch (member_info->ending_type)
- {
- case G_VARIANT_MEMBER_ENDING_FIXED:
- end = offset + fixed_size;
- break;
-
- case G_VARIANT_MEMBER_ENDING_LAST:
- end = offset_ptr;
- break;
-
- case G_VARIANT_MEMBER_ENDING_OFFSET:
- offset_ptr -= offset_size;
-
- if (offset_ptr < offset)
- return FALSE;
-
- end = gvs_read_unaligned_le (value.data + offset_ptr, offset_size);
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- if (end < offset || end > offset_ptr)
- return FALSE;
-
- child.size = end - offset;
-
- if (child.size == 0)
- child.data = NULL;
-
- if (!g_variant_serialised_is_normal (child))
- return FALSE;
-
- offset = end;
- }
-
- {
- gsize fixed_size;
- guint alignment;
-
- g_variant_type_info_query (value.type_info, &alignment, &fixed_size);
-
- if (fixed_size)
- {
- g_assert (fixed_size == value.size);
- g_assert (offset_ptr == value.size);
-
- if (i == 0)
- {
- if (value.data[offset++] != '\0')
- return FALSE;
- }
- else
- {
- while (offset & alignment)
- if (value.data[offset++] != '\0')
- return FALSE;
- }
-
- g_assert (offset == value.size);
- }
- }
-
- return offset_ptr == offset;
-}
-
-/* Variants {{{2
- *
- * Variants are stored by storing the serialised data of the child,
- * followed by a '\0' character, followed by the type string of the
- * child.
- *
- * In the case that a value is presented that contains no '\0'
- * character, or doesn't have a single well-formed definite type string
- * following that character, the variant must be taken as containing the
- * unit tuple: ().
- */
-
-static inline gsize
-gvs_variant_n_children (GVariantSerialised value)
-{
- return 1;
-}
-
-static inline GVariantSerialised
-gvs_variant_get_child (GVariantSerialised value,
- gsize index_)
-{
- GVariantSerialised child = { 0, };
-
- /* NOTE: not O(1) and impossible for it to be... */
- if (value.size)
- {
- /* find '\0' character */
- for (child.size = value.size - 1; child.size; child.size--)
- if (value.data[child.size] == '\0')
- break;
-
- /* ensure we didn't just hit the start of the string */
- if (value.data[child.size] == '\0')
- {
- const gchar *type_string = (gchar *) &value.data[child.size + 1];
- const gchar *limit = (gchar *) &value.data[value.size];
- const gchar *end;
-
- if (g_variant_type_string_scan (type_string, limit, &end) &&
- end == limit)
- {
- const GVariantType *type = (GVariantType *) type_string;
-
- if (g_variant_type_is_definite (type))
- {
- gsize fixed_size;
-
- child.type_info = g_variant_type_info_get (type);
-
- if (child.size != 0)
- /* only set to non-%NULL if size > 0 */
- child.data = value.data;
-
- g_variant_type_info_query (child.type_info,
- NULL, &fixed_size);
-
- if (!fixed_size || fixed_size == child.size)
- return child;
-
- g_variant_type_info_unref (child.type_info);
- }
- }
- }
- }
-
- child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
- child.data = NULL;
- child.size = 1;
-
- return child;
-}
-
-static inline gsize
-gvs_variant_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- GVariantSerialised child = { 0, };
- const gchar *type_string;
-
- gvs_filler (&child, children[0]);
- type_string = g_variant_type_info_get_type_string (child.type_info);
-
- return child.size + 1 + strlen (type_string);
-}
-
-static inline void
-gvs_variant_serialise (GVariantSerialised value,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- GVariantSerialised child = { 0, };
- const gchar *type_string;
-
- child.data = value.data;
-
- gvs_filler (&child, children[0]);
- type_string = g_variant_type_info_get_type_string (child.type_info);
- value.data[child.size] = '\0';
- memcpy (value.data + child.size + 1, type_string, strlen (type_string));
-}
-
-static inline gboolean
-gvs_variant_is_normal (GVariantSerialised value)
-{
- GVariantSerialised child;
- gboolean normal;
-
- child = gvs_variant_get_child (value, 0);
-
- normal = (child.data != NULL || child.size == 0) &&
- g_variant_serialised_is_normal (child);
-
- g_variant_type_info_unref (child.type_info);
-
- return normal;
-}
-
-
-
-/* PART 2: Serialiser API {{{1
- *
- * This is the implementation of the API of the serialiser as advertised
- * in gvariant-serialiser.h.
- */
-
-/* Dispatch Utilities {{{2
- *
- * These macros allow a given function (for example,
- * g_variant_serialiser_serialise) to be dispatched to the appropriate
- * type-specific function above (fixed/variable-sized maybe,
- * fixed/variable-sized array, tuple or variant).
- */
-#define DISPATCH_FIXED(type_info, before, after) \
- { \
- gsize fixed_size; \
- \
- g_variant_type_info_query_element (type_info, NULL, \
- &fixed_size); \
- \
- if (fixed_size) \
- { \
- before ## fixed_sized ## after \
- } \
- else \
- { \
- before ## variable_sized ## after \
- } \
- }
-
-#define DISPATCH_CASES(type_info, before, after) \
- switch (g_variant_type_info_get_type_char (type_info)) \
- { \
- case G_VARIANT_TYPE_INFO_CHAR_MAYBE: \
- DISPATCH_FIXED (type_info, before, _maybe ## after) \
- \
- case G_VARIANT_TYPE_INFO_CHAR_ARRAY: \
- DISPATCH_FIXED (type_info, before, _array ## after) \
- \
- case G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY: \
- case G_VARIANT_TYPE_INFO_CHAR_TUPLE: \
- { \
- before ## tuple ## after \
- } \
- \
- case G_VARIANT_TYPE_INFO_CHAR_VARIANT: \
- { \
- before ## variant ## after \
- } \
- }
-
-/* Serialiser entry points {{{2
- *
- * These are the functions that are called in order for the serialiser
- * to do its thing.
- */
-
-/* < private >
- * g_variant_serialised_n_children:
- * @serialised: a #GVariantSerialised
- * @returns: the number of children
- *
- * For serialised data that represents a container value (maybes,
- * tuples, arrays, variants), determine how many child items are inside
- * that container.
- */
-gsize
-g_variant_serialised_n_children (GVariantSerialised serialised)
-{
- g_variant_serialised_check (serialised);
-
- DISPATCH_CASES (serialised.type_info,
-
- return gvs_/**/,/**/_n_children (serialised);
-
- )
- g_assert_not_reached ();
-}
-
-/* < private >
- * g_variant_serialised_get_child:
- * @serialised: a #GVariantSerialised
- * @index_: the index of the child to fetch
- * @returns: a #GVariantSerialised for the child
- *
- * Extracts a child from a serialised data representing a container
- * value.
- *
- * It is an error to call this function with an index out of bounds.
- *
- * If the result .data == %NULL and .size > 0 then there has been an
- * error extracting the requested fixed-sized value. This number of
- * zero bytes needs to be allocated instead.
- *
- * In the case that .data == %NULL and .size == 0 then a zero-sized
- * item of a variable-sized type is being returned.
- *
- * .data is never non-%NULL if size is 0.
- */
-GVariantSerialised
-g_variant_serialised_get_child (GVariantSerialised serialised,
- gsize index_)
-{
- GVariantSerialised child;
-
- g_variant_serialised_check (serialised);
-
- if G_LIKELY (index_ < g_variant_serialised_n_children (serialised))
- {
- DISPATCH_CASES (serialised.type_info,
-
- child = gvs_/**/,/**/_get_child (serialised, index_);
- g_assert (child.size || child.data == NULL);
- g_variant_serialised_check (child);
- return child;
-
- )
- g_assert_not_reached ();
- }
-
- g_error ("Attempt to access item %"G_GSIZE_FORMAT
- " in a container with only %"G_GSIZE_FORMAT" items",
- index_, g_variant_serialised_n_children (serialised));
-}
-
-/* < private >
- * g_variant_serialiser_serialise:
- * @serialised: a #GVariantSerialised, properly set up
- * @gvs_filler: the filler function
- * @children: an array of child items
- * @n_children: the size of @children
- *
- * Writes data in serialised form.
- *
- * The type_info field of @serialised must be filled in to type info for
- * the type that we are serialising.
- *
- * The size field of @serialised must be filled in with the value
- * returned by a previous call to g_variant_serialiser_needed_size().
- *
- * The data field of @serialised must be a pointer to a properly-aligned
- * memory region large enough to serialise into (ie: at least as big as
- * the size field).
- *
- * This function is only resonsible for serialising the top-level
- * container. @gvs_filler is called on each child of the container in
- * order for all of the data of that child to be filled in.
- */
-void
-g_variant_serialiser_serialise (GVariantSerialised serialised,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- g_variant_serialised_check (serialised);
-
- DISPATCH_CASES (serialised.type_info,
-
- gvs_/**/,/**/_serialise (serialised, gvs_filler,
- children, n_children);
- return;
-
- )
- g_assert_not_reached ();
-}
-
-/* < private >
- * g_variant_serialiser_needed_size:
- * @type_info: the type to serialise for
- * @gvs_filler: the filler function
- * @children: an array of child items
- * @n_children: the size of @children
- *
- * Determines how much memory would be needed to serialise this value.
- *
- * This function is only resonsible for performing calculations for the
- * top-level container. @gvs_filler is called on each child of the
- * container in order to determine its size.
- */
-gsize
-g_variant_serialiser_needed_size (GVariantTypeInfo *type_info,
- GVariantSerialisedFiller gvs_filler,
- const gpointer *children,
- gsize n_children)
-{
- DISPATCH_CASES (type_info,
-
- return gvs_/**/,/**/_needed_size (type_info, gvs_filler,
- children, n_children);
-
- )
- g_assert_not_reached ();
-}
-
-/* Byteswapping {{{2 */
-
-/* < private >
- * g_variant_serialised_byteswap:
- * @value: a #GVariantSerialised
- *
- * Byte-swap serialised data. The result of this function is only
- * well-defined if the data is in normal form.
- */
-void
-g_variant_serialised_byteswap (GVariantSerialised serialised)
-{
- gsize fixed_size;
- guint alignment;
-
- g_variant_serialised_check (serialised);
-
- if (!serialised.data)
- return;
-
- /* the types we potentially need to byteswap are
- * exactly those with alignment requirements.
- */
- g_variant_type_info_query (serialised.type_info, &alignment, &fixed_size);
- if (!alignment)
- return;
-
- /* if fixed size and alignment are equal then we are down
- * to the base integer type and we should swap it. the
- * only exception to this is if we have a tuple with a
- * single item, and then swapping it will be OK anyway.
- */
- if (alignment + 1 == fixed_size)
- {
- switch (fixed_size)
- {
- case 2:
- {
- guint16 *ptr = (guint16 *) serialised.data;
-
- g_assert_cmpint (serialised.size, ==, 2);
- *ptr = GUINT16_SWAP_LE_BE (*ptr);
- }
- return;
-
- case 4:
- {
- guint32 *ptr = (guint32 *) serialised.data;
-
- g_assert_cmpint (serialised.size, ==, 4);
- *ptr = GUINT32_SWAP_LE_BE (*ptr);
- }
- return;
-
- case 8:
- {
- guint64 *ptr = (guint64 *) serialised.data;
-
- g_assert_cmpint (serialised.size, ==, 8);
- *ptr = GUINT64_SWAP_LE_BE (*ptr);
- }
- return;
-
- default:
- g_assert_not_reached ();
- }
- }
-
- /* else, we have a container that potentially contains
- * some children that need to be byteswapped.
- */
- else
- {
- gsize children, i;
-
- children = g_variant_serialised_n_children (serialised);
- for (i = 0; i < children; i++)
- {
- GVariantSerialised child;
-
- child = g_variant_serialised_get_child (serialised, i);
- g_variant_serialised_byteswap (child);
- g_variant_type_info_unref (child.type_info);
- }
- }
-}
-
-/* Normal form checking {{{2 */
-
-/* < private >
- * g_variant_serialised_is_normal:
- * @serialised: a #GVariantSerialised
- *
- * Determines, recursively if @serialised is in normal form. There is
- * precisely one normal form of serialised data for each possible value.
- *
- * It is possible that multiple byte sequences form the serialised data
- * for a given value if, for example, the padding bytes are filled in
- * with something other than zeros, but only one form is the normal
- * form.
- */
-gboolean
-g_variant_serialised_is_normal (GVariantSerialised serialised)
-{
- DISPATCH_CASES (serialised.type_info,
-
- return gvs_/**/,/**/_is_normal (serialised);
-
- )
-
- if (serialised.data == NULL)
- return FALSE;
-
- /* some hard-coded terminal cases */
- switch (g_variant_type_info_get_type_char (serialised.type_info))
- {
- case 'b': /* boolean */
- return serialised.data[0] < 2;
-
- case 's': /* string */
- return g_variant_serialiser_is_string (serialised.data,
- serialised.size);
-
- case 'o':
- return g_variant_serialiser_is_object_path (serialised.data,
- serialised.size);
-
- case 'g':
- return g_variant_serialiser_is_signature (serialised.data,
- serialised.size);
-
- default:
- /* all of the other types are fixed-sized numerical types for
- * which all possible values are valid (including various NaN
- * representations for floating point values).
- */
- return TRUE;
- }
-}
-
-/* Validity-checking functions {{{2
- *
- * Checks if strings, object paths and signature strings are valid.
- */
-
-/* < private >
- * g_variant_serialiser_is_string:
- * @data: a possible string
- * @size: the size of @data
- *
- * Ensures that @data is a valid string with a nul terminator at the end
- * and no nul bytes embedded.
- */
-gboolean
-g_variant_serialiser_is_string (gconstpointer data,
- gsize size)
-{
- const gchar *end;
-
- g_utf8_validate (data, size, &end);
-
- return data == end - (size - 1);
-}
-
-/* < private >
- * g_variant_serialiser_is_object_path:
- * @data: a possible DBus object path
- * @size: the size of @data
- *
- * Performs the checks for being a valid string.
- *
- * Also, ensures that @data is a valid DBus object path, as per the DBus
- * specification.
- */
-gboolean
-g_variant_serialiser_is_object_path (gconstpointer data,
- gsize size)
-{
- const gchar *string = data;
- gsize i;
-
- if (!g_variant_serialiser_is_string (data, size))
- return FALSE;
-
- /* The path must begin with an ASCII '/' (integer 47) character */
- if (string[0] != '/')
- return FALSE;
-
- for (i = 1; string[i]; i++)
- /* Each element must only contain the ASCII characters
- * "[A-Z][a-z][0-9]_"
- */
- if (g_ascii_isalnum (string[i]) || string[i] == '_')
- ;
-
- /* must consist of elements separated by slash characters. */
- else if (string[i] == '/')
- {
- /* No element may be the empty string. */
- /* Multiple '/' characters cannot occur in sequence. */
- if (string[i - 1] == '/')
- return FALSE;
- }
-
- else
- return FALSE;
-
- /* A trailing '/' character is not allowed unless the path is the
- * root path (a single '/' character).
- */
- if (i > 1 && string[i - 1] == '/')
- return FALSE;
-
- return TRUE;
-}
-
-/* < private >
- * g_variant_serialiser_is_signature:
- * @data: a possible DBus signature
- * @size: the size of @data
- *
- * Performs the checks for being a valid string.
- *
- * Also, ensures that @data is a valid DBus type signature, as per the
- * DBus specification.
- */
-gboolean
-g_variant_serialiser_is_signature (gconstpointer data,
- gsize size)
-{
- const gchar *string = data;
- gsize first_invalid;
-
- if (!g_variant_serialiser_is_string (data, size))
- return FALSE;
-
- /* make sure no non-definite characters appear */
- first_invalid = strspn (string, "ybnqiuxthdvasog(){}");
- if (string[first_invalid])
- return FALSE;
-
- /* make sure each type string is well-formed */
- while (*string)
- if (!g_variant_type_string_scan (string, NULL, &string))
- return FALSE;
-
- return TRUE;
-}
-
-/* Epilogue {{{1 */
-/* vim:set foldmethod=marker: */
+++ /dev/null
-/*
- * Copyright © 2007, 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-/* Prologue {{{1 */
-
-#include "config.h"
-
-#include <glib/gvariant-serialiser.h>
-#include "gvariant-internal.h"
-#include <glib/gvariant-core.h>
-#include <glib/gtestutils.h>
-#include <glib/gstrfuncs.h>
-#include <glib/ghash.h>
-#include <glib/gmem.h>
-
-#include <string.h>
-
-
-/**
- * SECTION: gvariant
- * @title: GVariant
- * @short_description: strongly typed value datatype
- * @see_also: GVariantType
- *
- * #GVariant is a variant datatype; it stores a value along with
- * information about the type of that value. The range of possible
- * values is determined by the type. The type system used by #GVariant
- * is #GVariantType.
- *
- * #GVariant instances always have a type and a value (which are given
- * at construction time). The type and value of a #GVariant instance
- * can never change other than by the #GVariant itself being
- * destroyed. A #GVariant can not contain a pointer.
- *
- * #GVariant is reference counted using g_variant_ref() and
- * g_variant_unref(). #GVariant also has floating reference counts --
- * see g_variant_ref_sink().
- *
- * #GVariant is completely threadsafe. A #GVariant instance can be
- * concurrently accessed in any way from any number of threads without
- * problems.
- *
- * #GVariant is heavily optimised for dealing with data in serialised
- * form. It works particularly well with data located in memory-mapped
- * files. It can perform nearly all deserialisation operations in a
- * small constant time, usually touching only a single memory page.
- * Serialised #GVariant data can also be sent over the network.
- *
- * #GVariant is largely compatible with DBus. Almost all types of
- * #GVariant instances can be sent over DBus. See #GVariantType for
- * exceptions.
- *
- * For convenience to C programmers, #GVariant features powerful
- * varargs-based value construction and destruction. This feature is
- * designed to be embedded in other libraries.
- *
- * There is a Python-inspired text language for describing #GVariant
- * values. #GVariant includes a printer for this language and a parser
- * with type inferencing.
- *
- * <refsect2>
- * <title>Memory Use</title>
- * <para>
- * #GVariant tries to be quite efficient with respect to memory use.
- * This section gives a rough idea of how much memory is used by the
- * current implementation. The information here is subject to change
- * in the future.
- * </para>
- * <para>
- * The memory allocated by #GVariant can be grouped into 4 broad
- * purposes: memory for serialised data, memory for the type
- * information cache, buffer management memory and memory for the
- * #GVariant structure itself.
- * </para>
- * <refsect3>
- * <title>Serialised Data Memory</title>
- * <para>
- * This is the memory that is used for storing GVariant data in
- * serialised form. This is what would be sent over the network or
- * what would end up on disk.
- * </para>
- * <para>
- * The amount of memory required to store a boolean is 1 byte. 16,
- * 32 and 64 bit integers and double precision floating point numbers
- * use their "natural" size. Strings (including object path and
- * signature strings) are stored with a nul terminator, and as such
- * use the length of the string plus 1 byte.
- * </para>
- * <para>
- * Maybe types use no space at all to represent the null value and
- * use the same amount of space (sometimes plus one byte) as the
- * equivalent non-maybe-typed value to represent the non-null case.
- * </para>
- * <para>
- * Arrays use the amount of space required to store each of their
- * members, concatenated. Additionally, if the items stored in an
- * array are not of a fixed-size (ie: strings, other arrays, etc)
- * then an additional framing offset is stored for each item. The
- * size of this offset is either 1, 2 or 4 bytes depending on the
- * overall size of the container. Additionally, extra padding bytes
- * are added as required for alignment of child values.
- * </para>
- * <para>
- * Tuples (including dictionary entries) use the amount of space
- * required to store each of their members, concatenated, plus one
- * framing offset (as per arrays) for each non-fixed-sized item in
- * the tuple, except for the last one. Additionally, extra padding
- * bytes are added as required for alignment of child values.
- * </para>
- * <para>
- * Variants use the same amount of space as the item inside of the
- * variant, plus 1 byte, plus the length of the type string for the
- * item inside the variant.
- * </para>
- * <para>
- * As an example, consider a dictionary mapping strings to variants.
- * In the case that the dictionary is empty, 0 bytes are required for
- * the serialisation.
- * </para>
- * <para>
- * If we add an item "width" that maps to the int32 value of 500 then
- * we will use 4 byte to store the int32 (so 6 for the variant
- * containing it) and 6 bytes for the string. The variant must be
- * aligned to 8 after the 6 bytes of the string, so that's 2 extra
- * bytes. 6 (string) + 2 (padding) + 6 (variant) is 14 bytes used
- * for the dictionary entry. An additional 1 byte is added to the
- * array as a framing offset making a total of 15 bytes.
- * </para>
- * <para>
- * If we add another entry, "title" that maps to a nullable string
- * that happens to have a value of null, then we use 0 bytes for the
- * null value (and 3 bytes for the variant to contain it along with
- * its type string) plus 6 bytes for the string. Again, we need 2
- * padding bytes. That makes a total of 6 + 2 + 3 = 11 bytes.
- * </para>
- * <para>
- * We now require extra padding between the two items in the array.
- * After the 14 bytes of the first item, that's 2 bytes required. We
- * now require 2 framing offsets for an extra two bytes. 14 + 2 + 11
- * + 2 = 29 bytes to encode the entire two-item dictionary.
- * </para>
- * </refsect3>
- * <refsect3>
- * <title>Type Information Cache</title>
- * <para>
- * For each GVariant type that currently exists in the program a type
- * information structure is kept in the type information cache. The
- * type information structure is required for rapid deserialisation.
- * </para>
- * <para>
- * Continuing with the above example, if a #GVariant exists with the
- * type "a{sv}" then a type information struct will exist for
- * "a{sv}", "{sv}", "s", and "v". Multiple uses of the same type
- * will share the same type information. Additionally, all
- * single-digit types are stored in read-only static memory and do
- * not contribute to the writable memory footprint of a program using
- * #GVariant.
- * </para>
- * <para>
- * Aside from the type information structures stored in read-only
- * memory, there are two forms of type information. One is used for
- * container types where there is a single element type: arrays and
- * maybe types. The other is used for container types where there
- * are multiple element types: tuples and dictionary entries.
- * </para>
- * <para>
- * Array type info structures are 6 * sizeof (void *), plus the
- * memory required to store the type string itself. This means that
- * on 32bit systems, the cache entry for "a{sv}" would require 30
- * bytes of memory (plus malloc overhead).
- * </para>
- * <para>
- * Tuple type info structures are 6 * sizeof (void *), plus 4 *
- * sizeof (void *) for each item in the tuple, plus the memory
- * required to store the type string itself. A 2-item tuple, for
- * example, would have a type information structure that consumed
- * writable memory in the size of 14 * sizeof (void *) (plus type
- * string) This means that on 32bit systems, the cache entry for
- * "{sv}" would require 61 bytes of memory (plus malloc overhead).
- * </para>
- * <para>
- * This means that in total, for our "a{sv}" example, 91 bytes of
- * type information would be allocated.
- * </para>
- * <para>
- * The type information cache, additionally, uses a #GHashTable to
- * store and lookup the cached items and stores a pointer to this
- * hash table in static storage. The hash table is freed when there
- * are zero items in the type cache.
- * </para>
- * <para>
- * Although these sizes may seem large it is important to remember
- * that a program will probably only have a very small number of
- * different types of values in it and that only one type information
- * structure is required for many different values of the same type.
- * </para>
- * </refsect3>
- * <refsect3>
- * <title>Buffer Management Memory</title>
- * <para>
- * #GVariant uses an internal buffer management structure to deal
- * with the various different possible sources of serialised data
- * that it uses. The buffer is responsible for ensuring that the
- * correct call is made when the data is no longer in use by
- * #GVariant. This may involve a g_free() or a g_slice_free() or
- * even g_mapped_file_unref().
- * </para>
- * <para>
- * One buffer management structure is used for each chunk of
- * serialised data. The size of the buffer management structure is 4
- * * (void *). On 32bit systems, that's 16 bytes.
- * </para>
- * </refsect3>
- * <refsect3>
- * <title>GVariant structure</title>
- * <para>
- * The size of a #GVariant structure is 6 * (void *). On 32 bit
- * systems, that's 24 bytes.
- * </para>
- * <para>
- * #GVariant structures only exist if they are explicitly created
- * with API calls. For example, if a #GVariant is constructed out of
- * serialised data for the example given above (with the dictionary)
- * then although there are 9 individual values that comprise the
- * entire dictionary (two keys, two values, two variants containing
- * the values, two dictionary entries, plus the dictionary itself),
- * only 1 #GVariant instance exists -- the one refering to the
- * dictionary.
- * </para>
- * <para>
- * If calls are made to start accessing the other values then
- * #GVariant instances will exist for those values only for as long
- * as they are in use (ie: until you call g_variant_unref()). The
- * type information is shared. The serialised data and the buffer
- * management structure for that serialised data is shared by the
- * child.
- * </para>
- * </refsect3>
- * <refsect3>
- * <title>Summary</title>
- * <para>
- * To put the entire example together, for our dictionary mapping
- * strings to variants (with two entries, as given above), we are
- * using 91 bytes of memory for type information, 29 byes of memory
- * for the serialised data, 16 bytes for buffer management and 24
- * bytes for the #GVariant instance, or a total of 160 bytes, plus
- * malloc overhead. If we were to use g_variant_get_child_value() to
- * access the two dictionary entries, we would use an additional 48
- * bytes. If we were to have other dictionaries of the same type, we
- * would use more memory for the serialised data and buffer
- * management for those dictionaries, but the type information would
- * be shared.
- * </para>
- * </refsect3>
- * </refsect2>
- */
-
-/* definition of GVariant structure is in gvariant-core.c */
-
-/* this is a g_return_val_if_fail() for making
- * sure a (GVariant *) has the required type.
- */
-#define TYPE_CHECK(value, TYPE, val) \
- if G_UNLIKELY (!g_variant_is_of_type (value, TYPE)) { \
- g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, \
- "g_variant_is_of_type (" #value \
- ", " #TYPE ")"); \
- return val; \
- }
-
-/* Numeric Type Constructor/Getters {{{1 */
-/* < private >
- * g_variant_new_from_trusted:
- * @type: the #GVariantType
- * @data: the data to use
- * @size: the size of @data
- * @returns: a new floating #GVariant
- *
- * Constructs a new trusted #GVariant instance from the provided data.
- * This is used to implement g_variant_new_* for all the basic types.
- */
-static GVariant *
-g_variant_new_from_trusted (const GVariantType *type,
- gconstpointer data,
- gsize size)
-{
- GVariant *value;
- GBuffer *buffer;
-
- buffer = g_buffer_new_from_data (data, size);
- value = g_variant_new_from_buffer (type, buffer, TRUE);
- g_buffer_unref (buffer);
-
- return value;
-}
-
-/**
- * g_variant_new_boolean:
- * @boolean: a #gboolean value
- * @returns: a floating reference to a new boolean #GVariant instance
- *
- * Creates a new boolean #GVariant instance -- either %TRUE or %FALSE.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_boolean (gboolean value)
-{
- guchar v = value;
-
- return g_variant_new_from_trusted (G_VARIANT_TYPE_BOOLEAN, &v, 1);
-}
-
-/**
- * g_variant_get_boolean:
- * @value: a boolean #GVariant instance
- * @returns: %TRUE or %FALSE
- *
- * Returns the boolean value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_BOOLEAN.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_get_boolean (GVariant *value)
-{
- const guchar *data;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_BOOLEAN, FALSE);
-
- data = g_variant_get_data (value);
-
- return data != NULL ? *data != 0 : FALSE;
-}
-
-/* the constructors and accessors for byte, int{16,32,64}, handles and
- * doubles all look pretty much exactly the same, so we reduce
- * copy/pasting here.
- */
-#define NUMERIC_TYPE(TYPE, type, ctype) \
- GVariant *g_variant_new_##type (ctype value) { \
- return g_variant_new_from_trusted (G_VARIANT_TYPE_##TYPE, \
- &value, sizeof value); \
- } \
- ctype g_variant_get_##type (GVariant *value) { \
- const ctype *data; \
- TYPE_CHECK (value, G_VARIANT_TYPE_ ## TYPE, 0); \
- data = g_variant_get_data (value); \
- return data != NULL ? *data : 0; \
- }
-
-
-/**
- * g_variant_new_byte:
- * @byte: a #guint8 value
- * @returns: a floating reference to a new byte #GVariant instance
- *
- * Creates a new byte #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_byte:
- * @value: a byte #GVariant instance
- * @returns: a #guchar
- *
- * Returns the byte value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_BYTE.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (BYTE, byte, guchar)
-
-/**
- * g_variant_new_int16:
- * @int16: a #gint16 value
- * @returns: a floating reference to a new int16 #GVariant instance
- *
- * Creates a new int16 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_int16:
- * @value: a int16 #GVariant instance
- * @returns: a #gint16
- *
- * Returns the 16-bit signed integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_INT16.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (INT16, int16, gint16)
-
-/**
- * g_variant_new_uint16:
- * @uint16: a #guint16 value
- * @returns: a floating reference to a new uint16 #GVariant instance
- *
- * Creates a new uint16 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_uint16:
- * @value: a uint16 #GVariant instance
- * @returns: a #guint16
- *
- * Returns the 16-bit unsigned integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_UINT16.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (UINT16, uint16, guint16)
-
-/**
- * g_variant_new_int32:
- * @int32: a #gint32 value
- * @returns: a floating reference to a new int32 #GVariant instance
- *
- * Creates a new int32 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_int32:
- * @value: a int32 #GVariant instance
- * @returns: a #gint32
- *
- * Returns the 32-bit signed integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_INT32.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (INT32, int32, gint32)
-
-/**
- * g_variant_new_uint32:
- * @uint32: a #guint32 value
- * @returns: a floating reference to a new uint32 #GVariant instance
- *
- * Creates a new uint32 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_uint32:
- * @value: a uint32 #GVariant instance
- * @returns: a #guint32
- *
- * Returns the 32-bit unsigned integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_UINT32.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (UINT32, uint32, guint32)
-
-/**
- * g_variant_new_int64:
- * @int64: a #gint64 value
- * @returns: a floating reference to a new int64 #GVariant instance
- *
- * Creates a new int64 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_int64:
- * @value: a int64 #GVariant instance
- * @returns: a #gint64
- *
- * Returns the 64-bit signed integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_INT64.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (INT64, int64, gint64)
-
-/**
- * g_variant_new_uint64:
- * @uint64: a #guint64 value
- * @returns: a floating reference to a new uint64 #GVariant instance
- *
- * Creates a new uint64 #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_uint64:
- * @value: a uint64 #GVariant instance
- * @returns: a #guint64
- *
- * Returns the 64-bit unsigned integer value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_UINT64.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (UINT64, uint64, guint64)
-
-/**
- * g_variant_new_handle:
- * @handle: a #gint32 value
- * @returns: a floating reference to a new handle #GVariant instance
- *
- * Creates a new handle #GVariant instance.
- *
- * By convention, handles are indexes into an array of file descriptors
- * that are sent alongside a DBus message. If you're not interacting
- * with DBus, you probably don't need them.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_handle:
- * @value: a handle #GVariant instance
- * @returns: a #gint32
- *
- * Returns the 32-bit signed integer value of @value.
- *
- * It is an error to call this function with a @value of any type other
- * than %G_VARIANT_TYPE_HANDLE.
- *
- * By convention, handles are indexes into an array of file descriptors
- * that are sent alongside a DBus message. If you're not interacting
- * with DBus, you probably don't need them.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (HANDLE, handle, gint32)
-
-/**
- * g_variant_new_double:
- * @floating: a #gdouble floating point value
- * @returns: a floating reference to a new double #GVariant instance
- *
- * Creates a new double #GVariant instance.
- *
- * Since: 2.24
- **/
-/**
- * g_variant_get_double:
- * @value: a double #GVariant instance
- * @returns: a #gdouble
- *
- * Returns the double precision floating point value of @value.
- *
- * It is an error to call this function with a @value of any type
- * other than %G_VARIANT_TYPE_DOUBLE.
- *
- * Since: 2.24
- **/
-NUMERIC_TYPE (DOUBLE, double, gdouble)
-
-/* Container type Constructor / Deconstructors {{{1 */
-/**
- * g_variant_new_maybe:
- * @child_type: (allow-none): the #GVariantType of the child, or %NULL
- * @child: (allow-none): the child value, or %NULL
- * @returns: a floating reference to a new #GVariant maybe instance
- *
- * Depending on if @child is %NULL, either wraps @child inside of a
- * maybe container or creates a Nothing instance for the given @type.
- *
- * At least one of @child_type and @child must be non-%NULL.
- * If @child_type is non-%NULL then it must be a definite type.
- * If they are both non-%NULL then @child_type must be the type
- * of @child.
- *
- * If @child is a floating reference (see g_variant_ref_sink()), the new
- * instance takes ownership of @child.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_maybe (const GVariantType *child_type,
- GVariant *child)
-{
- GVariantType *maybe_type;
- GVariant *value;
-
- g_return_val_if_fail (child_type == NULL || g_variant_type_is_definite
- (child_type), 0);
- g_return_val_if_fail (child_type != NULL || child != NULL, NULL);
- g_return_val_if_fail (child_type == NULL || child == NULL ||
- g_variant_is_of_type (child, child_type),
- NULL);
-
- if (child_type == NULL)
- child_type = g_variant_get_type (child);
-
- maybe_type = g_variant_type_new_maybe (child_type);
-
- if (child != NULL)
- {
- GVariant **children;
- gboolean trusted;
-
- children = g_new (GVariant *, 1);
- children[0] = g_variant_ref_sink (child);
- trusted = g_variant_is_trusted (children[0]);
-
- value = g_variant_new_from_children (maybe_type, children, 1, trusted);
- }
- else
- value = g_variant_new_from_children (maybe_type, NULL, 0, TRUE);
-
- g_variant_type_free (maybe_type);
-
- return value;
-}
-
-/**
- * g_variant_get_maybe:
- * @value: a maybe-typed value
- * @returns: (allow-none): the contents of @value, or %NULL
- *
- * Given a maybe-typed #GVariant instance, extract its value. If the
- * value is Nothing, then this function returns %NULL.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_get_maybe (GVariant *value)
-{
- TYPE_CHECK (value, G_VARIANT_TYPE_MAYBE, NULL);
-
- if (g_variant_n_children (value))
- return g_variant_get_child_value (value, 0);
-
- return NULL;
-}
-
-/**
- * g_variant_new_variant:
- * @value: a #GVariance instance
- * @returns: a floating reference to a new variant #GVariant instance
- *
- * Boxes @value. The result is a #GVariant instance representing a
- * variant containing the original value.
- *
- * If @child is a floating reference (see g_variant_ref_sink()), the new
- * instance takes ownership of @child.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_variant (GVariant *value)
-{
- g_return_val_if_fail (value != NULL, NULL);
-
- g_variant_ref_sink (value);
-
- return g_variant_new_from_children (G_VARIANT_TYPE_VARIANT,
- g_memdup (&value, sizeof value),
- 1, g_variant_is_trusted (value));
-}
-
-/**
- * g_variant_get_variant:
- * @value: a variant #GVariance instance
- * @returns: the item contained in the variant
- *
- * Unboxes @value. The result is the #GVariant instance that was
- * contained in @value.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_get_variant (GVariant *value)
-{
- TYPE_CHECK (value, G_VARIANT_TYPE_VARIANT, NULL);
-
- return g_variant_get_child_value (value, 0);
-}
-
-/**
- * g_variant_new_array:
- * @child_type: (allow-none): the element type of the new array
- * @children: (allow-none) (array length=n_children): an array of
- * #GVariant pointers, the children
- * @n_children: the length of @children
- * @returns: a floating reference to a new #GVariant array
- *
- * Creates a new #GVariant array from @children.
- *
- * @child_type must be non-%NULL if @n_children is zero. Otherwise, the
- * child type is determined by inspecting the first element of the
- * @children array. If @child_type is non-%NULL then it must be a
- * definite type.
- *
- * The items of the array are taken from the @children array. No entry
- * in the @children array may be %NULL.
- *
- * All items in the array must have the same type, which must be the
- * same as @child_type, if given.
- *
- * If the @children are floating references (see g_variant_ref_sink()), the
- * new instance takes ownership of them as if via g_variant_ref_sink().
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_array (const GVariantType *child_type,
- GVariant * const *children,
- gsize n_children)
-{
- GVariantType *array_type;
- GVariant **my_children;
- gboolean trusted;
- GVariant *value;
- gsize i;
-
- g_return_val_if_fail (n_children > 0 || child_type != NULL, NULL);
- g_return_val_if_fail (n_children == 0 || children != NULL, NULL);
- g_return_val_if_fail (child_type == NULL ||
- g_variant_type_is_definite (child_type), NULL);
-
- my_children = g_new (GVariant *, n_children);
- trusted = TRUE;
-
- if (child_type == NULL)
- child_type = g_variant_get_type (children[0]);
- array_type = g_variant_type_new_array (child_type);
-
- for (i = 0; i < n_children; i++)
- {
- TYPE_CHECK (children[i], child_type, NULL);
- my_children[i] = g_variant_ref_sink (children[i]);
- trusted &= g_variant_is_trusted (children[i]);
- }
-
- value = g_variant_new_from_children (array_type, my_children,
- n_children, trusted);
- g_variant_type_free (array_type);
-
- return value;
-}
-
-/*< private >
- * g_variant_make_tuple_type:
- * @children: (array length=n_children): an array of GVariant *
- * @n_children: the length of @children
- *
- * Return the type of a tuple containing @children as its items.
- **/
-static GVariantType *
-g_variant_make_tuple_type (GVariant * const *children,
- gsize n_children)
-{
- const GVariantType **types;
- GVariantType *type;
- gsize i;
-
- types = g_new (const GVariantType *, n_children);
-
- for (i = 0; i < n_children; i++)
- types[i] = g_variant_get_type (children[i]);
-
- type = g_variant_type_new_tuple (types, n_children);
- g_free (types);
-
- return type;
-}
-
-/**
- * g_variant_new_tuple:
- * @children: (array length=n_children): the items to make the tuple out of
- * @n_children: the length of @children
- * @returns: a floating reference to a new #GVariant tuple
- *
- * Creates a new tuple #GVariant out of the items in @children. The
- * type is determined from the types of @children. No entry in the
- * @children array may be %NULL.
- *
- * If @n_children is 0 then the unit tuple is constructed.
- *
- * If the @children are floating references (see g_variant_ref_sink()), the
- * new instance takes ownership of them as if via g_variant_ref_sink().
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_tuple (GVariant * const *children,
- gsize n_children)
-{
- GVariantType *tuple_type;
- GVariant **my_children;
- gboolean trusted;
- GVariant *value;
- gsize i;
-
- g_return_val_if_fail (n_children == 0 || children != NULL, NULL);
-
- my_children = g_new (GVariant *, n_children);
- trusted = TRUE;
-
- for (i = 0; i < n_children; i++)
- {
- my_children[i] = g_variant_ref_sink (children[i]);
- trusted &= g_variant_is_trusted (children[i]);
- }
-
- tuple_type = g_variant_make_tuple_type (children, n_children);
- value = g_variant_new_from_children (tuple_type, my_children,
- n_children, trusted);
- g_variant_type_free (tuple_type);
-
- return value;
-}
-
-/*< private >
- * g_variant_make_dict_entry_type:
- * @key: a #GVariant, the key
- * @val: a #GVariant, the value
- *
- * Return the type of a dictionary entry containing @key and @val as its
- * children.
- **/
-static GVariantType *
-g_variant_make_dict_entry_type (GVariant *key,
- GVariant *val)
-{
- return g_variant_type_new_dict_entry (g_variant_get_type (key),
- g_variant_get_type (val));
-}
-
-/**
- * g_variant_new_dict_entry:
- * @key: a basic #GVariant, the key
- * @value: a #GVariant, the value
- * @returns: a floating reference to a new dictionary entry #GVariant
- *
- * Creates a new dictionary entry #GVariant. @key and @value must be
- * non-%NULL.
- *
- * @key must be a value of a basic type (ie: not a container).
- *
- * If the @key or @value are floating references (see g_variant_ref_sink()),
- * the new instance takes ownership of them as if via g_variant_ref_sink().
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_dict_entry (GVariant *key,
- GVariant *value)
-{
- GVariantType *dict_type;
- GVariant **children;
- gboolean trusted;
-
- g_return_val_if_fail (key != NULL && value != NULL, NULL);
- g_return_val_if_fail (!g_variant_is_container (key), NULL);
-
- children = g_new (GVariant *, 2);
- children[0] = g_variant_ref_sink (key);
- children[1] = g_variant_ref_sink (value);
- trusted = g_variant_is_trusted (key) && g_variant_is_trusted (value);
-
- dict_type = g_variant_make_dict_entry_type (key, value);
- value = g_variant_new_from_children (dict_type, children, 2, trusted);
- g_variant_type_free (dict_type);
-
- return value;
-}
-
-/**
- * g_variant_get_fixed_array:
- * @value: a #GVariant array with fixed-sized elements
- * @n_elements: a pointer to the location to store the number of items
- * @element_size: the size of each element
- * @returns: (array length=n_elements): a pointer to the fixed array
- *
- * Provides access to the serialised data for an array of fixed-sized
- * items.
- *
- * @value must be an array with fixed-sized elements. Numeric types are
- * fixed-size as are tuples containing only other fixed-sized types.
- *
- * @element_size must be the size of a single element in the array. For
- * example, if calling this function for an array of 32 bit integers,
- * you might say <code>sizeof (gint32)</code>. This value isn't used
- * except for the purpose of a double-check that the form of the
- * seralised data matches the caller's expectation.
- *
- * @n_elements, which must be non-%NULL is set equal to the number of
- * items in the array.
- *
- * Since: 2.24
- **/
-gconstpointer
-g_variant_get_fixed_array (GVariant *value,
- gsize *n_elements,
- gsize element_size)
-{
- GVariantTypeInfo *array_info;
- gsize array_element_size;
- gconstpointer data;
- gsize size;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_ARRAY, NULL);
-
- g_return_val_if_fail (n_elements != NULL, NULL);
- g_return_val_if_fail (element_size > 0, NULL);
-
- array_info = g_variant_get_type_info (value);
- g_variant_type_info_query_element (array_info, NULL, &array_element_size);
-
- g_return_val_if_fail (array_element_size, NULL);
-
- if G_UNLIKELY (array_element_size != element_size)
- {
- if (array_element_size)
- g_critical ("g_variant_get_fixed_array: assertion "
- "`g_variant_array_has_fixed_size (value, element_size)' "
- "failed: array size %"G_GSIZE_FORMAT" does not match "
- "given element_size %"G_GSIZE_FORMAT".",
- array_element_size, element_size);
- else
- g_critical ("g_variant_get_fixed_array: assertion "
- "`g_variant_array_has_fixed_size (value, element_size)' "
- "failed: array does not have fixed size.");
- }
-
- data = g_variant_get_data (value);
- size = g_variant_get_size (value);
-
- if (size % element_size)
- *n_elements = 0;
- else
- *n_elements = size / element_size;
-
- if (*n_elements)
- return data;
-
- return NULL;
-}
-
-/* String type constructor/getters/validation {{{1 */
-/**
- * g_variant_new_string:
- * @string: a normal utf8 nul-terminated string
- * @returns: a floating reference to a new string #GVariant instance
- *
- * Creates a string #GVariant with the contents of @string.
- *
- * @string must be valid utf8.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_string (const gchar *string)
-{
- g_return_val_if_fail (string != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (string, -1, NULL), NULL);
-
- return g_variant_new_from_trusted (G_VARIANT_TYPE_STRING,
- string, strlen (string) + 1);
-}
-
-/**
- * g_variant_new_object_path:
- * @object_path: a normal C nul-terminated string
- * @returns: a floating reference to a new object path #GVariant instance
- *
- * Creates a DBus object path #GVariant with the contents of @string.
- * @string must be a valid DBus object path. Use
- * g_variant_is_object_path() if you're not sure.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_object_path (const gchar *object_path)
-{
- g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
-
- return g_variant_new_from_trusted (G_VARIANT_TYPE_OBJECT_PATH,
- object_path, strlen (object_path) + 1);
-}
-
-/**
- * g_variant_is_object_path:
- * @string: a normal C nul-terminated string
- * @returns: %TRUE if @string is a DBus object path
- *
- * Determines if a given string is a valid DBus object path. You
- * should ensure that a string is a valid DBus object path before
- * passing it to g_variant_new_object_path().
- *
- * A valid object path starts with '/' followed by zero or more
- * sequences of characters separated by '/' characters. Each sequence
- * must contain only the characters "[A-Z][a-z][0-9]_". No sequence
- * (including the one following the final '/' character) may be empty.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_is_object_path (const gchar *string)
-{
- g_return_val_if_fail (string != NULL, FALSE);
-
- return g_variant_serialiser_is_object_path (string, strlen (string) + 1);
-}
-
-/**
- * g_variant_new_signature:
- * @signature: a normal C nul-terminated string
- * @returns: a floating reference to a new signature #GVariant instance
- *
- * Creates a DBus type signature #GVariant with the contents of
- * @string. @string must be a valid DBus type signature. Use
- * g_variant_is_signature() if you're not sure.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_signature (const gchar *signature)
-{
- g_return_val_if_fail (g_variant_is_signature (signature), NULL);
-
- return g_variant_new_from_trusted (G_VARIANT_TYPE_SIGNATURE,
- signature, strlen (signature) + 1);
-}
-
-/**
- * g_variant_is_signature:
- * @string: a normal C nul-terminated string
- * @returns: %TRUE if @string is a DBus type signature
- *
- * Determines if a given string is a valid DBus type signature. You
- * should ensure that a string is a valid DBus type signature before
- * passing it to g_variant_new_signature().
- *
- * DBus type signatures consist of zero or more definite #GVariantType
- * strings in sequence.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_is_signature (const gchar *string)
-{
- g_return_val_if_fail (string != NULL, FALSE);
-
- return g_variant_serialiser_is_signature (string, strlen (string) + 1);
-}
-
-/**
- * g_variant_get_string:
- * @value: a string #GVariant instance
- * @length: (allow-none) (default NULL) (out): a pointer to a #gsize,
- * to store the length
- * @returns: the constant string, utf8 encoded
- *
- * Returns the string value of a #GVariant instance with a string
- * type. This includes the types %G_VARIANT_TYPE_STRING,
- * %G_VARIANT_TYPE_OBJECT_PATH and %G_VARIANT_TYPE_SIGNATURE.
- *
- * The string will always be utf8 encoded.
- *
- * If @length is non-%NULL then the length of the string (in bytes) is
- * returned there. For trusted values, this information is already
- * known. For untrusted values, a strlen() will be performed.
- *
- * It is an error to call this function with a @value of any type
- * other than those three.
- *
- * The return value remains valid as long as @value exists.
- *
- * Since: 2.24
- **/
-const gchar *
-g_variant_get_string (GVariant *value,
- gsize *length)
-{
- gconstpointer data;
- gsize size;
-
- g_return_val_if_fail (value != NULL, NULL);
- g_return_val_if_fail (
- g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) ||
- g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH) ||
- g_variant_is_of_type (value, G_VARIANT_TYPE_SIGNATURE), NULL);
-
- data = g_variant_get_data (value);
- size = g_variant_get_size (value);
-
- if (!g_variant_is_trusted (value))
- {
- switch (g_variant_classify (value))
- {
- case G_VARIANT_CLASS_STRING:
- if (g_variant_serialiser_is_string (data, size))
- break;
-
- data = "";
- size = 1;
- break;
-
- case G_VARIANT_CLASS_OBJECT_PATH:
- if (g_variant_serialiser_is_object_path (data, size))
- break;
-
- data = "/";
- size = 2;
- break;
-
- case G_VARIANT_CLASS_SIGNATURE:
- if (g_variant_serialiser_is_signature (data, size))
- break;
-
- data = "";
- size = 1;
- break;
-
- default:
- g_assert_not_reached ();
- }
- }
-
- if (length)
- *length = size - 1;
-
- return data;
-}
-
-/**
- * g_variant_dup_string:
- * @value: a string #GVariant instance
- * @length: a pointer to a #gsize, to store the length
- * @returns: a newly allocated string, utf8 encoded
- *
- * Similar to g_variant_get_string() except that instead of returning
- * a constant string, the string is duplicated.
- *
- * The string will always be utf8 encoded.
- *
- * The return value must be freed using g_free().
- *
- * Since: 2.24
- **/
-gchar *
-g_variant_dup_string (GVariant *value,
- gsize *length)
-{
- return g_strdup (g_variant_get_string (value, length));
-}
-
-/**
- * g_variant_new_strv:
- * @strv: (array length=length) (element-type utf8): an array of strings
- * @length: the length of @strv, or -1
- * @returns: a new floating #GVariant instance
- *
- * Constructs an array of strings #GVariant from the given array of
- * strings.
- *
- * If @length is -1 then @strv is %NULL-terminated.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_strv (const gchar * const *strv,
- gssize length)
-{
- GVariant **strings;
- gsize i;
-
- g_return_val_if_fail (length == 0 || strv != NULL, NULL);
-
- if (length < 0)
- length = g_strv_length ((gchar **) strv);
-
- strings = g_new (GVariant *, length);
- for (i = 0; i < length; i++)
- strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i]));
-
- return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY,
- strings, length, TRUE);
-}
-
-/**
- * g_variant_get_strv:
- * @value: an array of strings #GVariant
- * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length) (transfer container): an array of constant
- * strings
- *
- * Gets the contents of an array of strings #GVariant. This call
- * makes a shallow copy; the return result should be released with
- * g_free(), but the individual strings must not be modified.
- *
- * If @length is non-%NULL then the number of elements in the result
- * is stored there. In any case, the resulting array will be
- * %NULL-terminated.
- *
- * For an empty array, @length will be set to 0 and a pointer to a
- * %NULL pointer will be returned.
- *
- * Since: 2.24
- **/
-const gchar **
-g_variant_get_strv (GVariant *value,
- gsize *length)
-{
- const gchar **strv;
- gsize n;
- gsize i;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL);
-
- g_variant_get_data (value);
- n = g_variant_n_children (value);
- strv = g_new (const gchar *, n + 1);
-
- for (i = 0; i < n; i++)
- {
- GVariant *string;
-
- string = g_variant_get_child_value (value, i);
- strv[i] = g_variant_get_string (string, NULL);
- g_variant_unref (string);
- }
- strv[i] = NULL;
-
- if (length)
- *length = n;
-
- return strv;
-}
-
-/**
- * g_variant_dup_strv:
- * @value: an array of strings #GVariant
- * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length): an array of strings
- *
- * Gets the contents of an array of strings #GVariant. This call
- * makes a deep copy; the return result should be released with
- * g_strfreev().
- *
- * If @length is non-%NULL then the number of elements in the result
- * is stored there. In any case, the resulting array will be
- * %NULL-terminated.
- *
- * For an empty array, @length will be set to 0 and a pointer to a
- * %NULL pointer will be returned.
- *
- * Since: 2.24
- **/
-gchar **
-g_variant_dup_strv (GVariant *value,
- gsize *length)
-{
- gchar **strv;
- gsize n;
- gsize i;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_STRING_ARRAY, NULL);
-
- n = g_variant_n_children (value);
- strv = g_new (gchar *, n + 1);
-
- for (i = 0; i < n; i++)
- {
- GVariant *string;
-
- string = g_variant_get_child_value (value, i);
- strv[i] = g_variant_dup_string (string, NULL);
- g_variant_unref (string);
- }
- strv[i] = NULL;
-
- if (length)
- *length = n;
-
- return strv;
-}
-
-/**
- * g_variant_new_bytestring:
- * @string: a normal nul-terminated string in no particular encoding
- * @returns: a floating reference to a new bytestring #GVariant instance
- *
- * Creates an array-of-bytes #GVariant with the contents of @string.
- * This function is just like g_variant_new_string() except that the
- * string need not be valid utf8.
- *
- * The nul terminator character at the end of the string is stored in
- * the array.
- *
- * Since: 2.26
- **/
-GVariant *
-g_variant_new_bytestring (const gchar *string)
-{
- g_return_val_if_fail (string != NULL, NULL);
-
- return g_variant_new_from_trusted (G_VARIANT_TYPE_BYTESTRING,
- string, strlen (string) + 1);
-}
-
-/**
- * g_variant_get_bytestring:
- * @value: an array-of-bytes #GVariant instance
- * @returns: the constant string
- *
- * Returns the string value of a #GVariant instance with an
- * array-of-bytes type. The string has no particular encoding.
- *
- * If the array does not end with a nul terminator character, the empty
- * string is returned. For this reason, you can always trust that a
- * non-%NULL nul-terminated string will be returned by this function.
- *
- * If the array contains a nul terminator character somewhere other than
- * the last byte then the returned string is the string, up to the first
- * such nul character.
- *
- * It is an error to call this function with a @value that is not an
- * array of bytes.
- *
- * The return value remains valid as long as @value exists.
- *
- * Since: 2.26
- **/
-const gchar *
-g_variant_get_bytestring (GVariant *value)
-{
- const gchar *string;
- gsize size;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING, NULL);
-
- /* Won't be NULL since this is an array type */
- string = g_variant_get_data (value);
- size = g_variant_get_size (value);
-
- if (size && string[size - 1] == '\0')
- return string;
- else
- return "";
-}
-
-/**
- * g_variant_dup_bytestring:
- * @value: an array-of-bytes #GVariant instance
- * @length: (allow-none) (default NULL): a pointer to a #gsize, to store
- * the length (not including the nul terminator)
- * @returns: a newly allocated string
- *
- * Similar to g_variant_get_bytestring() except that instead of
- * returning a constant string, the string is duplicated.
- *
- * The return value must be freed using g_free().
- *
- * Since: 2.26
- **/
-gchar *
-g_variant_dup_bytestring (GVariant *value,
- gsize *length)
-{
- const gchar *original = g_variant_get_bytestring (value);
- gsize size;
-
- /* don't crash in case get_bytestring() had an assert failure */
- if (original == NULL)
- return NULL;
-
- size = strlen (original);
-
- if (length)
- *length = size;
-
- return g_memdup (original, size + 1);
-}
-
-/**
- * g_variant_new_bytestring_array:
- * @strv: (array length=length): an array of strings
- * @length: the length of @strv, or -1
- * @returns: a new floating #GVariant instance
- *
- * Constructs an array of bytestring #GVariant from the given array of
- * strings.
- *
- * If @length is -1 then @strv is %NULL-terminated.
- *
- * Since: 2.26
- **/
-GVariant *
-g_variant_new_bytestring_array (const gchar * const *strv,
- gssize length)
-{
- GVariant **strings;
- gsize i;
-
- g_return_val_if_fail (length == 0 || strv != NULL, NULL);
-
- if (length < 0)
- length = g_strv_length ((gchar **) strv);
-
- strings = g_new (GVariant *, length);
- for (i = 0; i < length; i++)
- strings[i] = g_variant_ref_sink (g_variant_new_bytestring (strv[i]));
-
- return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY,
- strings, length, TRUE);
-}
-
-/**
- * g_variant_get_bytestring_array:
- * @value: an array of array of bytes #GVariant ('aay')
- * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length): an array of constant strings
- *
- * Gets the contents of an array of array of bytes #GVariant. This call
- * makes a shallow copy; the return result should be released with
- * g_free(), but the individual strings must not be modified.
- *
- * If @length is non-%NULL then the number of elements in the result is
- * stored there. In any case, the resulting array will be
- * %NULL-terminated.
- *
- * For an empty array, @length will be set to 0 and a pointer to a
- * %NULL pointer will be returned.
- *
- * Since: 2.26
- **/
-const gchar **
-g_variant_get_bytestring_array (GVariant *value,
- gsize *length)
-{
- const gchar **strv;
- gsize n;
- gsize i;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL);
-
- g_variant_get_data (value);
- n = g_variant_n_children (value);
- strv = g_new (const gchar *, n + 1);
-
- for (i = 0; i < n; i++)
- {
- GVariant *string;
-
- string = g_variant_get_child_value (value, i);
- strv[i] = g_variant_get_bytestring (string);
- g_variant_unref (string);
- }
- strv[i] = NULL;
-
- if (length)
- *length = n;
-
- return strv;
-}
-
-/**
- * g_variant_dup_bytestring_array:
- * @value: an array of array of bytes #GVariant ('aay')
- * @length: (allow-none): the length of the result, or %NULL
- * @returns: (array length=length): an array of strings
- *
- * Gets the contents of an array of array of bytes #GVariant. This call
- * makes a deep copy; the return result should be released with
- * g_strfreev().
- *
- * If @length is non-%NULL then the number of elements in the result is
- * stored there. In any case, the resulting array will be
- * %NULL-terminated.
- *
- * For an empty array, @length will be set to 0 and a pointer to a
- * %NULL pointer will be returned.
- *
- * Since: 2.26
- **/
-gchar **
-g_variant_dup_bytestring_array (GVariant *value,
- gsize *length)
-{
- gchar **strv;
- gsize n;
- gsize i;
-
- TYPE_CHECK (value, G_VARIANT_TYPE_BYTESTRING_ARRAY, NULL);
-
- g_variant_get_data (value);
- n = g_variant_n_children (value);
- strv = g_new (gchar *, n + 1);
-
- for (i = 0; i < n; i++)
- {
- GVariant *string;
-
- string = g_variant_get_child_value (value, i);
- strv[i] = g_variant_dup_bytestring (string, NULL);
- g_variant_unref (string);
- }
- strv[i] = NULL;
-
- if (length)
- *length = n;
-
- return strv;
-}
-
-/* Type checking and querying {{{1 */
-/**
- * g_variant_get_type:
- * @value: a #GVariant
- * @returns: a #GVariantType
- *
- * Determines the type of @value.
- *
- * The return value is valid for the lifetime of @value and must not
- * be freed.
- *
- * Since: 2.24
- **/
-const GVariantType *
-g_variant_get_type (GVariant *value)
-{
- GVariantTypeInfo *type_info;
-
- g_return_val_if_fail (value != NULL, NULL);
-
- type_info = g_variant_get_type_info (value);
-
- return (GVariantType *) g_variant_type_info_get_type_string (type_info);
-}
-
-/**
- * g_variant_get_type_string:
- * @value: a #GVariant
- * @returns: the type string for the type of @value
- *
- * Returns the type string of @value. Unlike the result of calling
- * g_variant_type_peek_string(), this string is nul-terminated. This
- * string belongs to #GVariant and must not be freed.
- *
- * Since: 2.24
- **/
-const gchar *
-g_variant_get_type_string (GVariant *value)
-{
- GVariantTypeInfo *type_info;
-
- g_return_val_if_fail (value != NULL, NULL);
-
- type_info = g_variant_get_type_info (value);
-
- return g_variant_type_info_get_type_string (type_info);
-}
-
-/**
- * g_variant_is_of_type:
- * @value: a #GVariant instance
- * @type: a #GVariantType
- * @returns: %TRUE if the type of @value matches @type
- *
- * Checks if a value has a type matching the provided type.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_is_of_type (GVariant *value,
- const GVariantType *type)
-{
- return g_variant_type_is_subtype_of (g_variant_get_type (value), type);
-}
-
-/**
- * g_variant_is_container:
- * @value: a #GVariant instance
- * @returns: %TRUE if @value is a container
- *
- * Checks if @value is a container.
- */
-gboolean
-g_variant_is_container (GVariant *value)
-{
- return g_variant_type_is_container (g_variant_get_type (value));
-}
-
-
-/**
- * g_variant_classify:
- * @value: a #GVariant
- * @returns: the #GVariantClass of @value
- *
- * Classifies @value according to its top-level type.
- *
- * Since: 2.24
- **/
-/**
- * GVariantClass:
- * @G_VARIANT_CLASS_BOOLEAN: The #GVariant is a boolean.
- * @G_VARIANT_CLASS_BYTE: The #GVariant is a byte.
- * @G_VARIANT_CLASS_INT16: The #GVariant is a signed 16 bit integer.
- * @G_VARIANT_CLASS_UINT16: The #GVariant is an unsigned 16 bit integer.
- * @G_VARIANT_CLASS_INT32: The #GVariant is a signed 32 bit integer.
- * @G_VARIANT_CLASS_UINT32: The #GVariant is an unsigned 32 bit integer.
- * @G_VARIANT_CLASS_INT64: The #GVariant is a signed 64 bit integer.
- * @G_VARIANT_CLASS_UINT64: The #GVariant is an unsigned 64 bit integer.
- * @G_VARIANT_CLASS_HANDLE: The #GVariant is a file handle index.
- * @G_VARIANT_CLASS_DOUBLE: The #GVariant is a double precision floating
- * point value.
- * @G_VARIANT_CLASS_STRING: The #GVariant is a normal string.
- * @G_VARIANT_CLASS_OBJECT_PATH: The #GVariant is a DBus object path
- * string.
- * @G_VARIANT_CLASS_SIGNATURE: The #GVariant is a DBus signature string.
- * @G_VARIANT_CLASS_VARIANT: The #GVariant is a variant.
- * @G_VARIANT_CLASS_MAYBE: The #GVariant is a maybe-typed value.
- * @G_VARIANT_CLASS_ARRAY: The #GVariant is an array.
- * @G_VARIANT_CLASS_TUPLE: The #GVariant is a tuple.
- * @G_VARIANT_CLASS_DICT_ENTRY: The #GVariant is a dictionary entry.
- *
- * The range of possible top-level types of #GVariant instances.
- *
- * Since: 2.24
- **/
-GVariantClass
-g_variant_classify (GVariant *value)
-{
- g_return_val_if_fail (value != NULL, 0);
-
- return *g_variant_get_type_string (value);
-}
-
-/* Pretty printer {{{1 */
-/**
- * g_variant_print_string:
- * @value: a #GVariant
- * @string: (allow-none) (default NULL): a #GString, or %NULL
- * @type_annotate: %TRUE if type information should be included in
- * the output
- * @returns: a #GString containing the string
- *
- * Behaves as g_variant_print(), but operates on a #GString.
- *
- * If @string is non-%NULL then it is appended to and returned. Else,
- * a new empty #GString is allocated and it is returned.
- *
- * Since: 2.24
- **/
-GString *
-g_variant_print_string (GVariant *value,
- GString *string,
- gboolean type_annotate)
-{
- if G_UNLIKELY (string == NULL)
- string = g_string_new (NULL);
-
- switch (g_variant_classify (value))
- {
- case G_VARIANT_CLASS_MAYBE:
- if (type_annotate)
- g_string_append_printf (string, "@%s ",
- g_variant_get_type_string (value));
-
- if (g_variant_n_children (value))
- {
- gchar *printed_child;
- GVariant *element;
-
- /* Nested maybes:
- *
- * Consider the case of the type "mmi". In this case we could
- * write "just just 4", but "4" alone is totally unambiguous,
- * so we try to drop "just" where possible.
- *
- * We have to be careful not to always drop "just", though,
- * since "nothing" needs to be distinguishable from "just
- * nothing". The case where we need to ensure we keep the
- * "just" is actually exactly the case where we have a nested
- * Nothing.
- *
- * Instead of searching for that nested Nothing, we just print
- * the contained value into a separate string and see if we
- * end up with "nothing" at the end of it. If so, we need to
- * add "just" at our level.
- */
- element = g_variant_get_child_value (value, 0);
- printed_child = g_variant_print (element, FALSE);
- g_variant_unref (element);
-
- if (g_str_has_suffix (printed_child, "nothing"))
- g_string_append (string, "just ");
- g_string_append (string, printed_child);
- g_free (printed_child);
- }
- else
- g_string_append (string, "nothing");
-
- break;
-
- case G_VARIANT_CLASS_ARRAY:
- /* it's an array so the first character of the type string is 'a'
- *
- * if the first two characters are 'ay' then it's a bytestring.
- * under certain conditions we print those as strings.
- */
- if (g_variant_get_type_string (value)[1] == 'y')
- {
- const gchar *str;
- gsize size;
- gsize i;
-
- /* first determine if it is a byte string.
- * that's when there's a single nul character: at the end.
- */
- str = g_variant_get_data (value);
- size = g_variant_get_size (value);
-
- for (i = 0; i < size; i++)
- if (str[i] == '\0')
- break;
-
- /* first nul byte is the last byte -> it's a byte string. */
- if (i == size - 1)
- {
- gchar *escaped = g_strescape (str, NULL);
-
- /* use double quotes only if a ' is in the string */
- if (strchr (str, '\''))
- g_string_append_printf (string, "b\"%s\"", escaped);
- else
- g_string_append_printf (string, "b'%s'", escaped);
-
- g_free (escaped);
- break;
- }
-
- else
- /* fall through and handle normally... */;
- }
-
- /*
- * if the first two characters are 'a{' then it's an array of
- * dictionary entries (ie: a dictionary) so we print that
- * differently.
- */
- if (g_variant_get_type_string (value)[1] == '{')
- /* dictionary */
- {
- const gchar *comma = "";
- gsize n, i;
-
- if ((n = g_variant_n_children (value)) == 0)
- {
- if (type_annotate)
- g_string_append_printf (string, "@%s ",
- g_variant_get_type_string (value));
- g_string_append (string, "{}");
- break;
- }
-
- g_string_append_c (string, '{');
- for (i = 0; i < n; i++)
- {
- GVariant *entry, *key, *val;
-
- g_string_append (string, comma);
- comma = ", ";
-
- entry = g_variant_get_child_value (value, i);
- key = g_variant_get_child_value (entry, 0);
- val = g_variant_get_child_value (entry, 1);
- g_variant_unref (entry);
-
- g_variant_print_string (key, string, type_annotate);
- g_variant_unref (key);
- g_string_append (string, ": ");
- g_variant_print_string (val, string, type_annotate);
- g_variant_unref (val);
- type_annotate = FALSE;
- }
- g_string_append_c (string, '}');
- }
- else
- /* normal (non-dictionary) array */
- {
- const gchar *comma = "";
- gsize n, i;
-
- if ((n = g_variant_n_children (value)) == 0)
- {
- if (type_annotate)
- g_string_append_printf (string, "@%s ",
- g_variant_get_type_string (value));
- g_string_append (string, "[]");
- break;
- }
-
- g_string_append_c (string, '[');
- for (i = 0; i < n; i++)
- {
- GVariant *element;
-
- g_string_append (string, comma);
- comma = ", ";
-
- element = g_variant_get_child_value (value, i);
-
- g_variant_print_string (element, string, type_annotate);
- g_variant_unref (element);
- type_annotate = FALSE;
- }
- g_string_append_c (string, ']');
- }
-
- break;
-
- case G_VARIANT_CLASS_TUPLE:
- {
- gsize n, i;
-
- n = g_variant_n_children (value);
-
- g_string_append_c (string, '(');
- for (i = 0; i < n; i++)
- {
- GVariant *element;
-
- element = g_variant_get_child_value (value, i);
- g_variant_print_string (element, string, type_annotate);
- g_string_append (string, ", ");
- g_variant_unref (element);
- }
-
- /* for >1 item: remove final ", "
- * for 1 item: remove final " ", but leave the ","
- * for 0 items: there is only "(", so remove nothing
- */
- g_string_truncate (string, string->len - (n > 0) - (n > 1));
- g_string_append_c (string, ')');
- }
- break;
-
- case G_VARIANT_CLASS_DICT_ENTRY:
- {
- GVariant *element;
-
- g_string_append_c (string, '{');
-
- element = g_variant_get_child_value (value, 0);
- g_variant_print_string (element, string, type_annotate);
- g_variant_unref (element);
-
- g_string_append (string, ", ");
-
- element = g_variant_get_child_value (value, 1);
- g_variant_print_string (element, string, type_annotate);
- g_variant_unref (element);
-
- g_string_append_c (string, '}');
- }
- break;
-
- case G_VARIANT_CLASS_VARIANT:
- {
- GVariant *child = g_variant_get_variant (value);
-
- /* Always annotate types in nested variants, because they are
- * (by nature) of variable type.
- */
- g_string_append_c (string, '<');
- g_variant_print_string (child, string, TRUE);
- g_string_append_c (string, '>');
-
- g_variant_unref (child);
- }
- break;
-
- case G_VARIANT_CLASS_BOOLEAN:
- if (g_variant_get_boolean (value))
- g_string_append (string, "true");
- else
- g_string_append (string, "false");
- break;
-
- case G_VARIANT_CLASS_STRING:
- {
- const gchar *str = g_variant_get_string (value, NULL);
- gunichar quote = strchr (str, '\'') ? '"' : '\'';
-
- g_string_append_c (string, quote);
-
- while (*str)
- {
- gunichar c = g_utf8_get_char (str);
-
- if (c == quote || c == '\\')
- g_string_append_c (string, '\\');
-
- if (g_unichar_isprint (c))
- g_string_append_unichar (string, c);
-
- else
- {
- g_string_append_c (string, '\\');
- if (c < 0x10000)
- switch (c)
- {
- case '\a':
- g_string_append_c (string, 'a');
- break;
-
- case '\b':
- g_string_append_c (string, 'b');
- break;
-
- case '\f':
- g_string_append_c (string, 'f');
- break;
-
- case '\n':
- g_string_append_c (string, 'n');
- break;
-
- case '\r':
- g_string_append_c (string, 'r');
- break;
-
- case '\t':
- g_string_append_c (string, 't');
- break;
-
- case '\v':
- g_string_append_c (string, 'v');
- break;
-
- default:
- g_string_append_printf (string, "u%04x", c);
- break;
- }
- else
- g_string_append_printf (string, "U%08x", c);
- }
-
- str = g_utf8_next_char (str);
- }
-
- g_string_append_c (string, quote);
- }
- break;
-
- case G_VARIANT_CLASS_BYTE:
- if (type_annotate)
- g_string_append (string, "byte ");
- g_string_append_printf (string, "0x%02x",
- g_variant_get_byte (value));
- break;
-
- case G_VARIANT_CLASS_INT16:
- if (type_annotate)
- g_string_append (string, "int16 ");
- g_string_append_printf (string, "%"G_GINT16_FORMAT,
- g_variant_get_int16 (value));
- break;
-
- case G_VARIANT_CLASS_UINT16:
- if (type_annotate)
- g_string_append (string, "uint16 ");
- g_string_append_printf (string, "%"G_GUINT16_FORMAT,
- g_variant_get_uint16 (value));
- break;
-
- case G_VARIANT_CLASS_INT32:
- /* Never annotate this type because it is the default for numbers
- * (and this is a *pretty* printer)
- */
- g_string_append_printf (string, "%"G_GINT32_FORMAT,
- g_variant_get_int32 (value));
- break;
-
- case G_VARIANT_CLASS_HANDLE:
- if (type_annotate)
- g_string_append (string, "handle ");
- g_string_append_printf (string, "%"G_GINT32_FORMAT,
- g_variant_get_handle (value));
- break;
-
- case G_VARIANT_CLASS_UINT32:
- if (type_annotate)
- g_string_append (string, "uint32 ");
- g_string_append_printf (string, "%"G_GUINT32_FORMAT,
- g_variant_get_uint32 (value));
- break;
-
- case G_VARIANT_CLASS_INT64:
- if (type_annotate)
- g_string_append (string, "int64 ");
- g_string_append_printf (string, "%"G_GINT64_FORMAT,
- g_variant_get_int64 (value));
- break;
-
- case G_VARIANT_CLASS_UINT64:
- if (type_annotate)
- g_string_append (string, "uint64 ");
- g_string_append_printf (string, "%"G_GUINT64_FORMAT,
- g_variant_get_uint64 (value));
- break;
-
- case G_VARIANT_CLASS_DOUBLE:
- {
- gchar buffer[100];
- gint i;
-
- g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_double (value));
-
- for (i = 0; buffer[i]; i++)
- if (buffer[i] == '.' || buffer[i] == 'e' ||
- buffer[i] == 'n' || buffer[i] == 'N')
- break;
-
- /* if there is no '.' or 'e' in the float then add one */
- if (buffer[i] == '\0')
- {
- buffer[i++] = '.';
- buffer[i++] = '0';
- buffer[i++] = '\0';
- }
-
- g_string_append (string, buffer);
- }
- break;
-
- case G_VARIANT_CLASS_OBJECT_PATH:
- if (type_annotate)
- g_string_append (string, "objectpath ");
- g_string_append_printf (string, "\'%s\'",
- g_variant_get_string (value, NULL));
- break;
-
- case G_VARIANT_CLASS_SIGNATURE:
- if (type_annotate)
- g_string_append (string, "signature ");
- g_string_append_printf (string, "\'%s\'",
- g_variant_get_string (value, NULL));
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- return string;
-}
-
-/**
- * g_variant_print:
- * @value: a #GVariant
- * @type_annotate: %TRUE if type information should be included in
- * the output
- * @returns: a newly-allocated string holding the result.
- *
- * Pretty-prints @value in the format understood by g_variant_parse().
- *
- * If @type_annotate is %TRUE, then type information is included in
- * the output.
- */
-gchar *
-g_variant_print (GVariant *value,
- gboolean type_annotate)
-{
- return g_string_free (g_variant_print_string (value, NULL, type_annotate),
- FALSE);
-};
-
-/* Hash, Equal, Compare {{{1 */
-/**
- * g_variant_hash:
- * @value: (type GVariant): a basic #GVariant value as a #gconstpointer
- * @returns: a hash value corresponding to @value
- *
- * Generates a hash value for a #GVariant instance.
- *
- * The output of this function is guaranteed to be the same for a given
- * value only per-process. It may change between different processor
- * architectures or even different versions of GLib. Do not use this
- * function as a basis for building protocols or file formats.
- *
- * The type of @value is #gconstpointer only to allow use of this
- * function with #GHashTable. @value must be a #GVariant.
- *
- * Since: 2.24
- **/
-guint
-g_variant_hash (gconstpointer value_)
-{
- GVariant *value = (GVariant *) value_;
-
- switch (g_variant_classify (value))
- {
- case G_VARIANT_CLASS_STRING:
- case G_VARIANT_CLASS_OBJECT_PATH:
- case G_VARIANT_CLASS_SIGNATURE:
- return g_str_hash (g_variant_get_string (value, NULL));
-
- case G_VARIANT_CLASS_BOOLEAN:
- /* this is a very odd thing to hash... */
- return g_variant_get_boolean (value);
-
- case G_VARIANT_CLASS_BYTE:
- return g_variant_get_byte (value);
-
- case G_VARIANT_CLASS_INT16:
- case G_VARIANT_CLASS_UINT16:
- {
- const guint16 *ptr;
-
- ptr = g_variant_get_data (value);
-
- if (ptr)
- return *ptr;
- else
- return 0;
- }
-
- case G_VARIANT_CLASS_INT32:
- case G_VARIANT_CLASS_UINT32:
- case G_VARIANT_CLASS_HANDLE:
- {
- const guint *ptr;
-
- ptr = g_variant_get_data (value);
-
- if (ptr)
- return *ptr;
- else
- return 0;
- }
-
- case G_VARIANT_CLASS_INT64:
- case G_VARIANT_CLASS_UINT64:
- case G_VARIANT_CLASS_DOUBLE:
- /* need a separate case for these guys because otherwise
- * performance could be quite bad on big endian systems
- */
- {
- const guint *ptr;
-
- ptr = g_variant_get_data (value);
-
- if (ptr)
- return ptr[0] + ptr[1];
- else
- return 0;
- }
-
- default:
- g_return_val_if_fail (!g_variant_is_container (value), 0);
- g_assert_not_reached ();
- }
-}
-
-/**
- * g_variant_equal:
- * @one: (type GVariant): a #GVariant instance
- * @two: (type GVariant): a #GVariant instance
- * @returns: %TRUE if @one and @two are equal
- *
- * Checks if @one and @two have the same type and value.
- *
- * The types of @one and @two are #gconstpointer only to allow use of
- * this function with #GHashTable. They must each be a #GVariant.
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_equal (gconstpointer one,
- gconstpointer two)
-{
- gboolean equal;
-
- g_return_val_if_fail (one != NULL && two != NULL, FALSE);
-
- if (g_variant_get_type_info ((GVariant *) one) !=
- g_variant_get_type_info ((GVariant *) two))
- return FALSE;
-
- /* if both values are trusted to be in their canonical serialised form
- * then a simple memcmp() of their serialised data will answer the
- * question.
- *
- * if not, then this might generate a false negative (since it is
- * possible for two different byte sequences to represent the same
- * value). for now we solve this by pretty-printing both values and
- * comparing the result.
- */
- if (g_variant_is_trusted ((GVariant *) one) &&
- g_variant_is_trusted ((GVariant *) two))
- {
- gconstpointer data_one, data_two;
- gsize size_one, size_two;
-
- size_one = g_variant_get_size ((GVariant *) one);
- size_two = g_variant_get_size ((GVariant *) two);
-
- if (size_one != size_two)
- return FALSE;
-
- data_one = g_variant_get_data ((GVariant *) one);
- data_two = g_variant_get_data ((GVariant *) two);
-
- equal = memcmp (data_one, data_two, size_one) == 0;
- }
- else
- {
- gchar *strone, *strtwo;
-
- strone = g_variant_print ((GVariant *) one, FALSE);
- strtwo = g_variant_print ((GVariant *) two, FALSE);
- equal = strcmp (strone, strtwo) == 0;
- g_free (strone);
- g_free (strtwo);
- }
-
- return equal;
-}
-
-/**
- * g_variant_compare:
- * @one: (type GVariant): a basic-typed #GVariant instance
- * @two: (type GVariant): a #GVariant instance of the same type
- * @returns: negative value if a < b;
- * zero if a = b;
- * positive value if a > b.
- *
- * Compares @one and @two.
- *
- * The types of @one and @two are #gconstpointer only to allow use of
- * this function with #GTree, #GPtrArray, etc. They must each be a
- * #GVariant.
- *
- * Comparison is only defined for basic types (ie: booleans, numbers,
- * strings). For booleans, %FALSE is less than %TRUE. Numbers are
- * ordered in the usual way. Strings are in ASCII lexographical order.
- *
- * It is a programmer error to attempt to compare container values or
- * two values that have types that are not exactly equal. For example,
- * you can not compare a 32-bit signed integer with a 32-bit unsigned
- * integer. Also note that this function is not particularly
- * well-behaved when it comes to comparison of doubles; in particular,
- * the handling of incomparable values (ie: NaN) is undefined.
- *
- * If you only require an equality comparison, g_variant_equal() is more
- * general.
- *
- * Since: 2.26
- **/
-gint
-g_variant_compare (gconstpointer one,
- gconstpointer two)
-{
- GVariant *a = (GVariant *) one;
- GVariant *b = (GVariant *) two;
-
- g_return_val_if_fail (g_variant_classify (a) == g_variant_classify (b), 0);
-
- switch (g_variant_classify (a))
- {
- case G_VARIANT_CLASS_BYTE:
- return ((gint) g_variant_get_byte (a)) -
- ((gint) g_variant_get_byte (b));
-
- case G_VARIANT_CLASS_INT16:
- return ((gint) g_variant_get_int16 (a)) -
- ((gint) g_variant_get_int16 (b));
-
- case G_VARIANT_CLASS_UINT16:
- return ((gint) g_variant_get_uint16 (a)) -
- ((gint) g_variant_get_uint16 (b));
-
- case G_VARIANT_CLASS_INT32:
- {
- gint32 a_val = g_variant_get_int32 (a);
- gint32 b_val = g_variant_get_int32 (b);
-
- return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
- }
-
- case G_VARIANT_CLASS_UINT32:
- {
- guint32 a_val = g_variant_get_uint32 (a);
- guint32 b_val = g_variant_get_uint32 (b);
-
- return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
- }
-
- case G_VARIANT_CLASS_INT64:
- {
- gint64 a_val = g_variant_get_int64 (a);
- gint64 b_val = g_variant_get_int64 (b);
-
- return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
- }
-
- case G_VARIANT_CLASS_UINT64:
- {
- guint64 a_val = g_variant_get_int32 (a);
- guint64 b_val = g_variant_get_int32 (b);
-
- return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
- }
-
- case G_VARIANT_CLASS_DOUBLE:
- {
- gdouble a_val = g_variant_get_double (a);
- gdouble b_val = g_variant_get_double (b);
-
- return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
- }
-
- case G_VARIANT_CLASS_STRING:
- case G_VARIANT_CLASS_OBJECT_PATH:
- case G_VARIANT_CLASS_SIGNATURE:
- return strcmp (g_variant_get_string (a, NULL),
- g_variant_get_string (b, NULL));
-
- default:
- g_return_val_if_fail (!g_variant_is_container (a), 0);
- g_assert_not_reached ();
- }
-}
-
-/* GVariantIter {{{1 */
-/**
- * GVariantIter:
- *
- * #GVariantIter is an opaque data structure and can only be accessed
- * using the following functions.
- **/
-struct stack_iter
-{
- GVariant *value;
- gssize n, i;
-
- const gchar *loop_format;
-
- gsize padding[3];
- gsize magic;
-};
-
-G_STATIC_ASSERT (sizeof (struct stack_iter) <= sizeof (GVariantIter));
-
-struct heap_iter
-{
- struct stack_iter iter;
-
- GVariant *value_ref;
- gsize magic;
-};
-
-#define GVSI(i) ((struct stack_iter *) (i))
-#define GVHI(i) ((struct heap_iter *) (i))
-#define GVSI_MAGIC ((gsize) 3579507750u)
-#define GVHI_MAGIC ((gsize) 1450270775u)
-#define is_valid_iter(i) (i != NULL && \
- GVSI(i)->magic == GVSI_MAGIC)
-#define is_valid_heap_iter(i) (GVHI(i)->magic == GVHI_MAGIC && \
- is_valid_iter(i))
-
-/**
- * g_variant_iter_new:
- * @value: a container #GVariant
- * @returns: a new heap-allocated #GVariantIter
- *
- * Creates a heap-allocated #GVariantIter for iterating over the items
- * in @value.
- *
- * Use g_variant_iter_free() to free the return value when you no longer
- * need it.
- *
- * A reference is taken to @value and will be released only when
- * g_variant_iter_free() is called.
- *
- * Since: 2.24
- **/
-GVariantIter *
-g_variant_iter_new (GVariant *value)
-{
- GVariantIter *iter;
-
- iter = (GVariantIter *) g_slice_new (struct heap_iter);
- GVHI(iter)->value_ref = g_variant_ref (value);
- GVHI(iter)->magic = GVHI_MAGIC;
-
- g_variant_iter_init (iter, value);
-
- return iter;
-}
-
-/**
- * g_variant_iter_init:
- * @iter: a pointer to a #GVariantIter
- * @value: a container #GVariant
- * @returns: the number of items in @value
- *
- * Initialises (without allocating) a #GVariantIter. @iter may be
- * completely uninitialised prior to this call; its old value is
- * ignored.
- *
- * The iterator remains valid for as long as @value exists, and need not
- * be freed in any way.
- *
- * Since: 2.24
- **/
-gsize
-g_variant_iter_init (GVariantIter *iter,
- GVariant *value)
-{
- GVSI(iter)->magic = GVSI_MAGIC;
- GVSI(iter)->value = value;
- GVSI(iter)->n = g_variant_n_children (value);
- GVSI(iter)->i = -1;
- GVSI(iter)->loop_format = NULL;
-
- return GVSI(iter)->n;
-}
-
-/**
- * g_variant_iter_copy:
- * @iter: a #GVariantIter
- * @returns: a new heap-allocated #GVariantIter
- *
- * Creates a new heap-allocated #GVariantIter to iterate over the
- * container that was being iterated over by @iter. Iteration begins on
- * the new iterator from the current position of the old iterator but
- * the two copies are independent past that point.
- *
- * Use g_variant_iter_free() to free the return value when you no longer
- * need it.
- *
- * A reference is taken to the container that @iter is iterating over
- * and will be releated only when g_variant_iter_free() is called.
- *
- * Since: 2.24
- **/
-GVariantIter *
-g_variant_iter_copy (GVariantIter *iter)
-{
- GVariantIter *copy;
-
- g_return_val_if_fail (is_valid_iter (iter), 0);
-
- copy = g_variant_iter_new (GVSI(iter)->value);
- GVSI(copy)->i = GVSI(iter)->i;
-
- return copy;
-}
-
-/**
- * g_variant_iter_n_children:
- * @iter: a #GVariantIter
- * @returns: the number of children in the container
- *
- * Queries the number of child items in the container that we are
- * iterating over. This is the total number of items -- not the number
- * of items remaining.
- *
- * This function might be useful for preallocation of arrays.
- *
- * Since: 2.24
- **/
-gsize
-g_variant_iter_n_children (GVariantIter *iter)
-{
- g_return_val_if_fail (is_valid_iter (iter), 0);
-
- return GVSI(iter)->n;
-}
-
-/**
- * g_variant_iter_free:
- * @iter: a heap-allocated #GVariantIter
- *
- * Frees a heap-allocated #GVariantIter. Only call this function on
- * iterators that were returned by g_variant_iter_new() or
- * g_variant_iter_copy().
- *
- * Since: 2.24
- **/
-void
-g_variant_iter_free (GVariantIter *iter)
-{
- g_return_if_fail (is_valid_heap_iter (iter));
-
- g_variant_unref (GVHI(iter)->value_ref);
- GVHI(iter)->magic = 0;
-
- g_slice_free (struct heap_iter, GVHI(iter));
-}
-
-/**
- * g_variant_iter_next_value:
- * @iter: a #GVariantIter
- * @returns: (allow-none): a #GVariant, or %NULL
- *
- * Gets the next item in the container. If no more items remain then
- * %NULL is returned.
- *
- * Use g_variant_unref() to drop your reference on the return value when
- * you no longer need it.
- *
- * <example>
- * <title>Iterating with g_variant_iter_next_value()</title>
- * <programlisting>
- * /<!-- -->* recursively iterate a container *<!-- -->/
- * void
- * iterate_container_recursive (GVariant *container)
- * {
- * GVariantIter iter;
- * GVariant *child;
- *
- * g_variant_iter_init (&iter, dictionary);
- * while ((child = g_variant_iter_next_value (&iter)))
- * {
- * g_print ("type '%s'\n", g_variant_get_type_string (child));
- *
- * if (g_variant_is_container (child))
- * iterate_container_recursive (child);
- *
- * g_variant_unref (child);
- * }
- * }
- * </programlisting>
- * </example>
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_iter_next_value (GVariantIter *iter)
-{
- g_return_val_if_fail (is_valid_iter (iter), FALSE);
-
- if G_UNLIKELY (GVSI(iter)->i >= GVSI(iter)->n)
- {
- g_critical ("g_variant_iter_next_value: must not be called again "
- "after NULL has already been returned.");
- return NULL;
- }
-
- GVSI(iter)->i++;
-
- if (GVSI(iter)->i < GVSI(iter)->n)
- return g_variant_get_child_value (GVSI(iter)->value, GVSI(iter)->i);
-
- return NULL;
-}
-
-/* GVariantBuilder {{{1 */
-/**
- * GVariantBuilder:
- *
- * A utility type for constructing container-type #GVariant instances.
- *
- * This is an opaque structure and may only be accessed using the
- * following functions.
- *
- * #GVariantBuilder is not threadsafe in any way. Do not attempt to
- * access it from more than one thread.
- **/
-
-struct stack_builder
-{
- GVariantBuilder *parent;
- GVariantType *type;
-
- /* type constraint explicitly specified by 'type'.
- * for tuple types, this moves along as we add more items.
- */
- const GVariantType *expected_type;
-
- /* type constraint implied by previous array item.
- */
- const GVariantType *prev_item_type;
-
- /* constraints on the number of children. max = -1 for unlimited. */
- gsize min_items;
- gsize max_items;
-
- /* dynamically-growing pointer array */
- GVariant **children;
- gsize allocated_children;
- gsize offset;
-
- /* set to '1' if all items in the container will have the same type
- * (ie: maybe, array, variant) '0' if not (ie: tuple, dict entry)
- */
- guint uniform_item_types : 1;
-
- /* set to '1' initially and changed to '0' if an untrusted value is
- * added
- */
- guint trusted : 1;
-
- gsize magic;
-};
-
-G_STATIC_ASSERT (sizeof (struct stack_builder) <= sizeof (GVariantBuilder));
-
-struct heap_builder
-{
- GVariantBuilder builder;
- gsize magic;
-
- gint ref_count;
-};
-
-#define GVSB(b) ((struct stack_builder *) (b))
-#define GVHB(b) ((struct heap_builder *) (b))
-#define GVSB_MAGIC ((gsize) 1033660112u)
-#define GVHB_MAGIC ((gsize) 3087242682u)
-#define is_valid_builder(b) (b != NULL && \
- GVSB(b)->magic == GVSB_MAGIC)
-#define is_valid_heap_builder(b) (GVHB(b)->magic == GVHB_MAGIC)
-
-/**
- * g_variant_builder_new:
- * @type: a container type
- * @returns: a #GVariantBuilder
- *
- * Allocates and initialises a new #GVariantBuilder.
- *
- * You should call g_variant_builder_unref() on the return value when it
- * is no longer needed. The memory will not be automatically freed by
- * any other call.
- *
- * In most cases it is easier to place a #GVariantBuilder directly on
- * the stack of the calling function and initialise it with
- * g_variant_builder_init().
- *
- * Since: 2.24
- **/
-GVariantBuilder *
-g_variant_builder_new (const GVariantType *type)
-{
- GVariantBuilder *builder;
-
- builder = (GVariantBuilder *) g_slice_new (struct heap_builder);
- g_variant_builder_init (builder, type);
- GVHB(builder)->magic = GVHB_MAGIC;
- GVHB(builder)->ref_count = 1;
-
- return builder;
-}
-
-/**
- * g_variant_builder_unref:
- * @builder: a #GVariantBuilder allocated by g_variant_builder_new()
- *
- * Decreases the reference count on @builder.
- *
- * In the event that there are no more references, releases all memory
- * associated with the #GVariantBuilder.
- *
- * Don't call this on stack-allocated #GVariantBuilder instances or bad
- * things will happen.
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_unref (GVariantBuilder *builder)
-{
- g_return_if_fail (is_valid_heap_builder (builder));
-
- if (--GVHB(builder)->ref_count)
- return;
-
- g_variant_builder_clear (builder);
- GVHB(builder)->magic = 0;
-
- g_slice_free (struct heap_builder, GVHB(builder));
-}
-
-/**
- * g_variant_builder_ref:
- * @builder: a #GVariantBuilder allocated by g_variant_builder_new()
- * @returns: a new reference to @builder
- *
- * Increases the reference count on @builder.
- *
- * Don't call this on stack-allocated #GVariantBuilder instances or bad
- * things will happen.
- *
- * Since: 2.24
- **/
-GVariantBuilder *
-g_variant_builder_ref (GVariantBuilder *builder)
-{
- g_return_val_if_fail (is_valid_heap_builder (builder), NULL);
-
- GVHB(builder)->ref_count++;
-
- return builder;
-}
-
-/**
- * g_variant_builder_clear:
- * @builder: a #GVariantBuilder
- *
- * Releases all memory associated with a #GVariantBuilder without
- * freeing the #GVariantBuilder structure itself.
- *
- * It typically only makes sense to do this on a stack-allocated
- * #GVariantBuilder if you want to abort building the value part-way
- * through. This function need not be called if you call
- * g_variant_builder_end() and it also doesn't need to be called on
- * builders allocated with g_variant_builder_new (see
- * g_variant_builder_free() for that).
- *
- * This function leaves the #GVariantBuilder structure set to all-zeros.
- * It is valid to call this function on either an initialised
- * #GVariantBuilder or one that is set to all-zeros but it is not valid
- * to call this function on uninitialised memory.
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_clear (GVariantBuilder *builder)
-{
- gsize i;
-
- if (GVSB(builder)->magic == 0)
- /* all-zeros case */
- return;
-
- g_return_if_fail (is_valid_builder (builder));
-
- g_variant_type_free (GVSB(builder)->type);
-
- for (i = 0; i < GVSB(builder)->offset; i++)
- g_variant_unref (GVSB(builder)->children[i]);
-
- g_free (GVSB(builder)->children);
-
- if (GVSB(builder)->parent)
- {
- g_variant_builder_clear (GVSB(builder)->parent);
- g_slice_free (GVariantBuilder, GVSB(builder)->parent);
- }
-
- memset (builder, 0, sizeof (GVariantBuilder));
-}
-
-/**
- * g_variant_builder_init:
- * @builder: a #GVariantBuilder
- * @type: a container type
- *
- * Initialises a #GVariantBuilder structure.
- *
- * @type must be non-%NULL. It specifies the type of container to
- * construct. It can be an indefinite type such as
- * %G_VARIANT_TYPE_ARRAY or a definite type such as "as" or "(ii)".
- * Maybe, array, tuple, dictionary entry and variant-typed values may be
- * constructed.
- *
- * After the builder is initialised, values are added using
- * g_variant_builder_add_value() or g_variant_builder_add().
- *
- * After all the child values are added, g_variant_builder_end() frees
- * the memory associated with the builder and returns the #GVariant that
- * was created.
- *
- * This function completely ignores the previous contents of @builder.
- * On one hand this means that it is valid to pass in completely
- * uninitialised memory. On the other hand, this means that if you are
- * initialising over top of an existing #GVariantBuilder you need to
- * first call g_variant_builder_clear() in order to avoid leaking
- * memory.
- *
- * You must not call g_variant_builder_ref() or
- * g_variant_builder_unref() on a #GVariantBuilder that was initialised
- * with this function. If you ever pass a reference to a
- * #GVariantBuilder outside of the control of your own code then you
- * should assume that the person receiving that reference may try to use
- * reference counting; you should use g_variant_builder_new() instead of
- * this function.
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_init (GVariantBuilder *builder,
- const GVariantType *type)
-{
- g_return_if_fail (type != NULL);
- g_return_if_fail (g_variant_type_is_container (type));
-
- memset (builder, 0, sizeof (GVariantBuilder));
-
- GVSB(builder)->type = g_variant_type_copy (type);
- GVSB(builder)->magic = GVSB_MAGIC;
- GVSB(builder)->trusted = TRUE;
-
- switch (*(const gchar *) type)
- {
- case G_VARIANT_CLASS_VARIANT:
- GVSB(builder)->uniform_item_types = TRUE;
- GVSB(builder)->allocated_children = 1;
- GVSB(builder)->expected_type = NULL;
- GVSB(builder)->min_items = 1;
- GVSB(builder)->max_items = 1;
- break;
-
- case G_VARIANT_CLASS_ARRAY:
- GVSB(builder)->uniform_item_types = TRUE;
- GVSB(builder)->allocated_children = 8;
- GVSB(builder)->expected_type =
- g_variant_type_element (GVSB(builder)->type);
- GVSB(builder)->min_items = 0;
- GVSB(builder)->max_items = -1;
- break;
-
- case G_VARIANT_CLASS_MAYBE:
- GVSB(builder)->uniform_item_types = TRUE;
- GVSB(builder)->allocated_children = 1;
- GVSB(builder)->expected_type =
- g_variant_type_element (GVSB(builder)->type);
- GVSB(builder)->min_items = 0;
- GVSB(builder)->max_items = 1;
- break;
-
- case G_VARIANT_CLASS_DICT_ENTRY:
- GVSB(builder)->uniform_item_types = FALSE;
- GVSB(builder)->allocated_children = 2;
- GVSB(builder)->expected_type =
- g_variant_type_key (GVSB(builder)->type);
- GVSB(builder)->min_items = 2;
- GVSB(builder)->max_items = 2;
- break;
-
- case 'r': /* G_VARIANT_TYPE_TUPLE was given */
- GVSB(builder)->uniform_item_types = FALSE;
- GVSB(builder)->allocated_children = 8;
- GVSB(builder)->expected_type = NULL;
- GVSB(builder)->min_items = 0;
- GVSB(builder)->max_items = -1;
- break;
-
- case G_VARIANT_CLASS_TUPLE: /* a definite tuple type was given */
- GVSB(builder)->allocated_children = g_variant_type_n_items (type);
- GVSB(builder)->expected_type =
- g_variant_type_first (GVSB(builder)->type);
- GVSB(builder)->min_items = GVSB(builder)->allocated_children;
- GVSB(builder)->max_items = GVSB(builder)->allocated_children;
- GVSB(builder)->uniform_item_types = FALSE;
- break;
-
- default:
- g_assert_not_reached ();
- }
-
- GVSB(builder)->children = g_new (GVariant *,
- GVSB(builder)->allocated_children);
-}
-
-static void
-g_variant_builder_make_room (struct stack_builder *builder)
-{
- if (builder->offset == builder->allocated_children)
- {
- builder->allocated_children *= 2;
- builder->children = g_renew (GVariant *, builder->children,
- builder->allocated_children);
- }
-}
-
-/**
- * g_variant_builder_add_value:
- * @builder: a #GVariantBuilder
- * @value: a #GVariant
- *
- * Adds @value to @builder.
- *
- * It is an error to call this function in any way that would create an
- * inconsistent value to be constructed. Some examples of this are
- * putting different types of items into an array, putting the wrong
- * types or number of items in a tuple, putting more than one value into
- * a variant, etc.
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_add_value (GVariantBuilder *builder,
- GVariant *value)
-{
- g_return_if_fail (is_valid_builder (builder));
- g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items);
- g_return_if_fail (!GVSB(builder)->expected_type ||
- g_variant_is_of_type (value,
- GVSB(builder)->expected_type));
- g_return_if_fail (!GVSB(builder)->prev_item_type ||
- g_variant_is_of_type (value,
- GVSB(builder)->prev_item_type));
-
- GVSB(builder)->trusted &= g_variant_is_trusted (value);
-
- if (!GVSB(builder)->uniform_item_types)
- {
- /* advance our expected type pointers */
- if (GVSB(builder)->expected_type)
- GVSB(builder)->expected_type =
- g_variant_type_next (GVSB(builder)->expected_type);
-
- if (GVSB(builder)->prev_item_type)
- GVSB(builder)->prev_item_type =
- g_variant_type_next (GVSB(builder)->prev_item_type);
- }
- else
- GVSB(builder)->prev_item_type = g_variant_get_type (value);
-
- g_variant_builder_make_room (GVSB(builder));
-
- GVSB(builder)->children[GVSB(builder)->offset++] =
- g_variant_ref_sink (value);
-}
-
-/**
- * g_variant_builder_open:
- * @builder: a #GVariantBuilder
- * @type: a #GVariantType
- *
- * Opens a subcontainer inside the given @builder. When done adding
- * items to the subcontainer, g_variant_builder_close() must be called.
- *
- * It is an error to call this function in any way that would cause an
- * inconsistent value to be constructed (ie: adding too many values or
- * a value of an incorrect type).
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_open (GVariantBuilder *builder,
- const GVariantType *type)
-{
- GVariantBuilder *parent;
-
- g_return_if_fail (is_valid_builder (builder));
- g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items);
- g_return_if_fail (!GVSB(builder)->expected_type ||
- g_variant_type_is_subtype_of (type,
- GVSB(builder)->expected_type));
- g_return_if_fail (!GVSB(builder)->prev_item_type ||
- g_variant_type_is_subtype_of (GVSB(builder)->prev_item_type,
- type));
-
- parent = g_slice_dup (GVariantBuilder, builder);
- g_variant_builder_init (builder, type);
- GVSB(builder)->parent = parent;
-
- /* push the prev_item_type down into the subcontainer */
- if (GVSB(parent)->prev_item_type)
- {
- if (!GVSB(builder)->uniform_item_types)
- /* tuples and dict entries */
- GVSB(builder)->prev_item_type =
- g_variant_type_first (GVSB(parent)->prev_item_type);
-
- else if (!g_variant_type_is_variant (GVSB(builder)->type))
- /* maybes and arrays */
- GVSB(builder)->prev_item_type =
- g_variant_type_element (GVSB(parent)->prev_item_type);
- }
-}
-
-/**
- * g_variant_builder_close:
- * @builder: a #GVariantBuilder
- *
- * Closes the subcontainer inside the given @builder that was opened by
- * the most recent call to g_variant_builder_open().
- *
- * It is an error to call this function in any way that would create an
- * inconsistent value to be constructed (ie: too few values added to the
- * subcontainer).
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_close (GVariantBuilder *builder)
-{
- GVariantBuilder *parent;
-
- g_return_if_fail (is_valid_builder (builder));
- g_return_if_fail (GVSB(builder)->parent != NULL);
-
- parent = GVSB(builder)->parent;
- GVSB(builder)->parent = NULL;
-
- g_variant_builder_add_value (parent, g_variant_builder_end (builder));
- *builder = *parent;
-
- g_slice_free (GVariantBuilder, parent);
-}
-
-/*< private >
- * g_variant_make_maybe_type:
- * @element: a #GVariant
- *
- * Return the type of a maybe containing @element.
- */
-static GVariantType *
-g_variant_make_maybe_type (GVariant *element)
-{
- return g_variant_type_new_maybe (g_variant_get_type (element));
-}
-
-/*< private >
- * g_variant_make_array_type:
- * @element: a #GVariant
- *
- * Return the type of an array containing @element.
- */
-static GVariantType *
-g_variant_make_array_type (GVariant *element)
-{
- return g_variant_type_new_array (g_variant_get_type (element));
-}
-
-/**
- * g_variant_builder_end:
- * @builder: a #GVariantBuilder
- * @returns: (transfer none): a new, floating, #GVariant
- *
- * Ends the builder process and returns the constructed value.
- *
- * It is not permissible to use @builder in any way after this call
- * except for reference counting operations (in the case of a
- * heap-allocated #GVariantBuilder) or by reinitialising it with
- * g_variant_builder_init() (in the case of stack-allocated).
- *
- * It is an error to call this function in any way that would create an
- * inconsistent value to be constructed (ie: insufficient number of
- * items added to a container with a specific number of children
- * required). It is also an error to call this function if the builder
- * was created with an indefinite array or maybe type and no children
- * have been added; in this case it is impossible to infer the type of
- * the empty array.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_builder_end (GVariantBuilder *builder)
-{
- GVariantType *my_type;
- GVariant *value;
-
- g_return_val_if_fail (is_valid_builder (builder), NULL);
- g_return_val_if_fail (GVSB(builder)->offset >= GVSB(builder)->min_items,
- NULL);
- g_return_val_if_fail (!GVSB(builder)->uniform_item_types ||
- GVSB(builder)->prev_item_type != NULL ||
- g_variant_type_is_definite (GVSB(builder)->type),
- NULL);
-
- if (g_variant_type_is_definite (GVSB(builder)->type))
- my_type = g_variant_type_copy (GVSB(builder)->type);
-
- else if (g_variant_type_is_maybe (GVSB(builder)->type))
- my_type = g_variant_make_maybe_type (GVSB(builder)->children[0]);
-
- else if (g_variant_type_is_array (GVSB(builder)->type))
- my_type = g_variant_make_array_type (GVSB(builder)->children[0]);
-
- else if (g_variant_type_is_tuple (GVSB(builder)->type))
- my_type = g_variant_make_tuple_type (GVSB(builder)->children,
- GVSB(builder)->offset);
-
- else if (g_variant_type_is_dict_entry (GVSB(builder)->type))
- my_type = g_variant_make_dict_entry_type (GVSB(builder)->children[0],
- GVSB(builder)->children[1]);
- else
- g_assert_not_reached ();
-
- value = g_variant_new_from_children (my_type,
- g_renew (GVariant *,
- GVSB(builder)->children,
- GVSB(builder)->offset),
- GVSB(builder)->offset,
- GVSB(builder)->trusted);
- GVSB(builder)->children = NULL;
- GVSB(builder)->offset = 0;
-
- g_variant_builder_clear (builder);
- g_variant_type_free (my_type);
-
- return value;
-}
-
-/* Format strings {{{1 */
-/*< private >
- * g_variant_format_string_scan:
- * @string: a string that may be prefixed with a format string
- * @limit: (allow-none) (default NULL): a pointer to the end of @string,
- * or %NULL
- * @endptr: (allow-none) (default NULL): location to store the end pointer,
- * or %NULL
- * @returns: %TRUE if there was a valid format string
- *
- * Checks the string pointed to by @string for starting with a properly
- * formed #GVariant varargs format string. If no valid format string is
- * found then %FALSE is returned.
- *
- * If @string does start with a valid format string then %TRUE is
- * returned. If @endptr is non-%NULL then it is updated to point to the
- * first character after the format string.
- *
- * If @limit is non-%NULL then @limit (and any charater after it) will
- * not be accessed and the effect is otherwise equivalent to if the
- * character at @limit were nul.
- *
- * See the section on <link linkend='gvariant-format-strings'>GVariant
- * Format Strings</link>.
- *
- * Since: 2.24
- */
-gboolean
-g_variant_format_string_scan (const gchar *string,
- const gchar *limit,
- const gchar **endptr)
-{
-#define next_char() (string == limit ? '\0' : *string++)
-#define peek_char() (string == limit ? '\0' : *string)
- char c;
-
- switch (next_char())
- {
- case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
- case 'x': case 't': case 'h': case 'd': case 's': case 'o':
- case 'g': case 'v': case '*': case '?': case 'r':
- break;
-
- case 'm':
- return g_variant_format_string_scan (string, limit, endptr);
-
- case 'a':
- case '@':
- return g_variant_type_string_scan (string, limit, endptr);
-
- case '(':
- while (peek_char() != ')')
- if (!g_variant_format_string_scan (string, limit, &string))
- return FALSE;
-
- next_char(); /* consume ')' */
- break;
-
- case '{':
- c = next_char();
-
- if (c == '&')
- {
- c = next_char ();
-
- if (c != 's' && c != 'o' && c != 'g')
- return FALSE;
- }
- else
- {
- if (c == '@')
- c = next_char ();
-
- /* ISO/IEC 9899:1999 (C99) §7.21.5.2:
- * The terminating null character is considered to be
- * part of the string.
- */
- if (c != '\0' && strchr ("bynqiuxthdsog?", c) == NULL)
- return FALSE;
- }
-
- if (!g_variant_format_string_scan (string, limit, &string))
- return FALSE;
-
- if (next_char() != '}')
- return FALSE;
-
- break;
-
- case '^':
- if ((c = next_char()) == 'a')
- {
- if ((c = next_char()) == '&')
- {
- if ((c = next_char()) == 'a')
- {
- if ((c = next_char()) == 'y')
- break; /* '^a&ay' */
- }
-
- else if (c == 's')
- break; /* '^a&s' */
- }
-
- else if (c == 'a')
- {
- if ((c = next_char()) == 'y')
- break; /* '^aay' */
- }
-
- else if (c == 's')
- break; /* '^as' */
-
- else if (c == 'y')
- break; /* '^ay' */
- }
- else if (c == '&')
- {
- if ((c = next_char()) == 'a')
- {
- if ((c = next_char()) == 'y')
- break; /* '^&ay' */
- }
- }
-
- return FALSE;
-
- case '&':
- c = next_char();
-
- if (c != 's' && c != 'o' && c != 'g')
- return FALSE;
-
- break;
-
- default:
- return FALSE;
- }
-
- if (endptr != NULL)
- *endptr = string;
-
-#undef next_char
-#undef peek_char
-
- return TRUE;
-}
-
-/*< private >
- * g_variant_format_string_scan_type:
- * @string: a string that may be prefixed with a format string
- * @limit: (allow-none) (default NULL): a pointer to the end of @string,
- * or %NULL
- * @endptr: (allow-none) (default NULL): location to store the end pointer,
- * or %NULL
- * @returns: (allow-none): a #GVariantType if there was a valid format string
- *
- * If @string starts with a valid format string then this function will
- * return the type that the format string corresponds to. Otherwise
- * this function returns %NULL.
- *
- * Use g_variant_type_free() to free the return value when you no longer
- * need it.
- *
- * This function is otherwise exactly like
- * g_variant_format_string_scan().
- *
- * Since: 2.24
- */
-GVariantType *
-g_variant_format_string_scan_type (const gchar *string,
- const gchar *limit,
- const gchar **endptr)
-{
- const gchar *my_end;
- gchar *dest;
- gchar *new;
-
- if (endptr == NULL)
- endptr = &my_end;
-
- if (!g_variant_format_string_scan (string, limit, endptr))
- return NULL;
-
- dest = new = g_malloc (*endptr - string + 1);
- while (string != *endptr)
- {
- if (*string != '@' && *string != '&' && *string != '^')
- *dest++ = *string;
- string++;
- }
- *dest = '\0';
-
- return (GVariantType *) G_VARIANT_TYPE (new);
-}
-
-static gboolean
-valid_format_string (const gchar *format_string,
- gboolean single,
- GVariant *value)
-{
- const gchar *endptr;
- GVariantType *type;
-
- type = g_variant_format_string_scan_type (format_string, NULL, &endptr);
-
- if G_UNLIKELY (type == NULL || (single && *endptr != '\0'))
- {
- if (single)
- g_critical ("`%s' is not a valid GVariant format string",
- format_string);
- else
- g_critical ("`%s' does not have a valid GVariant format "
- "string as a prefix", format_string);
-
- if (type != NULL)
- g_variant_type_free (type);
-
- return FALSE;
- }
-
- if G_UNLIKELY (value && !g_variant_is_of_type (value, type))
- {
- gchar *fragment;
- gchar *typestr;
-
- fragment = g_strndup (format_string, endptr - format_string);
- typestr = g_variant_type_dup_string (type);
-
- g_critical ("the GVariant format string `%s' has a type of "
- "`%s' but the given value has a type of `%s'",
- fragment, typestr, g_variant_get_type_string (value));
-
- g_variant_type_free (type);
-
- return FALSE;
- }
-
- g_variant_type_free (type);
-
- return TRUE;
-}
-
-/* Variable Arguments {{{1 */
-/* We consider 2 main classes of format strings:
- *
- * - recursive format strings
- * these are ones that result in recursion and the collection of
- * possibly more than one argument. Maybe types, tuples,
- * dictionary entries.
- *
- * - leaf format string
- * these result in the collection of a single argument.
- *
- * Leaf format strings are further subdivided into two categories:
- *
- * - single non-null pointer ("nnp")
- * these either collect or return a single non-null pointer.
- *
- * - other
- * these collect or return something else (bool, number, etc).
- *
- * Based on the above, the varargs handling code is split into 4 main parts:
- *
- * - nnp handling code
- * - leaf handling code (which may invoke nnp code)
- * - generic handling code (may be recursive, may invoke leaf code)
- * - user-facing API (which invokes the generic code)
- *
- * Each section implements some of the following functions:
- *
- * - skip:
- * collect the arguments for the format string as if
- * g_variant_new() had been called, but do nothing with them. used
- * for skipping over arguments when constructing a Nothing maybe
- * type.
- *
- * - new:
- * create a GVariant *
- *
- * - get:
- * unpack a GVariant *
- *
- * - free (nnp only):
- * free a previously allocated item
- */
-
-static gboolean
-g_variant_format_string_is_leaf (const gchar *str)
-{
- return str[0] != 'm' && str[0] != '(' && str[0] != '{';
-}
-
-static gboolean
-g_variant_format_string_is_nnp (const gchar *str)
-{
- return str[0] == 'a' || str[0] == 's' || str[0] == 'o' || str[0] == 'g' ||
- str[0] == '^' || str[0] == '@' || str[0] == '*' || str[0] == '?' ||
- str[0] == 'r' || str[0] == 'v' || str[0] == '&';
-}
-
-/* Single non-null pointer ("nnp") {{{2 */
-static void
-g_variant_valist_free_nnp (const gchar *str,
- gpointer ptr)
-{
- switch (*str)
- {
- case 'a':
- g_variant_iter_free (ptr);
- break;
-
- case '^':
- if (str[2] != '&') /* '^as' */
- g_strfreev (ptr);
- else /* '^a&s' */
- g_free (ptr);
- break;
-
- case 's':
- case 'o':
- case 'g':
- g_free (ptr);
- break;
-
- case '@':
- case '*':
- case '?':
- case 'v':
- g_variant_unref (ptr);
- break;
-
- case '&':
- break;
-
- default:
- g_assert_not_reached ();
- }
-}
-
-static gchar
-g_variant_scan_convenience (const gchar **str,
- gboolean *constant,
- guint *arrays)
-{
- *constant = FALSE;
- *arrays = 0;
-
- for (;;)
- {
- char c = *(*str)++;
-
- if (c == '&')
- *constant = TRUE;
-
- else if (c == 'a')
- (*arrays)++;
-
- else
- return c;
- }
-}
-
-static GVariant *
-g_variant_valist_new_nnp (const gchar **str,
- gpointer ptr)
-{
- if (**str == '&')
- (*str)++;
-
- switch (*(*str)++)
- {
- case 'a':
- {
- const GVariantType *type;
- GVariant *value;
-
- value = g_variant_builder_end (ptr);
- type = g_variant_get_type (value);
-
- if G_UNLIKELY (!g_variant_type_is_array (type))
- g_error ("g_variant_new: expected array GVariantBuilder but "
- "the built value has type `%s'",
- g_variant_get_type_string (value));
-
- type = g_variant_type_element (type);
-
- if G_UNLIKELY (!g_variant_type_is_subtype_of (type, (GVariantType *) *str))
- g_error ("g_variant_new: expected GVariantBuilder array element "
- "type `%s' but the built value has element type `%s'",
- g_variant_type_dup_string ((GVariantType *) *str),
- g_variant_get_type_string (value) + 1);
-
- g_variant_type_string_scan (*str, NULL, str);
-
- return value;
- }
-
- case 's':
- return g_variant_new_string (ptr);
-
- case 'o':
- return g_variant_new_object_path (ptr);
-
- case 'g':
- return g_variant_new_signature (ptr);
-
- case '^':
- {
- gboolean constant;
- guint arrays;
-
- if (g_variant_scan_convenience (str, &constant, &arrays) == 's')
- return g_variant_new_strv (ptr, -1);
-
- if (arrays > 1)
- return g_variant_new_bytestring_array (ptr, -1);
-
- return g_variant_new_bytestring (ptr);
- }
-
- case '@':
- if G_UNLIKELY (!g_variant_is_of_type (ptr, (GVariantType *) *str))
- g_error ("g_variant_new: expected GVariant of type `%s' but "
- "received value has type `%s'",
- g_variant_type_dup_string ((GVariantType *) *str),
- g_variant_get_type_string (ptr));
-
- g_variant_type_string_scan (*str, NULL, str);
-
- return ptr;
-
- case '*':
- return ptr;
-
- case '?':
- if G_UNLIKELY (!g_variant_type_is_basic (g_variant_get_type (ptr)))
- g_error ("g_variant_new: format string `?' expects basic-typed "
- "GVariant, but received value has type `%s'",
- g_variant_get_type_string (ptr));
-
- return ptr;
-
- case 'r':
- if G_UNLIKELY (!g_variant_type_is_tuple (g_variant_get_type (ptr)))
- g_error ("g_variant_new: format string `r` expects tuple-typed "
- "GVariant, but received value has type `%s'",
- g_variant_get_type_string (ptr));
-
- return ptr;
-
- case 'v':
- return g_variant_new_variant (ptr);
-
- default:
- g_assert_not_reached ();
- }
-}
-
-static gpointer
-g_variant_valist_get_nnp (const gchar **str,
- GVariant *value)
-{
- switch (*(*str)++)
- {
- case 'a':
- g_variant_type_string_scan (*str, NULL, str);
- return g_variant_iter_new (value);
-
- case '&':
- (*str)++;
- return (gchar *) g_variant_get_string (value, NULL);
-
- case 's':
- case 'o':
- case 'g':
- return g_variant_dup_string (value, NULL);
-
- case '^':
- {
- gboolean constant;
- guint arrays;
-
- if (g_variant_scan_convenience (str, &constant, &arrays) == 's')
- {
- if (constant)
- return g_variant_get_strv (value, NULL);
- else
- return g_variant_dup_strv (value, NULL);
- }
-
- else if (arrays > 1)
- {
- if (constant)
- return g_variant_get_bytestring_array (value, NULL);
- else
- return g_variant_dup_bytestring_array (value, NULL);
- }
-
- else
- {
- if (constant)
- return (gchar *) g_variant_get_bytestring (value);
- else
- return g_variant_dup_bytestring (value, NULL);
- }
- }
-
- case '@':
- g_variant_type_string_scan (*str, NULL, str);
- /* fall through */
-
- case '*':
- case '?':
- case 'r':
- return g_variant_ref (value);
-
- case 'v':
- return g_variant_get_variant (value);
-
- default:
- g_assert_not_reached ();
- }
-}
-
-/* Leaves {{{2 */
-static void
-g_variant_valist_skip_leaf (const gchar **str,
- va_list *app)
-{
- if (g_variant_format_string_is_nnp (*str))
- {
- g_variant_format_string_scan (*str, NULL, str);
- va_arg (*app, gpointer);
- return;
- }
-
- switch (*(*str)++)
- {
- case 'b':
- case 'y':
- case 'n':
- case 'q':
- case 'i':
- case 'u':
- case 'h':
- va_arg (*app, int);
- return;
-
- case 'x':
- case 't':
- va_arg (*app, guint64);
- return;
-
- case 'd':
- va_arg (*app, gdouble);
- return;
-
- default:
- g_assert_not_reached ();
- }
-}
-
-static GVariant *
-g_variant_valist_new_leaf (const gchar **str,
- va_list *app)
-{
- if (g_variant_format_string_is_nnp (*str))
- return g_variant_valist_new_nnp (str, va_arg (*app, gpointer));
-
- switch (*(*str)++)
- {
- case 'b':
- return g_variant_new_boolean (va_arg (*app, gboolean));
-
- case 'y':
- return g_variant_new_byte (va_arg (*app, guint));
-
- case 'n':
- return g_variant_new_int16 (va_arg (*app, gint));
-
- case 'q':
- return g_variant_new_uint16 (va_arg (*app, guint));
-
- case 'i':
- return g_variant_new_int32 (va_arg (*app, gint));
-
- case 'u':
- return g_variant_new_uint32 (va_arg (*app, guint));
-
- case 'x':
- return g_variant_new_int64 (va_arg (*app, gint64));
-
- case 't':
- return g_variant_new_uint64 (va_arg (*app, guint64));
-
- case 'h':
- return g_variant_new_handle (va_arg (*app, gint));
-
- case 'd':
- return g_variant_new_double (va_arg (*app, gdouble));
-
- default:
- g_assert_not_reached ();
- }
-}
-
-/* The code below assumes this */
-G_STATIC_ASSERT (sizeof (gboolean) == sizeof (guint32));
-G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
-
-static void
-g_variant_valist_get_leaf (const gchar **str,
- GVariant *value,
- gboolean free,
- va_list *app)
-{
- gpointer ptr = va_arg (*app, gpointer);
-
- if (ptr == NULL)
- {
- g_variant_format_string_scan (*str, NULL, str);
- return;
- }
-
- if (g_variant_format_string_is_nnp (*str))
- {
- gpointer *nnp = (gpointer *) ptr;
-
- if (free && *nnp != NULL)
- g_variant_valist_free_nnp (*str, *nnp);
-
- *nnp = NULL;
-
- if (value != NULL)
- *nnp = g_variant_valist_get_nnp (str, value);
- else
- g_variant_format_string_scan (*str, NULL, str);
-
- return;
- }
-
- if (value != NULL)
- {
- switch (*(*str)++)
- {
- case 'b':
- *(gboolean *) ptr = g_variant_get_boolean (value);
- return;
-
- case 'y':
- *(guchar *) ptr = g_variant_get_byte (value);
- return;
-
- case 'n':
- *(gint16 *) ptr = g_variant_get_int16 (value);
- return;
-
- case 'q':
- *(guint16 *) ptr = g_variant_get_uint16 (value);
- return;
-
- case 'i':
- *(gint32 *) ptr = g_variant_get_int32 (value);
- return;
-
- case 'u':
- *(guint32 *) ptr = g_variant_get_uint32 (value);
- return;
-
- case 'x':
- *(gint64 *) ptr = g_variant_get_int64 (value);
- return;
-
- case 't':
- *(guint64 *) ptr = g_variant_get_uint64 (value);
- return;
-
- case 'h':
- *(gint32 *) ptr = g_variant_get_handle (value);
- return;
-
- case 'd':
- *(gdouble *) ptr = g_variant_get_double (value);
- return;
- }
- }
- else
- {
- switch (*(*str)++)
- {
- case 'y':
- *(guchar *) ptr = 0;
- return;
-
- case 'n':
- case 'q':
- *(guint16 *) ptr = 0;
- return;
-
- case 'i':
- case 'u':
- case 'h':
- case 'b':
- *(guint32 *) ptr = 0;
- return;
-
- case 'x':
- case 't':
- case 'd':
- *(guint64 *) ptr = 0;
- return;
- }
- }
-
- g_assert_not_reached ();
-}
-
-/* Generic (recursive) {{{2 */
-static void
-g_variant_valist_skip (const gchar **str,
- va_list *app)
-{
- if (g_variant_format_string_is_leaf (*str))
- g_variant_valist_skip_leaf (str, app);
-
- else if (**str == 'm') /* maybe */
- {
- (*str)++;
-
- if (!g_variant_format_string_is_nnp (*str))
- va_arg (*app, gboolean);
-
- g_variant_valist_skip (str, app);
- }
- else /* tuple, dictionary entry */
- {
- g_assert (**str == '(' || **str == '{');
- (*str)++;
- while (**str != ')' && **str != '}')
- g_variant_valist_skip (str, app);
- (*str)++;
- }
-}
-
-static GVariant *
-g_variant_valist_new (const gchar **str,
- va_list *app)
-{
- if (g_variant_format_string_is_leaf (*str))
- return g_variant_valist_new_leaf (str, app);
-
- if (**str == 'm') /* maybe */
- {
- GVariantType *type = NULL;
- GVariant *value = NULL;
-
- (*str)++;
-
- if (g_variant_format_string_is_nnp (*str))
- {
- gpointer nnp = va_arg (*app, gpointer);
-
- if (nnp != NULL)
- value = g_variant_valist_new_nnp (str, nnp);
- else
- type = g_variant_format_string_scan_type (*str, NULL, str);
- }
- else
- {
- gboolean just = va_arg (*app, gboolean);
-
- if (just)
- value = g_variant_valist_new (str, app);
- else
- {
- type = g_variant_format_string_scan_type (*str, NULL, NULL);
- g_variant_valist_skip (str, app);
- }
- }
-
- value = g_variant_new_maybe (type, value);
-
- if (type != NULL)
- g_variant_type_free (type);
-
- return value;
- }
- else /* tuple, dictionary entry */
- {
- GVariantBuilder b;
-
- if (**str == '(')
- g_variant_builder_init (&b, G_VARIANT_TYPE_TUPLE);
- else
- {
- g_assert (**str == '{');
- g_variant_builder_init (&b, G_VARIANT_TYPE_DICT_ENTRY);
- }
-
- (*str)++; /* '(' */
- while (**str != ')' && **str != '}')
- g_variant_builder_add_value (&b, g_variant_valist_new (str, app));
- (*str)++; /* ')' */
-
- return g_variant_builder_end (&b);
- }
-}
-
-static void
-g_variant_valist_get (const gchar **str,
- GVariant *value,
- gboolean free,
- va_list *app)
-{
- if (g_variant_format_string_is_leaf (*str))
- g_variant_valist_get_leaf (str, value, free, app);
-
- else if (**str == 'm')
- {
- (*str)++;
-
- if (value != NULL)
- value = g_variant_get_maybe (value);
-
- if (!g_variant_format_string_is_nnp (*str))
- {
- gboolean *ptr = va_arg (*app, gboolean *);
-
- if (ptr != NULL)
- *ptr = value != NULL;
- }
-
- g_variant_valist_get (str, value, free, app);
-
- if (value != NULL)
- g_variant_unref (value);
- }
-
- else /* tuple, dictionary entry */
- {
- gint index = 0;
-
- g_assert (**str == '(' || **str == '{');
-
- (*str)++;
- while (**str != ')' && **str != '}')
- {
- if (value != NULL)
- {
- GVariant *child = g_variant_get_child_value (value, index++);
- g_variant_valist_get (str, child, free, app);
- g_variant_unref (child);
- }
- else
- g_variant_valist_get (str, NULL, free, app);
- }
- (*str)++;
- }
-}
-
-/* User-facing API {{{2 */
-/**
- * g_variant_new:
- * @format_string: a #GVariant format string
- * @...: arguments, as per @format_string
- * @returns: a new floating #GVariant instance
- *
- * Creates a new #GVariant instance.
- *
- * Think of this function as an analogue to g_strdup_printf().
- *
- * The type of the created instance and the arguments that are
- * expected by this function are determined by @format_string. See the
- * section on <link linkend='gvariant-format-strings'>GVariant Format
- * Strings</link>. Please note that the syntax of the format string is
- * very likely to be extended in the future.
- *
- * The first character of the format string must not be '*' '?' '@' or
- * 'r'; in essence, a new #GVariant must always be constructed by this
- * function (and not merely passed through it unmodified).
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new (const gchar *format_string,
- ...)
-{
- GVariant *value;
- va_list ap;
-
- g_return_val_if_fail (valid_format_string (format_string, TRUE, NULL) &&
- format_string[0] != '?' && format_string[0] != '@' &&
- format_string[0] != '*' && format_string[0] != 'r',
- NULL);
-
- va_start (ap, format_string);
- value = g_variant_new_va (format_string, NULL, &ap);
- va_end (ap);
-
- return value;
-}
-
-/**
- * g_variant_new_va:
- * @format_string: a string that is prefixed with a format string
- * @endptr: (allow-none) (default NULL): location to store the end pointer,
- * or %NULL
- * @app: a pointer to a #va_list
- * @returns: a new, usually floating, #GVariant
- *
- * This function is intended to be used by libraries based on
- * #GVariant that want to provide g_variant_new()-like functionality
- * to their users.
- *
- * The API is more general than g_variant_new() to allow a wider range
- * of possible uses.
- *
- * @format_string must still point to a valid format string, but it only
- * needs to be nul-terminated if @endptr is %NULL. If @endptr is
- * non-%NULL then it is updated to point to the first character past the
- * end of the format string.
- *
- * @app is a pointer to a #va_list. The arguments, according to
- * @format_string, are collected from this #va_list and the list is left
- * pointing to the argument following the last.
- *
- * These two generalisations allow mixing of multiple calls to
- * g_variant_new_va() and g_variant_get_va() within a single actual
- * varargs call by the user.
- *
- * The return value will be floating if it was a newly created GVariant
- * instance (for example, if the format string was "(ii)"). In the case
- * that the format_string was '*', '?', 'r', or a format starting with
- * '@' then the collected #GVariant pointer will be returned unmodified,
- * without adding any additional references.
- *
- * In order to behave correctly in all cases it is necessary for the
- * calling function to g_variant_ref_sink() the return result before
- * returning control to the user that originally provided the pointer.
- * At this point, the caller will have their own full reference to the
- * result. This can also be done by adding the result to a container,
- * or by passing it to another g_variant_new() call.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_va (const gchar *format_string,
- const gchar **endptr,
- va_list *app)
-{
- GVariant *value;
-
- g_return_val_if_fail (valid_format_string (format_string, !endptr, NULL),
- NULL);
- g_return_val_if_fail (app != NULL, NULL);
-
- value = g_variant_valist_new (&format_string, app);
-
- if (endptr != NULL)
- *endptr = format_string;
-
- return value;
-}
-
-/**
- * g_variant_get:
- * @value: a #GVariant instance
- * @format_string: a #GVariant format string
- * @...: arguments, as per @format_string
- *
- * Deconstructs a #GVariant instance.
- *
- * Think of this function as an analogue to scanf().
- *
- * The arguments that are expected by this function are entirely
- * determined by @format_string. @format_string also restricts the
- * permissible types of @value. It is an error to give a value with
- * an incompatible type. See the section on <link
- * linkend='gvariant-format-strings'>GVariant Format Strings</link>.
- * Please note that the syntax of the format string is very likely to be
- * extended in the future.
- *
- * Since: 2.24
- **/
-void
-g_variant_get (GVariant *value,
- const gchar *format_string,
- ...)
-{
- va_list ap;
-
- g_return_if_fail (valid_format_string (format_string, TRUE, value));
-
- /* if any direct-pointer-access formats are in use, flatten first */
- if (strchr (format_string, '&'))
- g_variant_get_data (value);
-
- va_start (ap, format_string);
- g_variant_get_va (value, format_string, NULL, &ap);
- va_end (ap);
-}
-
-/**
- * g_variant_get_va:
- * @value: a #GVariant
- * @format_string: a string that is prefixed with a format string
- * @endptr: (allow-none) (default NULL): location to store the end pointer,
- * or %NULL
- * @app: a pointer to a #va_list
- *
- * This function is intended to be used by libraries based on #GVariant
- * that want to provide g_variant_get()-like functionality to their
- * users.
- *
- * The API is more general than g_variant_get() to allow a wider range
- * of possible uses.
- *
- * @format_string must still point to a valid format string, but it only
- * need to be nul-terminated if @endptr is %NULL. If @endptr is
- * non-%NULL then it is updated to point to the first character past the
- * end of the format string.
- *
- * @app is a pointer to a #va_list. The arguments, according to
- * @format_string, are collected from this #va_list and the list is left
- * pointing to the argument following the last.
- *
- * These two generalisations allow mixing of multiple calls to
- * g_variant_new_va() and g_variant_get_va() within a single actual
- * varargs call by the user.
- *
- * Since: 2.24
- **/
-void
-g_variant_get_va (GVariant *value,
- const gchar *format_string,
- const gchar **endptr,
- va_list *app)
-{
- g_return_if_fail (valid_format_string (format_string, !endptr, value));
- g_return_if_fail (value != NULL);
- g_return_if_fail (app != NULL);
-
- /* if any direct-pointer-access formats are in use, flatten first */
- if (strchr (format_string, '&'))
- g_variant_get_data (value);
-
- g_variant_valist_get (&format_string, value, FALSE, app);
-
- if (endptr != NULL)
- *endptr = format_string;
-}
-
-/* Varargs-enabled Utility Functions {{{1 */
-
-/**
- * g_variant_builder_add:
- * @builder: a #GVariantBuilder
- * @format_string: a #GVariant varargs format string
- * @...: arguments, as per @format_string
- *
- * Adds to a #GVariantBuilder.
- *
- * This call is a convenience wrapper that is exactly equivalent to
- * calling g_variant_new() followed by g_variant_builder_add_value().
- *
- * This function might be used as follows:
- *
- * <programlisting>
- * GVariant *
- * make_pointless_dictionary (void)
- * {
- * GVariantBuilder *builder;
- * int i;
- *
- * builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
- * for (i = 0; i < 16; i++)
- * {
- * gchar buf[3];
- *
- * sprintf (buf, "%d", i);
- * g_variant_builder_add (builder, "{is}", i, buf);
- * }
- *
- * return g_variant_builder_end (builder);
- * }
- * </programlisting>
- *
- * Since: 2.24
- **/
-void
-g_variant_builder_add (GVariantBuilder *builder,
- const gchar *format_string,
- ...)
-{
- GVariant *variant;
- va_list ap;
-
- va_start (ap, format_string);
- variant = g_variant_new_va (format_string, NULL, &ap);
- va_end (ap);
-
- g_variant_builder_add_value (builder, variant);
-}
-
-/**
- * g_variant_get_child:
- * @value: a container #GVariant
- * @index_: the index of the child to deconstruct
- * @format_string: a #GVariant format string
- * @...: arguments, as per @format_string
- *
- * Reads a child item out of a container #GVariant instance and
- * deconstructs it according to @format_string. This call is
- * essentially a combination of g_variant_get_child_value() and
- * g_variant_get().
- *
- * Since: 2.24
- **/
-void
-g_variant_get_child (GVariant *value,
- gsize index_,
- const gchar *format_string,
- ...)
-{
- GVariant *child;
- va_list ap;
-
- child = g_variant_get_child_value (value, index_);
- g_return_if_fail (valid_format_string (format_string, TRUE, child));
-
- va_start (ap, format_string);
- g_variant_get_va (child, format_string, NULL, &ap);
- va_end (ap);
-
- g_variant_unref (child);
-}
-
-/**
- * g_variant_iter_next:
- * @iter: a #GVariantIter
- * @format_string: a GVariant format string
- * @...: the arguments to unpack the value into
- * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
- * value
- *
- * Gets the next item in the container and unpacks it into the variable
- * argument list according to @format_string, returning %TRUE.
- *
- * If no more items remain then %FALSE is returned.
- *
- * All of the pointers given on the variable arguments list of this
- * function are assumed to point at uninitialised memory. It is the
- * responsibility of the caller to free all of the values returned by
- * the unpacking process.
- *
- * See the section on <link linkend='gvariant-format-strings'>GVariant
- * Format Strings</link>.
- *
- * <example>
- * <title>Memory management with g_variant_iter_next()</title>
- * <programlisting>
- * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
- * void
- * iterate_dictionary (GVariant *dictionary)
- * {
- * GVariantIter iter;
- * GVariant *value;
- * gchar *key;
- *
- * g_variant_iter_init (&iter, dictionary);
- * while (g_variant_iter_next (&iter, "{sv}", &key, &value))
- * {
- * g_print ("Item '%s' has type '%s'\n", key,
- * g_variant_get_type_string (value));
- *
- * /<!-- -->* must free data for ourselves *<!-- -->/
- * g_variant_unref (value);
- * g_free (key);
- * }
- * }
- * </programlisting>
- * </example>
- *
- * For a solution that is likely to be more convenient to C programmers
- * when dealing with loops, see g_variant_iter_loop().
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_iter_next (GVariantIter *iter,
- const gchar *format_string,
- ...)
-{
- GVariant *value;
-
- value = g_variant_iter_next_value (iter);
-
- g_return_val_if_fail (valid_format_string (format_string, TRUE, value),
- FALSE);
-
- if (value != NULL)
- {
- va_list ap;
-
- va_start (ap, format_string);
- g_variant_valist_get (&format_string, value, FALSE, &ap);
- va_end (ap);
-
- g_variant_unref (value);
- }
-
- return value != NULL;
-}
-
-/**
- * g_variant_iter_loop:
- * @iter: a #GVariantIter
- * @format_string: a GVariant format string
- * @...: the arguments to unpack the value into
- * @returns: %TRUE if a value was unpacked, or %FALSE if there as no
- * value
- *
- * Gets the next item in the container and unpacks it into the variable
- * argument list according to @format_string, returning %TRUE.
- *
- * If no more items remain then %FALSE is returned.
- *
- * On the first call to this function, the pointers appearing on the
- * variable argument list are assumed to point at uninitialised memory.
- * On the second and later calls, it is assumed that the same pointers
- * will be given and that they will point to the memory as set by the
- * previous call to this function. This allows the previous values to
- * be freed, as appropriate.
- *
- * This function is intended to be used with a while loop as
- * demonstrated in the following example. This function can only be
- * used when iterating over an array. It is only valid to call this
- * function with a string constant for the format string and the same
- * string constant must be used each time. Mixing calls to this
- * function and g_variant_iter_next() or g_variant_iter_next_value() on
- * the same iterator is not recommended.
- *
- * See the section on <link linkend='gvariant-format-strings'>GVariant
- * Format Strings</link>.
- *
- * <example>
- * <title>Memory management with g_variant_iter_loop()</title>
- * <programlisting>
- * /<!-- -->* Iterates a dictionary of type 'a{sv}' *<!-- -->/
- * void
- * iterate_dictionary (GVariant *dictionary)
- * {
- * GVariantIter iter;
- * GVariant *value;
- * gchar *key;
- *
- * g_variant_iter_init (&iter, dictionary);
- * while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
- * {
- * g_print ("Item '%s' has type '%s'\n", key,
- * g_variant_get_type_string (value));
- *
- * /<!-- -->* no need to free 'key' and 'value' here *<!-- -->/
- * }
- * }
- * </programlisting>
- * </example>
- *
- * If you want a slightly less magical alternative that requires more
- * typing, see g_variant_iter_next().
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_iter_loop (GVariantIter *iter,
- const gchar *format_string,
- ...)
-{
- gboolean first_time = GVSI(iter)->loop_format == NULL;
- GVariant *value;
- va_list ap;
-
- g_return_val_if_fail (first_time ||
- format_string == GVSI(iter)->loop_format,
- FALSE);
-
- if (first_time)
- {
- TYPE_CHECK (GVSI(iter)->value, G_VARIANT_TYPE_ARRAY, FALSE);
- GVSI(iter)->loop_format = format_string;
-
- if (strchr (format_string, '&'))
- g_variant_get_data (GVSI(iter)->value);
- }
-
- value = g_variant_iter_next_value (iter);
-
- g_return_val_if_fail (!first_time ||
- valid_format_string (format_string, TRUE, value),
- FALSE);
-
- va_start (ap, format_string);
- g_variant_valist_get (&format_string, value, !first_time, &ap);
- va_end (ap);
-
- if (value != NULL)
- g_variant_unref (value);
-
- return value != NULL;
-}
-
-/* Serialised data {{{1 */
-static GVariant *
-g_variant_deep_copy (GVariant *value)
-{
- switch (g_variant_classify (value))
- {
- case G_VARIANT_CLASS_MAYBE:
- case G_VARIANT_CLASS_ARRAY:
- case G_VARIANT_CLASS_TUPLE:
- case G_VARIANT_CLASS_DICT_ENTRY:
- case G_VARIANT_CLASS_VARIANT:
- {
- GVariantBuilder builder;
- GVariantIter iter;
- GVariant *child;
-
- g_variant_builder_init (&builder, g_variant_get_type (value));
- g_variant_iter_init (&iter, value);
-
- while ((child = g_variant_iter_next_value (&iter)))
- {
- g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
- g_variant_unref (child);
- }
-
- return g_variant_builder_end (&builder);
- }
-
- case G_VARIANT_CLASS_BOOLEAN:
- return g_variant_new_boolean (g_variant_get_boolean (value));
-
- case G_VARIANT_CLASS_BYTE:
- return g_variant_new_byte (g_variant_get_byte (value));
-
- case G_VARIANT_CLASS_INT16:
- return g_variant_new_int16 (g_variant_get_int16 (value));
-
- case G_VARIANT_CLASS_UINT16:
- return g_variant_new_uint16 (g_variant_get_uint16 (value));
-
- case G_VARIANT_CLASS_INT32:
- return g_variant_new_int32 (g_variant_get_int32 (value));
-
- case G_VARIANT_CLASS_UINT32:
- return g_variant_new_uint32 (g_variant_get_uint32 (value));
-
- case G_VARIANT_CLASS_INT64:
- return g_variant_new_int64 (g_variant_get_int64 (value));
-
- case G_VARIANT_CLASS_UINT64:
- return g_variant_new_uint64 (g_variant_get_uint64 (value));
-
- case G_VARIANT_CLASS_HANDLE:
- return g_variant_new_handle (g_variant_get_handle (value));
-
- case G_VARIANT_CLASS_DOUBLE:
- return g_variant_new_double (g_variant_get_double (value));
-
- case G_VARIANT_CLASS_STRING:
- return g_variant_new_string (g_variant_get_string (value, NULL));
-
- case G_VARIANT_CLASS_OBJECT_PATH:
- return g_variant_new_object_path (g_variant_get_string (value, NULL));
-
- case G_VARIANT_CLASS_SIGNATURE:
- return g_variant_new_signature (g_variant_get_string (value, NULL));
- }
-
- g_assert_not_reached ();
-}
-
-/**
- * g_variant_get_normal_form:
- * @value: a #GVariant
- * @returns: a trusted #GVariant
- *
- * Gets a #GVariant instance that has the same value as @value and is
- * trusted to be in normal form.
- *
- * If @value is already trusted to be in normal form then a new
- * reference to @value is returned.
- *
- * If @value is not already trusted, then it is scanned to check if it
- * is in normal form. If it is found to be in normal form then it is
- * marked as trusted and a new reference to it is returned.
- *
- * If @value is found not to be in normal form then a new trusted
- * #GVariant is created with the same value as @value.
- *
- * It makes sense to call this function if you've received #GVariant
- * data from untrusted sources and you want to ensure your serialised
- * output is definitely in normal form.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_get_normal_form (GVariant *value)
-{
- GVariant *trusted;
-
- if (g_variant_is_normal_form (value))
- return g_variant_ref (value);
-
- trusted = g_variant_deep_copy (value);
- g_assert (g_variant_is_trusted (trusted));
-
- return g_variant_ref_sink (trusted);
-}
-
-/**
- * g_variant_byteswap:
- * @value: a #GVariant
- * @returns: the byteswapped form of @value
- *
- * Performs a byteswapping operation on the contents of @value. The
- * result is that all multi-byte numeric data contained in @value is
- * byteswapped. That includes 16, 32, and 64bit signed and unsigned
- * integers as well as file handles and double precision floating point
- * values.
- *
- * This function is an identity mapping on any value that does not
- * contain multi-byte numeric data. That include strings, booleans,
- * bytes and containers containing only these things (recursively).
- *
- * The returned value is always in normal form and is marked as trusted.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_byteswap (GVariant *value)
-{
- GVariantSerialised serialised;
- GVariant *trusted;
- GBuffer *buffer;
- GVariant *new;
-
- trusted = g_variant_get_normal_form (value);
- serialised.type_info = g_variant_get_type_info (trusted);
- serialised.size = g_variant_get_size (trusted);
- serialised.data = g_malloc (serialised.size);
- g_variant_store (trusted, serialised.data);
- g_variant_unref (trusted);
-
- g_variant_serialised_byteswap (serialised);
-
- buffer = g_buffer_new_take_data (serialised.data, serialised.size);
- new = g_variant_new_from_buffer (g_variant_get_type (value), buffer, TRUE);
- g_buffer_unref (buffer);
-
- return g_variant_ref_sink (new);
-}
-
-/**
- * g_variant_new_from_data:
- * @type: a definite #GVariantType
- * @data: the serialised data
- * @size: the size of @data
- * @trusted: %TRUE if @data is definitely in normal form
- * @notify: function to call when @data is no longer needed
- * @user_data: data for @notify
- * @returns: a new floating #GVariant of type @type
- *
- * Creates a new #GVariant instance from serialised data.
- *
- * @type is the type of #GVariant instance that will be constructed.
- * The interpretation of @data depends on knowing the type.
- *
- * @data is not modified by this function and must remain valid with an
- * unchanging value until such a time as @notify is called with
- * @user_data. If the contents of @data change before that time then
- * the result is undefined.
- *
- * If @data is trusted to be serialised data in normal form then
- * @trusted should be %TRUE. This applies to serialised data created
- * within this process or read from a trusted location on the disk (such
- * as a file installed in /usr/lib alongside your application). You
- * should set trusted to %FALSE if @data is read from the network, a
- * file in the user's home directory, etc.
- *
- * @notify will be called with @user_data when @data is no longer
- * needed. The exact time of this call is unspecified and might even be
- * before this function returns.
- *
- * Since: 2.24
- **/
-GVariant *
-g_variant_new_from_data (const GVariantType *type,
- gconstpointer data,
- gsize size,
- gboolean trusted,
- GDestroyNotify notify,
- gpointer user_data)
-{
- GVariant *value;
- GBuffer *buffer;
-
- g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
- g_return_val_if_fail (data != NULL || size == 0, NULL);
-
- if (notify)
- buffer = g_buffer_new_from_pointer (data, size, notify, user_data);
- else
- buffer = g_buffer_new_from_static_data (data, size);
-
- value = g_variant_new_from_buffer (type, buffer, trusted);
- g_buffer_unref (buffer);
-
- return value;
-}
-
-/* Epilogue {{{1 */
-/* vim:set foldmethod=marker: */
+++ /dev/null
-/*
- * Copyright © 2007, 2008 Ryan Lortie
- * Copyright © 2009, 2010 Codethink Limited
- *
- * 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 of the licence, 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "config.h"
-
-#include "gvarianttype.h"
-
-#include <glib/gtestutils.h>
-#include <glib/gstrfuncs.h>
-
-#include <string.h>
-
-
-/**
- * SECTION: gvarianttype
- * @title: GVariantType
- * @short_description: introduction to the GVariant type system
- * @see_also: #GVariantType, #GVariant
- *
- * This section introduces the GVariant type system. It is based, in
- * large part, on the DBus type system, with two major changes and some minor
- * lifting of restrictions. The <ulink
- * url='http://dbus.freedesktop.org/doc/dbus-specification.html'>DBus
- * specification</ulink>, therefore, provides a significant amount of
- * information that is useful when working with GVariant.
- *
- * The first major change with respect to the DBus type system is the
- * introduction of maybe (or "nullable") types. Any type in GVariant can be
- * converted to a maybe type, in which case, "nothing" (or "null") becomes a
- * valid value. Maybe types have been added by introducing the
- * character "<literal>m</literal>" to type strings.
- *
- * The second major change is that the GVariant type system supports the
- * concept of "indefinite types" -- types that are less specific than
- * the normal types found in DBus. For example, it is possible to speak
- * of "an array of any type" in GVariant, where the DBus type system
- * would require you to speak of "an array of integers" or "an array of
- * strings". Indefinite types have been added by introducing the
- * characters "<literal>*</literal>", "<literal>?</literal>" and
- * "<literal>r</literal>" to type strings.
- *
- * Finally, all arbitrary restrictions relating to the complexity of
- * types are lifted along with the restriction that dictionary entries
- * may only appear nested inside of arrays.
- *
- * Just as in DBus, GVariant types are described with strings ("type
- * strings"). Subject to the differences mentioned above, these strings
- * are of the same form as those found in DBus. Note, however: DBus
- * always works in terms of messages and therefore individual type
- * strings appear nowhere in its interface. Instead, "signatures"
- * are a concatenation of the strings of the type of each argument in a
- * message. GVariant deals with single values directly so GVariant type
- * strings always describe the type of exactly one value. This means
- * that a DBus signature string is generally not a valid GVariant type
- * string -- except in the case that it is the signature of a message
- * containing exactly one argument.
- *
- * An indefinite type is similar in spirit to what may be called an
- * abstract type in other type systems. No value can exist that has an
- * indefinite type as its type, but values can exist that have types
- * that are subtypes of indefinite types. That is to say,
- * g_variant_get_type() will never return an indefinite type, but
- * calling g_variant_is_a() with an indefinite type may return %TRUE.
- * For example, you can not have a value that represents "an array of no
- * particular type", but you can have an "array of integers" which
- * certainly matches the type of "an array of no particular type", since
- * "array of integers" is a subtype of "array of no particular type".
- *
- * This is similar to how instances of abstract classes may not
- * directly exist in other type systems, but instances of their
- * non-abstract subtypes may. For example, in GTK, no object that has
- * the type of #GtkBin can exist (since #GtkBin is an abstract class),
- * but a #GtkWindow can certainly be instantiated, and you would say
- * that the #GtkWindow is a #GtkBin (since #GtkWindow is a subclass of
- * #GtkBin).
- *
- * A detailed description of GVariant type strings is given here:
- *
- * <refsect2 id='gvariant-typestrings'>
- * <title>GVariant Type Strings</title>
- * <para>
- * A GVariant type string can be any of the following:
- * </para>
- * <itemizedlist>
- * <listitem>
- * <para>
- * any basic type string (listed below)
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * "<literal>v</literal>", "<literal>r</literal>" or
- * "<literal>*</literal>"
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * one of the characters '<literal>a</literal>' or
- * '<literal>m</literal>', followed by another type string
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * the character '<literal>(</literal>', followed by a concatenation
- * of zero or more other type strings, followed by the character
- * '<literal>)</literal>'
- * </para>
- * </listitem>
- * <listitem>
- * <para>
- * the character '<literal>{</literal>', followed by a basic type
- * string (see below), followed by another type string, followed by
- * the character '<literal>}</literal>'
- * </para>
- * </listitem>
- * </itemizedlist>
- * <para>
- * A basic type string describes a basic type (as per
- * g_variant_type_is_basic()) and is always a single
- * character in length. The valid basic type strings are
- * "<literal>b</literal>", "<literal>y</literal>",
- * "<literal>n</literal>", "<literal>q</literal>",
- * "<literal>i</literal>", "<literal>u</literal>",
- * "<literal>x</literal>", "<literal>t</literal>",
- * "<literal>h</literal>", "<literal>d</literal>",
- * "<literal>s</literal>", "<literal>o</literal>",
- * "<literal>g</literal>" and "<literal>?</literal>".
- * </para>
- * <para>
- * The above definition is recursive to arbitrary depth.
- * "<literal>aaaaai</literal>" and "<literal>(ui(nq((y)))s)</literal>"
- * are both valid type strings, as is
- * "<literal>a(aa(ui)(qna{ya(yd)}))</literal>".
- * </para>
- * <para>
- * The meaning of each of the characters is as follows:
- * </para>
- * <informaltable>
- * <tgroup cols='2'>
- * <tbody>
- * <row>
- * <entry>
- * <para>
- * <emphasis role='strong'>Character</emphasis>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * <emphasis role='strong'>Meaning</emphasis>
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>b</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_BOOLEAN; a boolean value.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>y</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_BYTE; a byte.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>n</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_INT16; a signed 16 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>q</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_UINT16; an unsigned 16 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>i</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_INT32; a signed 32 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>u</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_UINT32; an unsigned 32 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>x</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_INT64; a signed 64 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>t</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_UINT64; an unsigned 64 bit
- * integer.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>h</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_HANDLE; a signed 32 bit
- * value that, by convention, is used as an index into an array
- * of file descriptors that are sent alongside a DBus message.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>d</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_DOUBLE; a double precision
- * floating point value.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>s</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_STRING; a string.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>o</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_OBJECT_PATH; a string in
- * the form of a DBus object path.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>g</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_STRING; a string in the
- * form of a DBus type signature.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>?</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_BASIC; an indefinite type
- * that is a supertype of any of the basic types.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>v</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_VARIANT; a container type
- * that contain any other type of value.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>a</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * used as a prefix on another type string to mean an array of
- * that type; the type string "<literal>ai</literal>", for
- * example, is the type of an array of 32 bit signed integers.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>m</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * used as a prefix on another type string to mean a "maybe", or
- * "nullable", version of that type; the type string
- * "<literal>ms</literal>", for example, is the type of a value
- * that maybe contains a string, or maybe contains nothing.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>()</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * used to enclose zero or more other concatenated type strings
- * to create a tuple type; the type string
- * "<literal>(is)</literal>", for example, is the type of a pair
- * of an integer and a string.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>r</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_TUPLE; an indefinite type
- * that is a supertype of any tuple type, regardless of the
- * number of items.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>{}</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * used to enclose a basic type string concatenated with another
- * type string to create a dictionary entry type, which usually
- * appears inside of an array to form a dictionary; the type
- * string "<literal>a{sd}</literal>", for example, is the type of
- * a dictionary that maps strings to double precision floating
- * point values.
- * </para>
- * <para>
- * The first type (the basic type) is the key type and the second
- * type is the value type. The reason that the first type is
- * restricted to being a basic type is so that it can easily be
- * hashed.
- * </para>
- * </entry>
- * </row>
- * <row>
- * <entry>
- * <para>
- * <literal>*</literal>
- * </para>
- * </entry>
- * <entry>
- * <para>
- * the type string of %G_VARIANT_TYPE_ANY; the indefinite type
- * that is a supertype of all types. Note that, as with all type
- * strings, this character represents exactly one type. It
- * cannot be used inside of tuples to mean "any number of items".
- * </para>
- * </entry>
- * </row>
- * </tbody>
- * </tgroup>
- * </informaltable>
- * <para>
- * Any type string of a container that contains an indefinite type is,
- * itself, an indefinite type. For example, the type string
- * "<literal>a*</literal>" (corresponding to %G_VARIANT_TYPE_ARRAY) is
- * an indefinite type that is a supertype of every array type.
- * "<literal>(*s)</literal>" is a supertype of all tuples that
- * contain exactly two items where the second item is a string.
- * </para>
- * <para>
- * "<literal>a{?*}</literal>" is an indefinite type that is a
- * supertype of all arrays containing dictionary entries where the key
- * is any basic type and the value is any type at all. This is, by
- * definition, a dictionary, so this type string corresponds to
- * %G_VARIANT_TYPE_DICTIONARY. Note that, due to the restriction that
- * the key of a dictionary entry must be a basic type,
- * "<literal>{**}</literal>" is not a valid type string.
- * </para>
- * </refsect2>
- */
-
-
-static gboolean
-g_variant_type_check (const GVariantType *type)
-{
- const gchar *type_string;
-
- if (type == NULL)
- return FALSE;
-
- type_string = (const gchar *) type;
-#ifndef G_DISABLE_CHECKS
- return g_variant_type_string_scan (type_string, NULL, NULL);
-#else
- return TRUE;
-#endif
-}
-
-/**
- * g_variant_type_string_scan:
- * @string: a pointer to any string
- * @limit: the end of @string, or %NULL
- * @endptr: location to store the end pointer, or %NULL
- * @returns: %TRUE if a valid type string was found
- *
- * Scan for a single complete and valid GVariant type string in @string.
- * The memory pointed to by @limit (or bytes beyond it) is never
- * accessed.
- *
- * If a valid type string is found, @endptr is updated to point to the
- * first character past the end of the string that was found and %TRUE
- * is returned.
- *
- * If there is no valid type string starting at @string, or if the type
- * string does not end before @limit then %FALSE is returned.
- *
- * For the simple case of checking if a string is a valid type string,
- * see g_variant_type_string_is_valid().
- *
- * Since: 2.24
- **/
-gboolean
-g_variant_type_string_scan (const gchar *string,
- const gchar *limit,
- const gchar **endptr)
-{
- g_return_val_if_fail (string != NULL, FALSE);
-
- if (string == limit || *string == '\0')
- return FALSE;
-
- switch (*string++)
- {
- case '(':
- while (string == limit || *string != ')')
- if (!g_variant_type_string_scan (string, limit, &string))
- return FALSE;
-
- string++;
- break;
-
- case '{':
- if (string == limit || *string == '\0' || /* { */
- !strchr ("bynqihuxtdsog?", *string++) || /* key */
- !g_variant_type_string_scan (string, limit, &string) || /* value */
- string == limit || *string++ != '}') /* } */
- return FALSE;
-
- break;
-
- case 'm': case 'a':
- return g_variant_type_string_scan (string, limit, endptr);
-
- case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
- case 'x': case 't': case 'd': case 's': case 'o': case 'g':
- case 'v': case 'r': case '*': case '?': case 'h':
- break;
-
- default:
- return FALSE;
- }
-
- if (endptr != NULL)
- *endptr = string;
-
- return TRUE;
-}
-
-/**
- * g_variant_type_string_is_valid:
- * @type_string: a pointer to any string
- * @returns: %TRUE if @type_string is exactly one valid type string
- *
- * Checks if @type_string is a valid GVariant type string. This call is
- * equivalent to calling g_variant_type_string_scan() and confirming
- * that the following character is a nul terminator.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_string_is_valid (const gchar *type_string)
-{
- const gchar *endptr;
-
- g_return_val_if_fail (type_string != NULL, FALSE);
-
- if (!g_variant_type_string_scan (type_string, NULL, &endptr))
- return FALSE;
-
- return *endptr == '\0';
-}
-
-/**
- * g_variant_type_free:
- * @type: a #GVariantType, or %NULL
- *
- * Frees a #GVariantType that was allocated with
- * g_variant_type_copy(), g_variant_type_new() or one of the container
- * type constructor functions.
- *
- * In the case that @type is %NULL, this function does nothing.
- *
- * Since 2.24
- **/
-void
-g_variant_type_free (GVariantType *type)
-{
- g_return_if_fail (type == NULL || g_variant_type_check (type));
-
- g_free (type);
-}
-
-/**
- * g_variant_type_copy:
- * @type: a #GVariantType
- * @returns: a new #GVariantType
- *
- * Makes a copy of a #GVariantType. It is appropriate to call
- * g_variant_type_free() on the return value. @type may not be %NULL.
- *
- * Since 2.24
- **/
-GVariantType *
-g_variant_type_copy (const GVariantType *type)
-{
- gsize length;
- gchar *new;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- length = g_variant_type_get_string_length (type);
- new = g_malloc (length + 1);
-
- memcpy (new, type, length);
- new[length] = '\0';
-
- return (GVariantType *) new;
-}
-
-/**
- * g_variant_type_new:
- * @type_string: a valid GVariant type string
- * @returns: a new #GVariantType
- *
- * Creates a new #GVariantType corresponding to the type string given
- * by @type_string. It is appropriate to call g_variant_type_free() on
- * the return value.
- *
- * It is a programmer error to call this function with an invalid type
- * string. Use g_variant_type_string_is_valid() if you are unsure.
- *
- * Since: 2.24
- */
-GVariantType *
-g_variant_type_new (const gchar *type_string)
-{
- g_return_val_if_fail (type_string != NULL, NULL);
-
- return g_variant_type_copy (G_VARIANT_TYPE (type_string));
-}
-
-/**
- * g_variant_type_get_string_length:
- * @type: a #GVariantType
- * @returns: the length of the corresponding type string
- *
- * Returns the length of the type string corresponding to the given
- * @type. This function must be used to determine the valid extent of
- * the memory region returned by g_variant_type_peek_string().
- *
- * Since 2.24
- **/
-gsize
-g_variant_type_get_string_length (const GVariantType *type)
-{
- const gchar *type_string = (const gchar *) type;
- gint brackets = 0;
- gsize index = 0;
-
- g_return_val_if_fail (g_variant_type_check (type), 0);
-
- do
- {
- while (type_string[index] == 'a' || type_string[index] == 'm')
- index++;
-
- if (type_string[index] == '(' || type_string[index] == '{')
- brackets++;
-
- else if (type_string[index] == ')' || type_string[index] == '}')
- brackets--;
-
- index++;
- }
- while (brackets);
-
- return index;
-}
-
-/**
- * g_variant_type_peek_string:
- * @type: a #GVariantType
- * @returns: the corresponding type string (not nul-terminated)
- *
- * Returns the type string corresponding to the given @type. The
- * result is not nul-terminated; in order to determine its length you
- * must call g_variant_type_get_string_length().
- *
- * To get a nul-terminated string, see g_variant_type_dup_string().
- *
- * Since 2.24
- **/
-const gchar *
-g_variant_type_peek_string (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- return (const gchar *) type;
-}
-
-/**
- * g_variant_type_dup_string:
- * @type: a #GVariantType
- * @returns: the corresponding type string
- *
- * Returns a newly-allocated copy of the type string corresponding to
- * @type. The returned string is nul-terminated. It is appropriate to
- * call g_free() on the return value.
- *
- * Since 2.24
- **/
-gchar *
-g_variant_type_dup_string (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- return g_strndup (g_variant_type_peek_string (type),
- g_variant_type_get_string_length (type));
-}
-
-/**
- * g_variant_type_is_definite:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is definite
- *
- * Determines if the given @type is definite (ie: not indefinite).
- *
- * A type is definite if its type string does not contain any indefinite
- * type characters ('*', '?', or 'r').
- *
- * A #GVariant instance may not have an indefinite type, so calling
- * this function on the result of g_variant_get_type() will always
- * result in %TRUE being returned. Calling this function on an
- * indefinite type like %G_VARIANT_TYPE_ARRAY, however, will result in
- * %FALSE being returned.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_definite (const GVariantType *type)
-{
- const gchar *type_string;
- gsize type_length;
- gsize i;
-
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- type_length = g_variant_type_get_string_length (type);
- type_string = g_variant_type_peek_string (type);
-
- for (i = 0; i < type_length; i++)
- if (type_string[i] == '*' ||
- type_string[i] == '?' ||
- type_string[i] == 'r')
- return FALSE;
-
- return TRUE;
-}
-
-/**
- * g_variant_type_is_container:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is a container type
- *
- * Determines if the given @type is a container type.
- *
- * Container types are any array, maybe, tuple, or dictionary
- * entry types plus the variant type.
- *
- * This function returns %TRUE for any indefinite type for which every
- * definite subtype is a container -- %G_VARIANT_TYPE_ARRAY, for
- * example.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_container (const GVariantType *type)
-{
- gchar first_char;
-
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- first_char = g_variant_type_peek_string (type)[0];
- switch (first_char)
- {
- case 'a':
- case 'm':
- case 'r':
- case '(':
- case '{':
- case 'v':
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
-/**
- * g_variant_type_is_basic:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is a basic type
- *
- * Determines if the given @type is a basic type.
- *
- * Basic types are booleans, bytes, integers, doubles, strings, object
- * paths and signatures.
- *
- * Only a basic type may be used as the key of a dictionary entry.
- *
- * This function returns %FALSE for all indefinite types except
- * %G_VARIANT_TYPE_BASIC.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_basic (const GVariantType *type)
-{
- gchar first_char;
-
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- first_char = g_variant_type_peek_string (type)[0];
- switch (first_char)
- {
- case 'b':
- case 'y':
- case 'n':
- case 'q':
- case 'i':
- case 'h':
- case 'u':
- case 't':
- case 'x':
- case 'd':
- case 's':
- case 'o':
- case 'g':
- case '?':
- return TRUE;
-
- default:
- return FALSE;
- }
-}
-
-/**
- * g_variant_type_is_maybe:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is a maybe type
- *
- * Determines if the given @type is a maybe type. This is true if the
- * type string for @type starts with an 'm'.
- *
- * This function returns %TRUE for any indefinite type for which every
- * definite subtype is a maybe type -- %G_VARIANT_TYPE_MAYBE, for
- * example.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_maybe (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- return g_variant_type_peek_string (type)[0] == 'm';
-}
-
-/**
- * g_variant_type_is_array:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is an array type
- *
- * Determines if the given @type is an array type. This is true if the
- * type string for @type starts with an 'a'.
- *
- * This function returns %TRUE for any indefinite type for which every
- * definite subtype is an array type -- %G_VARIANT_TYPE_ARRAY, for
- * example.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_array (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- return g_variant_type_peek_string (type)[0] == 'a';
-}
-
-/**
- * g_variant_type_is_tuple:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is a tuple type
- *
- * Determines if the given @type is a tuple type. This is true if the
- * type string for @type starts with a '(' or if @type is
- * %G_VARIANT_TYPE_TUPLE.
- *
- * This function returns %TRUE for any indefinite type for which every
- * definite subtype is a tuple type -- %G_VARIANT_TYPE_TUPLE, for
- * example.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_tuple (const GVariantType *type)
-{
- gchar type_char;
-
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- type_char = g_variant_type_peek_string (type)[0];
- return type_char == 'r' || type_char == '(';
-}
-
-/**
- * g_variant_type_is_dict_entry:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is a dictionary entry type
- *
- * Determines if the given @type is a dictionary entry type. This is
- * true if the type string for @type starts with a '{'.
- *
- * This function returns %TRUE for any indefinite type for which every
- * definite subtype is a dictionary entry type --
- * %G_VARIANT_TYPE_DICT_ENTRY, for example.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_dict_entry (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- return g_variant_type_peek_string (type)[0] == '{';
-}
-
-/**
- * g_variant_type_is_variant:
- * @type: a #GVariantType
- * @returns: %TRUE if @type is the variant type
- *
- * Determines if the given @type is the variant type.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_variant (const GVariantType *type)
-{
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
-
- return g_variant_type_peek_string (type)[0] == 'v';
-}
-
-/**
- * g_variant_type_hash:
- * @type: a #GVariantType
- * @returns: the hash value
- *
- * Hashes @type.
- *
- * The argument type of @type is only #gconstpointer to allow use with
- * #GHashTable without function pointer casting. A valid
- * #GVariantType must be provided.
- *
- * Since 2.24
- **/
-guint
-g_variant_type_hash (gconstpointer type)
-{
- const gchar *type_string;
- guint value = 0;
- gsize length;
- gsize i;
-
- g_return_val_if_fail (g_variant_type_check (type), 0);
-
- type_string = g_variant_type_peek_string (type);
- length = g_variant_type_get_string_length (type);
-
- for (i = 0; i < length; i++)
- value = (value << 5) - value + type_string[i];
-
- return value;
-}
-
-/**
- * g_variant_type_equal:
- * @type1: a #GVariantType
- * @type2: a #GVariantType
- * @returns: %TRUE if @type1 and @type2 are exactly equal
- *
- * Compares @type1 and @type2 for equality.
- *
- * Only returns %TRUE if the types are exactly equal. Even if one type
- * is an indefinite type and the other is a subtype of it, %FALSE will
- * be returned if they are not exactly equal. If you want to check for
- * subtypes, use g_variant_type_is_subtype_of().
- *
- * The argument types of @type1 and @type2 are only #gconstpointer to
- * allow use with #GHashTable without function pointer casting. For
- * both arguments, a valid #GVariantType must be provided.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_equal (gconstpointer type1,
- gconstpointer type2)
-{
- const gchar *string1, *string2;
- gsize size1, size2;
-
- g_return_val_if_fail (g_variant_type_check (type1), FALSE);
- g_return_val_if_fail (g_variant_type_check (type2), FALSE);
-
- if (type1 == type2)
- return TRUE;
-
- size1 = g_variant_type_get_string_length (type1);
- size2 = g_variant_type_get_string_length (type2);
-
- if (size1 != size2)
- return FALSE;
-
- string1 = g_variant_type_peek_string (type1);
- string2 = g_variant_type_peek_string (type2);
-
- return memcmp (string1, string2, size1) == 0;
-}
-
-/**
- * g_variant_type_is_subtype_of:
- * @type: a #GVariantType
- * @supertype: a #GVariantType
- * @returns: %TRUE if @type is a subtype of @supertype
- *
- * Checks if @type is a subtype of @supertype.
- *
- * This function returns %TRUE if @type is a subtype of @supertype. All
- * types are considered to be subtypes of themselves. Aside from that,
- * only indefinite types can have subtypes.
- *
- * Since 2.24
- **/
-gboolean
-g_variant_type_is_subtype_of (const GVariantType *type,
- const GVariantType *supertype)
-{
- const gchar *supertype_string;
- const gchar *supertype_end;
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), FALSE);
- g_return_val_if_fail (g_variant_type_check (supertype), FALSE);
-
- supertype_string = g_variant_type_peek_string (supertype);
- type_string = g_variant_type_peek_string (type);
-
- supertype_end = supertype_string +
- g_variant_type_get_string_length (supertype);
-
- /* we know that type and supertype are both well-formed, so it's
- * safe to treat this merely as a text processing problem.
- */
- while (supertype_string < supertype_end)
- {
- char supertype_char = *supertype_string++;
-
- if (supertype_char == *type_string)
- type_string++;
-
- else if (*type_string == ')')
- return FALSE;
-
- else
- {
- const GVariantType *target_type = (GVariantType *) type_string;
-
- switch (supertype_char)
- {
- case 'r':
- if (!g_variant_type_is_tuple (target_type))
- return FALSE;
- break;
-
- case '*':
- break;
-
- case '?':
- if (!g_variant_type_is_basic (target_type))
- return FALSE;
- break;
-
- default:
- return FALSE;
- }
-
- type_string += g_variant_type_get_string_length (target_type);
- }
- }
-
- return TRUE;
-}
-
-/**
- * g_variant_type_element:
- * @type: an array or maybe #GVariantType
- * @returns: the element type of @type
- *
- * Determines the element type of an array or maybe type.
- *
- * This function may only be used with array or maybe types.
- *
- * Since 2.24
- **/
-const GVariantType *
-g_variant_type_element (const GVariantType *type)
-{
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- type_string = g_variant_type_peek_string (type);
-
- g_assert (type_string[0] == 'a' || type_string[0] == 'm');
-
- return (const GVariantType *) &type_string[1];
-}
-
-/**
- * g_variant_type_first:
- * @type: a tuple or dictionary entry #GVariantType
- * @returns: the first item type of @type, or %NULL
- *
- * Determines the first item type of a tuple or dictionary entry
- * type.
- *
- * This function may only be used with tuple or dictionary entry types,
- * but must not be used with the generic tuple type
- * %G_VARIANT_TYPE_TUPLE.
- *
- * In the case of a dictionary entry type, this returns the type of
- * the key.
- *
- * %NULL is returned in case of @type being %G_VARIANT_TYPE_UNIT.
- *
- * This call, together with g_variant_type_next() provides an iterator
- * interface over tuple and dictionary entry types.
- *
- * Since 2.24
- **/
-const GVariantType *
-g_variant_type_first (const GVariantType *type)
-{
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- type_string = g_variant_type_peek_string (type);
- g_assert (type_string[0] == '(' || type_string[0] == '{');
-
- if (type_string[1] == ')')
- return NULL;
-
- return (const GVariantType *) &type_string[1];
-}
-
-/**
- * g_variant_type_next:
- * @type: a #GVariantType from a previous call
- * @returns: the next #GVariantType after @type, or %NULL
- *
- * Determines the next item type of a tuple or dictionary entry
- * type.
- *
- * @type must be the result of a previous call to
- * g_variant_type_first() or g_variant_type_next().
- *
- * If called on the key type of a dictionary entry then this call
- * returns the value type. If called on the value type of a dictionary
- * entry then this call returns %NULL.
- *
- * For tuples, %NULL is returned when @type is the last item in a tuple.
- *
- * Since 2.24
- **/
-const GVariantType *
-g_variant_type_next (const GVariantType *type)
-{
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- type_string = g_variant_type_peek_string (type);
- type_string += g_variant_type_get_string_length (type);
-
- if (*type_string == ')' || *type_string == '}')
- return NULL;
-
- return (const GVariantType *) type_string;
-}
-
-/**
- * g_variant_type_n_items:
- * @type: a tuple or dictionary entry #GVariantType
- * @returns: the number of items in @type
- *
- * Determines the number of items contained in a tuple or
- * dictionary entry type.
- *
- * This function may only be used with tuple or dictionary entry types,
- * but must not be used with the generic tuple type
- * %G_VARIANT_TYPE_TUPLE.
- *
- * In the case of a dictionary entry type, this function will always
- * return 2.
- *
- * Since 2.24
- **/
-gsize
-g_variant_type_n_items (const GVariantType *type)
-{
- gsize count = 0;
-
- g_return_val_if_fail (g_variant_type_check (type), 0);
-
- for (type = g_variant_type_first (type);
- type;
- type = g_variant_type_next (type))
- count++;
-
- return count;
-}
-
-/**
- * g_variant_type_key:
- * @type: a dictionary entry #GVariantType
- * @returns: the key type of the dictionary entry
- *
- * Determines the key type of a dictionary entry type.
- *
- * This function may only be used with a dictionary entry type. Other
- * than the additional restriction, this call is equivalent to
- * g_variant_type_first().
- *
- * Since 2.24
- **/
-const GVariantType *
-g_variant_type_key (const GVariantType *type)
-{
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- type_string = g_variant_type_peek_string (type);
- g_assert (type_string[0] == '{');
-
- return (const GVariantType *) &type_string[1];
-}
-
-/**
- * g_variant_type_value:
- * @type: a dictionary entry #GVariantType
- * @returns: the value type of the dictionary entry
- *
- * Determines the value type of a dictionary entry type.
- *
- * This function may only be used with a dictionary entry type.
- *
- * Since 2.24
- **/
-const GVariantType *
-g_variant_type_value (const GVariantType *type)
-{
- const gchar *type_string;
-
- g_return_val_if_fail (g_variant_type_check (type), NULL);
-
- type_string = g_variant_type_peek_string (type);
- g_assert (type_string[0] == '{');
-
- return g_variant_type_next (g_variant_type_key (type));
-}
-
-/**
- * g_variant_type_new_tuple:
- * @items: an array of #GVariantTypes, one for each item
- * @length: the length of @items, or -1
- * @returns: a new tuple #GVariantType
- *
- * Constructs a new tuple type, from @items.
- *
- * @length is the number of items in @items, or -1 to indicate that
- * @items is %NULL-terminated.
- *
- * It is appropriate to call g_variant_type_free() on the return value.
- *
- * Since 2.24
- **/
-static GVariantType *
-g_variant_type_new_tuple_slow (const GVariantType * const *items,
- gint length)
-{
- /* the "slow" version is needed in case the static buffer of 1024
- * bytes is exceeded when running the normal version. this will
- * happen only in truly insane code, so it can be slow.
- */
- GString *string;
- gsize i;
-
- string = g_string_new ("(");
- for (i = 0; i < length; i++)
- {
- const GVariantType *type;
- gsize size;
-
- g_return_val_if_fail (g_variant_type_check (items[i]), NULL);
-
- type = items[i];
- size = g_variant_type_get_string_length (type);
- g_string_append_len (string, (const gchar *) type, size);
- }
- g_string_append_c (string, ')');
-
- return (GVariantType *) g_string_free (string, FALSE);
-}
-
-GVariantType *
-g_variant_type_new_tuple (const GVariantType * const *items,
- gint length)
-{
- char buffer[1024];
- gsize offset;
- gsize i;
-
- g_return_val_if_fail (length == 0 || items != NULL, NULL);
-
- if (length < 0)
- for (length = 0; items[length] != NULL; length++);
-
- offset = 0;
- buffer[offset++] = '(';
-
- for (i = 0; i < length; i++)
- {
- const GVariantType *type;
- gsize size;
-
- g_return_val_if_fail (g_variant_type_check (items[i]), NULL);
-
- type = items[i];
- size = g_variant_type_get_string_length (type);
-
- if (offset + size >= sizeof buffer) /* leave room for ')' */
- return g_variant_type_new_tuple_slow (items, length);
-
- memcpy (&buffer[offset], type, size);
- offset += size;
- }
-
- g_assert (offset < sizeof buffer);
- buffer[offset++] = ')';
-
- return (GVariantType *) g_memdup (buffer, offset);
-}
-
-/**
- * g_variant_type_new_array:
- * @element: a #GVariantType
- * @returns: a new array #GVariantType
- *
- * Constructs the type corresponding to an array of elements of the
- * type @type.
- *
- * It is appropriate to call g_variant_type_free() on the return value.
- *
- * Since 2.24
- **/
-GVariantType *
-g_variant_type_new_array (const GVariantType *element)
-{
- gsize size;
- gchar *new;
-
- g_return_val_if_fail (g_variant_type_check (element), NULL);
-
- size = g_variant_type_get_string_length (element);
- new = g_malloc (size + 1);
-
- new[0] = 'a';
- memcpy (new + 1, element, size);
-
- return (GVariantType *) new;
-}
-
-/**
- * g_variant_type_new_maybe:
- * @element: a #GVariantType
- * @returns: a new maybe #GVariantType
- *
- * Constructs the type corresponding to a maybe instance containing
- * type @type or Nothing.
- *
- * It is appropriate to call g_variant_type_free() on the return value.
- *
- * Since 2.24
- **/
-GVariantType *
-g_variant_type_new_maybe (const GVariantType *element)
-{
- gsize size;
- gchar *new;
-
- g_return_val_if_fail (g_variant_type_check (element), NULL);
-
- size = g_variant_type_get_string_length (element);
- new = g_malloc (size + 1);
-
- new[0] = 'm';
- memcpy (new + 1, element, size);
-
- return (GVariantType *) new;
-}
-
-/**
- * g_variant_type_new_dict_entry:
- * @key: a basic #GVariantType
- * @value: a #GVariantType
- * @returns: a new dictionary entry #GVariantType
- *
- * Constructs the type corresponding to a dictionary entry with a key
- * of type @key and a value of type @value.
- *
- * It is appropriate to call g_variant_type_free() on the return value.
- *
- * Since 2.24
- **/
-GVariantType *
-g_variant_type_new_dict_entry (const GVariantType *key,
- const GVariantType *value)
-{
- gsize keysize, valsize;
- gchar *new;
-
- g_return_val_if_fail (g_variant_type_check (key), NULL);
- g_return_val_if_fail (g_variant_type_check (value), NULL);
-
- keysize = g_variant_type_get_string_length (key);
- valsize = g_variant_type_get_string_length (value);
-
- new = g_malloc (1 + keysize + valsize + 1);
-
- new[0] = '{';
- memcpy (new + 1, key, keysize);
- memcpy (new + 1 + keysize, value, valsize);
- new[1 + keysize + valsize] = '}';
-
- return (GVariantType *) new;
-}
-
-/* private */
-const GVariantType *
-g_variant_type_checked_ (const gchar *type_string)
-{
- g_return_val_if_fail (g_variant_type_string_is_valid (type_string), NULL);
- return (const GVariantType *) type_string;
-}
+++ /dev/null
-/*
- * Copyright © 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * 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 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.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "config.h"
-
-#include "gvarianttypeinfo.h"
-
-#include <glib/gtestutils.h>
-#include <glib/gthread.h>
-#include <glib/ghash.h>
-
-
-/* < private >
- * GVariantTypeInfo:
- *
- * This structure contains the necessary information to facilitate the
- * serialisation and fast deserialisation of a given type of GVariant
- * value. A GVariant instance holds a pointer to one of these
- * structures to provide for efficient operation.
- *
- * The GVariantTypeInfo structures for all of the base types, plus the
- * "variant" type are stored in a read-only static array.
- *
- * For container types, a hash table and reference counting is used to
- * ensure that only one of these structures exists for any given type.
- * In general, a container GVariantTypeInfo will exist for a given type
- * only if one or more GVariant instances of that type exist or if
- * another GVariantTypeInfo has that type as a subtype. For example, if
- * a process contains a single GVariant instance with type "(asv)", then
- * container GVariantTypeInfo structures will exist for "(asv)" and
- * for "as" (note that "s" and "v" always exist in the static array).
- *
- * The trickiest part of GVariantTypeInfo (and in fact, the major reason
- * for its existance) is the storage of somewhat magical constants that
- * allow for O(1) lookups of items in tuples. This is described below.
- *
- * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is
- * contained inside of an ArrayInfo or TupleInfo, respectively. This
- * allows the storage of the necessary additional information.
- *
- * 'fixed_size' is set to the fixed size of the type, if applicable, or
- * 0 otherwise (since no type has a fixed size of 0).
- *
- * 'alignment' is set to one less than the alignment requirement for
- * this type. This makes many operations much more convenient.
- */
-struct _GVariantTypeInfo
-{
- gsize fixed_size;
- guchar alignment;
- guchar container_class;
-};
-
-/* Container types are reference counted. They also need to have their
- * type string stored explicitly since it is not merely a single letter.
- */
-typedef struct
-{
- GVariantTypeInfo info;
-
- gchar *type_string;
- gint ref_count;
-} ContainerInfo;
-
-/* For 'array' and 'maybe' types, we store some extra information on the
- * end of the GVariantTypeInfo struct -- the element type (ie: "s" for
- * "as"). The container GVariantTypeInfo structure holds a reference to
- * the element typeinfo.
- */
-typedef struct
-{
- ContainerInfo container;
-
- GVariantTypeInfo *element;
-} ArrayInfo;
-
-/* For 'tuple' and 'dict entry' types, we store extra information for
- * each member -- its type and how to find it inside the serialised data
- * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'. See the
- * comment on GVariantMemberInfo in gvarianttypeinfo.h.
- */
-typedef struct
-{
- ContainerInfo container;
-
- GVariantMemberInfo *members;
- gsize n_members;
-} TupleInfo;
-
-
-/* Hard-code the base types in a constant array */
-static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
-#define fixed_aligned(x) x, x - 1
-#define not_a_type 0,
-#define unaligned 0, 0
-#define aligned(x) 0, x - 1
- /* 'b' */ { fixed_aligned(1) }, /* boolean */
- /* 'c' */ { not_a_type },
- /* 'd' */ { fixed_aligned(8) }, /* double */
- /* 'e' */ { not_a_type },
- /* 'f' */ { not_a_type },
- /* 'g' */ { unaligned }, /* signature string */
- /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
- /* 'i' */ { fixed_aligned(4) }, /* int32 */
- /* 'j' */ { not_a_type },
- /* 'k' */ { not_a_type },
- /* 'l' */ { not_a_type },
- /* 'm' */ { not_a_type },
- /* 'n' */ { fixed_aligned(2) }, /* int16 */
- /* 'o' */ { unaligned }, /* object path string */
- /* 'p' */ { not_a_type },
- /* 'q' */ { fixed_aligned(2) }, /* uint16 */
- /* 'r' */ { not_a_type },
- /* 's' */ { unaligned }, /* string */
- /* 't' */ { fixed_aligned(8) }, /* uint64 */
- /* 'u' */ { fixed_aligned(4) }, /* uint32 */
- /* 'v' */ { aligned(8) }, /* variant */
- /* 'w' */ { not_a_type },
- /* 'x' */ { fixed_aligned(8) }, /* int64 */
- /* 'y' */ { fixed_aligned(1) }, /* byte */
-#undef fixed_aligned
-#undef not_a_type
-#undef unaligned
-#undef aligned
-};
-
-/* We need to have type strings to return for the base types. We store
- * those in another array. Since all base type strings are single
- * characters this is easy. By not storing pointers to strings into the
- * GVariantTypeInfo itself, we save a bunch of relocations.
- */
-static const char g_variant_type_info_basic_chars[24][2] = {
- "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
- "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
-};
-
-/* sanity checks to make debugging easier */
-static void
-g_variant_type_info_check (const GVariantTypeInfo *info,
- char container_class)
-{
- g_assert (!container_class || info->container_class == container_class);
-
- /* alignment can only be one of these */
- g_assert (info->alignment == 0 || info->alignment == 1 ||
- info->alignment == 3 || info->alignment == 7);
-
- if (info->container_class)
- {
- ContainerInfo *container = (ContainerInfo *) info;
-
- /* extra checks for containers */
- g_assert_cmpint (container->ref_count, >, 0);
- g_assert (container->type_string != NULL);
- }
- else
- {
- gint index;
-
- /* if not a container, then ensure that it is a valid member of
- * the basic types table
- */
- index = info - g_variant_type_info_basic_table;
-
- g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
- g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24);
- g_assert (0 <= index && index < 24);
- g_assert (g_variant_type_info_basic_chars[index][0] != ' ');
- }
-}
-
-/* < private >
- * g_variant_type_info_get_type_string:
- * @info: a #GVariantTypeInfo
- *
- * Gets the type string for @info. The string is nul-terminated.
- */
-const gchar *
-g_variant_type_info_get_type_string (GVariantTypeInfo *info)
-{
- g_variant_type_info_check (info, 0);
-
- if (info->container_class)
- {
- ContainerInfo *container = (ContainerInfo *) info;
-
- /* containers have their type string stored inside them */
- return container->type_string;
- }
- else
- {
- gint index;
-
- /* look up the type string in the base type array. the call to
- * g_variant_type_info_check() above already ensured validity.
- */
- index = info - g_variant_type_info_basic_table;
-
- return g_variant_type_info_basic_chars[index];
- }
-}
-
-/* < private >
- * g_variant_type_info_query:
- * @info: a #GVariantTypeInfo
- * @alignment: the location to store the alignment, or %NULL
- * @fixed_size: the location to store the fixed size, or %NULL
- *
- * Queries @info to determine the alignment requirements and fixed size
- * (if any) of the type.
- *
- * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0
- * to indicate that the type is a variable-sized type. No type has a
- * fixed size of 0.
- *
- * @alignment, if non-%NULL, is set to one less than the required
- * alignment of the type. For example, for a 32bit integer, @alignment
- * would be set to 3. This allows you to round an integer up to the
- * proper alignment by performing the following efficient calculation:
- *
- * offset += ((-offset) & alignment);
- */
-void
-g_variant_type_info_query (GVariantTypeInfo *info,
- guint *alignment,
- gsize *fixed_size)
-{
- g_variant_type_info_check (info, 0);
-
- if (alignment)
- *alignment = info->alignment;
-
- if (fixed_size)
- *fixed_size = info->fixed_size;
-}
-
-/* == array == */
-#define ARRAY_INFO_CLASS 'a'
-static ArrayInfo *
-ARRAY_INFO (GVariantTypeInfo *info)
-{
- g_variant_type_info_check (info, ARRAY_INFO_CLASS);
-
- return (ArrayInfo *) info;
-}
-
-static void
-array_info_free (GVariantTypeInfo *info)
-{
- ArrayInfo *array_info;
-
- g_assert (info->container_class == ARRAY_INFO_CLASS);
- array_info = (ArrayInfo *) info;
-
- g_variant_type_info_unref (array_info->element);
- g_slice_free (ArrayInfo, array_info);
-}
-
-static ContainerInfo *
-array_info_new (const GVariantType *type)
-{
- ArrayInfo *info;
-
- info = g_slice_new (ArrayInfo);
- info->container.info.container_class = ARRAY_INFO_CLASS;
-
- info->element = g_variant_type_info_get (g_variant_type_element (type));
- info->container.info.alignment = info->element->alignment;
- info->container.info.fixed_size = 0;
-
- return (ContainerInfo *) info;
-}
-
-/* < private >
- * g_variant_type_info_element:
- * @info: a #GVariantTypeInfo for an array or maybe type
- *
- * Returns the element type for the array or maybe type. A reference is
- * not added, so the caller must add their own.
- */
-GVariantTypeInfo *
-g_variant_type_info_element (GVariantTypeInfo *info)
-{
- return ARRAY_INFO (info)->element;
-}
-
-/* < private >
- * g_variant_type_query_element:
- * @info: a #GVariantTypeInfo for an array or maybe type
- * @alignment: the location to store the alignment, or %NULL
- * @fixed_size: the location to store the fixed size, or %NULL
- *
- * Returns the alignment requires and fixed size (if any) for the
- * element type of the array. This call is a convenience wrapper around
- * g_variant_type_info_element() and g_variant_type_info_query().
- */
-void
-g_variant_type_info_query_element (GVariantTypeInfo *info,
- guint *alignment,
- gsize *fixed_size)
-{
- g_variant_type_info_query (ARRAY_INFO (info)->element,
- alignment, fixed_size);
-}
-
-/* == tuple == */
-#define TUPLE_INFO_CLASS 'r'
-static TupleInfo *
-TUPLE_INFO (GVariantTypeInfo *info)
-{
- g_variant_type_info_check (info, TUPLE_INFO_CLASS);
-
- return (TupleInfo *) info;
-}
-
-static void
-tuple_info_free (GVariantTypeInfo *info)
-{
- TupleInfo *tuple_info;
- gint i;
-
- g_assert (info->container_class == TUPLE_INFO_CLASS);
- tuple_info = (TupleInfo *) info;
-
- for (i = 0; i < tuple_info->n_members; i++)
- g_variant_type_info_unref (tuple_info->members[i].type_info);
-
- g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members,
- tuple_info->members);
- g_slice_free (TupleInfo, tuple_info);
-}
-
-static void
-tuple_allocate_members (const GVariantType *type,
- GVariantMemberInfo **members,
- gsize *n_members)
-{
- const GVariantType *item_type;
- gsize i = 0;
-
- *n_members = g_variant_type_n_items (type);
- *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members);
-
- item_type = g_variant_type_first (type);
- while (item_type)
- {
- GVariantMemberInfo *member = &(*members)[i++];
-
- member->type_info = g_variant_type_info_get (item_type);
- item_type = g_variant_type_next (item_type);
-
- if (member->type_info->fixed_size)
- member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED;
- else if (item_type == NULL)
- member->ending_type = G_VARIANT_MEMBER_ENDING_LAST;
- else
- member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET;
- }
-
- g_assert (i == *n_members);
-}
-
-/* this is g_variant_type_info_query for a given member of the tuple.
- * before the access is done, it is ensured that the item is within
- * range and %FALSE is returned if not.
- */
-static gboolean
-tuple_get_item (TupleInfo *info,
- GVariantMemberInfo *item,
- gsize *d,
- gsize *e)
-{
- if (&info->members[info->n_members] == item)
- return FALSE;
-
- *d = item->type_info->alignment;
- *e = item->type_info->fixed_size;
- return TRUE;
-}
-
-/* Read the documentation for #GVariantMemberInfo in gvarianttype.h
- * before attempting to understand this.
- *
- * This function adds one set of "magic constant" values (for one item
- * in the tuple) to the table.
- *
- * The algorithm in tuple_generate_table() calculates values of 'a', 'b'
- * and 'c' for each item, such that the procedure for finding the item
- * is to start at the end of the previous variable-sized item, add 'a',
- * then round up to the nearest multiple of 'b', then then add 'c'.
- * Note that 'b' is stored in the usual "one less than" form. ie:
- *
- * start = ROUND_UP(prev_end + a, (b + 1)) + c;
- *
- * We tweak these values a little to allow for a slightly easier
- * computation and more compact storage.
- */
-static void
-tuple_table_append (GVariantMemberInfo **items,
- gsize i,
- gsize a,
- gsize b,
- gsize c)
-{
- GVariantMemberInfo *item = (*items)++;
-
- /* We can shift multiples of the alignment size from 'c' into 'a'.
- * As long as we're shifting whole multiples, it won't affect the
- * result. This means that we can take the "aligned" portion off of
- * 'c' and add it into 'a'.
- *
- * Imagine (for sake of clarity) that ROUND_10 rounds up to the
- * nearest 10. It is clear that:
- *
- * ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10)
- *
- * ie: remove the 10s portion of 'c' and add it onto 'a'.
- *
- * To put some numbers on it, imagine we start with a = 34 and c = 27:
- *
- * ROUND_10(34) + 27 = 40 + 27 = 67
- *
- * but also, we can split 27 up into 20 and 7 and do this:
- *
- * ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67
- * ^^ ^
- * without affecting the result. We do that here.
- *
- * This reduction in the size of 'c' means that we can store it in a
- * gchar instead of a gsize. Due to how the structure is packed, this
- * ends up saving us 'two pointer sizes' per item in each tuple when
- * allocating using GSlice.
- */
- a += ~b & c; /* take the "aligned" part of 'c' and add to 'a' */
- c &= b; /* chop 'c' to contain only the unaligned part */
-
-
- /* Finally, we made one last adjustment. Recall:
- *
- * start = ROUND_UP(prev_end + a, (b + 1)) + c;
- *
- * Forgetting the '+ c' for the moment:
- *
- * ROUND_UP(prev_end + a, (b + 1));
- *
- * we can do a "round up" operation by adding 1 less than the amount
- * to round up to, then rounding down. ie:
- *
- * #define ROUND_UP(x, y) ROUND_DOWN(x + (y-1), y)
- *
- * Of course, for rounding down to a power of two, we can just mask
- * out the appropriate number of low order bits:
- *
- * #define ROUND_DOWN(x, y) (x & ~(y - 1))
- *
- * Which gives us
- *
- * #define ROUND_UP(x, y) (x + (y - 1) & ~(y - 1))
- *
- * but recall that our alignment value 'b' is already "one less".
- * This means that to round 'prev_end + a' up to 'b' we can just do:
- *
- * ((prev_end + a) + b) & ~b
- *
- * Associativity, and putting the 'c' back on:
- *
- * (prev_end + (a + b)) & ~b + c
- *
- * Now, since (a + b) is constant, we can just add 'b' to 'a' now and
- * store that as the number to add to prev_end. Then we use ~b as the
- * number to take a bitwise 'and' with. Finally, 'c' is added on.
- *
- * Note, however, that all the low order bits of the 'aligned' value
- * are masked out and that all of the high order bits of 'c' have been
- * "moved" to 'a' (in the previous step). This means that there are
- * no overlapping bits in the addition -- so we can do a bitwise 'or'
- * equivalently.
- *
- * This means that we can now compute the start address of a given
- * item in the tuple using the algorithm given in the documentation
- * for #GVariantMemberInfo:
- *
- * item_start = ((prev_end + a) & b) | c;
- */
-
- item->i = i;
- item->a = a + b;
- item->b = ~b;
- item->c = c;
-}
-
-static gsize
-tuple_align (gsize offset,
- guint alignment)
-{
- return offset + ((-offset) & alignment);
-}
-
-/* This function is the heart of the algorithm for calculating 'i', 'a',
- * 'b' and 'c' for each item in the tuple.
- *
- * Imagine we want to find the start of the "i" in the type "(su(qx)ni)".
- * That's a string followed by a uint32, then a tuple containing a
- * uint16 and a int64, then an int16, then our "i". In order to get to
- * our "i" we:
- *
- * Start at the end of the string, align to 4 (for the uint32), add 4.
- * Align to 8, add 16 (for the tuple). Align to 2, add 2 (for the
- * int16). Then we're there. It turns out that, given 3 simple rules,
- * we can flatten this iteration into one addition, one alignment, then
- * one more addition.
- *
- * The loop below plays through each item in the tuple, querying its
- * alignment and fixed_size into 'd' and 'e', respectively. At all
- * times the variables 'a', 'b', and 'c' are maintained such that in
- * order to get to the current point, you add 'a', align to 'b' then add
- * 'c'. 'b' is kept in "one less than" form. For each item, the proper
- * alignment is applied to find the values of 'a', 'b' and 'c' to get to
- * the start of that item. Those values are recorded into the table.
- * The fixed size of the item (if applicable) is then added on.
- *
- * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and
- * addition of fixed size. They have been proven correct but are
- * presented here, without proof:
- *
- * 1) in order to "align to 'd'" where 'd' is less than or equal to the
- * largest level of alignment seen so far ('b'), you align 'c' to
- * 'd'.
- * 2) in order to "align to 'd'" where 'd' is greater than the largest
- * level of alignment seen so far, you add 'c' aligned to 'b' to the
- * value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment
- * seen') and reset 'c' to 0.
- * 3) in order to "add 'e'", just add 'e' to 'c'.
- */
-static void
-tuple_generate_table (TupleInfo *info)
-{
- GVariantMemberInfo *items = info->members;
- gsize i = -1, a = 0, b = 0, c = 0, d, e;
-
- /* iterate over each item in the tuple.
- * 'd' will be the alignment of the item (in one-less form)
- * 'e' will be the fixed size (or 0 for variable-size items)
- */
- while (tuple_get_item (info, items, &d, &e))
- {
- /* align to 'd' */
- if (d <= b)
- c = tuple_align (c, d); /* rule 1 */
- else
- a += tuple_align (c, b), b = d, c = 0; /* rule 2 */
-
- /* the start of the item is at this point (ie: right after we
- * have aligned for it). store this information in the table.
- */
- tuple_table_append (&items, i, a, b, c);
-
- /* "move past" the item by adding in its size. */
- if (e == 0)
- /* variable size:
- *
- * we'll have an offset stored to mark the end of this item, so
- * just bump the offset index to give us a new starting point
- * and reset all the counters.
- */
- i++, a = b = c = 0;
- else
- /* fixed size */
- c += e; /* rule 3 */
- }
-}
-
-static void
-tuple_set_base_info (TupleInfo *info)
-{
- GVariantTypeInfo *base = &info->container.info;
-
- if (info->n_members > 0)
- {
- GVariantMemberInfo *m;
-
- /* the alignment requirement of the tuple is the alignment
- * requirement of its largest item.
- */
- base->alignment = 0;
- for (m = info->members; m < &info->members[info->n_members]; m++)
- /* can find the max of a list of "one less than" powers of two
- * by 'or'ing them
- */
- base->alignment |= m->type_info->alignment;
-
- m--; /* take 'm' back to the last item */
-
- /* the structure only has a fixed size if no variable-size
- * offsets are stored and the last item is fixed-sized too (since
- * an offset is never stored for the last item).
- */
- if (m->i == -1 && m->type_info->fixed_size)
- /* in that case, the fixed size can be found by finding the
- * start of the last item (in the usual way) and adding its
- * fixed size.
- *
- * if a tuple has a fixed size then it is always a multiple of
- * the alignment requirement (to make packing into arrays
- * easier) so we round up to that here.
- */
- base->fixed_size =
- tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size,
- base->alignment);
- else
- /* else, the tuple is not fixed size */
- base->fixed_size = 0;
- }
- else
- {
- /* the empty tuple: '()'.
- *
- * has a size of 1 and an no alignment requirement.
- *
- * It has a size of 1 (not 0) for two practical reasons:
- *
- * 1) So we can determine how many of them are in an array
- * without dividing by zero or without other tricks.
- *
- * 2) Even if we had some trick to know the number of items in
- * the array (as GVariant did at one time) this would open a
- * potential denial of service attack: an attacker could send
- * you an extremely small array (in terms of number of bytes)
- * containing trillions of zero-sized items. If you iterated
- * over this array you would effectively infinite-loop your
- * program. By forcing a size of at least one, we bound the
- * amount of computation done in response to a message to a
- * reasonable function of the size of that message.
- */
- base->alignment = 0;
- base->fixed_size = 1;
- }
-}
-
-static ContainerInfo *
-tuple_info_new (const GVariantType *type)
-{
- TupleInfo *info;
-
- info = g_slice_new (TupleInfo);
- info->container.info.container_class = TUPLE_INFO_CLASS;
-
- tuple_allocate_members (type, &info->members, &info->n_members);
- tuple_generate_table (info);
- tuple_set_base_info (info);
-
- return (ContainerInfo *) info;
-}
-
-/* < private >
- * g_variant_type_info_n_members:
- * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
- *
- * Returns the number of members in a tuple or dictionary entry type.
- * For a dictionary entry this will always be 2.
- */
-gsize
-g_variant_type_info_n_members (GVariantTypeInfo *info)
-{
- return TUPLE_INFO (info)->n_members;
-}
-
-/* < private >
- * g_variant_type_info_member_info:
- * @info: a #GVariantTypeInfo for a tuple or dictionary entry type
- * @index: the member to fetch information for
- *
- * Returns the #GVariantMemberInfo for a given member. See
- * documentation for that structure for why you would want this
- * information.
- *
- * @index must refer to a valid child (ie: strictly less than
- * g_variant_type_info_n_members() returns).
- */
-const GVariantMemberInfo *
-g_variant_type_info_member_info (GVariantTypeInfo *info,
- gsize index)
-{
- TupleInfo *tuple_info = TUPLE_INFO (info);
-
- if (index < tuple_info->n_members)
- return &tuple_info->members[index];
-
- return NULL;
-}
-
-/* == new/ref/unref == */
-static GStaticRecMutex g_variant_type_info_lock = G_STATIC_REC_MUTEX_INIT;
-static GHashTable *g_variant_type_info_table;
-
-/* < private >
- * g_variant_type_info_get:
- * @type: a #GVariantType
- *
- * Returns a reference to a #GVariantTypeInfo for @type.
- *
- * If an info structure already exists for this type, a new reference is
- * returned. If not, the required calculations are performed and a new
- * info structure is returned.
- *
- * It is appropriate to call g_variant_type_info_unref() on the return
- * value.
- */
-GVariantTypeInfo *
-g_variant_type_info_get (const GVariantType *type)
-{
- char type_char;
-
- type_char = g_variant_type_peek_string (type)[0];
-
- if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
- type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY ||
- type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE ||
- type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY)
- {
- GVariantTypeInfo *info;
- gchar *type_string;
-
- type_string = g_variant_type_dup_string (type);
-
- g_static_rec_mutex_lock (&g_variant_type_info_lock);
-
- if (g_variant_type_info_table == NULL)
- g_variant_type_info_table = g_hash_table_new (g_str_hash,
- g_str_equal);
- info = g_hash_table_lookup (g_variant_type_info_table, type_string);
-
- if (info == NULL)
- {
- ContainerInfo *container;
-
- if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE ||
- type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY)
- {
- container = array_info_new (type);
- }
- else /* tuple or dict entry */
- {
- container = tuple_info_new (type);
- }
-
- info = (GVariantTypeInfo *) container;
- container->type_string = type_string;
- container->ref_count = 1;
-
- g_hash_table_insert (g_variant_type_info_table, type_string, info);
- type_string = NULL;
- }
- else
- g_variant_type_info_ref (info);
-
- g_static_rec_mutex_unlock (&g_variant_type_info_lock);
- g_variant_type_info_check (info, 0);
- g_free (type_string);
-
- return info;
- }
- else
- {
- const GVariantTypeInfo *info;
- int index;
-
- index = type_char - 'b';
- g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24);
- g_assert_cmpint (0, <=, index);
- g_assert_cmpint (index, <, 24);
-
- info = g_variant_type_info_basic_table + index;
- g_variant_type_info_check (info, 0);
-
- return (GVariantTypeInfo *) info;
- }
-}
-
-/* < private >
- * g_variant_type_info_ref:
- * @info: a #GVariantTypeInfo
- *
- * Adds a reference to @info.
- */
-GVariantTypeInfo *
-g_variant_type_info_ref (GVariantTypeInfo *info)
-{
- g_variant_type_info_check (info, 0);
-
- if (info->container_class)
- {
- ContainerInfo *container = (ContainerInfo *) info;
-
- g_assert_cmpint (container->ref_count, >, 0);
- g_atomic_int_inc (&container->ref_count);
- }
-
- return info;
-}
-
-/* < private >
- * g_variant_type_info_unref:
- * @info: a #GVariantTypeInfo
- *
- * Releases a reference held on @info. This may result in @info being
- * freed.
- */
-void
-g_variant_type_info_unref (GVariantTypeInfo *info)
-{
- g_variant_type_info_check (info, 0);
-
- if (info->container_class)
- {
- ContainerInfo *container = (ContainerInfo *) info;
-
- g_static_rec_mutex_lock (&g_variant_type_info_lock);
- if (g_atomic_int_dec_and_test (&container->ref_count))
- {
- g_hash_table_remove (g_variant_type_info_table,
- container->type_string);
- if (g_hash_table_size (g_variant_type_info_table) == 0)
- {
- g_hash_table_unref (g_variant_type_info_table);
- g_variant_type_info_table = NULL;
- }
- g_static_rec_mutex_unlock (&g_variant_type_info_lock);
-
- g_free (container->type_string);
-
- if (info->container_class == ARRAY_INFO_CLASS)
- array_info_free (info);
-
- else if (info->container_class == TUPLE_INFO_CLASS)
- tuple_info_free (info);
-
- else
- g_assert_not_reached ();
- }
- else
- g_static_rec_mutex_unlock (&g_variant_type_info_lock);
- }
-}
-
-void
-g_variant_type_info_assert_no_infos (void)
-{
- g_assert (g_variant_type_info_table == NULL);
-}
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1998 Peter Mattis, Spencer Kimball and Josh MacDonald
- * Copyright (C) 1998-1999 Tor Lillqvist
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
- */
-
-#include "config.h"
-
-#include "glibconfig.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <wchar.h>
-#include <errno.h>
-
-#define STRICT /* Strict typing, please */
-#include <windows.h>
-#undef STRICT
-#ifndef G_WITH_CYGWIN
-#include <direct.h>
-#endif
-#include <errno.h>
-#include <ctype.h>
-#if defined(_MSC_VER) || defined(__DMC__)
-# include <io.h>
-#endif /* _MSC_VER || __DMC__ */
-
-#include "glib.h"
-
-#ifdef G_WITH_CYGWIN
-#include <sys/cygwin.h>
-#endif
-
-#ifndef G_WITH_CYGWIN
-
-gint
-g_win32_ftruncate (gint fd,
- guint size)
-{
- return _chsize (fd, size);
-}
-
-#endif
-
-/**
- * g_win32_getlocale:
- *
- * The setlocale() function in the Microsoft C library uses locale
- * names of the form "English_United States.1252" etc. We want the
- * UNIXish standard form "en_US", "zh_TW" etc. This function gets the
- * current thread locale from Windows - without any encoding info -
- * and returns it as a string of the above form for use in forming
- * file names etc. The returned string should be deallocated with
- * g_free().
- *
- * Returns: newly-allocated locale name.
- **/
-
-#ifndef SUBLANG_SERBIAN_LATIN_BA
-#define SUBLANG_SERBIAN_LATIN_BA 0x06
-#endif
-
-gchar *
-g_win32_getlocale (void)
-{
- LCID lcid;
- LANGID langid;
- gchar *ev;
- gint primary, sub;
- char iso639[10];
- char iso3166[10];
- const gchar *script = NULL;
-
- /* Let the user override the system settings through environment
- * variables, as on POSIX systems. Note that in GTK+ applications
- * since GTK+ 2.10.7 setting either LC_ALL or LANG also sets the
- * Win32 locale and C library locale through code in gtkmain.c.
- */
- if (((ev = getenv ("LC_ALL")) != NULL && ev[0] != '\0')
- || ((ev = getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0')
- || ((ev = getenv ("LANG")) != NULL && ev[0] != '\0'))
- return g_strdup (ev);
-
- lcid = GetThreadLocale ();
-
- if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) ||
- !GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166)))
- return g_strdup ("C");
-
- /* Strip off the sorting rules, keep only the language part. */
- langid = LANGIDFROMLCID (lcid);
-
- /* Split into language and territory part. */
- primary = PRIMARYLANGID (langid);
- sub = SUBLANGID (langid);
-
- /* Handle special cases */
- switch (primary)
- {
- case LANG_AZERI:
- switch (sub)
- {
- case SUBLANG_AZERI_LATIN:
- script = "@Latn";
- break;
- case SUBLANG_AZERI_CYRILLIC:
- script = "@Cyrl";
- break;
- }
- break;
- case LANG_SERBIAN: /* LANG_CROATIAN == LANG_SERBIAN */
- switch (sub)
- {
- case SUBLANG_SERBIAN_LATIN:
- case 0x06: /* Serbian (Latin) - Bosnia and Herzegovina */
- script = "@Latn";
- break;
- }
- break;
- case LANG_UZBEK:
- switch (sub)
- {
- case SUBLANG_UZBEK_LATIN:
- script = "@Latn";
- break;
- case SUBLANG_UZBEK_CYRILLIC:
- script = "@Cyrl";
- break;
- }
- break;
- }
- return g_strconcat (iso639, "_", iso3166, script, NULL);
-}
-
-/**
- * g_win32_error_message:
- * @error: error code.
- *
- * Translate a Win32 error code (as returned by GetLastError()) into
- * the corresponding message. The message is either language neutral,
- * or in the thread's language, or the user's language, the system's
- * language, or US English (see docs for FormatMessage()). The
- * returned string is in UTF-8. It should be deallocated with
- * g_free().
- *
- * Returns: newly-allocated error message
- **/
-gchar *
-g_win32_error_message (gint error)
-{
- gchar *retval;
- wchar_t *msg = NULL;
- int nchars;
-
- FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER
- |FORMAT_MESSAGE_IGNORE_INSERTS
- |FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, error, 0,
- (LPWSTR) &msg, 0, NULL);
- if (msg != NULL)
- {
- nchars = wcslen (msg);
-
- if (nchars > 2 && msg[nchars-1] == '\n' && msg[nchars-2] == '\r')
- msg[nchars-2] = '\0';
-
- retval = g_utf16_to_utf8 (msg, -1, NULL, NULL, NULL);
-
- LocalFree (msg);
- }
- else
- retval = g_strdup ("");
-
- return retval;
-}
-
-/**
- * g_win32_get_package_installation_directory_of_module:
- * @hmodule: The Win32 handle for a DLL loaded into the current process, or %NULL
- *
- * This function tries to determine the installation directory of a
- * software package based on the location of a DLL of the software
- * package.
- *
- * @hmodule should be the handle of a loaded DLL or %NULL. The
- * function looks up the directory that DLL was loaded from. If
- * @hmodule is NULL, the directory the main executable of the current
- * process is looked up. If that directory's last component is "bin"
- * or "lib", its parent directory is returned, otherwise the directory
- * itself.
- *
- * It thus makes sense to pass only the handle to a "public" DLL of a
- * software package to this function, as such DLLs typically are known
- * to be installed in a "bin" or occasionally "lib" subfolder of the
- * installation folder. DLLs that are of the dynamically loaded module
- * or plugin variety are often located in more private locations
- * deeper down in the tree, from which it is impossible for GLib to
- * deduce the root of the package installation.
- *
- * The typical use case for this function is to have a DllMain() that
- * saves the handle for the DLL. Then when code in the DLL needs to
- * construct names of files in the installation tree it calls this
- * function passing the DLL handle.
- *
- * Returns: a string containing the guessed installation directory for
- * the software package @hmodule is from. The string is in the GLib
- * file name encoding, i.e. UTF-8. The return value should be freed
- * with g_free() when not needed any longer. If the function fails
- * %NULL is returned.
- *
- * Since: 2.16
- */
-gchar *
-g_win32_get_package_installation_directory_of_module (gpointer hmodule)
-{
- gchar *retval;
- gchar *p;
- wchar_t wc_fn[MAX_PATH];
-
- if (!GetModuleFileNameW (hmodule, wc_fn, MAX_PATH))
- return NULL;
-
- retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
-
- if ((p = strrchr (retval, G_DIR_SEPARATOR)) != NULL)
- *p = '\0';
-
- p = strrchr (retval, G_DIR_SEPARATOR);
- if (p && (g_ascii_strcasecmp (p + 1, "bin") == 0 ||
- g_ascii_strcasecmp (p + 1, "lib") == 0))
- *p = '\0';
-
-#ifdef G_WITH_CYGWIN
- /* In Cygwin we need to have POSIX paths */
- {
- gchar tmp[MAX_PATH];
-
- cygwin_conv_to_posix_path (retval, tmp);
- g_free (retval);
- retval = g_strdup (tmp);
- }
-#endif
-
- return retval;
-}
-
-static gchar *
-get_package_directory_from_module (const gchar *module_name)
-{
- static GHashTable *module_dirs = NULL;
- G_LOCK_DEFINE_STATIC (module_dirs);
- HMODULE hmodule = NULL;
- gchar *fn;
-
- G_LOCK (module_dirs);
-
- if (module_dirs == NULL)
- module_dirs = g_hash_table_new (g_str_hash, g_str_equal);
-
- fn = g_hash_table_lookup (module_dirs, module_name ? module_name : "");
-
- if (fn)
- {
- G_UNLOCK (module_dirs);
- return g_strdup (fn);
- }
-
- if (module_name)
- {
- wchar_t *wc_module_name = g_utf8_to_utf16 (module_name, -1, NULL, NULL, NULL);
- hmodule = GetModuleHandleW (wc_module_name);
- g_free (wc_module_name);
-
- if (!hmodule)
- {
- G_UNLOCK (module_dirs);
- return NULL;
- }
- }
-
- fn = g_win32_get_package_installation_directory_of_module (hmodule);
-
- if (fn == NULL)
- {
- G_UNLOCK (module_dirs);
- return NULL;
- }
-
- g_hash_table_insert (module_dirs, module_name ? g_strdup (module_name) : "", fn);
-
- G_UNLOCK (module_dirs);
-
- return g_strdup (fn);
-}
-
-/**
- * g_win32_get_package_installation_directory:
- * @package: You should pass %NULL for this.
- * @dll_name: The name of a DLL that a package provides in UTF-8, or %NULL.
- *
- * Try to determine the installation directory for a software package.
- *
- * This function is deprecated. Use
- * g_win32_get_package_installation_directory_of_module() instead.
- *
- * The use of @package is deprecated. You should always pass %NULL. A
- * warning is printed if non-NULL is passed as @package.
- *
- * The original intended use of @package was for a short identifier of
- * the package, typically the same identifier as used for
- * <literal>GETTEXT_PACKAGE</literal> in software configured using GNU
- * autotools. The function first looks in the Windows Registry for the
- * value <literal>#InstallationDirectory</literal> in the key
- * <literal>#HKLM\Software\@package</literal>, and if that value
- * exists and is a string, returns that.
- *
- * It is strongly recommended that packagers of GLib-using libraries
- * for Windows do not store installation paths in the Registry to be
- * used by this function as that interfers with having several
- * parallel installations of the library. Enabling multiple
- * installations of different versions of some GLib-using library, or
- * GLib itself, is desirable for various reasons.
- *
- * For this reason it is recommeded to always pass %NULL as
- * @package to this function, to avoid the temptation to use the
- * Registry. In version 2.20 of GLib the @package parameter
- * will be ignored and this function won't look in the Registry at all.
- *
- * If @package is %NULL, or the above value isn't found in the
- * Registry, but @dll_name is non-%NULL, it should name a DLL loaded
- * into the current process. Typically that would be the name of the
- * DLL calling this function, looking for its installation
- * directory. The function then asks Windows what directory that DLL
- * was loaded from. If that directory's last component is "bin" or
- * "lib", the parent directory is returned, otherwise the directory
- * itself. If that DLL isn't loaded, the function proceeds as if
- * @dll_name was %NULL.
- *
- * If both @package and @dll_name are %NULL, the directory from where
- * the main executable of the process was loaded is used instead in
- * the same way as above.
- *
- * Returns: a string containing the installation directory for
- * @package. The string is in the GLib file name encoding,
- * i.e. UTF-8. The return value should be freed with g_free() when not
- * needed any longer. If the function fails %NULL is returned.
- *
- * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
- * g_win32_get_package_installation_directory_of_module() instead.
- **/
-
- gchar *
-g_win32_get_package_installation_directory_utf8 (const gchar *package,
- const gchar *dll_name)
-{
- gchar *result = NULL;
-
- if (package != NULL)
- g_warning ("Passing a non-NULL package to g_win32_get_package_installation_directory() is deprecated and it is ignored.");
-
- if (dll_name != NULL)
- result = get_package_directory_from_module (dll_name);
-
- if (result == NULL)
- result = get_package_directory_from_module (NULL);
-
- return result;
-}
-
-#if !defined (_WIN64)
-
-/* DLL ABI binary compatibility version that uses system codepage file names */
-
-gchar *
-g_win32_get_package_installation_directory (const gchar *package,
- const gchar *dll_name)
-{
- gchar *utf8_package = NULL, *utf8_dll_name = NULL;
- gchar *utf8_retval, *retval;
-
- if (package != NULL)
- utf8_package = g_locale_to_utf8 (package, -1, NULL, NULL, NULL);
-
- if (dll_name != NULL)
- utf8_dll_name = g_locale_to_utf8 (dll_name, -1, NULL, NULL, NULL);
-
- utf8_retval =
- g_win32_get_package_installation_directory_utf8 (utf8_package,
- utf8_dll_name);
-
- retval = g_locale_from_utf8 (utf8_retval, -1, NULL, NULL, NULL);
-
- g_free (utf8_package);
- g_free (utf8_dll_name);
- g_free (utf8_retval);
-
- return retval;
-}
-
-#endif
-
-/**
- * g_win32_get_package_installation_subdirectory:
- * @package: You should pass %NULL for this.
- * @dll_name: The name of a DLL that a package provides, in UTF-8, or %NULL.
- * @subdir: A subdirectory of the package installation directory, also in UTF-8
- *
- * This function is deprecated. Use
- * g_win32_get_package_installation_directory_of_module() and
- * g_build_filename() instead.
- *
- * Returns a newly-allocated string containing the path of the
- * subdirectory @subdir in the return value from calling
- * g_win32_get_package_installation_directory() with the @package and
- * @dll_name parameters. See the documentation for
- * g_win32_get_package_installation_directory() for more details. In
- * particular, note that it is deprecated to pass anything except NULL
- * as @package.
- *
- * Returns: a string containing the complete path to @subdir inside
- * the installation directory of @package. The returned string is in
- * the GLib file name encoding, i.e. UTF-8. The return value should be
- * freed with g_free() when no longer needed. If something goes wrong,
- * %NULL is returned.
- *
- * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
- * g_win32_get_package_installation_directory_of_module() instead, and
- * then construct a subdirectory pathname with g_build_filename().
- **/
-
-gchar *
-g_win32_get_package_installation_subdirectory_utf8 (const gchar *package,
- const gchar *dll_name,
- const gchar *subdir)
-{
- gchar *prefix;
- gchar *dirname;
-
- prefix = g_win32_get_package_installation_directory_utf8 (package, dll_name);
-
- dirname = g_build_filename (prefix, subdir, NULL);
- g_free (prefix);
-
- return dirname;
-}
-
-#if !defined (_WIN64)
-
-/* DLL ABI binary compatibility version that uses system codepage file names */
-
-gchar *
-g_win32_get_package_installation_subdirectory (const gchar *package,
- const gchar *dll_name,
- const gchar *subdir)
-{
- gchar *prefix;
- gchar *dirname;
-
- prefix = g_win32_get_package_installation_directory (package, dll_name);
-
- dirname = g_build_filename (prefix, subdir, NULL);
- g_free (prefix);
-
- return dirname;
-}
-
-#endif
-
-static guint windows_version;
-
-static void
-g_win32_windows_version_init (void)
-{
- static gboolean beenhere = FALSE;
-
- if (!beenhere)
- {
- beenhere = TRUE;
- windows_version = GetVersion ();
-
- if (windows_version & 0x80000000)
- g_error ("This version of GLib requires NT-based Windows.");
- }
-}
-
-void
-_g_win32_thread_init (void)
-{
- g_win32_windows_version_init ();
-}
-
-/**
- * g_win32_get_windows_version:
- *
- * Returns version information for the Windows operating system the
- * code is running on. See MSDN documentation for the GetVersion()
- * function. To summarize, the most significant bit is one on Win9x,
- * and zero on NT-based systems. Since version 2.14, GLib works only
- * on NT-based systems, so checking whether your are running on Win9x
- * in your own software is moot. The least significant byte is 4 on
- * Windows NT 4, and 5 on Windows XP. Software that needs really
- * detailled version and feature information should use Win32 API like
- * GetVersionEx() and VerifyVersionInfo().
- *
- * Returns: The version information.
- *
- * Since: 2.6
- **/
-guint
-g_win32_get_windows_version (void)
-{
- g_win32_windows_version_init ();
-
- return windows_version;
-}
-
-/**
- * g_win32_locale_filename_from_utf8:
- * @utf8filename: a UTF-8 encoded filename.
- *
- * Converts a filename from UTF-8 to the system codepage.
- *
- * On NT-based Windows, on NTFS file systems, file names are in
- * Unicode. It is quite possible that Unicode file names contain
- * characters not representable in the system codepage. (For instance,
- * Greek or Cyrillic characters on Western European or US Windows
- * installations, or various less common CJK characters on CJK Windows
- * installations.)
- *
- * In such a case, and if the filename refers to an existing file, and
- * the file system stores alternate short (8.3) names for directory
- * entries, the short form of the filename is returned. Note that the
- * "short" name might in fact be longer than the Unicode name if the
- * Unicode name has very short pathname components containing
- * non-ASCII characters. If no system codepage name for the file is
- * possible, %NULL is returned.
- *
- * The return value is dynamically allocated and should be freed with
- * g_free() when no longer needed.
- *
- * Return value: The converted filename, or %NULL on conversion
- * failure and lack of short names.
- *
- * Since: 2.8
- */
-gchar *
-g_win32_locale_filename_from_utf8 (const gchar *utf8filename)
-{
- gchar *retval = g_locale_from_utf8 (utf8filename, -1, NULL, NULL, NULL);
-
- if (retval == NULL)
- {
- /* Conversion failed, so convert to wide chars, check if there
- * is a 8.3 version, and use that.
- */
- wchar_t *wname = g_utf8_to_utf16 (utf8filename, -1, NULL, NULL, NULL);
- if (wname != NULL)
- {
- wchar_t wshortname[MAX_PATH + 1];
- if (GetShortPathNameW (wname, wshortname, G_N_ELEMENTS (wshortname)))
- {
- gchar *tem = g_utf16_to_utf8 (wshortname, -1, NULL, NULL, NULL);
- retval = g_locale_from_utf8 (tem, -1, NULL, NULL, NULL);
- g_free (tem);
- }
- g_free (wname);
- }
- }
- return retval;
-}
+++ /dev/null
-charset.alias
-ref-add.sed
-ref-del.sed
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-include $(top_srcdir)/Makefile.decl
-
-INCLUDES = \
- -DLIBDIR=\"$(libdir)\" $(config_h_INCLUDES)
-
-noinst_LTLIBRARIES = libcharset.la
-
-libcharset_la_SOURCES = \
- libcharset.h \
- localcharset.h \
- localcharset.c
-
-EXTRA_DIST += \
- README \
- config.charset \
- ref-add.sin \
- ref-del.sin \
- glibc21.m4 \
- codeset.m4 \
- update.sh \
- make-patch.sh \
- libcharset-glib.patch
-
-charset_alias = $(DESTDIR)$(libdir)/charset.alias
-charset_tmp = $(DESTDIR)$(libdir)/charset.tmp
-install-exec-local: all-local
- $(mkinstalldirs) $(DESTDIR)$(libdir)
- if test -f $(charset_alias); then \
- sed -f ref-add.sed $(charset_alias) > $(charset_tmp) ; \
- $(INSTALL_DATA) $(charset_tmp) $(charset_alias) ; \
- rm -f $(charset_tmp) ; \
- else \
- if test @GLIBC21@ = no; then \
- sed -f ref-add.sed charset.alias > $(charset_tmp) ; \
- $(INSTALL_DATA) $(charset_tmp) $(charset_alias) ; \
- rm -f $(charset_tmp) ; \
- fi ; \
- fi
-
-uninstall-local: all-local
- if test -f $(charset_alias); then \
- sed -f ref-del.sed $(charset_alias) > $(charset_tmp); \
- if grep '^# Packages using this file: $$' $(charset_tmp) \
- > /dev/null; then \
- rm -f $(charset_alias); \
- else \
- $(INSTALL_DATA) $(charset_tmp) $(charset_alias); \
- fi; \
- rm -f $(charset_tmp); \
- fi
-
-charset.alias: config.charset
- $(SHELL) $(srcdir)/config.charset '@host@' > t-$@
- mv t-$@ $@
-
-all-local: ref-add.sed ref-del.sed charset.alias
-
-SUFFIXES = .sed .sin
-.sin.sed:
- sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $< > t-$@
- mv t-$@ $@
-
-CLEANFILES = charset.alias ref-add.sed ref-del.sed
+++ /dev/null
-The sources are derived from Bruno Haible's libcharset library included
-with libiconv:
-
- http//www.gnu.org/software/libiconv
-
-The 'update.sh' script in this directory, when pointed at
-the original sources updates the files in this directory
-(and elsewhere in the GLib distribution) to the new version
-
-The 'make-patch.sh' script in this directory regenerates
-the patch files included in this directory from a copy
-of the pristine sources and the files in this directory.
-
-The license on the portions from libiconv portions is reproduced
-below.
-
-Owen Taylor
-26 September 2001
-
-Updated to libiconv-1.12.
-
-Behdad Esfahbod
-20 May 2008
-
-====
-
-/* Determine a canonical name for the current locale's character encoding.
-
- Copyright (C) 2000-2001 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- USA. */
-
-/* Written by Bruno Haible <haible@clisp.cons.org>. */
+++ /dev/null
-# codeset.m4 serial AM1 (gettext-0.10.40)
-dnl Copyright (C) 2000-2002 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-dnl From Bruno Haible.
-
-AC_DEFUN([AM_LANGINFO_CODESET],
-[
- AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
- [AC_TRY_LINK([#include <langinfo.h>],
- [char* cs = nl_langinfo(CODESET);],
- am_cv_langinfo_codeset=yes,
- am_cv_langinfo_codeset=no)
- ])
- if test $am_cv_langinfo_codeset = yes; then
- AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
- [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
- fi
-])
+++ /dev/null
-#! /bin/sh
-# Output a system dependent table of character encoding aliases.
-#
-# Copyright (C) 2000-2004, 2006 Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Library General Public License as published
-# by the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Library General Public License for more details.
-#
-# You should have received a copy of the GNU Library 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.
-#
-# The table consists of lines of the form
-# ALIAS CANONICAL
-#
-# ALIAS is the (system dependent) result of "nl_langinfo (CODESET)".
-# ALIAS is compared in a case sensitive way.
-#
-# CANONICAL is the GNU canonical name for this character encoding.
-# It must be an encoding supported by libiconv. Support by GNU libc is
-# also desirable. CANONICAL is case insensitive. Usually an upper case
-# MIME charset name is preferred.
-# The current list of GNU canonical charset names is as follows.
-#
-# name MIME? used by which systems
-# ASCII, ANSI_X3.4-1968 glibc solaris freebsd netbsd darwin
-# ISO-8859-1 Y glibc aix hpux irix osf solaris freebsd netbsd darwin
-# ISO-8859-2 Y glibc aix hpux irix osf solaris freebsd netbsd darwin
-# ISO-8859-3 Y glibc solaris
-# ISO-8859-4 Y osf solaris freebsd netbsd darwin
-# ISO-8859-5 Y glibc aix hpux irix osf solaris freebsd netbsd darwin
-# ISO-8859-6 Y glibc aix hpux solaris
-# ISO-8859-7 Y glibc aix hpux irix osf solaris netbsd darwin
-# ISO-8859-8 Y glibc aix hpux osf solaris
-# ISO-8859-9 Y glibc aix hpux irix osf solaris darwin
-# ISO-8859-13 glibc netbsd darwin
-# ISO-8859-14 glibc
-# ISO-8859-15 glibc aix osf solaris freebsd darwin
-# KOI8-R Y glibc solaris freebsd netbsd darwin
-# KOI8-U Y glibc freebsd netbsd darwin
-# KOI8-T glibc
-# CP437 dos
-# CP775 dos
-# CP850 aix osf dos
-# CP852 dos
-# CP855 dos
-# CP856 aix
-# CP857 dos
-# CP861 dos
-# CP862 dos
-# CP864 dos
-# CP865 dos
-# CP866 freebsd netbsd darwin dos
-# CP869 dos
-# CP874 woe32 dos
-# CP922 aix
-# CP932 aix woe32 dos
-# CP943 aix
-# CP949 osf woe32 dos
-# CP950 woe32 dos
-# CP1046 aix
-# CP1124 aix
-# CP1125 dos
-# CP1129 aix
-# CP1250 woe32
-# CP1251 glibc solaris netbsd darwin woe32
-# CP1252 aix woe32
-# CP1253 woe32
-# CP1254 woe32
-# CP1255 glibc woe32
-# CP1256 woe32
-# CP1257 woe32
-# GB2312 Y glibc aix hpux irix solaris freebsd netbsd darwin
-# EUC-JP Y glibc aix hpux irix osf solaris freebsd netbsd darwin
-# EUC-KR Y glibc aix hpux irix osf solaris freebsd netbsd darwin
-# EUC-TW glibc aix hpux irix osf solaris netbsd
-# BIG5 Y glibc aix hpux osf solaris freebsd netbsd darwin
-# BIG5-HKSCS glibc solaris
-# GBK glibc aix osf solaris woe32 dos
-# GB18030 glibc solaris netbsd
-# SHIFT_JIS Y hpux osf solaris freebsd netbsd darwin
-# JOHAB glibc solaris woe32
-# TIS-620 glibc aix hpux osf solaris
-# VISCII Y glibc
-# TCVN5712-1 glibc
-# GEORGIAN-PS glibc
-# HP-ROMAN8 hpux
-# HP-ARABIC8 hpux
-# HP-GREEK8 hpux
-# HP-HEBREW8 hpux
-# HP-TURKISH8 hpux
-# HP-KANA8 hpux
-# DEC-KANJI osf
-# DEC-HANYU osf
-# UTF-8 Y glibc aix hpux osf solaris netbsd darwin
-#
-# Note: Names which are not marked as being a MIME name should not be used in
-# Internet protocols for information interchange (mail, news, etc.).
-#
-# Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names. Applications
-# must understand both names and treat them as equivalent.
-#
-# The first argument passed to this file is the canonical host specification,
-# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
-# or
-# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
-
-host="$1"
-os=`echo "$host" | sed -e 's/^[^-]*-[^-]*-\(.*\)$/\1/'`
-echo "# This file contains a table of character encoding aliases,"
-echo "# suitable for operating system '${os}'."
-echo "# It was automatically generated from config.charset."
-# List of references, updated during installation:
-echo "# Packages using this file: "
-case "$os" in
- linux-gnulibc1*)
- # Linux libc5 doesn't have nl_langinfo(CODESET); therefore
- # localcharset.c falls back to using the full locale name
- # from the environment variables.
- echo "C ASCII"
- echo "POSIX ASCII"
- for l in af af_ZA ca ca_ES da da_DK de de_AT de_BE de_CH de_DE de_LU \
- en en_AU en_BW en_CA en_DK en_GB en_IE en_NZ en_US en_ZA \
- en_ZW es es_AR es_BO es_CL es_CO es_DO es_EC es_ES es_GT \
- es_HN es_MX es_PA es_PE es_PY es_SV es_US es_UY es_VE et \
- et_EE eu eu_ES fi fi_FI fo fo_FO fr fr_BE fr_CA fr_CH fr_FR \
- fr_LU ga ga_IE gl gl_ES id id_ID in in_ID is is_IS it it_CH \
- it_IT kl kl_GL nl nl_BE nl_NL no no_NO pt pt_BR pt_PT sv \
- sv_FI sv_SE; do
- echo "$l ISO-8859-1"
- echo "$l.iso-8859-1 ISO-8859-1"
- echo "$l.iso-8859-15 ISO-8859-15"
- echo "$l.iso-8859-15@euro ISO-8859-15"
- echo "$l@euro ISO-8859-15"
- echo "$l.cp-437 CP437"
- echo "$l.cp-850 CP850"
- echo "$l.cp-1252 CP1252"
- echo "$l.cp-1252@euro CP1252"
- #echo "$l.atari-st ATARI-ST" # not a commonly used encoding
- echo "$l.utf-8 UTF-8"
- echo "$l.utf-8@euro UTF-8"
- done
- for l in cs cs_CZ hr hr_HR hu hu_HU pl pl_PL ro ro_RO sk sk_SK sl \
- sl_SI sr sr_CS sr_YU; do
- echo "$l ISO-8859-2"
- echo "$l.iso-8859-2 ISO-8859-2"
- echo "$l.cp-852 CP852"
- echo "$l.cp-1250 CP1250"
- echo "$l.utf-8 UTF-8"
- done
- for l in mk mk_MK ru ru_RU; do
- echo "$l ISO-8859-5"
- echo "$l.iso-8859-5 ISO-8859-5"
- echo "$l.koi8-r KOI8-R"
- echo "$l.cp-866 CP866"
- echo "$l.cp-1251 CP1251"
- echo "$l.utf-8 UTF-8"
- done
- for l in ar ar_SA; do
- echo "$l ISO-8859-6"
- echo "$l.iso-8859-6 ISO-8859-6"
- echo "$l.cp-864 CP864"
- #echo "$l.cp-868 CP868" # not a commonly used encoding
- echo "$l.cp-1256 CP1256"
- echo "$l.utf-8 UTF-8"
- done
- for l in el el_GR gr gr_GR; do
- echo "$l ISO-8859-7"
- echo "$l.iso-8859-7 ISO-8859-7"
- echo "$l.cp-869 CP869"
- echo "$l.cp-1253 CP1253"
- echo "$l.cp-1253@euro CP1253"
- echo "$l.utf-8 UTF-8"
- echo "$l.utf-8@euro UTF-8"
- done
- for l in he he_IL iw iw_IL; do
- echo "$l ISO-8859-8"
- echo "$l.iso-8859-8 ISO-8859-8"
- echo "$l.cp-862 CP862"
- echo "$l.cp-1255 CP1255"
- echo "$l.utf-8 UTF-8"
- done
- for l in tr tr_TR; do
- echo "$l ISO-8859-9"
- echo "$l.iso-8859-9 ISO-8859-9"
- echo "$l.cp-857 CP857"
- echo "$l.cp-1254 CP1254"
- echo "$l.utf-8 UTF-8"
- done
- for l in lt lt_LT lv lv_LV; do
- #echo "$l BALTIC" # not a commonly used encoding, wrong encoding name
- echo "$l ISO-8859-13"
- done
- for l in ru_UA uk uk_UA; do
- echo "$l KOI8-U"
- done
- for l in zh zh_CN; do
- #echo "$l GB_2312-80" # not a commonly used encoding, wrong encoding name
- echo "$l GB2312"
- done
- for l in ja ja_JP ja_JP.EUC; do
- echo "$l EUC-JP"
- done
- for l in ko ko_KR; do
- echo "$l EUC-KR"
- done
- for l in th th_TH; do
- echo "$l TIS-620"
- done
- for l in fa fa_IR; do
- #echo "$l ISIRI-3342" # a broken encoding
- echo "$l.utf-8 UTF-8"
- done
- ;;
- linux* | *-gnu*)
- # With glibc-2.1 or newer, we don't need any canonicalization,
- # because glibc has iconv and both glibc and libiconv support all
- # GNU canonical names directly. Therefore, the Makefile does not
- # need to install the alias file at all.
- # The following applies only to glibc-2.0.x and older libcs.
- echo "ISO_646.IRV:1983 ASCII"
- ;;
- aix*)
- echo "ISO8859-1 ISO-8859-1"
- echo "ISO8859-2 ISO-8859-2"
- echo "ISO8859-5 ISO-8859-5"
- echo "ISO8859-6 ISO-8859-6"
- echo "ISO8859-7 ISO-8859-7"
- echo "ISO8859-8 ISO-8859-8"
- echo "ISO8859-9 ISO-8859-9"
- echo "ISO8859-15 ISO-8859-15"
- echo "IBM-850 CP850"
- echo "IBM-856 CP856"
- echo "IBM-921 ISO-8859-13"
- echo "IBM-922 CP922"
- echo "IBM-932 CP932"
- echo "IBM-943 CP943"
- echo "IBM-1046 CP1046"
- echo "IBM-1124 CP1124"
- echo "IBM-1129 CP1129"
- echo "IBM-1252 CP1252"
- echo "IBM-eucCN GB2312"
- echo "IBM-eucJP EUC-JP"
- echo "IBM-eucKR EUC-KR"
- echo "IBM-eucTW EUC-TW"
- echo "big5 BIG5"
- echo "GBK GBK"
- echo "TIS-620 TIS-620"
- echo "UTF-8 UTF-8"
- ;;
- hpux*)
- echo "iso88591 ISO-8859-1"
- echo "iso88592 ISO-8859-2"
- echo "iso88595 ISO-8859-5"
- echo "iso88596 ISO-8859-6"
- echo "iso88597 ISO-8859-7"
- echo "iso88598 ISO-8859-8"
- echo "iso88599 ISO-8859-9"
- echo "iso885915 ISO-8859-15"
- echo "roman8 HP-ROMAN8"
- echo "arabic8 HP-ARABIC8"
- echo "greek8 HP-GREEK8"
- echo "hebrew8 HP-HEBREW8"
- echo "turkish8 HP-TURKISH8"
- echo "kana8 HP-KANA8"
- echo "tis620 TIS-620"
- echo "big5 BIG5"
- echo "eucJP EUC-JP"
- echo "eucKR EUC-KR"
- echo "eucTW EUC-TW"
- echo "hp15CN GB2312"
- #echo "ccdc ?" # what is this?
- echo "SJIS SHIFT_JIS"
- echo "utf8 UTF-8"
- ;;
- irix*)
- echo "ISO8859-1 ISO-8859-1"
- echo "ISO8859-2 ISO-8859-2"
- echo "ISO8859-5 ISO-8859-5"
- echo "ISO8859-7 ISO-8859-7"
- echo "ISO8859-9 ISO-8859-9"
- echo "eucCN GB2312"
- echo "eucJP EUC-JP"
- echo "eucKR EUC-KR"
- echo "eucTW EUC-TW"
- ;;
- osf*)
- echo "ISO8859-1 ISO-8859-1"
- echo "ISO8859-2 ISO-8859-2"
- echo "ISO8859-4 ISO-8859-4"
- echo "ISO8859-5 ISO-8859-5"
- echo "ISO8859-7 ISO-8859-7"
- echo "ISO8859-8 ISO-8859-8"
- echo "ISO8859-9 ISO-8859-9"
- echo "ISO8859-15 ISO-8859-15"
- echo "cp850 CP850"
- echo "big5 BIG5"
- echo "dechanyu DEC-HANYU"
- echo "dechanzi GB2312"
- echo "deckanji DEC-KANJI"
- echo "deckorean EUC-KR"
- echo "eucJP EUC-JP"
- echo "eucKR EUC-KR"
- echo "eucTW EUC-TW"
- echo "GBK GBK"
- echo "KSC5601 CP949"
- echo "sdeckanji EUC-JP"
- echo "SJIS SHIFT_JIS"
- echo "TACTIS TIS-620"
- echo "UTF-8 UTF-8"
- ;;
- solaris*)
- echo "646 ASCII"
- echo "ISO8859-1 ISO-8859-1"
- echo "ISO8859-2 ISO-8859-2"
- echo "ISO8859-3 ISO-8859-3"
- echo "ISO8859-4 ISO-8859-4"
- echo "ISO8859-5 ISO-8859-5"
- echo "ISO8859-6 ISO-8859-6"
- echo "ISO8859-7 ISO-8859-7"
- echo "ISO8859-8 ISO-8859-8"
- echo "ISO8859-9 ISO-8859-9"
- echo "ISO8859-15 ISO-8859-15"
- echo "koi8-r KOI8-R"
- echo "ansi-1251 CP1251"
- echo "BIG5 BIG5"
- echo "Big5-HKSCS BIG5-HKSCS"
- echo "gb2312 GB2312"
- echo "GBK GBK"
- echo "GB18030 GB18030"
- echo "cns11643 EUC-TW"
- echo "5601 EUC-KR"
- echo "ko_KR.johap92 JOHAB"
- echo "eucJP EUC-JP"
- echo "PCK SHIFT_JIS"
- echo "TIS620.2533 TIS-620"
- #echo "sun_eu_greek ?" # what is this?
- echo "UTF-8 UTF-8"
- ;;
- freebsd* | os2*)
- # FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore
- # localcharset.c falls back to using the full locale name
- # from the environment variables.
- # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just
- # reuse FreeBSD's locale data for OS/2.
- echo "C ASCII"
- echo "US-ASCII ASCII"
- for l in la_LN lt_LN; do
- echo "$l.ASCII ASCII"
- done
- for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \
- fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT la_LN \
- lt_LN nl_BE nl_NL no_NO pt_PT sv_SE; do
- echo "$l.ISO_8859-1 ISO-8859-1"
- echo "$l.DIS_8859-15 ISO-8859-15"
- done
- for l in cs_CZ hr_HR hu_HU la_LN lt_LN pl_PL sl_SI; do
- echo "$l.ISO_8859-2 ISO-8859-2"
- done
- for l in la_LN lt_LT; do
- echo "$l.ISO_8859-4 ISO-8859-4"
- done
- for l in ru_RU ru_SU; do
- echo "$l.KOI8-R KOI8-R"
- echo "$l.ISO_8859-5 ISO-8859-5"
- echo "$l.CP866 CP866"
- done
- echo "uk_UA.KOI8-U KOI8-U"
- echo "zh_TW.BIG5 BIG5"
- echo "zh_TW.Big5 BIG5"
- echo "zh_CN.EUC GB2312"
- echo "ja_JP.EUC EUC-JP"
- echo "ja_JP.SJIS SHIFT_JIS"
- echo "ja_JP.Shift_JIS SHIFT_JIS"
- echo "ko_KR.EUC EUC-KR"
- ;;
- netbsd*)
- echo "646 ASCII"
- echo "ISO8859-1 ISO-8859-1"
- echo "ISO8859-2 ISO-8859-2"
- echo "ISO8859-4 ISO-8859-4"
- echo "ISO8859-5 ISO-8859-5"
- echo "ISO8859-7 ISO-8859-7"
- echo "ISO8859-13 ISO-8859-13"
- echo "ISO8859-15 ISO-8859-15"
- echo "eucCN GB2312"
- echo "eucJP EUC-JP"
- echo "eucKR EUC-KR"
- echo "eucTW EUC-TW"
- echo "BIG5 BIG5"
- echo "SJIS SHIFT_JIS"
- ;;
- darwin[56]*)
- # Darwin 6.8 doesn't have nl_langinfo(CODESET); therefore
- # localcharset.c falls back to using the full locale name
- # from the environment variables.
- echo "C ASCII"
- for l in en_AU en_CA en_GB en_US la_LN; do
- echo "$l.US-ASCII ASCII"
- done
- for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \
- fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT nl_BE \
- nl_NL no_NO pt_PT sv_SE; do
- echo "$l ISO-8859-1"
- echo "$l.ISO8859-1 ISO-8859-1"
- echo "$l.ISO8859-15 ISO-8859-15"
- done
- for l in la_LN; do
- echo "$l.ISO8859-1 ISO-8859-1"
- echo "$l.ISO8859-15 ISO-8859-15"
- done
- for l in cs_CZ hr_HR hu_HU la_LN pl_PL sl_SI; do
- echo "$l.ISO8859-2 ISO-8859-2"
- done
- for l in la_LN lt_LT; do
- echo "$l.ISO8859-4 ISO-8859-4"
- done
- for l in ru_RU; do
- echo "$l.KOI8-R KOI8-R"
- echo "$l.ISO8859-5 ISO-8859-5"
- echo "$l.CP866 CP866"
- done
- for l in bg_BG; do
- echo "$l.CP1251 CP1251"
- done
- echo "uk_UA.KOI8-U KOI8-U"
- echo "zh_TW.BIG5 BIG5"
- echo "zh_TW.Big5 BIG5"
- echo "zh_CN.EUC GB2312"
- echo "ja_JP.EUC EUC-JP"
- echo "ja_JP.SJIS SHIFT_JIS"
- echo "ko_KR.EUC EUC-KR"
- ;;
- darwin*)
- # Darwin 7.5 has nl_langinfo(CODESET), but it is useless:
- # - It returns the empty string when LANG is set to a locale of the
- # form ll_CC, although ll_CC/LC_CTYPE is a symlink to an UTF-8
- # LC_CTYPE file.
- # - The environment variables LANG, LC_CTYPE, LC_ALL are not set by
- # the system; nl_langinfo(CODESET) returns "US-ASCII" in this case.
- # - The documentation says:
- # "... all code that calls BSD system routines should ensure
- # that the const *char parameters of these routines are in UTF-8
- # encoding. All BSD system functions expect their string
- # parameters to be in UTF-8 encoding and nothing else."
- # It also says
- # "An additional caveat is that string parameters for files,
- # paths, and other file-system entities must be in canonical
- # UTF-8. In a canonical UTF-8 Unicode string, all decomposable
- # characters are decomposed ..."
- # but this is not true: You can pass non-decomposed UTF-8 strings
- # to file system functions, and it is the OS which will convert
- # them to decomposed UTF-8 before accessing the file system.
- # - The Apple Terminal application displays UTF-8 by default.
- # - However, other applications are free to use different encodings:
- # - xterm uses ISO-8859-1 by default.
- # - TextEdit uses MacRoman by default.
- # We prefer UTF-8 over decomposed UTF-8-MAC because one should
- # minimize the use of decomposed Unicode. Unfortunately, through the
- # Darwin file system, decomposed UTF-8 strings are leaked into user
- # space nevertheless.
- echo "* UTF-8"
- ;;
- beos*)
- # BeOS has a single locale, and it has UTF-8 encoding.
- echo "* UTF-8"
- ;;
- msdosdjgpp*)
- # DJGPP 2.03 doesn't have nl_langinfo(CODESET); therefore
- # localcharset.c falls back to using the full locale name
- # from the environment variables.
- echo "#"
- echo "# The encodings given here may not all be correct."
- echo "# If you find that the encoding given for your language and"
- echo "# country is not the one your DOS machine actually uses, just"
- echo "# correct it in this file, and send a mail to"
- echo "# Juan Manuel Guerrero <juan.guerrero@gmx.de>"
- echo "# and Bruno Haible <bruno@clisp.org>."
- echo "#"
- echo "C ASCII"
- # ISO-8859-1 languages
- echo "ca CP850"
- echo "ca_ES CP850"
- echo "da CP865" # not CP850 ??
- echo "da_DK CP865" # not CP850 ??
- echo "de CP850"
- echo "de_AT CP850"
- echo "de_CH CP850"
- echo "de_DE CP850"
- echo "en CP850"
- echo "en_AU CP850" # not CP437 ??
- echo "en_CA CP850"
- echo "en_GB CP850"
- echo "en_NZ CP437"
- echo "en_US CP437"
- echo "en_ZA CP850" # not CP437 ??
- echo "es CP850"
- echo "es_AR CP850"
- echo "es_BO CP850"
- echo "es_CL CP850"
- echo "es_CO CP850"
- echo "es_CR CP850"
- echo "es_CU CP850"
- echo "es_DO CP850"
- echo "es_EC CP850"
- echo "es_ES CP850"
- echo "es_GT CP850"
- echo "es_HN CP850"
- echo "es_MX CP850"
- echo "es_NI CP850"
- echo "es_PA CP850"
- echo "es_PY CP850"
- echo "es_PE CP850"
- echo "es_SV CP850"
- echo "es_UY CP850"
- echo "es_VE CP850"
- echo "et CP850"
- echo "et_EE CP850"
- echo "eu CP850"
- echo "eu_ES CP850"
- echo "fi CP850"
- echo "fi_FI CP850"
- echo "fr CP850"
- echo "fr_BE CP850"
- echo "fr_CA CP850"
- echo "fr_CH CP850"
- echo "fr_FR CP850"
- echo "ga CP850"
- echo "ga_IE CP850"
- echo "gd CP850"
- echo "gd_GB CP850"
- echo "gl CP850"
- echo "gl_ES CP850"
- echo "id CP850" # not CP437 ??
- echo "id_ID CP850" # not CP437 ??
- echo "is CP861" # not CP850 ??
- echo "is_IS CP861" # not CP850 ??
- echo "it CP850"
- echo "it_CH CP850"
- echo "it_IT CP850"
- echo "lt CP775"
- echo "lt_LT CP775"
- echo "lv CP775"
- echo "lv_LV CP775"
- echo "nb CP865" # not CP850 ??
- echo "nb_NO CP865" # not CP850 ??
- echo "nl CP850"
- echo "nl_BE CP850"
- echo "nl_NL CP850"
- echo "nn CP865" # not CP850 ??
- echo "nn_NO CP865" # not CP850 ??
- echo "no CP865" # not CP850 ??
- echo "no_NO CP865" # not CP850 ??
- echo "pt CP850"
- echo "pt_BR CP850"
- echo "pt_PT CP850"
- echo "sv CP850"
- echo "sv_SE CP850"
- # ISO-8859-2 languages
- echo "cs CP852"
- echo "cs_CZ CP852"
- echo "hr CP852"
- echo "hr_HR CP852"
- echo "hu CP852"
- echo "hu_HU CP852"
- echo "pl CP852"
- echo "pl_PL CP852"
- echo "ro CP852"
- echo "ro_RO CP852"
- echo "sk CP852"
- echo "sk_SK CP852"
- echo "sl CP852"
- echo "sl_SI CP852"
- echo "sq CP852"
- echo "sq_AL CP852"
- echo "sr CP852" # CP852 or CP866 or CP855 ??
- echo "sr_CS CP852" # CP852 or CP866 or CP855 ??
- echo "sr_YU CP852" # CP852 or CP866 or CP855 ??
- # ISO-8859-3 languages
- echo "mt CP850"
- echo "mt_MT CP850"
- # ISO-8859-5 languages
- echo "be CP866"
- echo "be_BE CP866"
- echo "bg CP866" # not CP855 ??
- echo "bg_BG CP866" # not CP855 ??
- echo "mk CP866" # not CP855 ??
- echo "mk_MK CP866" # not CP855 ??
- echo "ru CP866"
- echo "ru_RU CP866"
- echo "uk CP1125"
- echo "uk_UA CP1125"
- # ISO-8859-6 languages
- echo "ar CP864"
- echo "ar_AE CP864"
- echo "ar_DZ CP864"
- echo "ar_EG CP864"
- echo "ar_IQ CP864"
- echo "ar_IR CP864"
- echo "ar_JO CP864"
- echo "ar_KW CP864"
- echo "ar_MA CP864"
- echo "ar_OM CP864"
- echo "ar_QA CP864"
- echo "ar_SA CP864"
- echo "ar_SY CP864"
- # ISO-8859-7 languages
- echo "el CP869"
- echo "el_GR CP869"
- # ISO-8859-8 languages
- echo "he CP862"
- echo "he_IL CP862"
- # ISO-8859-9 languages
- echo "tr CP857"
- echo "tr_TR CP857"
- # Japanese
- echo "ja CP932"
- echo "ja_JP CP932"
- # Chinese
- echo "zh_CN GBK"
- echo "zh_TW CP950" # not CP938 ??
- # Korean
- echo "kr CP949" # not CP934 ??
- echo "kr_KR CP949" # not CP934 ??
- # Thai
- echo "th CP874"
- echo "th_TH CP874"
- # Other
- echo "eo CP850"
- echo "eo_EO CP850"
- ;;
-esac
+++ /dev/null
-# glibc21.m4 serial 3
-dnl Copyright (C) 2000-2002, 2004 Free Software Foundation, Inc.
-dnl This file is free software; the Free Software Foundation
-dnl gives unlimited permission to copy and/or distribute it,
-dnl with or without modifications, as long as this notice is preserved.
-
-# Test for the GNU C Library, version 2.1 or newer.
-# From Bruno Haible.
-
-AC_DEFUN([gl_GLIBC21],
- [
- AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
- ac_cv_gnu_library_2_1,
- [AC_EGREP_CPP([Lucky GNU user],
- [
-#include <features.h>
-#ifdef __GNU_LIBRARY__
- #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
- Lucky GNU user
- #endif
-#endif
- ],
- ac_cv_gnu_library_2_1=yes,
- ac_cv_gnu_library_2_1=no)
- ]
- )
- AC_SUBST(GLIBC21)
- GLIBC21="$ac_cv_gnu_library_2_1"
- ]
-)
+++ /dev/null
-# Patch against libcharset version 1.4
---- libiconv-1.12/libcharset//lib/localcharset.c 2006-10-18 07:55:49.000000000 -0400
-+++ localcharset.c 2008-05-20 18:36:24.000000000 -0400
-@@ -103,8 +103,8 @@
- static const char * volatile charset_aliases;
-
- /* Return a pointer to the contents of the charset.alias file. */
--static const char *
--get_charset_aliases (void)
-+const char *
-+_g_locale_get_charset_aliases (void)
- {
- const char *cp;
-
-@@ -280,14 +280,10 @@
- If the canonical name cannot be determined, the result is a non-canonical
- name. */
-
--#ifdef STATIC
--STATIC
--#endif
- const char *
--locale_charset (void)
-+_g_locale_charset_raw (void)
- {
- const char *codeset;
-- const char *aliases;
-
- #if !(defined WIN32_NATIVE || defined OS2)
-
-@@ -436,12 +432,20 @@
-
- #endif
-
-+ return codeset;
-+}
-+
-+const char *
-+_g_locale_charset_unalias (const char *codeset)
-+{
-+ const char *aliases;
-+
- if (codeset == NULL)
- /* The canonical name cannot be determined. */
- codeset = "";
-
- /* Resolve alias. */
-- for (aliases = get_charset_aliases ();
-+ for (aliases = _g_locale_get_charset_aliases ();
- *aliases != '\0';
- aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
- if (strcmp (codeset, aliases) == 0
---- libiconv-1.12/libcharset//include/libcharset.h.in 2005-05-19 13:14:56.000000000 -0400
-+++ libcharset.h 2008-05-20 18:39:44.000000000 -0400
-@@ -19,7 +19,7 @@
- #ifndef _LIBCHARSET_H
- #define _LIBCHARSET_H
-
--#include <localcharset.h>
-+#include "localcharset.h"
-
-
- #ifdef __cplusplus
---- libiconv-1.12/libcharset//include/localcharset.h.in 2005-05-19 13:14:57.000000000 -0400
-+++ localcharset.h 2008-05-20 18:36:24.000000000 -0400
-@@ -31,8 +31,9 @@
- The result must not be freed; it is statically allocated.
- If the canonical name cannot be determined, the result is a non-canonical
- name. */
--extern const char * locale_charset (void);
--
-+extern const char * _g_locale_charset_raw (void);
-+extern const char * _g_locale_charset_unalias (const char *codeset);
-+extern const char * _g_locale_get_charset_aliases (void);
-
- #ifdef __cplusplus
- }
+++ /dev/null
-/* Copyright (C) 2003 Free Software Foundation, Inc.
- This file is part of the GNU CHARSET Library.
-
- The GNU CHARSET Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The GNU CHARSET 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
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public License
- along with the GNU CHARSET Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 51 Franklin Street,
- Fifth Floor, Boston, MA 02110-1301, USA. */
-
-#ifndef _LIBCHARSET_H
-#define _LIBCHARSET_H
-
-#include "localcharset.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* Support for relocatable packages. */
-
-/* Sets the original and the current installation prefix of the package.
- Relocation simply replaces a pathname starting with the original prefix
- by the corresponding pathname with the current prefix instead. Both
- prefixes should be directory names without trailing slash (i.e. use ""
- instead of "/"). */
-extern void libcharset_set_relocation_prefix (const char *orig_prefix,
- const char *curr_prefix);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _LIBCHARSET_H */
+++ /dev/null
-/* Determine a canonical name for the current locale's character encoding.
-
- Copyright (C) 2000-2006 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library 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. */
-
-/* Written by Bruno Haible <bruno@clisp.org>. */
-
-#include "config.h"
-
-/* Specification. */
-#include "localcharset.h"
-
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#if defined _WIN32 || defined __WIN32__
-# define WIN32_NATIVE
-#endif
-
-#if defined __EMX__
-/* Assume EMX program runs on OS/2, even if compiled under DOS. */
-# define OS2
-#endif
-
-#if !defined WIN32_NATIVE
-# if HAVE_LANGINFO_CODESET
-# include <langinfo.h>
-# else
-# if 0 /* see comment below */
-# include <locale.h>
-# endif
-# endif
-# ifdef __CYGWIN__
-# define WIN32_LEAN_AND_MEAN
-# include <windows.h>
-# endif
-#elif defined WIN32_NATIVE
-# define WIN32_LEAN_AND_MEAN
-# include <windows.h>
-#endif
-#if defined OS2
-# define INCL_DOS
-# include <os2.h>
-#endif
-
-#if ENABLE_RELOCATABLE
-# include "relocatable.h"
-#else
-# define relocate(pathname) (pathname)
-#endif
-
-/* Get LIBDIR. */
-#ifndef LIBDIR
-# include "configmake.h"
-#endif
-
-#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
- /* Win32, Cygwin, OS/2, DOS */
-# define ISSLASH(C) ((C) == '/' || (C) == '\\')
-#endif
-
-#ifndef DIRECTORY_SEPARATOR
-# define DIRECTORY_SEPARATOR '/'
-#endif
-
-#ifndef ISSLASH
-# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
-#endif
-
-#if HAVE_DECL_GETC_UNLOCKED
-# undef getc
-# define getc getc_unlocked
-#endif
-
-/* The following static variable is declared 'volatile' to avoid a
- possible multithread problem in the function get_charset_aliases. If we
- are running in a threaded environment, and if two threads initialize
- 'charset_aliases' simultaneously, both will produce the same value,
- and everything will be ok if the two assignments to 'charset_aliases'
- are atomic. But I don't know what will happen if the two assignments mix. */
-#if __STDC__ != 1
-# define volatile /* empty */
-#endif
-/* Pointer to the contents of the charset.alias file, if it has already been
- read, else NULL. Its format is:
- ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0' */
-static const char * volatile charset_aliases;
-
-/* Return a pointer to the contents of the charset.alias file. */
-const char *
-_g_locale_get_charset_aliases (void)
-{
- const char *cp;
-
- cp = charset_aliases;
- if (cp == NULL)
- {
-#if !(defined VMS || defined WIN32_NATIVE || defined __CYGWIN__)
- FILE *fp;
- const char *dir;
- const char *base = "charset.alias";
- char *file_name;
-
- /* Make it possible to override the charset.alias location. This is
- necessary for running the testsuite before "make install". */
- dir = getenv ("CHARSETALIASDIR");
- if (dir == NULL || dir[0] == '\0')
- dir = relocate (LIBDIR);
-
- /* Concatenate dir and base into freshly allocated file_name. */
- {
- size_t dir_len = strlen (dir);
- size_t base_len = strlen (base);
- int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
- file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
- if (file_name != NULL)
- {
- memcpy (file_name, dir, dir_len);
- if (add_slash)
- file_name[dir_len] = DIRECTORY_SEPARATOR;
- memcpy (file_name + dir_len + add_slash, base, base_len + 1);
- }
- }
-
- if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
- /* Out of memory or file not found, treat it as empty. */
- cp = "";
- else
- {
- /* Parse the file's contents. */
- char *res_ptr = NULL;
- size_t res_size = 0;
-
- for (;;)
- {
- int c;
- char buf1[50+1];
- char buf2[50+1];
- size_t l1, l2;
- char *old_res_ptr;
-
- c = getc (fp);
- if (c == EOF)
- break;
- if (c == '\n' || c == ' ' || c == '\t')
- continue;
- if (c == '#')
- {
- /* Skip comment, to end of line. */
- do
- c = getc (fp);
- while (!(c == EOF || c == '\n'));
- if (c == EOF)
- break;
- continue;
- }
- ungetc (c, fp);
- if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
- break;
- l1 = strlen (buf1);
- l2 = strlen (buf2);
- old_res_ptr = res_ptr;
- if (res_size == 0)
- {
- res_size = l1 + 1 + l2 + 1;
- res_ptr = (char *) malloc (res_size + 1);
- }
- else
- {
- res_size += l1 + 1 + l2 + 1;
- res_ptr = (char *) realloc (res_ptr, res_size + 1);
- }
- if (res_ptr == NULL)
- {
- /* Out of memory. */
- res_size = 0;
- if (old_res_ptr != NULL)
- free (old_res_ptr);
- break;
- }
- strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
- strcpy (res_ptr + res_size - (l2 + 1), buf2);
- }
- fclose (fp);
- if (res_size == 0)
- cp = "";
- else
- {
- *(res_ptr + res_size) = '\0';
- cp = res_ptr;
- }
- }
-
- if (file_name != NULL)
- free (file_name);
-
-#else
-
-# if defined VMS
- /* To avoid the troubles of an extra file charset.alias_vms in the
- sources of many GNU packages, simply inline the aliases here. */
- /* The list of encodings is taken from the OpenVMS 7.3-1 documentation
- "Compaq C Run-Time Library Reference Manual for OpenVMS systems"
- section 10.7 "Handling Different Character Sets". */
- cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
- "ISO8859-2" "\0" "ISO-8859-2" "\0"
- "ISO8859-5" "\0" "ISO-8859-5" "\0"
- "ISO8859-7" "\0" "ISO-8859-7" "\0"
- "ISO8859-8" "\0" "ISO-8859-8" "\0"
- "ISO8859-9" "\0" "ISO-8859-9" "\0"
- /* Japanese */
- "eucJP" "\0" "EUC-JP" "\0"
- "SJIS" "\0" "SHIFT_JIS" "\0"
- "DECKANJI" "\0" "DEC-KANJI" "\0"
- "SDECKANJI" "\0" "EUC-JP" "\0"
- /* Chinese */
- "eucTW" "\0" "EUC-TW" "\0"
- "DECHANYU" "\0" "DEC-HANYU" "\0"
- "DECHANZI" "\0" "GB2312" "\0"
- /* Korean */
- "DECKOREAN" "\0" "EUC-KR" "\0";
-# endif
-
-# if defined WIN32_NATIVE || defined __CYGWIN__
- /* To avoid the troubles of installing a separate file in the same
- directory as the DLL and of retrieving the DLL's directory at
- runtime, simply inline the aliases here. */
-
- cp = "CP936" "\0" "GBK" "\0"
- "CP1361" "\0" "JOHAB" "\0"
- "CP20127" "\0" "ASCII" "\0"
- "CP20866" "\0" "KOI8-R" "\0"
- "CP20936" "\0" "GB2312" "\0"
- "CP21866" "\0" "KOI8-RU" "\0"
- "CP28591" "\0" "ISO-8859-1" "\0"
- "CP28592" "\0" "ISO-8859-2" "\0"
- "CP28593" "\0" "ISO-8859-3" "\0"
- "CP28594" "\0" "ISO-8859-4" "\0"
- "CP28595" "\0" "ISO-8859-5" "\0"
- "CP28596" "\0" "ISO-8859-6" "\0"
- "CP28597" "\0" "ISO-8859-7" "\0"
- "CP28598" "\0" "ISO-8859-8" "\0"
- "CP28599" "\0" "ISO-8859-9" "\0"
- "CP28605" "\0" "ISO-8859-15" "\0"
- "CP38598" "\0" "ISO-8859-8" "\0"
- "CP51932" "\0" "EUC-JP" "\0"
- "CP51936" "\0" "GB2312" "\0"
- "CP51949" "\0" "EUC-KR" "\0"
- "CP51950" "\0" "EUC-TW" "\0"
- "CP54936" "\0" "GB18030" "\0"
- "CP65001" "\0" "UTF-8" "\0";
-# endif
-#endif
-
- charset_aliases = cp;
- }
-
- return cp;
-}
-
-/* Determine the current locale's character encoding, and canonicalize it
- into one of the canonical names listed in config.charset.
- The result must not be freed; it is statically allocated.
- If the canonical name cannot be determined, the result is a non-canonical
- name. */
-
-const char *
-_g_locale_charset_raw (void)
-{
- const char *codeset;
-
-#if !(defined WIN32_NATIVE || defined OS2)
-
-# if HAVE_LANGINFO_CODESET
-
- /* Most systems support nl_langinfo (CODESET) nowadays. */
- codeset = nl_langinfo (CODESET);
-
-# ifdef __CYGWIN__
- /* Cygwin 2006 does not have locales. nl_langinfo (CODESET) always
- returns "US-ASCII". As long as this is not fixed, return the suffix
- of the locale name from the environment variables (if present) or
- the codepage as a number. */
- if (codeset != NULL && strcmp (codeset, "US-ASCII") == 0)
- {
- const char *locale;
- static char buf[2 + 10 + 1];
-
- locale = getenv ("LC_ALL");
- if (locale == NULL || locale[0] == '\0')
- {
- locale = getenv ("LC_CTYPE");
- if (locale == NULL || locale[0] == '\0')
- locale = getenv ("LANG");
- }
- if (locale != NULL && locale[0] != '\0')
- {
- /* If the locale name contains an encoding after the dot, return
- it. */
- const char *dot = strchr (locale, '.');
-
- if (dot != NULL)
- {
- const char *modifier;
-
- dot++;
- /* Look for the possible @... trailer and remove it, if any. */
- modifier = strchr (dot, '@');
- if (modifier == NULL)
- return dot;
- if (modifier - dot < sizeof (buf))
- {
- memcpy (buf, dot, modifier - dot);
- buf [modifier - dot] = '\0';
- return buf;
- }
- }
- }
-
- /* Woe32 has a function returning the locale's codepage as a number. */
- sprintf (buf, "CP%u", GetACP ());
- codeset = buf;
- }
-# endif
-
-# else
-
- /* On old systems which lack it, use setlocale or getenv. */
- const char *locale = NULL;
-
- /* But most old systems don't have a complete set of locales. Some
- (like SunOS 4 or DJGPP) have only the C locale. Therefore we don't
- use setlocale here; it would return "C" when it doesn't support the
- locale name the user has set. */
-# if 0
- locale = setlocale (LC_CTYPE, NULL);
-# endif
- if (locale == NULL || locale[0] == '\0')
- {
- locale = getenv ("LC_ALL");
- if (locale == NULL || locale[0] == '\0')
- {
- locale = getenv ("LC_CTYPE");
- if (locale == NULL || locale[0] == '\0')
- locale = getenv ("LANG");
- }
- }
-
- /* On some old systems, one used to set locale = "iso8859_1". On others,
- you set it to "language_COUNTRY.charset". In any case, we resolve it
- through the charset.alias file. */
- codeset = locale;
-
-# endif
-
-#elif defined WIN32_NATIVE
-
- static char buf[2 + 10 + 1];
-
- /* Woe32 has a function returning the locale's codepage as a number. */
- sprintf (buf, "CP%u", GetACP ());
- codeset = buf;
-
-#elif defined OS2
-
- const char *locale;
- static char buf[2 + 10 + 1];
- ULONG cp[3];
- ULONG cplen;
-
- /* Allow user to override the codeset, as set in the operating system,
- with standard language environment variables. */
- locale = getenv ("LC_ALL");
- if (locale == NULL || locale[0] == '\0')
- {
- locale = getenv ("LC_CTYPE");
- if (locale == NULL || locale[0] == '\0')
- locale = getenv ("LANG");
- }
- if (locale != NULL && locale[0] != '\0')
- {
- /* If the locale name contains an encoding after the dot, return it. */
- const char *dot = strchr (locale, '.');
-
- if (dot != NULL)
- {
- const char *modifier;
-
- dot++;
- /* Look for the possible @... trailer and remove it, if any. */
- modifier = strchr (dot, '@');
- if (modifier == NULL)
- return dot;
- if (modifier - dot < sizeof (buf))
- {
- memcpy (buf, dot, modifier - dot);
- buf [modifier - dot] = '\0';
- return buf;
- }
- }
-
- /* Resolve through the charset.alias file. */
- codeset = locale;
- }
- else
- {
- /* OS/2 has a function returning the locale's codepage as a number. */
- if (DosQueryCp (sizeof (cp), cp, &cplen))
- codeset = "";
- else
- {
- sprintf (buf, "CP%u", cp[0]);
- codeset = buf;
- }
- }
-
-#endif
-
- return codeset;
-}
-
-const char *
-_g_locale_charset_unalias (const char *codeset)
-{
- const char *aliases;
-
- if (codeset == NULL)
- /* The canonical name cannot be determined. */
- codeset = "";
-
- /* Resolve alias. */
- for (aliases = _g_locale_get_charset_aliases ();
- *aliases != '\0';
- aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
- if (strcmp (codeset, aliases) == 0
- || (aliases[0] == '*' && aliases[1] == '\0'))
- {
- codeset = aliases + strlen (aliases) + 1;
- break;
- }
-
- /* Don't return an empty string. GNU libc and GNU libiconv interpret
- the empty string as denoting "the locale's character encoding",
- thus GNU libiconv would call this function a second time. */
- if (codeset[0] == '\0')
- codeset = "ASCII";
-
- return codeset;
-}
+++ /dev/null
-/* Determine a canonical name for the current locale's character encoding.
- Copyright (C) 2000-2003 Free Software Foundation, Inc.
- This file is part of the GNU CHARSET Library.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published
- by the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library 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 _LOCALCHARSET_H
-#define _LOCALCHARSET_H
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/* Determine the current locale's character encoding, and canonicalize it
- into one of the canonical names listed in config.charset.
- The result must not be freed; it is statically allocated.
- If the canonical name cannot be determined, the result is a non-canonical
- name. */
-extern const char * _g_locale_charset_raw (void);
-extern const char * _g_locale_charset_unalias (const char *codeset);
-extern const char * _g_locale_get_charset_aliases (void);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif /* _LOCALCHARSET_H */
+++ /dev/null
-#!/bin/sh
-
-if test $# = 1 ; then
- ORIGINAL=$1
-else
- echo "Usage: make-patch.sh /path/to/libcharset" 1>&2
- exit 1
-fi
-
-if test -f $ORIGINAL/lib/localcharset.c ; then : ; else
- echo "Usage: make-patch.sh /path/to/libcharset" 1>&2
- exit 1
-fi
-
-VERSION=`grep VERSION= $ORIGINAL/configure.ac | sed s/VERSION=//`
-
-echo "# Patch against libcharset version $VERSION" > libcharset-glib.patch
-
-for i in localcharset.c ref-add.sin ref-del.sin ; do
- diff -u $ORIGINAL/lib/$i $i >> libcharset-glib.patch
-done
-
-for i in glibc21.m4 codeset.m4 ; do
- diff -u $ORIGINAL/m4/$i $i >> libcharset-glib.patch
-done
-
-diff -u $ORIGINAL/include/libcharset.h.in libcharset.h >> libcharset-glib.patch
-diff -u $ORIGINAL/include/localcharset.h.in localcharset.h >> libcharset-glib.patch
+++ /dev/null
-# Add this package to a list of references stored in a text file.
-#
-# Copyright (C) 2000 Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Library General Public License as published
-# by the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Library General Public License for more details.
-#
-# You should have received a copy of the GNU Library 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.
-#
-# Written by Bruno Haible <bruno@clisp.org>.
-#
-/^# Packages using this file: / {
- s/# Packages using this file://
- ta
- :a
- s/ @PACKAGE@ / @PACKAGE@ /
- tb
- s/ $/ @PACKAGE@ /
- :b
- s/^/# Packages using this file:/
-}
+++ /dev/null
-# Remove this package from a list of references stored in a text file.
-#
-# Copyright (C) 2000 Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Library General Public License as published
-# by the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Library General Public License for more details.
-#
-# You should have received a copy of the GNU Library 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.
-#
-# Written by Bruno Haible <bruno@clisp.org>.
-#
-/^# Packages using this file: / {
- s/# Packages using this file://
- s/ @PACKAGE@ / /
- s/^/# Packages using this file:/
-}
+++ /dev/null
-#!/bin/sh
-
-if test $# = 1 ; then
- ORIGINAL=$1
-else
- echo "Usage: update.sh /path/to/libcharset" 1>&2
- exit 1
-fi
-
-if test -f $ORIGINAL/lib/localcharset.c ; then : ; else
- echo "Usage: update.sh /path/to/libcharset" 1>&2
- exit 1
-fi
-
-VERSION=`grep VERSION= $ORIGINAL/configure.ac | sed s/VERSION=//`
-
-for i in localcharset.c ref-add.sin ref-del.sin config.charset ; do
- cp $ORIGINAL/lib/$i .
-done
-
-for i in libcharset.h localcharset.h ; do
- cp $ORIGINAL/include/$i.in ./$i
-done
-
-for i in codeset.m4 glibc21.m4 ; do
- cp $ORIGINAL/m4/$i .
-done
-
-patch -p0 < libcharset-glib.patch
-
-echo "dnl From libcharset $VERSION" > ../../aclibcharset.m4
-
-
+++ /dev/null
-import sys
-import gdb
-
-# Update module path.
-dir = '@datadir@/glib-2.0/gdb'
-if not dir in sys.path:
- sys.path.insert(0, dir)
-
-from glib import register
-register (gdb.current_objfile ())
+++ /dev/null
-## Makefile for building the GLib dlls with Microsoft C
-## Use: nmake -f makefile.msc
-
-TOP = ..\..
-
-!INCLUDE ..\build\win32\make.msc
-
-################################################################
-
-INCLUDES = -FImsvc_recommended_pragmas.h -I . -I ..
-DEFINES = \
- -DHAVE_CONFIG_H -DGLIB_COMPILATION -DG_LOG_DOMAIN=\"GLib\" \
- -DG_ENABLE_DEBUG -DPCRE_STATIC -DG_DISABLE_DEPRECATED \
- -DDLL_EXPORT=1
-
-DEPCFLAGS = -Zm400 $(INTL_CFLAGS) $(DIRENT_CFLAGS)
-
-all : \
- ..\config.h \
- ..\glibconfig.h \
- gnulib\gnulib.lib \
- pcre\pcre.lib \
- libglib-2.0-0.dll \
- glib-@GLIB_MAJOR_VERSION@.@GLIB_MINOR_VERSION@s.lib \
- gspawn-win32-helper.exe \
- gspawn-win32-helper-console.exe \
-
-
-gnulib\gnulib.lib :
- cd gnulib
- nmake -f makefile.msc
- cd ..
-
-pcre\pcre.lib :
- cd pcre
- nmake -f makefile.msc
- cd ..
-
-glib_OBJECTS = \
- garray.obj \
- gasyncqueue.obj \
- gatomic.obj \
- gbacktrace.obj \
- gbase64.obj \
- gbookmarkfile.obj \
- gcache.obj \
- gchecksum.obj \
- gcompletion.obj \
- gconvert.obj \
- gdataset.obj \
- gdate.obj \
- gdir.obj \
- gerror.obj \
- gfileutils.obj \
- ghash.obj \
- ghostutils.obj \
- ghook.obj \
- giochannel.obj \
- giowin32.obj \
- gpoll.obj \
- gkeyfile.obj \
- glist.obj \
- gmain.obj \
- gmappedfile.obj \
- gmarkup.obj \
- gmem.obj \
- gmessages.obj \
- gnode.obj \
- goption.obj \
- gpattern.obj \
- gprimes.obj \
- gprintf.obj \
- gqsort.obj \
- gqueue.obj \
- grand.obj \
- gregex.obj \
- grel.obj \
- gscanner.obj \
- gsequence.obj \
- gshell.obj \
- gslice.obj \
- gslist.obj \
- gspawn-win32.obj \
- gstdio.obj \
- gstrfuncs.obj \
- gstring.obj \
- gtestutils.obj \
- gthread.obj \
- gthreadpool.obj \
- gtimer.obj \
- gtree.obj \
- gunibreak.obj \
- gunicollate.obj \
- gunidecomp.obj \
- guniprop.obj \
- gurifuncs.obj \
- gutf8.obj \
- gutils.obj \
- gwin32.obj \
- localcharset.obj
-
-..\glibconfig.h: ..\glibconfig.h.win32
- copy ..\glibconfig.h.win32 ..\glibconfig.h
-
-..\config.h: ..\config.h.win32
- copy ..\config.h.win32 ..\config.h
-
-localcharset.obj : libcharset\localcharset.c
- $(CC) $(CFLAGS) -DLIBDIR=\".\" -c libcharset\localcharset.c
-
-glib.def: glib.symbols
- echo EXPORTS > glib.def
- cl /EP -DINCLUDE_VARIABLES -DG_OS_WIN32 -DINCLUDE_INTERNAL_SYMBOLS -DALL_FILES \
- -DG_GNUC_MALLOC= -DG_GNUC_CONST= -DG_GNUC_NULL_TERMINATED= -DG_GNUC_NORETURN= \
- -DG_GNUC_PRINTF=;G_GNUC_PRINTF -DG_GNUC_FORMAT=;G_GNUC_FORMAT glib.symbols >> glib.def
-
-glib.res : glib.rc
- rc -DBUILDNUMBER=0 -r -fo glib.res glib.rc
-
-################ glib
-
-# create a static libary
-# static library can well have the real version number in the name
-glib-@GLIB_MAJOR_VERSION@.@GLIB_MINOR_VERSION@s.lib : $(glib_OBJECTS) gnulib\gnulib.lib pcre\pcre.lib
- lib /out:glib-@GLIB_MAJOR_VERSION@.@GLIB_MINOR_VERSION@s.lib $(glib_OBJECTS) gnulib\gnulib.lib pcre\pcre.lib
-
-libglib-2.0-0.dll : $(glib_OBJECTS) gnulib\gnulib.lib pcre\pcre.lib glib.def glib.res
- $(CC) $(CFLAGS) -LD -Fe$@ $(glib_OBJECTS) glib.res $(INTL_LIBS) \
- gnulib\gnulib.lib pcre\pcre.lib $(DIRENT_LIBS) user32.lib advapi32.lib shell32.lib wsock32.lib ole32.lib ws2_32.lib \
- $(LDFLAGS) /implib:glib-2.0.lib /def:glib.def
-
-gspawn-win32-helper.exe : gspawn-win32-helper.c libglib-2.0-@LT_CURRENT_MINUS_AGE@.dll
- $(CC) $(CFLAGS) -Fe$@ -DG_LOG_DOMAIN=\"gspawn-win32-helper\" gspawn-win32-helper.c glib-2.0.lib $(LDFLAGS) /subsystem:windows user32.lib
-
-gspawn-win32-helper-console.exe : gspawn-win32-helper.c libglib-2.0-@LT_CURRENT_MINUS_AGE@.dll
- $(CC) $(CFLAGS) -Fe$@ -DG_LOG_DOMAIN=\"gspawn-win32-helper\" gspawn-win32-helper.c glib-2.0.lib $(LDFLAGS) /subsystem:console user32.lib
-
-################ other stuff
-
-clean::
- del ..\config.h
- del ..\glibconfig.h
-
+++ /dev/null
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- pcre_compile.c \
- pcre_chartables.c \
- pcre_config.c \
- pcre_dfa_exec.c \
- pcre_exec.c \
- pcre_fullinfo.c \
- pcre_get.c \
- pcre_globals.c \
- pcre_newline.c \
- pcre_ord2utf8.c \
- pcre_study.c \
- pcre_tables.c \
- pcre_try_flipped.c \
- pcre_ucp_searchfuncs.c \
- pcre_xclass.c
-
-LOCAL_MODULE:= libpcre
-
-LOCAL_C_INCLUDES := \
- $(GLIB_TOP)/android-internal \
- $(GLIB_TOP)/glib/android-internal \
- $(GLIB_C_INCLUDES)
-
-LOCAL_CFLAGS := \
- -DG_LOG_DOMAIN=\"GLib-GRegex\" \
- -DSUPPORT_UCP \
- -DSUPPORT_UTF8 \
- -DNEWLINE=-1 \
- -DMATCH_LIMIT=10000000 \
- -DMATCH_LIMIT_RECURSION=10000000 \
- -DMAX_NAME_SIZE=32 \
- -DMAX_NAME_COUNT=10000 \
- -DMAX_DUPLENGTH=30000 \
- -DLINK_SIZE=2 \
- -DPCRE_STATIC \
- -DPOSIX_MALLOC_THRESHOLD=10
-
-include $(BUILD_STATIC_LIBRARY)
+++ /dev/null
-PCRE LICENCE
-
-Please see the file LICENCE in the PCRE distribution for licensing details.
-
-End
+++ /dev/null
-include $(top_srcdir)/Makefile.decl
-
-INCLUDES = \
- -DG_LOG_DOMAIN=\"GLib-GRegex\" \
- -DSUPPORT_UCP \
- -DSUPPORT_UTF8 \
- -DNEWLINE=-1 \
- -DMATCH_LIMIT=10000000 \
- -DMATCH_LIMIT_RECURSION=8192 \
- -DMAX_NAME_SIZE=32 \
- -DMAX_NAME_COUNT=10000 \
- -DMAX_DUPLENGTH=30000 \
- -DLINK_SIZE=2 \
- -DPOSIX_MALLOC_THRESHOLD=10 \
- -DPCRE_STATIC \
- $(glib_INCLUDES) \
- @GLIB_DEBUG_FLAGS@ \
- -DG_DISABLE_DEPRECATED \
- -DGLIB_COMPILATION \
- $(DEPRECATED_FLAGS)\
- $(WARN_CFLAGS) \
- $(PCRE_WARN_CFLAGS) \
- $(DEP_CFLAGS)
-
-noinst_LTLIBRARIES = libpcre.la
-
-libpcre_headers =
-
-libpcre_la_SOURCES = \
- pcre_compile.c \
- pcre_chartables.c \
- pcre_config.c \
- pcre_dfa_exec.c \
- pcre_exec.c \
- pcre_fullinfo.c \
- pcre_get.c \
- pcre_globals.c \
- pcre_newline.c \
- pcre_ord2utf8.c \
- pcre_study.c \
- pcre_tables.c \
- pcre_try_flipped.c \
- pcre_ucp_searchfuncs.c \
- pcre_xclass.c \
- pcre.h \
- pcre_internal.h \
- ucp.h \
- $(libpcre_headers)
-
-libpcre_la_LIBADD = $(DEP_LIBS)
-
-libpcre_la_LDFLAGS = -no-undefined
-
-EXTRA_DIST += \
- COPYING \
- makefile.msc
-
+++ /dev/null
-TOP = ..\..\..
-!INCLUDE ..\..\build\win32\make.msc
-
-INCLUDES = \
- -I ..\.. \
- -I ..
-
-DEFINES = \
- -DPCRE_STATIC \
- -DHAVE_CONFIG_H \
- -DHAVE_LONG_LONG_FORMAT \
- -DSUPPORT_UCP \
- -DSUPPORT_UTF8 \
- -DNEWLINE=-1 \
- -DMATCH_LIMIT=10000000 \
- -DMATCH_LIMIT_RECURSION=10000000 \
- -DMAX_NAME_SIZE=32 \
- -DMAX_NAME_COUNT=10000 \
- -DMAX_DUPLENGTH=30000 \
- -DLINK_SIZE=2 \
- -DEBCDIC=0 \
- -DPOSIX_MALLOC_THRESHOLD=10
-
-OBJECTS = \
-
-
-all : pcre.lib
-
-pcre.lib : $(OBJECTS)
- lib -out:pcre.lib $(OBJECTS)
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* This is the public header file for the PCRE library, to be #included by
-applications that call the PCRE functions.
-
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-#ifndef _PCRE_H
-#define _PCRE_H
-
-/* The current PCRE version information. */
-
-#define PCRE_MAJOR 8
-#define PCRE_MINOR 02
-#define PCRE_PRERELEASE
-#define PCRE_DATE 2010-03-19
-
-/* When an application links to a PCRE DLL in Windows, the symbols that are
-imported have to be identified as such. When building PCRE, the appropriate
-export setting is defined in pcre_internal.h, which includes this file. So we
-don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */
-
-#if defined(_WIN32) && !defined(PCRE_STATIC)
-# ifndef PCRE_EXP_DECL
-# define PCRE_EXP_DECL extern __declspec(dllimport)
-# endif
-# ifdef __cplusplus
-# ifndef PCRECPP_EXP_DECL
-# define PCRECPP_EXP_DECL extern __declspec(dllimport)
-# endif
-# ifndef PCRECPP_EXP_DEFN
-# define PCRECPP_EXP_DEFN __declspec(dllimport)
-# endif
-# endif
-#endif
-
-/* By default, we use the standard "extern" declarations. */
-
-#ifndef PCRE_EXP_DECL
-# ifdef __cplusplus
-# define PCRE_EXP_DECL extern "C"
-# else
-# define PCRE_EXP_DECL extern
-# endif
-#endif
-
-#ifdef __cplusplus
-# ifndef PCRECPP_EXP_DECL
-# define PCRECPP_EXP_DECL extern
-# endif
-# ifndef PCRECPP_EXP_DEFN
-# define PCRECPP_EXP_DEFN
-# endif
-#endif
-
-/* Have to include stdlib.h in order to ensure that size_t is defined;
-it is needed here for malloc. */
-
-#include <stdlib.h>
-
-/* Allow for C++ users */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Options. Some are compile-time only, some are run-time only, and some are
-both, so we keep them all distinct. */
-
-#define PCRE_CASELESS 0x00000001
-#define PCRE_MULTILINE 0x00000002
-#define PCRE_DOTALL 0x00000004
-#define PCRE_EXTENDED 0x00000008
-#define PCRE_ANCHORED 0x00000010
-#define PCRE_DOLLAR_ENDONLY 0x00000020
-#define PCRE_EXTRA 0x00000040
-#define PCRE_NOTBOL 0x00000080
-#define PCRE_NOTEOL 0x00000100
-#define PCRE_UNGREEDY 0x00000200
-#define PCRE_NOTEMPTY 0x00000400
-#define PCRE_UTF8 0x00000800
-#define PCRE_NO_AUTO_CAPTURE 0x00001000
-#define PCRE_NO_UTF8_CHECK 0x00002000
-#define PCRE_AUTO_CALLOUT 0x00004000
-#define PCRE_PARTIAL_SOFT 0x00008000
-#define PCRE_PARTIAL 0x00008000 /* Backwards compatible synonym */
-#define PCRE_DFA_SHORTEST 0x00010000
-#define PCRE_DFA_RESTART 0x00020000
-#define PCRE_FIRSTLINE 0x00040000
-#define PCRE_DUPNAMES 0x00080000
-#define PCRE_NEWLINE_CR 0x00100000
-#define PCRE_NEWLINE_LF 0x00200000
-#define PCRE_NEWLINE_CRLF 0x00300000
-#define PCRE_NEWLINE_ANY 0x00400000
-#define PCRE_NEWLINE_ANYCRLF 0x00500000
-#define PCRE_BSR_ANYCRLF 0x00800000
-#define PCRE_BSR_UNICODE 0x01000000
-#define PCRE_JAVASCRIPT_COMPAT 0x02000000
-#define PCRE_NO_START_OPTIMIZE 0x04000000
-#define PCRE_NO_START_OPTIMISE 0x04000000
-#define PCRE_PARTIAL_HARD 0x08000000
-#define PCRE_NOTEMPTY_ATSTART 0x10000000
-
-/* Exec-time and get/set-time error codes */
-
-#define PCRE_ERROR_NOMATCH (-1)
-#define PCRE_ERROR_NULL (-2)
-#define PCRE_ERROR_BADOPTION (-3)
-#define PCRE_ERROR_BADMAGIC (-4)
-#define PCRE_ERROR_UNKNOWN_OPCODE (-5)
-#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */
-#define PCRE_ERROR_NOMEMORY (-6)
-#define PCRE_ERROR_NOSUBSTRING (-7)
-#define PCRE_ERROR_MATCHLIMIT (-8)
-#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
-#define PCRE_ERROR_BADUTF8 (-10)
-#define PCRE_ERROR_BADUTF8_OFFSET (-11)
-#define PCRE_ERROR_PARTIAL (-12)
-#define PCRE_ERROR_BADPARTIAL (-13)
-#define PCRE_ERROR_INTERNAL (-14)
-#define PCRE_ERROR_BADCOUNT (-15)
-#define PCRE_ERROR_DFA_UITEM (-16)
-#define PCRE_ERROR_DFA_UCOND (-17)
-#define PCRE_ERROR_DFA_UMLIMIT (-18)
-#define PCRE_ERROR_DFA_WSSIZE (-19)
-#define PCRE_ERROR_DFA_RECURSE (-20)
-#define PCRE_ERROR_RECURSIONLIMIT (-21)
-#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */
-#define PCRE_ERROR_BADNEWLINE (-23)
-
-/* Request types for pcre_fullinfo() */
-
-#define PCRE_INFO_OPTIONS 0
-#define PCRE_INFO_SIZE 1
-#define PCRE_INFO_CAPTURECOUNT 2
-#define PCRE_INFO_BACKREFMAX 3
-#define PCRE_INFO_FIRSTBYTE 4
-#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
-#define PCRE_INFO_FIRSTTABLE 5
-#define PCRE_INFO_LASTLITERAL 6
-#define PCRE_INFO_NAMEENTRYSIZE 7
-#define PCRE_INFO_NAMECOUNT 8
-#define PCRE_INFO_NAMETABLE 9
-#define PCRE_INFO_STUDYSIZE 10
-#define PCRE_INFO_DEFAULT_TABLES 11
-#define PCRE_INFO_OKPARTIAL 12
-#define PCRE_INFO_JCHANGED 13
-#define PCRE_INFO_HASCRORLF 14
-#define PCRE_INFO_MINLENGTH 15
-
-/* Request types for pcre_config(). Do not re-arrange, in order to remain
-compatible. */
-
-#define PCRE_CONFIG_UTF8 0
-#define PCRE_CONFIG_NEWLINE 1
-#define PCRE_CONFIG_LINK_SIZE 2
-#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
-#define PCRE_CONFIG_MATCH_LIMIT 4
-#define PCRE_CONFIG_STACKRECURSE 5
-#define PCRE_CONFIG_UNICODE_PROPERTIES 6
-#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7
-#define PCRE_CONFIG_BSR 8
-
-/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine
-these bits, just add new ones on the end, in order to remain compatible. */
-
-#define PCRE_EXTRA_STUDY_DATA 0x0001
-#define PCRE_EXTRA_MATCH_LIMIT 0x0002
-#define PCRE_EXTRA_CALLOUT_DATA 0x0004
-#define PCRE_EXTRA_TABLES 0x0008
-#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010
-
-/* Types */
-
-struct real_pcre; /* declaration; the definition is private */
-typedef struct real_pcre pcre;
-
-/* When PCRE is compiled as a C++ library, the subject pointer type can be
-replaced with a custom type. For conventional use, the public interface is a
-const char *. */
-
-#ifndef PCRE_SPTR
-#define PCRE_SPTR const char *
-#endif
-
-/* The structure for passing additional data to pcre_exec(). This is defined in
-such as way as to be extensible. Always add new fields at the end, in order to
-remain compatible. */
-
-typedef struct pcre_extra {
- unsigned long int flags; /* Bits for which fields are set */
- void *study_data; /* Opaque data from pcre_study() */
- unsigned long int match_limit; /* Maximum number of calls to match() */
- void *callout_data; /* Data passed back in callouts */
- const unsigned char *tables; /* Pointer to character tables */
- unsigned long int match_limit_recursion; /* Max recursive calls to match() */
-} pcre_extra;
-
-/* The structure for passing out data via the pcre_callout_function. We use a
-structure so that new fields can be added on the end in future versions,
-without changing the API of the function, thereby allowing old clients to work
-without modification. */
-
-typedef struct pcre_callout_block {
- int version; /* Identifies version of block */
- /* ------------------------ Version 0 ------------------------------- */
- int callout_number; /* Number compiled into pattern */
- int *offset_vector; /* The offset vector */
- PCRE_SPTR subject; /* The subject being matched */
- int subject_length; /* The length of the subject */
- int start_match; /* Offset to start of this match attempt */
- int current_position; /* Where we currently are in the subject */
- int capture_top; /* Max current capture */
- int capture_last; /* Most recently closed capture */
- void *callout_data; /* Data passed in with the call */
- /* ------------------- Added for Version 1 -------------------------- */
- int pattern_position; /* Offset to next item in the pattern */
- int next_item_length; /* Length of next item in the pattern */
- /* ------------------------------------------------------------------ */
-} pcre_callout_block;
-
-#include "glib.h"
-
-#define pcre_malloc g_try_malloc
-#define pcre_free g_free
-#define pcre_stack_malloc g_try_malloc
-
-PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *);
-
-/* Exported PCRE functions */
-
-PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *,
- const unsigned char *);
-PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **,
- int *, const unsigned char *);
-PCRE_EXP_DECL int pcre_config(int, void *);
-PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *,
- int *, int, const char *, char *, int);
-PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, char *,
- int);
-PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *,
- const char *, int, int, int, int *, int , int *, int);
-PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR,
- int, int, int, int *, int);
-PCRE_EXP_DECL void pcre_free_substring(const char *);
-PCRE_EXP_DECL void pcre_free_substring_list(const char **);
-PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int,
- void *);
-PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *,
- int *, int, const char *, const char **);
-PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *);
-PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *,
- char **, char **);
-PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int,
- const char **);
-PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int,
- const char ***);
-PCRE_EXP_DECL int pcre_info(const pcre *, int *, int *);
-PCRE_EXP_DECL const unsigned char *pcre_maketables(void);
-PCRE_EXP_DECL int pcre_refcount(pcre *, int);
-PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **);
-PCRE_EXP_DECL const char *pcre_version(void);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* End of pcre.h */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* This file contains character tables that are used when no external tables
-are passed to PCRE by the application that calls it. The tables are used only
-for characters whose code values are less than 256.
-
-This is a default version of the tables that assumes ASCII encoding. A program
-called dftables (which is distributed with PCRE) can be used to build
-alternative versions of this file. This is necessary if you are running in an
-EBCDIC environment, or if you want to default to a different encoding, for
-example ISO-8859-1. When dftables is run, it creates these tables in the
-current locale. If PCRE is configured with --enable-rebuild-chartables, this
-happens automatically.
-
-The following #includes are present because without the gcc 4.x may remove the
-array definition from the final binary if PCRE is built into a static library
-and dead code stripping is activated. This leads to link errors. Pulling in the
-header ensures that the array gets flagged as "someone outside this compilation
-unit might reference this" and so it will always be supplied to the linker. */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-const unsigned char _pcre_default_tables[] = {
-
-/* This table is a lower casing table. */
-
- 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,
-
-/* This table is a case flipping table. */
-
- 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, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90,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,
-
-/* This table contains bit maps for various character classes. Each map is 32
-bytes long and the bits run from the least significant end of each byte. The
-classes that have their own maps are: space, xdigit, digit, upper, lower, word,
-graph, print, punct, and cntrl. Other classes are built from combinations. */
-
- 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
- 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
- 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
- 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
- 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-
-/* This table identifies various classes of character by individual bits:
- 0x01 white space character
- 0x02 letter
- 0x04 decimal digit
- 0x08 hexadecimal digit
- 0x10 alphanumeric or '_'
- 0x80 regular expression metacharacter or binary zero
-*/
-
- 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
- 0x00,0x01,0x01,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
- 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */
- 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */
- 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */
- 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */
- 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */
- 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */
- 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */
- 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */
- 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
-
-/* End of pcre_chartables.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2010 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains the external function pcre_compile(), along with
-supporting internal functions that are not used by other modules. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define NLBLOCK cd /* Block containing newline information */
-#define PSSTART start_pattern /* Field containing processed string start */
-#define PSEND end_pattern /* Field containing processed string end */
-
-#include "pcre_internal.h"
-
-
-/* When PCRE_DEBUG is defined, we need the pcre_printint() function, which is
-also used by pcretest. PCRE_DEBUG is not defined when building a production
-library. */
-
-#ifdef PCRE_DEBUG
-#include "pcre_printint.src"
-#endif
-
-
-/* Macro for setting individual bits in class bitmaps. */
-
-#define SETBIT(a,b) a[b/8] |= (1 << (b%8))
-
-/* Maximum length value to check against when making sure that the integer that
-holds the compiled pattern length does not overflow. We make it a bit less than
-INT_MAX to allow for adding in group terminating bytes, so that we don't have
-to check them every time. */
-
-#define OFLOW_MAX (INT_MAX - 20)
-
-
-/*************************************************
-* Code parameters and static tables *
-*************************************************/
-
-/* This value specifies the size of stack workspace that is used during the
-first pre-compile phase that determines how much memory is required. The regex
-is partly compiled into this space, but the compiled parts are discarded as
-soon as they can be, so that hopefully there will never be an overrun. The code
-does, however, check for an overrun. The largest amount I've seen used is 218,
-so this number is very generous.
-
-The same workspace is used during the second, actual compile phase for
-remembering forward references to groups so that they can be filled in at the
-end. Each entry in this list occupies LINK_SIZE bytes, so even when LINK_SIZE
-is 4 there is plenty of room. */
-
-#define COMPILE_WORK_SIZE (4096)
-
-/* The overrun tests check for a slightly smaller size so that they detect the
-overrun before it actually does run off the end of the data block. */
-
-#define WORK_SIZE_CHECK (COMPILE_WORK_SIZE - 100)
-
-
-/* Table for handling escaped characters in the range '0'-'z'. Positive returns
-are simple data values; negative values are for special things like \d and so
-on. Zero means further processing is needed (for things like \x), or the escape
-is invalid. */
-
-#ifndef EBCDIC
-
-/* This is the "normal" table for ASCII systems or for EBCDIC systems running
-in UTF-8 mode. */
-
-static const short int escapes[] = {
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- 0, 0,
- CHAR_COLON, CHAR_SEMICOLON,
- CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN,
- CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK,
- CHAR_COMMERCIAL_AT, -ESC_A,
- -ESC_B, -ESC_C,
- -ESC_D, -ESC_E,
- 0, -ESC_G,
- -ESC_H, 0,
- 0, -ESC_K,
- 0, 0,
- 0, 0,
- -ESC_P, -ESC_Q,
- -ESC_R, -ESC_S,
- 0, 0,
- -ESC_V, -ESC_W,
- -ESC_X, 0,
- -ESC_Z, CHAR_LEFT_SQUARE_BRACKET,
- CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET,
- CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE,
- CHAR_GRAVE_ACCENT, 7,
- -ESC_b, 0,
- -ESC_d, ESC_e,
- ESC_f, 0,
- -ESC_h, 0,
- 0, -ESC_k,
- 0, 0,
- ESC_n, 0,
- -ESC_p, 0,
- ESC_r, -ESC_s,
- ESC_tee, 0,
- -ESC_v, -ESC_w,
- 0, 0,
- -ESC_z
-};
-
-#else
-
-/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. */
-
-static const short int escapes[] = {
-/* 48 */ 0, 0, 0, '.', '<', '(', '+', '|',
-/* 50 */ '&', 0, 0, 0, 0, 0, 0, 0,
-/* 58 */ 0, 0, '!', '$', '*', ')', ';', '~',
-/* 60 */ '-', '/', 0, 0, 0, 0, 0, 0,
-/* 68 */ 0, 0, '|', ',', '%', '_', '>', '?',
-/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0,
-/* 78 */ 0, '`', ':', '#', '@', '\'', '=', '"',
-/* 80 */ 0, 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0,
-/* 88 */-ESC_h, 0, 0, '{', 0, 0, 0, 0,
-/* 90 */ 0, 0, -ESC_k, 'l', 0, ESC_n, 0, -ESC_p,
-/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0,
-/* A0 */ 0, '~', -ESC_s, ESC_tee, 0,-ESC_v, -ESC_w, 0,
-/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0,
-/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0,
-/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-',
-/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G,
-/* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0,
-/* D0 */ '}', 0, -ESC_K, 0, 0, 0, 0, -ESC_P,
-/* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0,
-/* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X,
-/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0,
-/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0,
-/* F8 */ 0, 0, 0, 0, 0, 0, 0, 0
-};
-#endif
-
-
-/* Table of special "verbs" like (*PRUNE). This is a short table, so it is
-searched linearly. Put all the names into a single string, in order to reduce
-the number of relocations when a shared library is dynamically linked. The
-string is built from string macros so that it works in UTF-8 mode on EBCDIC
-platforms. */
-
-typedef struct verbitem {
- int len;
- int op;
-} verbitem;
-
-static const char verbnames[] =
- STRING_ACCEPT0
- STRING_COMMIT0
- STRING_F0
- STRING_FAIL0
- STRING_PRUNE0
- STRING_SKIP0
- STRING_THEN;
-
-static const verbitem verbs[] = {
- { 6, OP_ACCEPT },
- { 6, OP_COMMIT },
- { 1, OP_FAIL },
- { 4, OP_FAIL },
- { 5, OP_PRUNE },
- { 4, OP_SKIP },
- { 4, OP_THEN }
-};
-
-static const int verbcount = sizeof(verbs)/sizeof(verbitem);
-
-
-/* Tables of names of POSIX character classes and their lengths. The names are
-now all in a single string, to reduce the number of relocations when a shared
-library is dynamically loaded. The list of lengths is terminated by a zero
-length entry. The first three must be alpha, lower, upper, as this is assumed
-for handling case independence. */
-
-static const char posix_names[] =
- STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0
- STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0
- STRING_graph0 STRING_print0 STRING_punct0 STRING_space0
- STRING_word0 STRING_xdigit;
-
-static const uschar posix_name_lengths[] = {
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
-
-/* Table of class bit maps for each POSIX class. Each class is formed from a
-base map, with an optional addition or removal of another map. Then, for some
-classes, there is some additional tweaking: for [:blank:] the vertical space
-characters are removed, and for [:alpha:] and [:alnum:] the underscore
-character is removed. The triples in the table consist of the base map offset,
-second map offset or -1 if no second map, and a non-negative value for map
-addition or a negative value for map subtraction (if there are two maps). The
-absolute value of the third field has these meanings: 0 => no tweaking, 1 =>
-remove vertical space characters, 2 => remove underscore. */
-
-static const int posix_class_maps[] = {
- cbit_word, cbit_digit, -2, /* alpha */
- cbit_lower, -1, 0, /* lower */
- cbit_upper, -1, 0, /* upper */
- cbit_word, -1, 2, /* alnum - word without underscore */
- cbit_print, cbit_cntrl, 0, /* ascii */
- cbit_space, -1, 1, /* blank - a GNU extension */
- cbit_cntrl, -1, 0, /* cntrl */
- cbit_digit, -1, 0, /* digit */
- cbit_graph, -1, 0, /* graph */
- cbit_print, -1, 0, /* print */
- cbit_punct, -1, 0, /* punct */
- cbit_space, -1, 0, /* space */
- cbit_word, -1, 0, /* word - a Perl extension */
- cbit_xdigit,-1, 0 /* xdigit */
-};
-
-
-#define STRING(a) # a
-#define XSTRING(s) STRING(s)
-
-/* The texts of compile-time error messages. These are "char *" because they
-are passed to the outside world. Do not ever re-use any error number, because
-they are documented. Always add a new error instead. Messages marked DEAD below
-are no longer used. This used to be a table of strings, but in order to reduce
-the number of relocations needed when a shared library is loaded dynamically,
-it is now one long string. We cannot use a table of offsets, because the
-lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, we
-simply count through to the one we want - this isn't a performance issue
-because these strings are used only when there is a compilation error.
-
-Each substring ends with \0 to insert a null character. This includes the final
-substring, so that the whole string ends with \0\0, which can be detected when
-counting through. */
-
-static const char error_texts[] =
- "no error\0"
- "\\ at end of pattern\0"
- "\\c at end of pattern\0"
- "unrecognized character follows \\\0"
- "numbers out of order in {} quantifier\0"
- /* 5 */
- "number too big in {} quantifier\0"
- "missing terminating ] for character class\0"
- "invalid escape sequence in character class\0"
- "range out of order in character class\0"
- "nothing to repeat\0"
- /* 10 */
- "operand of unlimited repeat could match the empty string\0" /** DEAD **/
- "internal error: unexpected repeat\0"
- "unrecognized character after (? or (?-\0"
- "POSIX named classes are supported only within a class\0"
- "missing )\0"
- /* 15 */
- "reference to non-existent subpattern\0"
- "erroffset passed as NULL\0"
- "unknown option bit(s) set\0"
- "missing ) after comment\0"
- "parentheses nested too deeply\0" /** DEAD **/
- /* 20 */
- "regular expression is too large\0"
- "failed to get memory\0"
- "unmatched parentheses\0"
- "internal error: code overflow\0"
- "unrecognized character after (?<\0"
- /* 25 */
- "lookbehind assertion is not fixed length\0"
- "malformed number or name after (?(\0"
- "conditional group contains more than two branches\0"
- "assertion expected after (?(\0"
- "(?R or (?[+-]digits must be followed by )\0"
- /* 30 */
- "unknown POSIX class name\0"
- "POSIX collating elements are not supported\0"
- "this version of PCRE is not compiled with PCRE_UTF8 support\0"
- "spare error\0" /** DEAD **/
- "character value in \\x{...} sequence is too large\0"
- /* 35 */
- "invalid condition (?(0)\0"
- "\\C not allowed in lookbehind assertion\0"
- "PCRE does not support \\L, \\l, \\N, \\U, or \\u\0"
- "number after (?C is > 255\0"
- "closing ) for (?C expected\0"
- /* 40 */
- "recursive call could loop indefinitely\0"
- "unrecognized character after (?P\0"
- "syntax error in subpattern name (missing terminator)\0"
- "two named subpatterns have the same name\0"
- "invalid UTF-8 string\0"
- /* 45 */
- "support for \\P, \\p, and \\X has not been compiled\0"
- "malformed \\P or \\p sequence\0"
- "unknown property name after \\P or \\p\0"
- "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " characters)\0"
- "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0"
- /* 50 */
- "repeated subpattern is too long\0" /** DEAD **/
- "octal value is greater than \\377 (not in UTF-8 mode)\0"
- "internal error: overran compiling workspace\0"
- "internal error: previously-checked referenced subpattern not found\0"
- "DEFINE group contains more than one branch\0"
- /* 55 */
- "repeating a DEFINE group is not allowed\0"
- "inconsistent NEWLINE options\0"
- "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0"
- "a numbered reference must not be zero\0"
- "(*VERB) with an argument is not supported\0"
- /* 60 */
- "(*VERB) not recognized\0"
- "number is too big\0"
- "subpattern name expected\0"
- "digit expected after (?+\0"
- "] is an invalid data character in JavaScript compatibility mode\0"
- /* 65 */
- "different names for subpatterns of the same number are not allowed\0";
-
-
-/* Definition to allow mutual recursion */
-
-static BOOL
- compile_regex(int, int, uschar **, const uschar **, int *, BOOL, BOOL, int,
- int *, int *, branch_chain *, compile_data *, int *);
-
-
-
-/*************************************************
-* Find an error text *
-*************************************************/
-
-/* The error texts are now all in one long string, to save on relocations. As
-some of the text is of unknown length, we can't use a table of offsets.
-Instead, just count through the strings. This is not a performance issue
-because it happens only when there has been a compilation error.
-
-Argument: the error number
-Returns: pointer to the error string
-*/
-
-static const char *
-find_error_text(int n)
-{
-const char *s = error_texts;
-for (; n > 0; n--)
- {
- while (*s++ != 0) {};
- if (*s == 0) return "Error text not found (please report)";
- }
-return s;
-}
-
-
-/*************************************************
-* Handle escapes *
-*************************************************/
-
-/* This function is called when a \ has been encountered. It either returns a
-positive value for a simple escape such as \n, or a negative value which
-encodes one of the more complicated things such as \d. A backreference to group
-n is returned as -(ESC_REF + n); ESC_REF is the highest ESC_xxx macro. When
-UTF-8 is enabled, a positive value greater than 255 may be returned. On entry,
-ptr is pointing at the \. On exit, it is on the final character of the escape
-sequence.
-
-Arguments:
- ptrptr points to the pattern position pointer
- errorcodeptr points to the errorcode variable
- bracount number of previous extracting brackets
- options the options bits
- isclass TRUE if inside a character class
-
-Returns: zero or positive => a data character
- negative => a special escape sequence
- on error, errorcodeptr is set
-*/
-
-static int
-check_escape(const uschar **ptrptr, int *errorcodeptr, int bracount,
- int options, BOOL isclass)
-{
-BOOL utf8 = (options & PCRE_UTF8) != 0;
-const uschar *ptr = *ptrptr + 1;
-int c, i;
-
-GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
-ptr--; /* Set pointer back to the last byte */
-
-/* If backslash is at the end of the pattern, it's an error. */
-
-if (c == 0) *errorcodeptr = ERR1;
-
-/* Non-alphanumerics are literals. For digits or letters, do an initial lookup
-in a table. A non-zero result is something that can be returned immediately.
-Otherwise further processing may be required. */
-
-#ifndef EBCDIC /* ASCII/UTF-8 coding */
-else if (c < CHAR_0 || c > CHAR_z) {} /* Not alphanumeric */
-else if ((i = escapes[c - CHAR_0]) != 0) c = i;
-
-#else /* EBCDIC coding */
-else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {} /* Not alphanumeric */
-else if ((i = escapes[c - 0x48]) != 0) c = i;
-#endif
-
-/* Escapes that need further processing, or are illegal. */
-
-else
- {
- const uschar *oldptr;
- BOOL braced, negated;
-
- switch (c)
- {
- /* A number of Perl escapes are not handled by PCRE. We give an explicit
- error. */
-
- case CHAR_l:
- case CHAR_L:
- case CHAR_N:
- case CHAR_u:
- case CHAR_U:
- *errorcodeptr = ERR37;
- break;
-
- /* \g must be followed by one of a number of specific things:
-
- (1) A number, either plain or braced. If positive, it is an absolute
- backreference. If negative, it is a relative backreference. This is a Perl
- 5.10 feature.
-
- (2) Perl 5.10 also supports \g{name} as a reference to a named group. This
- is part of Perl's movement towards a unified syntax for back references. As
- this is synonymous with \k{name}, we fudge it up by pretending it really
- was \k.
-
- (3) For Oniguruma compatibility we also support \g followed by a name or a
- number either in angle brackets or in single quotes. However, these are
- (possibly recursive) subroutine calls, _not_ backreferences. Just return
- the -ESC_g code (cf \k). */
-
- case CHAR_g:
- if (ptr[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE)
- {
- c = -ESC_g;
- break;
- }
-
- /* Handle the Perl-compatible cases */
-
- if (ptr[1] == CHAR_LEFT_CURLY_BRACKET)
- {
- const uschar *p;
- for (p = ptr+2; *p != 0 && *p != CHAR_RIGHT_CURLY_BRACKET; p++)
- if (*p != CHAR_MINUS && g_ascii_isdigit(*p) == 0) break;
- if (*p != 0 && *p != CHAR_RIGHT_CURLY_BRACKET)
- {
- c = -ESC_k;
- break;
- }
- braced = TRUE;
- ptr++;
- }
- else braced = FALSE;
-
- if (ptr[1] == CHAR_MINUS)
- {
- negated = TRUE;
- ptr++;
- }
- else negated = FALSE;
-
- c = 0;
- while (g_ascii_isdigit(ptr[1]) != 0)
- c = c * 10 + *(++ptr) - CHAR_0;
-
- if (c < 0) /* Integer overflow */
- {
- *errorcodeptr = ERR61;
- break;
- }
-
- if (braced && *(++ptr) != CHAR_RIGHT_CURLY_BRACKET)
- {
- *errorcodeptr = ERR57;
- break;
- }
-
- if (c == 0)
- {
- *errorcodeptr = ERR58;
- break;
- }
-
- if (negated)
- {
- if (c > bracount)
- {
- *errorcodeptr = ERR15;
- break;
- }
- c = bracount - (c - 1);
- }
-
- c = -(ESC_REF + c);
- break;
-
- /* The handling of escape sequences consisting of a string of digits
- starting with one that is not zero is not straightforward. By experiment,
- the way Perl works seems to be as follows:
-
- Outside a character class, the digits are read as a decimal number. If the
- number is less than 10, or if there are that many previous extracting
- left brackets, then it is a back reference. Otherwise, up to three octal
- digits are read to form an escaped byte. Thus \123 is likely to be octal
- 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal
- value is greater than 377, the least significant 8 bits are taken. Inside a
- character class, \ followed by a digit is always an octal number. */
-
- case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5:
- case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
-
- if (!isclass)
- {
- oldptr = ptr;
- c -= CHAR_0;
- while (g_ascii_isdigit(ptr[1]) != 0)
- c = c * 10 + *(++ptr) - CHAR_0;
- if (c < 0) /* Integer overflow */
- {
- *errorcodeptr = ERR61;
- break;
- }
- if (c < 10 || c <= bracount)
- {
- c = -(ESC_REF + c);
- break;
- }
- ptr = oldptr; /* Put the pointer back and fall through */
- }
-
- /* Handle an octal number following \. If the first digit is 8 or 9, Perl
- generates a binary zero byte and treats the digit as a following literal.
- Thus we have to pull back the pointer by one. */
-
- if ((c = *ptr) >= CHAR_8)
- {
- ptr--;
- c = 0;
- break;
- }
-
- /* \0 always starts an octal number, but we may drop through to here with a
- larger first octal digit. The original code used just to take the least
- significant 8 bits of octal numbers (I think this is what early Perls used
- to do). Nowadays we allow for larger numbers in UTF-8 mode, but no more
- than 3 octal digits. */
-
- case CHAR_0:
- c -= CHAR_0;
- while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7)
- c = c * 8 + *(++ptr) - CHAR_0;
- if (!utf8 && c > 255) *errorcodeptr = ERR51;
- break;
-
- /* \x is complicated. \x{ddd} is a character number which can be greater
- than 0xff in utf8 mode, but only if the ddd are hex digits. If not, { is
- treated as a data character. */
-
- case CHAR_x:
- if (ptr[1] == CHAR_LEFT_CURLY_BRACKET)
- {
- const uschar *pt = ptr + 2;
- int count = 0;
-
- c = 0;
- while (g_ascii_isxdigit(*pt) != 0)
- {
- register int cc = *pt++;
- if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */
- count++;
-
-#ifndef EBCDIC /* ASCII/UTF-8 coding */
- if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */
- c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10));
-#else /* EBCDIC coding */
- if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */
- c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10));
-#endif
- }
-
- if (*pt == CHAR_RIGHT_CURLY_BRACKET)
- {
- if (c < 0 || count > (utf8? 8 : 2)) *errorcodeptr = ERR34;
- ptr = pt;
- break;
- }
-
- /* If the sequence of hex digits does not end with '}', then we don't
- recognize this construct; fall through to the normal \x handling. */
- }
-
- /* Read just a single-byte hex-defined char */
-
- c = 0;
- while (i++ < 2 && g_ascii_isxdigit(ptr[1]) != 0)
- {
- int cc; /* Some compilers don't like */
- cc = *(++ptr); /* ++ in initializers */
-#ifndef EBCDIC /* ASCII/UTF-8 coding */
- if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */
- c = c * 16 + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10));
-#else /* EBCDIC coding */
- if (cc <= CHAR_z) cc += 64; /* Convert to upper case */
- c = c * 16 + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10));
-#endif
- }
- break;
-
- /* For \c, a following letter is upper-cased; then the 0x40 bit is flipped.
- This coding is ASCII-specific, but then the whole concept of \cx is
- ASCII-specific. (However, an EBCDIC equivalent has now been added.) */
-
- case CHAR_c:
- c = *(++ptr);
- if (c == 0)
- {
- *errorcodeptr = ERR2;
- break;
- }
-
-#ifndef EBCDIC /* ASCII/UTF-8 coding */
- if (c >= CHAR_a && c <= CHAR_z) c -= 32;
- c ^= 0x40;
-#else /* EBCDIC coding */
- if (c >= CHAR_a && c <= CHAR_z) c += 64;
- c ^= 0xC0;
-#endif
- break;
-
- /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any
- other alphanumeric following \ is an error if PCRE_EXTRA was set;
- otherwise, for Perl compatibility, it is a literal. This code looks a bit
- odd, but there used to be some cases other than the default, and there may
- be again in future, so I haven't "optimized" it. */
-
- default:
- if ((options & PCRE_EXTRA) != 0) switch(c)
- {
- default:
- *errorcodeptr = ERR3;
- break;
- }
- break;
- }
- }
-
-*ptrptr = ptr;
-return c;
-}
-
-
-
-#ifdef SUPPORT_UCP
-/*************************************************
-* Handle \P and \p *
-*************************************************/
-
-/* This function is called after \P or \p has been encountered, provided that
-PCRE is compiled with support for Unicode properties. On entry, ptrptr is
-pointing at the P or p. On exit, it is pointing at the final character of the
-escape sequence.
-
-Argument:
- ptrptr points to the pattern position pointer
- negptr points to a boolean that is set TRUE for negation else FALSE
- dptr points to an int that is set to the detailed property value
- errorcodeptr points to the error code variable
-
-Returns: type value from ucp_type_table, or -1 for an invalid type
-*/
-
-static int
-get_ucp(const uschar **ptrptr, BOOL *negptr, int *dptr, int *errorcodeptr)
-{
-int c, i, bot, top;
-const uschar *ptr = *ptrptr;
-char name[32];
-
-c = *(++ptr);
-if (c == 0) goto ERROR_RETURN;
-
-*negptr = FALSE;
-
-/* \P or \p can be followed by a name in {}, optionally preceded by ^ for
-negation. */
-
-if (c == CHAR_LEFT_CURLY_BRACKET)
- {
- if (ptr[1] == CHAR_CIRCUMFLEX_ACCENT)
- {
- *negptr = TRUE;
- ptr++;
- }
- for (i = 0; i < (int)sizeof(name) - 1; i++)
- {
- c = *(++ptr);
- if (c == 0) goto ERROR_RETURN;
- if (c == CHAR_RIGHT_CURLY_BRACKET) break;
- name[i] = c;
- }
- if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN;
- name[i] = 0;
- }
-
-/* Otherwise there is just one following character */
-
-else
- {
- name[0] = c;
- name[1] = 0;
- }
-
-*ptrptr = ptr;
-
-/* Search for a recognized property name using binary chop */
-
-bot = 0;
-top = _pcre_utt_size;
-
-while (bot < top)
- {
- i = (bot + top) >> 1;
- c = strcmp(name, _pcre_utt_names + _pcre_utt[i].name_offset);
- if (c == 0)
- {
- *dptr = _pcre_utt[i].value;
- return _pcre_utt[i].type;
- }
- if (c > 0) bot = i + 1; else top = i;
- }
-
-*errorcodeptr = ERR47;
-*ptrptr = ptr;
-return -1;
-
-ERROR_RETURN:
-*errorcodeptr = ERR46;
-*ptrptr = ptr;
-return -1;
-}
-#endif
-
-
-
-
-/*************************************************
-* Check for counted repeat *
-*************************************************/
-
-/* This function is called when a '{' is encountered in a place where it might
-start a quantifier. It looks ahead to see if it really is a quantifier or not.
-It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd}
-where the ddds are digits.
-
-Arguments:
- p pointer to the first char after '{'
-
-Returns: TRUE or FALSE
-*/
-
-static BOOL
-is_counted_repeat(const uschar *p)
-{
-if (g_ascii_isdigit(*p++) == 0) return FALSE;
-while (g_ascii_isdigit(*p) != 0) p++;
-if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE;
-
-if (*p++ != CHAR_COMMA) return FALSE;
-if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE;
-
-if (g_ascii_isdigit(*p++) == 0) return FALSE;
-while (g_ascii_isdigit(*p) != 0) p++;
-
-return (*p == CHAR_RIGHT_CURLY_BRACKET);
-}
-
-
-
-/*************************************************
-* Read repeat counts *
-*************************************************/
-
-/* Read an item of the form {n,m} and return the values. This is called only
-after is_counted_repeat() has confirmed that a repeat-count quantifier exists,
-so the syntax is guaranteed to be correct, but we need to check the values.
-
-Arguments:
- p pointer to first char after '{'
- minp pointer to int for min
- maxp pointer to int for max
- returned as -1 if no max
- errorcodeptr points to error code variable
-
-Returns: pointer to '}' on success;
- current ptr on error, with errorcodeptr set non-zero
-*/
-
-static const uschar *
-read_repeat_counts(const uschar *p, int *minp, int *maxp, int *errorcodeptr)
-{
-int min = 0;
-int max = -1;
-
-/* Read the minimum value and do a paranoid check: a negative value indicates
-an integer overflow. */
-
-while (g_ascii_isdigit(*p) != 0) min = min * 10 + *p++ - CHAR_0;
-if (min < 0 || min > 65535)
- {
- *errorcodeptr = ERR5;
- return p;
- }
-
-/* Read the maximum value if there is one, and again do a paranoid on its size.
-Also, max must not be less than min. */
-
-if (*p == CHAR_RIGHT_CURLY_BRACKET) max = min; else
- {
- if (*(++p) != CHAR_RIGHT_CURLY_BRACKET)
- {
- max = 0;
- while(g_ascii_isdigit(*p) != 0) max = max * 10 + *p++ - CHAR_0;
- if (max < 0 || max > 65535)
- {
- *errorcodeptr = ERR5;
- return p;
- }
- if (max < min)
- {
- *errorcodeptr = ERR4;
- return p;
- }
- }
- }
-
-/* Fill in the required variables, and pass back the pointer to the terminating
-'}'. */
-
-*minp = min;
-*maxp = max;
-return p;
-}
-
-
-
-/*************************************************
-* Subroutine for finding forward reference *
-*************************************************/
-
-/* This recursive function is called only from find_parens() below. The
-top-level call starts at the beginning of the pattern. All other calls must
-start at a parenthesis. It scans along a pattern's text looking for capturing
-subpatterns, and counting them. If it finds a named pattern that matches the
-name it is given, it returns its number. Alternatively, if the name is NULL, it
-returns when it reaches a given numbered subpattern. We know that if (?P< is
-encountered, the name will be terminated by '>' because that is checked in the
-first pass. Recursion is used to keep track of subpatterns that reset the
-capturing group numbers - the (?| feature.
-
-Arguments:
- ptrptr address of the current character pointer (updated)
- cd compile background data
- name name to seek, or NULL if seeking a numbered subpattern
- lorn name length, or subpattern number if name is NULL
- xmode TRUE if we are in /x mode
- count pointer to the current capturing subpattern number (updated)
-
-Returns: the number of the named subpattern, or -1 if not found
-*/
-
-static int
-find_parens_sub(uschar **ptrptr, compile_data *cd, const uschar *name, int lorn,
- BOOL xmode, int *count)
-{
-uschar *ptr = *ptrptr;
-int start_count = *count;
-int hwm_count = start_count;
-BOOL dup_parens = FALSE;
-
-/* If the first character is a parenthesis, check on the type of group we are
-dealing with. The very first call may not start with a parenthesis. */
-
-if (ptr[0] == CHAR_LEFT_PARENTHESIS)
- {
- if (ptr[1] == CHAR_QUESTION_MARK &&
- ptr[2] == CHAR_VERTICAL_LINE)
- {
- ptr += 3;
- dup_parens = TRUE;
- }
-
- /* Handle a normal, unnamed capturing parenthesis */
-
- else if (ptr[1] != CHAR_QUESTION_MARK && ptr[1] != CHAR_ASTERISK)
- {
- *count += 1;
- if (name == NULL && *count == lorn) return *count;
- ptr++;
- }
-
- /* Handle a condition. If it is an assertion, just carry on so that it
- is processed as normal. If not, skip to the closing parenthesis of the
- condition (there can't be any nested parens. */
-
- else if (ptr[2] == CHAR_LEFT_PARENTHESIS)
- {
- ptr += 2;
- if (ptr[1] != CHAR_QUESTION_MARK)
- {
- while (*ptr != 0 && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++;
- if (*ptr != 0) ptr++;
- }
- }
-
- /* We have either (? or (* and not a condition */
-
- else
- {
- ptr += 2;
- if (*ptr == CHAR_P) ptr++; /* Allow optional P */
-
- /* We have to disambiguate (?<! and (?<= from (?<name> for named groups */
-
- if ((*ptr == CHAR_LESS_THAN_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK &&
- ptr[1] != CHAR_EQUALS_SIGN) || *ptr == CHAR_APOSTROPHE)
- {
- int term;
- const uschar *thisname;
- *count += 1;
- if (name == NULL && *count == lorn) return *count;
- term = *ptr++;
- if (term == CHAR_LESS_THAN_SIGN) term = CHAR_GREATER_THAN_SIGN;
- thisname = ptr;
- while (*ptr != term) ptr++;
- if (name != NULL && lorn == ptr - thisname &&
- strncmp((const char *)name, (const char *)thisname, lorn) == 0)
- return *count;
- term++;
- }
- }
- }
-
-/* Past any initial parenthesis handling, scan for parentheses or vertical
-bars. */
-
-for (; *ptr != 0; ptr++)
- {
- /* Skip over backslashed characters and also entire \Q...\E */
-
- if (*ptr == CHAR_BACKSLASH)
- {
- if (*(++ptr) == 0) goto FAIL_EXIT;
- if (*ptr == CHAR_Q) for (;;)
- {
- while (*(++ptr) != 0 && *ptr != CHAR_BACKSLASH) {};
- if (*ptr == 0) goto FAIL_EXIT;
- if (*(++ptr) == CHAR_E) break;
- }
- continue;
- }
-
- /* Skip over character classes; this logic must be similar to the way they
- are handled for real. If the first character is '^', skip it. Also, if the
- first few characters (either before or after ^) are \Q\E or \E we skip them
- too. This makes for compatibility with Perl. Note the use of STR macros to
- encode "Q\\E" so that it works in UTF-8 on EBCDIC platforms. */
-
- if (*ptr == CHAR_LEFT_SQUARE_BRACKET)
- {
- BOOL negate_class = FALSE;
- for (;;)
- {
- if (ptr[1] == CHAR_BACKSLASH)
- {
- if (ptr[2] == CHAR_E)
- ptr+= 2;
- else if (strncmp((const char *)ptr+2,
- STR_Q STR_BACKSLASH STR_E, 3) == 0)
- ptr += 4;
- else
- break;
- }
- else if (!negate_class && ptr[1] == CHAR_CIRCUMFLEX_ACCENT)
- {
- negate_class = TRUE;
- ptr++;
- }
- else break;
- }
-
- /* If the next character is ']', it is a data character that must be
- skipped, except in JavaScript compatibility mode. */
-
- if (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET &&
- (cd->external_options & PCRE_JAVASCRIPT_COMPAT) == 0)
- ptr++;
-
- while (*(++ptr) != CHAR_RIGHT_SQUARE_BRACKET)
- {
- if (*ptr == 0) return -1;
- if (*ptr == CHAR_BACKSLASH)
- {
- if (*(++ptr) == 0) goto FAIL_EXIT;
- if (*ptr == CHAR_Q) for (;;)
- {
- while (*(++ptr) != 0 && *ptr != CHAR_BACKSLASH) {};
- if (*ptr == 0) goto FAIL_EXIT;
- if (*(++ptr) == CHAR_E) break;
- }
- continue;
- }
- }
- continue;
- }
-
- /* Skip comments in /x mode */
-
- if (xmode && *ptr == CHAR_NUMBER_SIGN)
- {
- while (*(++ptr) != 0 && *ptr != CHAR_NL) {};
- if (*ptr == 0) goto FAIL_EXIT;
- continue;
- }
-
- /* Check for the special metacharacters */
-
- if (*ptr == CHAR_LEFT_PARENTHESIS)
- {
- int rc = find_parens_sub(&ptr, cd, name, lorn, xmode, count);
- if (rc > 0) return rc;
- if (*ptr == 0) goto FAIL_EXIT;
- }
-
- else if (*ptr == CHAR_RIGHT_PARENTHESIS)
- {
- if (dup_parens && *count < hwm_count) *count = hwm_count;
- *ptrptr = ptr;
- return -1;
- }
-
- else if (*ptr == CHAR_VERTICAL_LINE && dup_parens)
- {
- if (*count > hwm_count) hwm_count = *count;
- *count = start_count;
- }
- }
-
-FAIL_EXIT:
-*ptrptr = ptr;
-return -1;
-}
-
-
-
-
-/*************************************************
-* Find forward referenced subpattern *
-*************************************************/
-
-/* This function scans along a pattern's text looking for capturing
-subpatterns, and counting them. If it finds a named pattern that matches the
-name it is given, it returns its number. Alternatively, if the name is NULL, it
-returns when it reaches a given numbered subpattern. This is used for forward
-references to subpatterns. We used to be able to start this scan from the
-current compiling point, using the current count value from cd->bracount, and
-do it all in a single loop, but the addition of the possibility of duplicate
-subpattern numbers means that we have to scan from the very start, in order to
-take account of such duplicates, and to use a recursive function to keep track
-of the different types of group.
-
-Arguments:
- cd compile background data
- name name to seek, or NULL if seeking a numbered subpattern
- lorn name length, or subpattern number if name is NULL
- xmode TRUE if we are in /x mode
-
-Returns: the number of the found subpattern, or -1 if not found
-*/
-
-static int
-find_parens(compile_data *cd, const uschar *name, int lorn, BOOL xmode)
-{
-uschar *ptr = (uschar *)cd->start_pattern;
-int count = 0;
-int rc;
-
-/* If the pattern does not start with an opening parenthesis, the first call
-to find_parens_sub() will scan right to the end (if necessary). However, if it
-does start with a parenthesis, find_parens_sub() will return when it hits the
-matching closing parens. That is why we have to have a loop. */
-
-for (;;)
- {
- rc = find_parens_sub(&ptr, cd, name, lorn, xmode, &count);
- if (rc > 0 || *ptr++ == 0) break;
- }
-
-return rc;
-}
-
-
-
-
-/*************************************************
-* Find first significant op code *
-*************************************************/
-
-/* This is called by several functions that scan a compiled expression looking
-for a fixed first character, or an anchoring op code etc. It skips over things
-that do not influence this. For some calls, a change of option is important.
-For some calls, it makes sense to skip negative forward and all backward
-assertions, and also the \b assertion; for others it does not.
-
-Arguments:
- code pointer to the start of the group
- options pointer to external options
- optbit the option bit whose changing is significant, or
- zero if none are
- skipassert TRUE if certain assertions are to be skipped
-
-Returns: pointer to the first significant opcode
-*/
-
-static const uschar*
-first_significant_code(const uschar *code, int *options, int optbit,
- BOOL skipassert)
-{
-for (;;)
- {
- switch ((int)*code)
- {
- case OP_OPT:
- if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit))
- *options = (int)code[1];
- code += 2;
- break;
-
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- if (!skipassert) return code;
- do code += GET(code, 1); while (*code == OP_ALT);
- code += _pcre_OP_lengths[*code];
- break;
-
- case OP_WORD_BOUNDARY:
- case OP_NOT_WORD_BOUNDARY:
- if (!skipassert) return code;
- /* Fall through */
-
- case OP_CALLOUT:
- case OP_CREF:
- case OP_NCREF:
- case OP_RREF:
- case OP_NRREF:
- case OP_DEF:
- code += _pcre_OP_lengths[*code];
- break;
-
- default:
- return code;
- }
- }
-/* Control never reaches here */
-}
-
-
-
-
-/*************************************************
-* Find the fixed length of a branch *
-*************************************************/
-
-/* Scan a branch and compute the fixed length of subject that will match it,
-if the length is fixed. This is needed for dealing with backward assertions.
-In UTF8 mode, the result is in characters rather than bytes. The branch is
-temporarily terminated with OP_END when this function is called.
-
-This function is called when a backward assertion is encountered, so that if it
-fails, the error message can point to the correct place in the pattern.
-However, we cannot do this when the assertion contains subroutine calls,
-because they can be forward references. We solve this by remembering this case
-and doing the check at the end; a flag specifies which mode we are running in.
-
-Arguments:
- code points to the start of the pattern (the bracket)
- options the compiling options
- atend TRUE if called when the pattern is complete
- cd the "compile data" structure
-
-Returns: the fixed length,
- or -1 if there is no fixed length,
- or -2 if \C was encountered
- or -3 if an OP_RECURSE item was encountered and atend is FALSE
-*/
-
-static int
-find_fixedlength(uschar *code, int options, BOOL atend, compile_data *cd)
-{
-int length = -1;
-
-register int branchlength = 0;
-register uschar *cc = code + 1 + LINK_SIZE;
-
-/* Scan along the opcodes for this branch. If we get to the end of the
-branch, check the length against that of the other branches. */
-
-for (;;)
- {
- int d;
- uschar *ce, *cs;
- register int op = *cc;
- switch (op)
- {
- case OP_CBRA:
- case OP_BRA:
- case OP_ONCE:
- case OP_COND:
- d = find_fixedlength(cc + ((op == OP_CBRA)? 2:0), options, atend, cd);
- if (d < 0) return d;
- branchlength += d;
- do cc += GET(cc, 1); while (*cc == OP_ALT);
- cc += 1 + LINK_SIZE;
- break;
-
- /* Reached end of a branch; if it's a ket it is the end of a nested
- call. If it's ALT it is an alternation in a nested call. If it is
- END it's the end of the outer call. All can be handled by the same code. */
-
- case OP_ALT:
- case OP_KET:
- case OP_KETRMAX:
- case OP_KETRMIN:
- case OP_END:
- if (length < 0) length = branchlength;
- else if (length != branchlength) return -1;
- if (*cc != OP_ALT) return length;
- cc += 1 + LINK_SIZE;
- branchlength = 0;
- break;
-
- /* A true recursion implies not fixed length, but a subroutine call may
- be OK. If the subroutine is a forward reference, we can't deal with
- it until the end of the pattern, so return -3. */
-
- case OP_RECURSE:
- if (!atend) return -3;
- cs = ce = (uschar *)cd->start_code + GET(cc, 1); /* Start subpattern */
- do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */
- if (cc > cs && cc < ce) return -1; /* Recursion */
- d = find_fixedlength(cs + 2, options, atend, cd);
- if (d < 0) return d;
- branchlength += d;
- cc += 1 + LINK_SIZE;
- break;
-
- /* Skip over assertive subpatterns */
-
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- do cc += GET(cc, 1); while (*cc == OP_ALT);
- /* Fall through */
-
- /* Skip over things that don't match chars */
-
- case OP_REVERSE:
- case OP_CREF:
- case OP_NCREF:
- case OP_RREF:
- case OP_NRREF:
- case OP_DEF:
- case OP_OPT:
- case OP_CALLOUT:
- case OP_SOD:
- case OP_SOM:
- case OP_SET_SOM:
- case OP_EOD:
- case OP_EODN:
- case OP_CIRC:
- case OP_DOLL:
- case OP_NOT_WORD_BOUNDARY:
- case OP_WORD_BOUNDARY:
- cc += _pcre_OP_lengths[*cc];
- break;
-
- /* Handle literal characters */
-
- case OP_CHAR:
- case OP_CHARNC:
- case OP_NOT:
- branchlength++;
- cc += 2;
-#ifdef SUPPORT_UTF8
- if ((options & PCRE_UTF8) != 0 && cc[-1] >= 0xc0)
- cc += _pcre_utf8_table4[cc[-1] & 0x3f];
-#endif
- break;
-
- /* Handle exact repetitions. The count is already in characters, but we
- need to skip over a multibyte character in UTF8 mode. */
-
- case OP_EXACT:
- branchlength += GET2(cc,1);
- cc += 4;
-#ifdef SUPPORT_UTF8
- if ((options & PCRE_UTF8) != 0 && cc[-1] >= 0xc0)
- cc += _pcre_utf8_table4[cc[-1] & 0x3f];
-#endif
- break;
-
- case OP_TYPEEXACT:
- branchlength += GET2(cc,1);
- if (cc[3] == OP_PROP || cc[3] == OP_NOTPROP) cc += 2;
- cc += 4;
- break;
-
- /* Handle single-char matchers */
-
- case OP_PROP:
- case OP_NOTPROP:
- cc += 2;
- /* Fall through */
-
- case OP_NOT_DIGIT:
- case OP_DIGIT:
- case OP_NOT_WHITESPACE:
- case OP_WHITESPACE:
- case OP_NOT_WORDCHAR:
- case OP_WORDCHAR:
- case OP_ANY:
- case OP_ALLANY:
- branchlength++;
- cc++;
- break;
-
- /* The single-byte matcher isn't allowed */
-
- case OP_ANYBYTE:
- return -2;
-
- /* Check a class for variable quantification */
-
-#ifdef SUPPORT_UTF8
- case OP_XCLASS:
- cc += GET(cc, 1) - 33;
- /* Fall through */
-#endif
-
- case OP_CLASS:
- case OP_NCLASS:
- cc += 33;
-
- switch (*cc)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- return -1;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- if (GET2(cc,1) != GET2(cc,3)) return -1;
- branchlength += GET2(cc,1);
- cc += 5;
- break;
-
- default:
- branchlength++;
- }
- break;
-
- /* Anything else is variable length */
-
- default:
- return -1;
- }
- }
-/* Control never gets here */
-}
-
-
-
-
-/*************************************************
-* Scan compiled regex for specific bracket *
-*************************************************/
-
-/* This little function scans through a compiled pattern until it finds a
-capturing bracket with the given number, or, if the number is negative, an
-instance of OP_REVERSE for a lookbehind. The function is global in the C sense
-so that it can be called from pcre_study() when finding the minimum matching
-length.
-
-Arguments:
- code points to start of expression
- utf8 TRUE in UTF-8 mode
- number the required bracket number or negative to find a lookbehind
-
-Returns: pointer to the opcode for the bracket, or NULL if not found
-*/
-
-const uschar *
-_pcre_find_bracket(const uschar *code, BOOL utf8, int number)
-{
-for (;;)
- {
- register int c = *code;
- if (c == OP_END) return NULL;
-
- /* XCLASS is used for classes that cannot be represented just by a bit
- map. This includes negated single high-valued characters. The length in
- the table is zero; the actual length is stored in the compiled code. */
-
- if (c == OP_XCLASS) code += GET(code, 1);
-
- /* Handle recursion */
-
- else if (c == OP_REVERSE)
- {
- if (number < 0) return (uschar *)code;
- code += _pcre_OP_lengths[c];
- }
-
- /* Handle capturing bracket */
-
- else if (c == OP_CBRA)
- {
- int n = GET2(code, 1+LINK_SIZE);
- if (n == number) return (uschar *)code;
- code += _pcre_OP_lengths[c];
- }
-
- /* Otherwise, we can get the item's length from the table, except that for
- repeated character types, we have to test for \p and \P, which have an extra
- two bytes of parameters. */
-
- else
- {
- switch(c)
- {
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSSTAR:
- case OP_TYPEPOSPLUS:
- case OP_TYPEPOSQUERY:
- if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
- break;
-
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEEXACT:
- case OP_TYPEPOSUPTO:
- if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2;
- break;
- }
-
- /* Add in the fixed length from the table */
-
- code += _pcre_OP_lengths[c];
-
- /* In UTF-8 mode, opcodes that are followed by a character may be followed by
- a multi-byte character. The length in the table is a minimum, so we have to
- arrange to skip the extra bytes. */
-
-#ifdef SUPPORT_UTF8
- if (utf8) switch(c)
- {
- case OP_CHAR:
- case OP_CHARNC:
- case OP_EXACT:
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- if (code[-1] >= 0xc0) code += _pcre_utf8_table4[code[-1] & 0x3f];
- break;
- }
-#else
- (void)(utf8); /* Keep compiler happy by referencing function argument */
-#endif
- }
- }
-}
-
-
-
-/*************************************************
-* Scan compiled regex for recursion reference *
-*************************************************/
-
-/* This little function scans through a compiled pattern until it finds an
-instance of OP_RECURSE.
-
-Arguments:
- code points to start of expression
- utf8 TRUE in UTF-8 mode
-
-Returns: pointer to the opcode for OP_RECURSE, or NULL if not found
-*/
-
-static const uschar *
-find_recurse(const uschar *code, BOOL utf8)
-{
-for (;;)
- {
- register int c = *code;
- if (c == OP_END) return NULL;
- if (c == OP_RECURSE) return code;
-
- /* XCLASS is used for classes that cannot be represented just by a bit
- map. This includes negated single high-valued characters. The length in
- the table is zero; the actual length is stored in the compiled code. */
-
- if (c == OP_XCLASS) code += GET(code, 1);
-
- /* Otherwise, we can get the item's length from the table, except that for
- repeated character types, we have to test for \p and \P, which have an extra
- two bytes of parameters. */
-
- else
- {
- switch(c)
- {
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSSTAR:
- case OP_TYPEPOSPLUS:
- case OP_TYPEPOSQUERY:
- if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
- break;
-
- case OP_TYPEPOSUPTO:
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEEXACT:
- if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2;
- break;
- }
-
- /* Add in the fixed length from the table */
-
- code += _pcre_OP_lengths[c];
-
- /* In UTF-8 mode, opcodes that are followed by a character may be followed
- by a multi-byte character. The length in the table is a minimum, so we have
- to arrange to skip the extra bytes. */
-
-#ifdef SUPPORT_UTF8
- if (utf8) switch(c)
- {
- case OP_CHAR:
- case OP_CHARNC:
- case OP_EXACT:
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- if (code[-1] >= 0xc0) code += _pcre_utf8_table4[code[-1] & 0x3f];
- break;
- }
-#else
- (void)(utf8); /* Keep compiler happy by referencing function argument */
-#endif
- }
- }
-}
-
-
-
-/*************************************************
-* Scan compiled branch for non-emptiness *
-*************************************************/
-
-/* This function scans through a branch of a compiled pattern to see whether it
-can match the empty string or not. It is called from could_be_empty()
-below and from compile_branch() when checking for an unlimited repeat of a
-group that can match nothing. Note that first_significant_code() skips over
-backward and negative forward assertions when its final argument is TRUE. If we
-hit an unclosed bracket, we return "empty" - this means we've struck an inner
-bracket whose current branch will already have been scanned.
-
-Arguments:
- code points to start of search
- endcode points to where to stop
- utf8 TRUE if in UTF8 mode
- cd contains pointers to tables etc.
-
-Returns: TRUE if what is matched could be empty
-*/
-
-static BOOL
-could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8,
- compile_data *cd)
-{
-register int c;
-for (code = first_significant_code(code + _pcre_OP_lengths[*code], NULL, 0, TRUE);
- code < endcode;
- code = first_significant_code(code + _pcre_OP_lengths[c], NULL, 0, TRUE))
- {
- const uschar *ccode;
-
- c = *code;
-
- /* Skip over forward assertions; the other assertions are skipped by
- first_significant_code() with a TRUE final argument. */
-
- if (c == OP_ASSERT)
- {
- do code += GET(code, 1); while (*code == OP_ALT);
- c = *code;
- continue;
- }
-
- /* Groups with zero repeats can of course be empty; skip them. */
-
- if (c == OP_BRAZERO || c == OP_BRAMINZERO || c == OP_SKIPZERO)
- {
- code += _pcre_OP_lengths[c];
- do code += GET(code, 1); while (*code == OP_ALT);
- c = *code;
- continue;
- }
-
- /* For a recursion/subroutine call, if its end has been reached, which
- implies a subroutine call, we can scan it. */
-
- if (c == OP_RECURSE)
- {
- BOOL empty_branch = FALSE;
- const uschar *scode = cd->start_code + GET(code, 1);
- if (GET(scode, 1) == 0) return TRUE; /* Unclosed */
- do
- {
- if (could_be_empty_branch(scode, endcode, utf8, cd))
- {
- empty_branch = TRUE;
- break;
- }
- scode += GET(scode, 1);
- }
- while (*scode == OP_ALT);
- if (!empty_branch) return FALSE; /* All branches are non-empty */
- continue;
- }
-
- /* For other groups, scan the branches. */
-
- if (c == OP_BRA || c == OP_CBRA || c == OP_ONCE || c == OP_COND)
- {
- BOOL empty_branch;
- if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */
-
- /* If a conditional group has only one branch, there is a second, implied,
- empty branch, so just skip over the conditional, because it could be empty.
- Otherwise, scan the individual branches of the group. */
-
- if (c == OP_COND && code[GET(code, 1)] != OP_ALT)
- code += GET(code, 1);
- else
- {
- empty_branch = FALSE;
- do
- {
- if (!empty_branch && could_be_empty_branch(code, endcode, utf8, cd))
- empty_branch = TRUE;
- code += GET(code, 1);
- }
- while (*code == OP_ALT);
- if (!empty_branch) return FALSE; /* All branches are non-empty */
- }
-
- c = *code;
- continue;
- }
-
- /* Handle the other opcodes */
-
- switch (c)
- {
- /* Check for quantifiers after a class. XCLASS is used for classes that
- cannot be represented just by a bit map. This includes negated single
- high-valued characters. The length in _pcre_OP_lengths[] is zero; the
- actual length is stored in the compiled code, so we must update "code"
- here. */
-
-#ifdef SUPPORT_UTF8
- case OP_XCLASS:
- ccode = code += GET(code, 1);
- goto CHECK_CLASS_REPEAT;
-#endif
-
- case OP_CLASS:
- case OP_NCLASS:
- ccode = code + 33;
-
-#ifdef SUPPORT_UTF8
- CHECK_CLASS_REPEAT:
-#endif
-
- switch (*ccode)
- {
- case OP_CRSTAR: /* These could be empty; continue */
- case OP_CRMINSTAR:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- break;
-
- default: /* Non-repeat => class must match */
- case OP_CRPLUS: /* These repeats aren't empty */
- case OP_CRMINPLUS:
- return FALSE;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */
- break;
- }
- break;
-
- /* Opcodes that must match a character */
-
- case OP_PROP:
- case OP_NOTPROP:
- case OP_EXTUNI:
- case OP_NOT_DIGIT:
- case OP_DIGIT:
- case OP_NOT_WHITESPACE:
- case OP_WHITESPACE:
- case OP_NOT_WORDCHAR:
- case OP_WORDCHAR:
- case OP_ANY:
- case OP_ALLANY:
- case OP_ANYBYTE:
- case OP_CHAR:
- case OP_CHARNC:
- case OP_NOT:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_EXACT:
- case OP_NOTPLUS:
- case OP_NOTMINPLUS:
- case OP_NOTPOSPLUS:
- case OP_NOTEXACT:
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEPOSPLUS:
- case OP_TYPEEXACT:
- return FALSE;
-
- /* These are going to continue, as they may be empty, but we have to
- fudge the length for the \p and \P cases. */
-
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPOSSTAR:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSQUERY:
- if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
- break;
-
- /* Same for these */
-
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEPOSUPTO:
- if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2;
- break;
-
- /* End of branch */
-
- case OP_KET:
- case OP_KETRMAX:
- case OP_KETRMIN:
- case OP_ALT:
- return TRUE;
-
- /* In UTF-8 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO,
- MINUPTO, and POSUPTO may be followed by a multibyte character */
-
-#ifdef SUPPORT_UTF8
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- if (utf8 && code[1] >= 0xc0) code += _pcre_utf8_table4[code[1] & 0x3f];
- break;
-
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- if (utf8 && code[3] >= 0xc0) code += _pcre_utf8_table4[code[3] & 0x3f];
- break;
-#endif
-
- /* None of the remaining opcodes are required to match a character. */
-
- default:
- break;
- }
- }
-
-return TRUE;
-}
-
-
-
-/*************************************************
-* Scan compiled regex for non-emptiness *
-*************************************************/
-
-/* This function is called to check for left recursive calls. We want to check
-the current branch of the current pattern to see if it could match the empty
-string. If it could, we must look outwards for branches at other levels,
-stopping when we pass beyond the bracket which is the subject of the recursion.
-
-Arguments:
- code points to start of the recursion
- endcode points to where to stop (current RECURSE item)
- bcptr points to the chain of current (unclosed) branch starts
- utf8 TRUE if in UTF-8 mode
- cd pointers to tables etc
-
-Returns: TRUE if what is matched could be empty
-*/
-
-static BOOL
-could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr,
- BOOL utf8, compile_data *cd)
-{
-while (bcptr != NULL && bcptr->current_branch >= code)
- {
- if (!could_be_empty_branch(bcptr->current_branch, endcode, utf8, cd))
- return FALSE;
- bcptr = bcptr->outer;
- }
-return TRUE;
-}
-
-
-
-/*************************************************
-* Check for POSIX class syntax *
-*************************************************/
-
-/* This function is called when the sequence "[:" or "[." or "[=" is
-encountered in a character class. It checks whether this is followed by a
-sequence of characters terminated by a matching ":]" or ".]" or "=]". If we
-reach an unescaped ']' without the special preceding character, return FALSE.
-
-Originally, this function only recognized a sequence of letters between the
-terminators, but it seems that Perl recognizes any sequence of characters,
-though of course unknown POSIX names are subsequently rejected. Perl gives an
-"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE
-didn't consider this to be a POSIX class. Likewise for [:1234:].
-
-The problem in trying to be exactly like Perl is in the handling of escapes. We
-have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX
-class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code
-below handles the special case of \], but does not try to do any other escape
-processing. This makes it different from Perl for cases such as [:l\ower:]
-where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize
-"l\ower". This is a lesser evil that not diagnosing bad classes when Perl does,
-I think.
-
-Arguments:
- ptr pointer to the initial [
- endptr where to return the end pointer
-
-Returns: TRUE or FALSE
-*/
-
-static BOOL
-check_posix_syntax(const uschar *ptr, const uschar **endptr)
-{
-int terminator; /* Don't combine these lines; the Solaris cc */
-terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */
-for (++ptr; *ptr != 0; ptr++)
- {
- if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) ptr++; else
- {
- if (*ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE;
- if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET)
- {
- *endptr = ptr;
- return TRUE;
- }
- }
- }
-return FALSE;
-}
-
-
-
-
-/*************************************************
-* Check POSIX class name *
-*************************************************/
-
-/* This function is called to check the name given in a POSIX-style class entry
-such as [:alnum:].
-
-Arguments:
- ptr points to the first letter
- len the length of the name
-
-Returns: a value representing the name, or -1 if unknown
-*/
-
-static int
-check_posix_name(const uschar *ptr, int len)
-{
-const char *pn = posix_names;
-register int yield = 0;
-while (posix_name_lengths[yield] != 0)
- {
- if (len == posix_name_lengths[yield] &&
- strncmp((const char *)ptr, pn, len) == 0) return yield;
- pn += posix_name_lengths[yield] + 1;
- yield++;
- }
-return -1;
-}
-
-
-/*************************************************
-* Adjust OP_RECURSE items in repeated group *
-*************************************************/
-
-/* OP_RECURSE items contain an offset from the start of the regex to the group
-that is referenced. This means that groups can be replicated for fixed
-repetition simply by copying (because the recursion is allowed to refer to
-earlier groups that are outside the current group). However, when a group is
-optional (i.e. the minimum quantifier is zero), OP_BRAZERO or OP_SKIPZERO is
-inserted before it, after it has been compiled. This means that any OP_RECURSE
-items within it that refer to the group itself or any contained groups have to
-have their offsets adjusted. That one of the jobs of this function. Before it
-is called, the partially compiled regex must be temporarily terminated with
-OP_END.
-
-This function has been extended with the possibility of forward references for
-recursions and subroutine calls. It must also check the list of such references
-for the group we are dealing with. If it finds that one of the recursions in
-the current group is on this list, it adjusts the offset in the list, not the
-value in the reference (which is a group number).
-
-Arguments:
- group points to the start of the group
- adjust the amount by which the group is to be moved
- utf8 TRUE in UTF-8 mode
- cd contains pointers to tables etc.
- save_hwm the hwm forward reference pointer at the start of the group
-
-Returns: nothing
-*/
-
-static void
-adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd,
- uschar *save_hwm)
-{
-uschar *ptr = group;
-
-while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL)
- {
- int offset;
- uschar *hc;
-
- /* See if this recursion is on the forward reference list. If so, adjust the
- reference. */
-
- for (hc = save_hwm; hc < cd->hwm; hc += LINK_SIZE)
- {
- offset = GET(hc, 0);
- if (cd->start_code + offset == ptr + 1)
- {
- PUT(hc, 0, offset + adjust);
- break;
- }
- }
-
- /* Otherwise, adjust the recursion offset if it's after the start of this
- group. */
-
- if (hc >= cd->hwm)
- {
- offset = GET(ptr, 1);
- if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust);
- }
-
- ptr += 1 + LINK_SIZE;
- }
-}
-
-
-
-/*************************************************
-* Insert an automatic callout point *
-*************************************************/
-
-/* This function is called when the PCRE_AUTO_CALLOUT option is set, to insert
-callout points before each pattern item.
-
-Arguments:
- code current code pointer
- ptr current pattern pointer
- cd pointers to tables etc
-
-Returns: new code pointer
-*/
-
-static uschar *
-auto_callout(uschar *code, const uschar *ptr, compile_data *cd)
-{
-*code++ = OP_CALLOUT;
-*code++ = 255;
-PUT(code, 0, ptr - cd->start_pattern); /* Pattern offset */
-PUT(code, LINK_SIZE, 0); /* Default length */
-return code + 2*LINK_SIZE;
-}
-
-
-
-/*************************************************
-* Complete a callout item *
-*************************************************/
-
-/* A callout item contains the length of the next item in the pattern, which
-we can't fill in till after we have reached the relevant point. This is used
-for both automatic and manual callouts.
-
-Arguments:
- previous_callout points to previous callout item
- ptr current pattern pointer
- cd pointers to tables etc
-
-Returns: nothing
-*/
-
-static void
-complete_callout(uschar *previous_callout, const uschar *ptr, compile_data *cd)
-{
-int length = ptr - cd->start_pattern - GET(previous_callout, 2);
-PUT(previous_callout, 2 + LINK_SIZE, length);
-}
-
-
-
-#ifdef SUPPORT_UCP
-/*************************************************
-* Get othercase range *
-*************************************************/
-
-/* This function is passed the start and end of a class range, in UTF-8 mode
-with UCP support. It searches up the characters, looking for internal ranges of
-characters in the "other" case. Each call returns the next one, updating the
-start address.
-
-Arguments:
- cptr points to starting character value; updated
- d end value
- ocptr where to put start of othercase range
- odptr where to put end of othercase range
-
-Yield: TRUE when range returned; FALSE when no more
-*/
-
-static BOOL
-get_othercase_range(unsigned int *cptr, unsigned int d, unsigned int *ocptr,
- unsigned int *odptr)
-{
-unsigned int c, othercase, next;
-
-for (c = *cptr; c <= d; c++)
- { if ((othercase = UCD_OTHERCASE(c)) != c) break; }
-
-if (c > d) return FALSE;
-
-*ocptr = othercase;
-next = othercase + 1;
-
-for (++c; c <= d; c++)
- {
- if (UCD_OTHERCASE(c) != next) break;
- next++;
- }
-
-*odptr = next - 1;
-*cptr = c;
-
-return TRUE;
-}
-#endif /* SUPPORT_UCP */
-
-
-
-/*************************************************
-* Check if auto-possessifying is possible *
-*************************************************/
-
-/* This function is called for unlimited repeats of certain items, to see
-whether the next thing could possibly match the repeated item. If not, it makes
-sense to automatically possessify the repeated item.
-
-Arguments:
- op_code the repeated op code
- this data for this item, depends on the opcode
- utf8 TRUE in UTF-8 mode
- utf8_char used for utf8 character bytes, NULL if not relevant
- ptr next character in pattern
- options options bits
- cd contains pointers to tables etc.
-
-Returns: TRUE if possessifying is wanted
-*/
-
-static BOOL
-check_auto_possessive(int op_code, int item, BOOL utf8, uschar *utf8_char,
- const uschar *ptr, int options, compile_data *cd)
-{
-int next;
-
-/* Skip whitespace and comments in extended mode */
-
-if ((options & PCRE_EXTENDED) != 0)
- {
- for (;;)
- {
- while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++;
- if (*ptr == CHAR_NUMBER_SIGN)
- {
- while (*(++ptr) != 0)
- if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; }
- }
- else break;
- }
- }
-
-/* If the next item is one that we can handle, get its value. A non-negative
-value is a character, a negative value is an escape value. */
-
-if (*ptr == CHAR_BACKSLASH)
- {
- int temperrorcode = 0;
- next = check_escape(&ptr, &temperrorcode, cd->bracount, options, FALSE);
- if (temperrorcode != 0) return FALSE;
- ptr++; /* Point after the escape sequence */
- }
-
-else if ((cd->ctypes[*ptr] & ctype_meta) == 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8) { GETCHARINC(next, ptr); } else
-#endif
- next = *ptr++;
- }
-
-else return FALSE;
-
-/* Skip whitespace and comments in extended mode */
-
-if ((options & PCRE_EXTENDED) != 0)
- {
- for (;;)
- {
- while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++;
- if (*ptr == CHAR_NUMBER_SIGN)
- {
- while (*(++ptr) != 0)
- if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; }
- }
- else break;
- }
- }
-
-/* If the next thing is itself optional, we have to give up. */
-
-if (*ptr == CHAR_ASTERISK || *ptr == CHAR_QUESTION_MARK ||
- strncmp((char *)ptr, STR_LEFT_CURLY_BRACKET STR_0 STR_COMMA, 3) == 0)
- return FALSE;
-
-/* Now compare the next item with the previous opcode. If the previous is a
-positive single character match, "item" either contains the character or, if
-"item" is greater than 127 in utf8 mode, the character's bytes are in
-utf8_char. */
-
-
-/* Handle cases when the next item is a character. */
-
-if (next >= 0) switch(op_code)
- {
- case OP_CHAR:
-#ifdef SUPPORT_UTF8
- if (utf8 && item > 127) { GETCHAR(item, utf8_char); }
-#else
- (void)(utf8_char); /* Keep compiler happy by referencing function argument */
-#endif
- return item != next;
-
- /* For CHARNC (caseless character) we must check the other case. If we have
- Unicode property support, we can use it to test the other case of
- high-valued characters. */
-
- case OP_CHARNC:
-#ifdef SUPPORT_UTF8
- if (utf8 && item > 127) { GETCHAR(item, utf8_char); }
-#endif
- if (item == next) return FALSE;
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- unsigned int othercase;
- if (next < 128) othercase = cd->fcc[next]; else
-#ifdef SUPPORT_UCP
- othercase = UCD_OTHERCASE((unsigned int)next);
-#else
- othercase = NOTACHAR;
-#endif
- return (unsigned int)item != othercase;
- }
- else
-#endif /* SUPPORT_UTF8 */
- return (item != cd->fcc[next]); /* Non-UTF-8 mode */
-
- /* For OP_NOT, "item" must be a single-byte character. */
-
- case OP_NOT:
- if (item == next) return TRUE;
- if ((options & PCRE_CASELESS) == 0) return FALSE;
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- unsigned int othercase;
- if (next < 128) othercase = cd->fcc[next]; else
-#ifdef SUPPORT_UCP
- othercase = UCD_OTHERCASE(next);
-#else
- othercase = NOTACHAR;
-#endif
- return (unsigned int)item == othercase;
- }
- else
-#endif /* SUPPORT_UTF8 */
- return (item == cd->fcc[next]); /* Non-UTF-8 mode */
-
- case OP_DIGIT:
- return next > 127 || (cd->ctypes[next] & ctype_digit) == 0;
-
- case OP_NOT_DIGIT:
- return next <= 127 && (cd->ctypes[next] & ctype_digit) != 0;
-
- case OP_WHITESPACE:
- return next > 127 || (cd->ctypes[next] & ctype_space) == 0;
-
- case OP_NOT_WHITESPACE:
- return next <= 127 && (cd->ctypes[next] & ctype_space) != 0;
-
- case OP_WORDCHAR:
- return next > 127 || (cd->ctypes[next] & ctype_word) == 0;
-
- case OP_NOT_WORDCHAR:
- return next <= 127 && (cd->ctypes[next] & ctype_word) != 0;
-
- case OP_HSPACE:
- case OP_NOT_HSPACE:
- switch(next)
- {
- case 0x09:
- case 0x20:
- case 0xa0:
- case 0x1680:
- case 0x180e:
- case 0x2000:
- case 0x2001:
- case 0x2002:
- case 0x2003:
- case 0x2004:
- case 0x2005:
- case 0x2006:
- case 0x2007:
- case 0x2008:
- case 0x2009:
- case 0x200A:
- case 0x202f:
- case 0x205f:
- case 0x3000:
- return op_code != OP_HSPACE;
- default:
- return op_code == OP_HSPACE;
- }
-
- case OP_VSPACE:
- case OP_NOT_VSPACE:
- switch(next)
- {
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x85:
- case 0x2028:
- case 0x2029:
- return op_code != OP_VSPACE;
- default:
- return op_code == OP_VSPACE;
- }
-
- default:
- return FALSE;
- }
-
-
-/* Handle the case when the next item is \d, \s, etc. */
-
-switch(op_code)
- {
- case OP_CHAR:
- case OP_CHARNC:
-#ifdef SUPPORT_UTF8
- if (utf8 && item > 127) { GETCHAR(item, utf8_char); }
-#endif
- switch(-next)
- {
- case ESC_d:
- return item > 127 || (cd->ctypes[item] & ctype_digit) == 0;
-
- case ESC_D:
- return item <= 127 && (cd->ctypes[item] & ctype_digit) != 0;
-
- case ESC_s:
- return item > 127 || (cd->ctypes[item] & ctype_space) == 0;
-
- case ESC_S:
- return item <= 127 && (cd->ctypes[item] & ctype_space) != 0;
-
- case ESC_w:
- return item > 127 || (cd->ctypes[item] & ctype_word) == 0;
-
- case ESC_W:
- return item <= 127 && (cd->ctypes[item] & ctype_word) != 0;
-
- case ESC_h:
- case ESC_H:
- switch(item)
- {
- case 0x09:
- case 0x20:
- case 0xa0:
- case 0x1680:
- case 0x180e:
- case 0x2000:
- case 0x2001:
- case 0x2002:
- case 0x2003:
- case 0x2004:
- case 0x2005:
- case 0x2006:
- case 0x2007:
- case 0x2008:
- case 0x2009:
- case 0x200A:
- case 0x202f:
- case 0x205f:
- case 0x3000:
- return -next != ESC_h;
- default:
- return -next == ESC_h;
- }
-
- case ESC_v:
- case ESC_V:
- switch(item)
- {
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x85:
- case 0x2028:
- case 0x2029:
- return -next != ESC_v;
- default:
- return -next == ESC_v;
- }
-
- default:
- return FALSE;
- }
-
- case OP_DIGIT:
- return next == -ESC_D || next == -ESC_s || next == -ESC_W ||
- next == -ESC_h || next == -ESC_v;
-
- case OP_NOT_DIGIT:
- return next == -ESC_d;
-
- case OP_WHITESPACE:
- return next == -ESC_S || next == -ESC_d || next == -ESC_w;
-
- case OP_NOT_WHITESPACE:
- return next == -ESC_s || next == -ESC_h || next == -ESC_v;
-
- case OP_HSPACE:
- return next == -ESC_S || next == -ESC_H || next == -ESC_d || next == -ESC_w;
-
- case OP_NOT_HSPACE:
- return next == -ESC_h;
-
- /* Can't have \S in here because VT matches \S (Perl anomaly) */
- case OP_VSPACE:
- return next == -ESC_V || next == -ESC_d || next == -ESC_w;
-
- case OP_NOT_VSPACE:
- return next == -ESC_v;
-
- case OP_WORDCHAR:
- return next == -ESC_W || next == -ESC_s || next == -ESC_h || next == -ESC_v;
-
- case OP_NOT_WORDCHAR:
- return next == -ESC_w || next == -ESC_d;
-
- default:
- return FALSE;
- }
-
-/* Control does not reach here */
-}
-
-
-
-/*************************************************
-* Compile one branch *
-*************************************************/
-
-/* Scan the pattern, compiling it into the a vector. If the options are
-changed during the branch, the pointer is used to change the external options
-bits. This function is used during the pre-compile phase when we are trying
-to find out the amount of memory needed, as well as during the real compile
-phase. The value of lengthptr distinguishes the two phases.
-
-Arguments:
- optionsptr pointer to the option bits
- codeptr points to the pointer to the current code point
- ptrptr points to the current pattern pointer
- errorcodeptr points to error code variable
- firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE)
- reqbyteptr set to the last literal character required, else < 0
- bcptr points to current branch chain
- cd contains pointers to tables etc.
- lengthptr NULL during the real compile phase
- points to length accumulator during pre-compile phase
-
-Returns: TRUE on success
- FALSE, with *errorcodeptr set non-zero on error
-*/
-
-static BOOL
-compile_branch(int *optionsptr, uschar **codeptr, const uschar **ptrptr,
- int *errorcodeptr, int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr,
- compile_data *cd, int *lengthptr)
-{
-int repeat_type, op_type;
-int repeat_min = 0, repeat_max = 0; /* To please picky compilers */
-int bravalue = 0;
-int greedy_default, greedy_non_default;
-int firstbyte, reqbyte;
-int zeroreqbyte, zerofirstbyte;
-int req_caseopt, reqvary, tempreqvary;
-int options = *optionsptr;
-int after_manual_callout = 0;
-int length_prevgroup = 0;
-register int c;
-register uschar *code = *codeptr;
-uschar *last_code = code;
-uschar *orig_code = code;
-uschar *tempcode;
-BOOL inescq = FALSE;
-BOOL groupsetfirstbyte = FALSE;
-const uschar *ptr = *ptrptr;
-const uschar *tempptr;
-uschar *previous = NULL;
-uschar *previous_callout = NULL;
-uschar *save_hwm = NULL;
-uschar classbits[32];
-
-#ifdef SUPPORT_UTF8
-BOOL class_utf8;
-BOOL utf8 = (options & PCRE_UTF8) != 0;
-uschar *class_utf8data;
-uschar *class_utf8data_base;
-uschar utf8_char[6];
-#else
-BOOL utf8 = FALSE;
-uschar *utf8_char = NULL;
-#endif
-
-#ifdef PCRE_DEBUG
-if (lengthptr != NULL) DPRINTF((">> start branch\n"));
-#endif
-
-/* Set up the default and non-default settings for greediness */
-
-greedy_default = ((options & PCRE_UNGREEDY) != 0);
-greedy_non_default = greedy_default ^ 1;
-
-/* Initialize no first byte, no required byte. REQ_UNSET means "no char
-matching encountered yet". It gets changed to REQ_NONE if we hit something that
-matches a non-fixed char first char; reqbyte just remains unset if we never
-find one.
-
-When we hit a repeat whose minimum is zero, we may have to adjust these values
-to take the zero repeat into account. This is implemented by setting them to
-zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual
-item types that can be repeated set these backoff variables appropriately. */
-
-firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET;
-
-/* The variable req_caseopt contains either the REQ_CASELESS value or zero,
-according to the current setting of the caseless flag. REQ_CASELESS is a bit
-value > 255. It is added into the firstbyte or reqbyte variables to record the
-case status of the value. This is used only for ASCII characters. */
-
-req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
-
-/* Switch on next character until the end of the branch */
-
-for (;; ptr++)
- {
- BOOL negate_class;
- BOOL should_flip_negation;
- BOOL possessive_quantifier;
- BOOL is_quantifier;
- BOOL is_recurse;
- BOOL reset_bracount;
- int class_charcount;
- int class_lastchar;
- int newoptions;
- int recno;
- int refsign;
- int skipbytes;
- int subreqbyte;
- int subfirstbyte;
- int terminator;
- int mclength;
- uschar mcbuffer[8];
-
- /* Get next byte in the pattern */
-
- c = *ptr;
-
- /* If we are in the pre-compile phase, accumulate the length used for the
- previous cycle of this loop. */
-
- if (lengthptr != NULL)
- {
-#ifdef PCRE_DEBUG
- if (code > cd->hwm) cd->hwm = code; /* High water info */
-#endif
- if (code > cd->start_workspace + WORK_SIZE_CHECK) /* Check for overrun */
- {
- *errorcodeptr = ERR52;
- goto FAILED;
- }
-
- /* There is at least one situation where code goes backwards: this is the
- case of a zero quantifier after a class (e.g. [ab]{0}). At compile time,
- the class is simply eliminated. However, it is created first, so we have to
- allow memory for it. Therefore, don't ever reduce the length at this point.
- */
-
- if (code < last_code) code = last_code;
-
- /* Paranoid check for integer overflow */
-
- if (OFLOW_MAX - *lengthptr < code - last_code)
- {
- *errorcodeptr = ERR20;
- goto FAILED;
- }
-
- *lengthptr += code - last_code;
- DPRINTF(("length=%d added %d c=%c\n", *lengthptr, code - last_code, c));
-
- /* If "previous" is set and it is not at the start of the work space, move
- it back to there, in order to avoid filling up the work space. Otherwise,
- if "previous" is NULL, reset the current code pointer to the start. */
-
- if (previous != NULL)
- {
- if (previous > orig_code)
- {
- memmove(orig_code, previous, code - previous);
- code -= previous - orig_code;
- previous = orig_code;
- }
- }
- else code = orig_code;
-
- /* Remember where this code item starts so we can pick up the length
- next time round. */
-
- last_code = code;
- }
-
- /* In the real compile phase, just check the workspace used by the forward
- reference list. */
-
- else if (cd->hwm > cd->start_workspace + WORK_SIZE_CHECK)
- {
- *errorcodeptr = ERR52;
- goto FAILED;
- }
-
- /* If in \Q...\E, check for the end; if not, we have a literal */
-
- if (inescq && c != 0)
- {
- if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E)
- {
- inescq = FALSE;
- ptr++;
- continue;
- }
- else
- {
- if (previous_callout != NULL)
- {
- if (lengthptr == NULL) /* Don't attempt in pre-compile phase */
- complete_callout(previous_callout, ptr, cd);
- previous_callout = NULL;
- }
- if ((options & PCRE_AUTO_CALLOUT) != 0)
- {
- previous_callout = code;
- code = auto_callout(code, ptr, cd);
- }
- goto NORMAL_CHAR;
- }
- }
-
- /* Fill in length of a previous callout, except when the next thing is
- a quantifier. */
-
- is_quantifier =
- c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK ||
- (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1));
-
- if (!is_quantifier && previous_callout != NULL &&
- after_manual_callout-- <= 0)
- {
- if (lengthptr == NULL) /* Don't attempt in pre-compile phase */
- complete_callout(previous_callout, ptr, cd);
- previous_callout = NULL;
- }
-
- /* In extended mode, skip white space and comments */
-
- if ((options & PCRE_EXTENDED) != 0)
- {
- if ((cd->ctypes[c] & ctype_space) != 0) continue;
- if (c == CHAR_NUMBER_SIGN)
- {
- while (*(++ptr) != 0)
- {
- if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; }
- }
- if (*ptr != 0) continue;
-
- /* Else fall through to handle end of string */
- c = 0;
- }
- }
-
- /* No auto callout for quantifiers. */
-
- if ((options & PCRE_AUTO_CALLOUT) != 0 && !is_quantifier)
- {
- previous_callout = code;
- code = auto_callout(code, ptr, cd);
- }
-
- switch(c)
- {
- /* ===================================================================*/
- case 0: /* The branch terminates at string end */
- case CHAR_VERTICAL_LINE: /* or | or ) */
- case CHAR_RIGHT_PARENTHESIS:
- *firstbyteptr = firstbyte;
- *reqbyteptr = reqbyte;
- *codeptr = code;
- *ptrptr = ptr;
- if (lengthptr != NULL)
- {
- if (OFLOW_MAX - *lengthptr < code - last_code)
- {
- *errorcodeptr = ERR20;
- goto FAILED;
- }
- *lengthptr += code - last_code; /* To include callout length */
- DPRINTF((">> end branch\n"));
- }
- return TRUE;
-
-
- /* ===================================================================*/
- /* Handle single-character metacharacters. In multiline mode, ^ disables
- the setting of any following char as a first character. */
-
- case CHAR_CIRCUMFLEX_ACCENT:
- if ((options & PCRE_MULTILINE) != 0)
- {
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- }
- previous = NULL;
- *code++ = OP_CIRC;
- break;
-
- case CHAR_DOLLAR_SIGN:
- previous = NULL;
- *code++ = OP_DOLL;
- break;
-
- /* There can never be a first char if '.' is first, whatever happens about
- repeats. The value of reqbyte doesn't change either. */
-
- case CHAR_DOT:
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- zerofirstbyte = firstbyte;
- zeroreqbyte = reqbyte;
- previous = code;
- *code++ = ((options & PCRE_DOTALL) != 0)? OP_ALLANY: OP_ANY;
- break;
-
-
- /* ===================================================================*/
- /* Character classes. If the included characters are all < 256, we build a
- 32-byte bitmap of the permitted characters, except in the special case
- where there is only one such character. For negated classes, we build the
- map as usual, then invert it at the end. However, we use a different opcode
- so that data characters > 255 can be handled correctly.
-
- If the class contains characters outside the 0-255 range, a different
- opcode is compiled. It may optionally have a bit map for characters < 256,
- but those above are are explicitly listed afterwards. A flag byte tells
- whether the bitmap is present, and whether this is a negated class or not.
-
- In JavaScript compatibility mode, an isolated ']' causes an error. In
- default (Perl) mode, it is treated as a data character. */
-
- case CHAR_RIGHT_SQUARE_BRACKET:
- if ((cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0)
- {
- *errorcodeptr = ERR64;
- goto FAILED;
- }
- goto NORMAL_CHAR;
-
- case CHAR_LEFT_SQUARE_BRACKET:
- previous = code;
-
- /* PCRE supports POSIX class stuff inside a class. Perl gives an error if
- they are encountered at the top level, so we'll do that too. */
-
- if ((ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT ||
- ptr[1] == CHAR_EQUALS_SIGN) &&
- check_posix_syntax(ptr, &tempptr))
- {
- *errorcodeptr = (ptr[1] == CHAR_COLON)? ERR13 : ERR31;
- goto FAILED;
- }
-
- /* If the first character is '^', set the negation flag and skip it. Also,
- if the first few characters (either before or after ^) are \Q\E or \E we
- skip them too. This makes for compatibility with Perl. */
-
- negate_class = FALSE;
- for (;;)
- {
- c = *(++ptr);
- if (c == CHAR_BACKSLASH)
- {
- if (ptr[1] == CHAR_E)
- ptr++;
- else if (strncmp((const char *)ptr+1,
- STR_Q STR_BACKSLASH STR_E, 3) == 0)
- ptr += 3;
- else
- break;
- }
- else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT)
- negate_class = TRUE;
- else break;
- }
-
- /* Empty classes are allowed in JavaScript compatibility mode. Otherwise,
- an initial ']' is taken as a data character -- the code below handles
- that. In JS mode, [] must always fail, so generate OP_FAIL, whereas
- [^] must match any character, so generate OP_ALLANY. */
-
- if (c == CHAR_RIGHT_SQUARE_BRACKET &&
- (cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0)
- {
- *code++ = negate_class? OP_ALLANY : OP_FAIL;
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- zerofirstbyte = firstbyte;
- break;
- }
-
- /* If a class contains a negative special such as \S, we need to flip the
- negation flag at the end, so that support for characters > 255 works
- correctly (they are all included in the class). */
-
- should_flip_negation = FALSE;
-
- /* Keep a count of chars with values < 256 so that we can optimize the case
- of just a single character (as long as it's < 256). However, For higher
- valued UTF-8 characters, we don't yet do any optimization. */
-
- class_charcount = 0;
- class_lastchar = -1;
-
- /* Initialize the 32-char bit map to all zeros. We build the map in a
- temporary bit of memory, in case the class contains only 1 character (less
- than 256), because in that case the compiled code doesn't use the bit map.
- */
-
- memset(classbits, 0, 32 * sizeof(uschar));
-
-#ifdef SUPPORT_UTF8
- class_utf8 = FALSE; /* No chars >= 256 */
- class_utf8data = code + LINK_SIZE + 2; /* For UTF-8 items */
- class_utf8data_base = class_utf8data; /* For resetting in pass 1 */
-#endif
-
- /* Process characters until ] is reached. By writing this as a "do" it
- means that an initial ] is taken as a data character. At the start of the
- loop, c contains the first byte of the character. */
-
- if (c != 0) do
- {
- const uschar *oldptr;
-
-#ifdef SUPPORT_UTF8
- if (utf8 && c > 127)
- { /* Braces are required because the */
- GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */
- }
-
- /* In the pre-compile phase, accumulate the length of any UTF-8 extra
- data and reset the pointer. This is so that very large classes that
- contain a zillion UTF-8 characters no longer overwrite the work space
- (which is on the stack). */
-
- if (lengthptr != NULL)
- {
- *lengthptr += class_utf8data - class_utf8data_base;
- class_utf8data = class_utf8data_base;
- }
-
-#endif
-
- /* Inside \Q...\E everything is literal except \E */
-
- if (inescq)
- {
- if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */
- {
- inescq = FALSE; /* Reset literal state */
- ptr++; /* Skip the 'E' */
- continue; /* Carry on with next */
- }
- goto CHECK_RANGE; /* Could be range if \E follows */
- }
-
- /* Handle POSIX class names. Perl allows a negation extension of the
- form [:^name:]. A square bracket that doesn't match the syntax is
- treated as a literal. We also recognize the POSIX constructions
- [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
- 5.6 and 5.8 do. */
-
- if (c == CHAR_LEFT_SQUARE_BRACKET &&
- (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT ||
- ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr))
- {
- BOOL local_negate = FALSE;
- int posix_class, taboffset, tabopt;
- register const uschar *cbits = cd->cbits;
- uschar pbits[32];
-
- if (ptr[1] != CHAR_COLON)
- {
- *errorcodeptr = ERR31;
- goto FAILED;
- }
-
- ptr += 2;
- if (*ptr == CHAR_CIRCUMFLEX_ACCENT)
- {
- local_negate = TRUE;
- should_flip_negation = TRUE; /* Note negative special */
- ptr++;
- }
-
- posix_class = check_posix_name(ptr, tempptr - ptr);
- if (posix_class < 0)
- {
- *errorcodeptr = ERR30;
- goto FAILED;
- }
-
- /* If matching is caseless, upper and lower are converted to
- alpha. This relies on the fact that the class table starts with
- alpha, lower, upper as the first 3 entries. */
-
- if ((options & PCRE_CASELESS) != 0 && posix_class <= 2)
- posix_class = 0;
-
- /* We build the bit map for the POSIX class in a chunk of local store
- because we may be adding and subtracting from it, and we don't want to
- subtract bits that may be in the main map already. At the end we or the
- result into the bit map that is being built. */
-
- posix_class *= 3;
-
- /* Copy in the first table (always present) */
-
- memcpy(pbits, cbits + posix_class_maps[posix_class],
- 32 * sizeof(uschar));
-
- /* If there is a second table, add or remove it as required. */
-
- taboffset = posix_class_maps[posix_class + 1];
- tabopt = posix_class_maps[posix_class + 2];
-
- if (taboffset >= 0)
- {
- if (tabopt >= 0)
- for (c = 0; c < 32; c++) pbits[c] |= cbits[c + taboffset];
- else
- for (c = 0; c < 32; c++) pbits[c] &= ~cbits[c + taboffset];
- }
-
- /* Not see if we need to remove any special characters. An option
- value of 1 removes vertical space and 2 removes underscore. */
-
- if (tabopt < 0) tabopt = -tabopt;
- if (tabopt == 1) pbits[1] &= ~0x3c;
- else if (tabopt == 2) pbits[11] &= 0x7f;
-
- /* Add the POSIX table or its complement into the main table that is
- being built and we are done. */
-
- if (local_negate)
- for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c];
- else
- for (c = 0; c < 32; c++) classbits[c] |= pbits[c];
-
- ptr = tempptr + 1;
- class_charcount = 10; /* Set > 1; assumes more than 1 per class */
- continue; /* End of POSIX syntax handling */
- }
-
- /* Backslash may introduce a single character, or it may introduce one
- of the specials, which just set a flag. The sequence \b is a special
- case. Inside a class (and only there) it is treated as backspace.
- Elsewhere it marks a word boundary. Other escapes have preset maps ready
- to 'or' into the one we are building. We assume they have more than one
- character in them, so set class_charcount bigger than one. */
-
- if (c == CHAR_BACKSLASH)
- {
- c = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE);
- if (*errorcodeptr != 0) goto FAILED;
-
- if (-c == ESC_b) c = CHAR_BS; /* \b is backspace in a class */
- else if (-c == ESC_X) c = CHAR_X; /* \X is literal X in a class */
- else if (-c == ESC_R) c = CHAR_R; /* \R is literal R in a class */
- else if (-c == ESC_Q) /* Handle start of quoted string */
- {
- if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E)
- {
- ptr += 2; /* avoid empty string */
- }
- else inescq = TRUE;
- continue;
- }
- else if (-c == ESC_E) continue; /* Ignore orphan \E */
-
- if (c < 0)
- {
- register const uschar *cbits = cd->cbits;
- class_charcount += 2; /* Greater than 1 is what matters */
-
- /* Save time by not doing this in the pre-compile phase. */
-
- if (lengthptr == NULL) switch (-c)
- {
- case ESC_d:
- for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit];
- continue;
-
- case ESC_D:
- should_flip_negation = TRUE;
- for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit];
- continue;
-
- case ESC_w:
- for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word];
- continue;
-
- case ESC_W:
- should_flip_negation = TRUE;
- for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word];
- continue;
-
- case ESC_s:
- for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space];
- classbits[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */
- continue;
-
- case ESC_S:
- should_flip_negation = TRUE;
- for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space];
- classbits[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */
- continue;
-
- default: /* Not recognized; fall through */
- break; /* Need "default" setting to stop compiler warning. */
- }
-
- /* In the pre-compile phase, just do the recognition. */
-
- else if (c == -ESC_d || c == -ESC_D || c == -ESC_w ||
- c == -ESC_W || c == -ESC_s || c == -ESC_S) continue;
-
- /* We need to deal with \H, \h, \V, and \v in both phases because
- they use extra memory. */
-
- if (-c == ESC_h)
- {
- SETBIT(classbits, 0x09); /* VT */
- SETBIT(classbits, 0x20); /* SPACE */
- SETBIT(classbits, 0xa0); /* NSBP */
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- class_utf8 = TRUE;
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(0x1680, class_utf8data);
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(0x180e, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x2000, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x200A, class_utf8data);
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(0x202f, class_utf8data);
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(0x205f, class_utf8data);
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(0x3000, class_utf8data);
- }
-#endif
- continue;
- }
-
- if (-c == ESC_H)
- {
- for (c = 0; c < 32; c++)
- {
- int x = 0xff;
- switch (c)
- {
- case 0x09/8: x ^= 1 << (0x09%8); break;
- case 0x20/8: x ^= 1 << (0x20%8); break;
- case 0xa0/8: x ^= 1 << (0xa0%8); break;
- default: break;
- }
- classbits[c] |= x;
- }
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- class_utf8 = TRUE;
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x0100, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x167f, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x1681, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x180d, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x180f, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x1fff, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x200B, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x202e, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x2030, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x205e, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x2060, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x2fff, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x3001, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x7fffffff, class_utf8data);
- }
-#endif
- continue;
- }
-
- if (-c == ESC_v)
- {
- SETBIT(classbits, 0x0a); /* LF */
- SETBIT(classbits, 0x0b); /* VT */
- SETBIT(classbits, 0x0c); /* FF */
- SETBIT(classbits, 0x0d); /* CR */
- SETBIT(classbits, 0x85); /* NEL */
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- class_utf8 = TRUE;
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x2028, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x2029, class_utf8data);
- }
-#endif
- continue;
- }
-
- if (-c == ESC_V)
- {
- for (c = 0; c < 32; c++)
- {
- int x = 0xff;
- switch (c)
- {
- case 0x0a/8: x ^= 1 << (0x0a%8);
- x ^= 1 << (0x0b%8);
- x ^= 1 << (0x0c%8);
- x ^= 1 << (0x0d%8);
- break;
- case 0x85/8: x ^= 1 << (0x85%8); break;
- default: break;
- }
- classbits[c] |= x;
- }
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- class_utf8 = TRUE;
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x0100, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x2027, class_utf8data);
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(0x2029, class_utf8data);
- class_utf8data += _pcre_ord2utf8(0x7fffffff, class_utf8data);
- }
-#endif
- continue;
- }
-
- /* We need to deal with \P and \p in both phases. */
-
-#ifdef SUPPORT_UCP
- if (-c == ESC_p || -c == ESC_P)
- {
- BOOL negated;
- int pdata;
- int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr);
- if (ptype < 0) goto FAILED;
- class_utf8 = TRUE;
- *class_utf8data++ = ((-c == ESC_p) != negated)?
- XCL_PROP : XCL_NOTPROP;
- *class_utf8data++ = ptype;
- *class_utf8data++ = pdata;
- class_charcount -= 2; /* Not a < 256 character */
- continue;
- }
-#endif
- /* Unrecognized escapes are faulted if PCRE is running in its
- strict mode. By default, for compatibility with Perl, they are
- treated as literals. */
-
- if ((options & PCRE_EXTRA) != 0)
- {
- *errorcodeptr = ERR7;
- goto FAILED;
- }
-
- class_charcount -= 2; /* Undo the default count from above */
- c = *ptr; /* Get the final character and fall through */
- }
-
- /* Fall through if we have a single character (c >= 0). This may be
- greater than 256 in UTF-8 mode. */
-
- } /* End of backslash handling */
-
- /* A single character may be followed by '-' to form a range. However,
- Perl does not permit ']' to be the end of the range. A '-' character
- at the end is treated as a literal. Perl ignores orphaned \E sequences
- entirely. The code for handling \Q and \E is messy. */
-
- CHECK_RANGE:
- while (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E)
- {
- inescq = FALSE;
- ptr += 2;
- }
-
- oldptr = ptr;
-
- /* Remember \r or \n */
-
- if (c == CHAR_CR || c == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF;
-
- /* Check for range */
-
- if (!inescq && ptr[1] == CHAR_MINUS)
- {
- int d;
- ptr += 2;
- while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) ptr += 2;
-
- /* If we hit \Q (not followed by \E) at this point, go into escaped
- mode. */
-
- while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q)
- {
- ptr += 2;
- if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E)
- { ptr += 2; continue; }
- inescq = TRUE;
- break;
- }
-
- if (*ptr == 0 || (!inescq && *ptr == CHAR_RIGHT_SQUARE_BRACKET))
- {
- ptr = oldptr;
- goto LONE_SINGLE_CHARACTER;
- }
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- { /* Braces are required because the */
- GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */
- }
- else
-#endif
- d = *ptr; /* Not UTF-8 mode */
-
- /* The second part of a range can be a single-character escape, but
- not any of the other escapes. Perl 5.6 treats a hyphen as a literal
- in such circumstances. */
-
- if (!inescq && d == CHAR_BACKSLASH)
- {
- d = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE);
- if (*errorcodeptr != 0) goto FAILED;
-
- /* \b is backspace; \X is literal X; \R is literal R; any other
- special means the '-' was literal */
-
- if (d < 0)
- {
- if (d == -ESC_b) d = CHAR_BS;
- else if (d == -ESC_X) d = CHAR_X;
- else if (d == -ESC_R) d = CHAR_R; else
- {
- ptr = oldptr;
- goto LONE_SINGLE_CHARACTER; /* A few lines below */
- }
- }
- }
-
- /* Check that the two values are in the correct order. Optimize
- one-character ranges */
-
- if (d < c)
- {
- *errorcodeptr = ERR8;
- goto FAILED;
- }
-
- if (d == c) goto LONE_SINGLE_CHARACTER; /* A few lines below */
-
- /* Remember \r or \n */
-
- if (d == CHAR_CR || d == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF;
-
- /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless
- matching, we have to use an XCLASS with extra data items. Caseless
- matching for characters > 127 is available only if UCP support is
- available. */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127)))
- {
- class_utf8 = TRUE;
-
- /* With UCP support, we can find the other case equivalents of
- the relevant characters. There may be several ranges. Optimize how
- they fit with the basic range. */
-
-#ifdef SUPPORT_UCP
- if ((options & PCRE_CASELESS) != 0)
- {
- unsigned int occ, ocd;
- unsigned int cc = c;
- unsigned int origd = d;
- while (get_othercase_range(&cc, origd, &occ, &ocd))
- {
- if (occ >= (unsigned int)c &&
- ocd <= (unsigned int)d)
- continue; /* Skip embedded ranges */
-
- if (occ < (unsigned int)c &&
- ocd >= (unsigned int)c - 1) /* Extend the basic range */
- { /* if there is overlap, */
- c = occ; /* noting that if occ < c */
- continue; /* we can't have ocd > d */
- } /* because a subrange is */
- if (ocd > (unsigned int)d &&
- occ <= (unsigned int)d + 1) /* always shorter than */
- { /* the basic range. */
- d = ocd;
- continue;
- }
-
- if (occ == ocd)
- {
- *class_utf8data++ = XCL_SINGLE;
- }
- else
- {
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(occ, class_utf8data);
- }
- class_utf8data += _pcre_ord2utf8(ocd, class_utf8data);
- }
- }
-#endif /* SUPPORT_UCP */
-
- /* Now record the original range, possibly modified for UCP caseless
- overlapping ranges. */
-
- *class_utf8data++ = XCL_RANGE;
- class_utf8data += _pcre_ord2utf8(c, class_utf8data);
- class_utf8data += _pcre_ord2utf8(d, class_utf8data);
-
- /* With UCP support, we are done. Without UCP support, there is no
- caseless matching for UTF-8 characters > 127; we can use the bit map
- for the smaller ones. */
-
-#ifdef SUPPORT_UCP
- continue; /* With next character in the class */
-#else
- if ((options & PCRE_CASELESS) == 0 || c > 127) continue;
-
- /* Adjust upper limit and fall through to set up the map */
-
- d = 127;
-
-#endif /* SUPPORT_UCP */
- }
-#endif /* SUPPORT_UTF8 */
-
- /* We use the bit map for all cases when not in UTF-8 mode; else
- ranges that lie entirely within 0-127 when there is UCP support; else
- for partial ranges without UCP support. */
-
- class_charcount += d - c + 1;
- class_lastchar = d;
-
- /* We can save a bit of time by skipping this in the pre-compile. */
-
- if (lengthptr == NULL) for (; c <= d; c++)
- {
- classbits[c/8] |= (1 << (c&7));
- if ((options & PCRE_CASELESS) != 0)
- {
- int uc = cd->fcc[c]; /* flip case */
- classbits[uc/8] |= (1 << (uc&7));
- }
- }
-
- continue; /* Go get the next char in the class */
- }
-
- /* Handle a lone single character - we can get here for a normal
- non-escape char, or after \ that introduces a single character or for an
- apparent range that isn't. */
-
- LONE_SINGLE_CHARACTER:
-
- /* Handle a character that cannot go in the bit map */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127)))
- {
- class_utf8 = TRUE;
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(c, class_utf8data);
-
-#ifdef SUPPORT_UCP
- if ((options & PCRE_CASELESS) != 0)
- {
- unsigned int othercase;
- if ((othercase = UCD_OTHERCASE(c)) != c)
- {
- *class_utf8data++ = XCL_SINGLE;
- class_utf8data += _pcre_ord2utf8(othercase, class_utf8data);
- }
- }
-#endif /* SUPPORT_UCP */
-
- }
- else
-#endif /* SUPPORT_UTF8 */
-
- /* Handle a single-byte character */
- {
- classbits[c/8] |= (1 << (c&7));
- if ((options & PCRE_CASELESS) != 0)
- {
- c = cd->fcc[c]; /* flip case */
- classbits[c/8] |= (1 << (c&7));
- }
- class_charcount++;
- class_lastchar = c;
- }
- }
-
- /* Loop until ']' reached. This "while" is the end of the "do" above. */
-
- while ((c = *(++ptr)) != 0 && (c != CHAR_RIGHT_SQUARE_BRACKET || inescq));
-
- if (c == 0) /* Missing terminating ']' */
- {
- *errorcodeptr = ERR6;
- goto FAILED;
- }
-
-
-/* This code has been disabled because it would mean that \s counts as
-an explicit \r or \n reference, and that's not really what is wanted. Now
-we set the flag only if there is a literal "\r" or "\n" in the class. */
-
-#if 0
- /* Remember whether \r or \n are in this class */
-
- if (negate_class)
- {
- if ((classbits[1] & 0x24) != 0x24) cd->external_flags |= PCRE_HASCRORLF;
- }
- else
- {
- if ((classbits[1] & 0x24) != 0) cd->external_flags |= PCRE_HASCRORLF;
- }
-#endif
-
-
- /* If class_charcount is 1, we saw precisely one character whose value is
- less than 256. As long as there were no characters >= 128 and there was no
- use of \p or \P, in other words, no use of any XCLASS features, we can
- optimize.
-
- In UTF-8 mode, we can optimize the negative case only if there were no
- characters >= 128 because OP_NOT and the related opcodes like OP_NOTSTAR
- operate on single-bytes only. This is an historical hangover. Maybe one day
- we can tidy these opcodes to handle multi-byte characters.
-
- The optimization throws away the bit map. We turn the item into a
- 1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note
- that OP_NOT does not support multibyte characters. In the positive case, it
- can cause firstbyte to be set. Otherwise, there can be no first char if
- this item is first, whatever repeat count may follow. In the case of
- reqbyte, save the previous value for reinstating. */
-
-#ifdef SUPPORT_UTF8
- if (class_charcount == 1 && !class_utf8 &&
- (!utf8 || !negate_class || class_lastchar < 128))
-#else
- if (class_charcount == 1)
-#endif
- {
- zeroreqbyte = reqbyte;
-
- /* The OP_NOT opcode works on one-byte characters only. */
-
- if (negate_class)
- {
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- zerofirstbyte = firstbyte;
- *code++ = OP_NOT;
- *code++ = class_lastchar;
- break;
- }
-
- /* For a single, positive character, get the value into mcbuffer, and
- then we can handle this with the normal one-character code. */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && class_lastchar > 127)
- mclength = _pcre_ord2utf8(class_lastchar, mcbuffer);
- else
-#endif
- {
- mcbuffer[0] = class_lastchar;
- mclength = 1;
- }
- goto ONE_CHAR;
- } /* End of 1-char optimization */
-
- /* The general case - not the one-char optimization. If this is the first
- thing in the branch, there can be no first char setting, whatever the
- repeat count. Any reqbyte setting must remain unchanged after any kind of
- repeat. */
-
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- zerofirstbyte = firstbyte;
- zeroreqbyte = reqbyte;
-
- /* If there are characters with values > 255, we have to compile an
- extended class, with its own opcode, unless there was a negated special
- such as \S in the class, because in that case all characters > 255 are in
- the class, so any that were explicitly given as well can be ignored. If
- (when there are explicit characters > 255 that must be listed) there are no
- characters < 256, we can omit the bitmap in the actual compiled code. */
-
-#ifdef SUPPORT_UTF8
- if (class_utf8 && !should_flip_negation)
- {
- *class_utf8data++ = XCL_END; /* Marks the end of extra data */
- *code++ = OP_XCLASS;
- code += LINK_SIZE;
- *code = negate_class? XCL_NOT : 0;
-
- /* If the map is required, move up the extra data to make room for it;
- otherwise just move the code pointer to the end of the extra data. */
-
- if (class_charcount > 0)
- {
- *code++ |= XCL_MAP;
- memmove(code + 32, code, class_utf8data - code);
- memcpy(code, classbits, 32);
- code = class_utf8data + 32;
- }
- else code = class_utf8data;
-
- /* Now fill in the complete length of the item */
-
- PUT(previous, 1, code - previous);
- break; /* End of class handling */
- }
-#endif
-
- /* If there are no characters > 255, set the opcode to OP_CLASS or
- OP_NCLASS, depending on whether the whole class was negated and whether
- there were negative specials such as \S in the class. Then copy the 32-byte
- map into the code vector, negating it if necessary. */
-
- *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS;
- if (negate_class)
- {
- if (lengthptr == NULL) /* Save time in the pre-compile phase */
- for (c = 0; c < 32; c++) code[c] = ~classbits[c];
- }
- else
- {
- memcpy(code, classbits, 32);
- }
- code += 32;
- break;
-
-
- /* ===================================================================*/
- /* Various kinds of repeat; '{' is not necessarily a quantifier, but this
- has been tested above. */
-
- case CHAR_LEFT_CURLY_BRACKET:
- if (!is_quantifier) goto NORMAL_CHAR;
- ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr);
- if (*errorcodeptr != 0) goto FAILED;
- goto REPEAT;
-
- case CHAR_ASTERISK:
- repeat_min = 0;
- repeat_max = -1;
- goto REPEAT;
-
- case CHAR_PLUS:
- repeat_min = 1;
- repeat_max = -1;
- goto REPEAT;
-
- case CHAR_QUESTION_MARK:
- repeat_min = 0;
- repeat_max = 1;
-
- REPEAT:
- if (previous == NULL)
- {
- *errorcodeptr = ERR9;
- goto FAILED;
- }
-
- if (repeat_min == 0)
- {
- firstbyte = zerofirstbyte; /* Adjust for zero repeat */
- reqbyte = zeroreqbyte; /* Ditto */
- }
-
- /* Remember whether this is a variable length repeat */
-
- reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;
-
- op_type = 0; /* Default single-char op codes */
- possessive_quantifier = FALSE; /* Default not possessive quantifier */
-
- /* Save start of previous item, in case we have to move it up to make space
- for an inserted OP_ONCE for the additional '+' extension. */
-
- tempcode = previous;
-
- /* If the next character is '+', we have a possessive quantifier. This
- implies greediness, whatever the setting of the PCRE_UNGREEDY option.
- If the next character is '?' this is a minimizing repeat, by default,
- but if PCRE_UNGREEDY is set, it works the other way round. We change the
- repeat type to the non-default. */
-
- if (ptr[1] == CHAR_PLUS)
- {
- repeat_type = 0; /* Force greedy */
- possessive_quantifier = TRUE;
- ptr++;
- }
- else if (ptr[1] == CHAR_QUESTION_MARK)
- {
- repeat_type = greedy_non_default;
- ptr++;
- }
- else repeat_type = greedy_default;
-
- /* If previous was a character match, abolish the item and generate a
- repeat item instead. If a char item has a minumum of more than one, ensure
- that it is set in reqbyte - it might not be if a sequence such as x{3} is
- the first thing in a branch because the x will have gone into firstbyte
- instead. */
-
- if (*previous == OP_CHAR || *previous == OP_CHARNC)
- {
- /* Deal with UTF-8 characters that take up more than one byte. It's
- easier to write this out separately than try to macrify it. Use c to
- hold the length of the character in bytes, plus 0x80 to flag that it's a
- length rather than a small character. */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && (code[-1] & 0x80) != 0)
- {
- uschar *lastchar = code - 1;
- while((*lastchar & 0xc0) == 0x80) lastchar--;
- c = code - lastchar; /* Length of UTF-8 character */
- memcpy(utf8_char, lastchar, c); /* Save the char */
- c |= 0x80; /* Flag c as a length */
- }
- else
-#endif
-
- /* Handle the case of a single byte - either with no UTF8 support, or
- with UTF-8 disabled, or for a UTF-8 character < 128. */
-
- {
- c = code[-1];
- if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt;
- }
-
- /* If the repetition is unlimited, it pays to see if the next thing on
- the line is something that cannot possibly match this character. If so,
- automatically possessifying this item gains some performance in the case
- where the match fails. */
-
- if (!possessive_quantifier &&
- repeat_max < 0 &&
- check_auto_possessive(*previous, c, utf8, utf8_char, ptr + 1,
- options, cd))
- {
- repeat_type = 0; /* Force greedy */
- possessive_quantifier = TRUE;
- }
-
- goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */
- }
-
- /* If previous was a single negated character ([^a] or similar), we use
- one of the special opcodes, replacing it. The code is shared with single-
- character repeats by setting opt_type to add a suitable offset into
- repeat_type. We can also test for auto-possessification. OP_NOT is
- currently used only for single-byte chars. */
-
- else if (*previous == OP_NOT)
- {
- op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */
- c = previous[1];
- if (!possessive_quantifier &&
- repeat_max < 0 &&
- check_auto_possessive(OP_NOT, c, utf8, NULL, ptr + 1, options, cd))
- {
- repeat_type = 0; /* Force greedy */
- possessive_quantifier = TRUE;
- }
- goto OUTPUT_SINGLE_REPEAT;
- }
-
- /* If previous was a character type match (\d or similar), abolish it and
- create a suitable repeat item. The code is shared with single-character
- repeats by setting op_type to add a suitable offset into repeat_type. Note
- the the Unicode property types will be present only when SUPPORT_UCP is
- defined, but we don't wrap the little bits of code here because it just
- makes it horribly messy. */
-
- else if (*previous < OP_EODN)
- {
- uschar *oldcode;
- int prop_type, prop_value;
- op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */
- c = *previous;
-
- if (!possessive_quantifier &&
- repeat_max < 0 &&
- check_auto_possessive(c, 0, utf8, NULL, ptr + 1, options, cd))
- {
- repeat_type = 0; /* Force greedy */
- possessive_quantifier = TRUE;
- }
-
- OUTPUT_SINGLE_REPEAT:
- if (*previous == OP_PROP || *previous == OP_NOTPROP)
- {
- prop_type = previous[1];
- prop_value = previous[2];
- }
- else prop_type = prop_value = -1;
-
- oldcode = code;
- code = previous; /* Usually overwrite previous item */
-
- /* If the maximum is zero then the minimum must also be zero; Perl allows
- this case, so we do too - by simply omitting the item altogether. */
-
- if (repeat_max == 0) goto END_REPEAT;
-
- /*--------------------------------------------------------------------*/
- /* This code is obsolete from release 8.00; the restriction was finally
- removed: */
-
- /* All real repeats make it impossible to handle partial matching (maybe
- one day we will be able to remove this restriction). */
-
- /* if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; */
- /*--------------------------------------------------------------------*/
-
- /* Combine the op_type with the repeat_type */
-
- repeat_type += op_type;
-
- /* A minimum of zero is handled either as the special case * or ?, or as
- an UPTO, with the maximum given. */
-
- if (repeat_min == 0)
- {
- if (repeat_max == -1) *code++ = OP_STAR + repeat_type;
- else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
- else
- {
- *code++ = OP_UPTO + repeat_type;
- PUT2INC(code, 0, repeat_max);
- }
- }
-
- /* A repeat minimum of 1 is optimized into some special cases. If the
- maximum is unlimited, we use OP_PLUS. Otherwise, the original item is
- left in place and, if the maximum is greater than 1, we use OP_UPTO with
- one less than the maximum. */
-
- else if (repeat_min == 1)
- {
- if (repeat_max == -1)
- *code++ = OP_PLUS + repeat_type;
- else
- {
- code = oldcode; /* leave previous item in place */
- if (repeat_max == 1) goto END_REPEAT;
- *code++ = OP_UPTO + repeat_type;
- PUT2INC(code, 0, repeat_max - 1);
- }
- }
-
- /* The case {n,n} is just an EXACT, while the general case {n,m} is
- handled as an EXACT followed by an UPTO. */
-
- else
- {
- *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */
- PUT2INC(code, 0, repeat_min);
-
- /* If the maximum is unlimited, insert an OP_STAR. Before doing so,
- we have to insert the character for the previous code. For a repeated
- Unicode property match, there are two extra bytes that define the
- required property. In UTF-8 mode, long characters have their length in
- c, with the 0x80 bit as a flag. */
-
- if (repeat_max < 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && c >= 128)
- {
- memcpy(code, utf8_char, c & 7);
- code += c & 7;
- }
- else
-#endif
- {
- *code++ = c;
- if (prop_type >= 0)
- {
- *code++ = prop_type;
- *code++ = prop_value;
- }
- }
- *code++ = OP_STAR + repeat_type;
- }
-
- /* Else insert an UPTO if the max is greater than the min, again
- preceded by the character, for the previously inserted code. If the
- UPTO is just for 1 instance, we can use QUERY instead. */
-
- else if (repeat_max != repeat_min)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && c >= 128)
- {
- memcpy(code, utf8_char, c & 7);
- code += c & 7;
- }
- else
-#endif
- *code++ = c;
- if (prop_type >= 0)
- {
- *code++ = prop_type;
- *code++ = prop_value;
- }
- repeat_max -= repeat_min;
-
- if (repeat_max == 1)
- {
- *code++ = OP_QUERY + repeat_type;
- }
- else
- {
- *code++ = OP_UPTO + repeat_type;
- PUT2INC(code, 0, repeat_max);
- }
- }
- }
-
- /* The character or character type itself comes last in all cases. */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && c >= 128)
- {
- memcpy(code, utf8_char, c & 7);
- code += c & 7;
- }
- else
-#endif
- *code++ = c;
-
- /* For a repeated Unicode property match, there are two extra bytes that
- define the required property. */
-
-#ifdef SUPPORT_UCP
- if (prop_type >= 0)
- {
- *code++ = prop_type;
- *code++ = prop_value;
- }
-#endif
- }
-
- /* If previous was a character class or a back reference, we put the repeat
- stuff after it, but just skip the item if the repeat was {0,0}. */
-
- else if (*previous == OP_CLASS ||
- *previous == OP_NCLASS ||
-#ifdef SUPPORT_UTF8
- *previous == OP_XCLASS ||
-#endif
- *previous == OP_REF)
- {
- if (repeat_max == 0)
- {
- code = previous;
- goto END_REPEAT;
- }
-
- /*--------------------------------------------------------------------*/
- /* This code is obsolete from release 8.00; the restriction was finally
- removed: */
-
- /* All real repeats make it impossible to handle partial matching (maybe
- one day we will be able to remove this restriction). */
-
- /* if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; */
- /*--------------------------------------------------------------------*/
-
- if (repeat_min == 0 && repeat_max == -1)
- *code++ = OP_CRSTAR + repeat_type;
- else if (repeat_min == 1 && repeat_max == -1)
- *code++ = OP_CRPLUS + repeat_type;
- else if (repeat_min == 0 && repeat_max == 1)
- *code++ = OP_CRQUERY + repeat_type;
- else
- {
- *code++ = OP_CRRANGE + repeat_type;
- PUT2INC(code, 0, repeat_min);
- if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */
- PUT2INC(code, 0, repeat_max);
- }
- }
-
- /* If previous was a bracket group, we may have to replicate it in certain
- cases. */
-
- else if (*previous == OP_BRA || *previous == OP_CBRA ||
- *previous == OP_ONCE || *previous == OP_COND)
- {
- register int i;
- int ketoffset = 0;
- int len = code - previous;
- uschar *bralink = NULL;
-
- /* Repeating a DEFINE group is pointless */
-
- if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_DEF)
- {
- *errorcodeptr = ERR55;
- goto FAILED;
- }
-
- /* If the maximum repeat count is unlimited, find the end of the bracket
- by scanning through from the start, and compute the offset back to it
- from the current code pointer. There may be an OP_OPT setting following
- the final KET, so we can't find the end just by going back from the code
- pointer. */
-
- if (repeat_max == -1)
- {
- register uschar *ket = previous;
- do ket += GET(ket, 1); while (*ket != OP_KET);
- ketoffset = code - ket;
- }
-
- /* The case of a zero minimum is special because of the need to stick
- OP_BRAZERO in front of it, and because the group appears once in the
- data, whereas in other cases it appears the minimum number of times. For
- this reason, it is simplest to treat this case separately, as otherwise
- the code gets far too messy. There are several special subcases when the
- minimum is zero. */
-
- if (repeat_min == 0)
- {
- /* If the maximum is also zero, we used to just omit the group from the
- output altogether, like this:
-
- ** if (repeat_max == 0)
- ** {
- ** code = previous;
- ** goto END_REPEAT;
- ** }
-
- However, that fails when a group is referenced as a subroutine from
- elsewhere in the pattern, so now we stick in OP_SKIPZERO in front of it
- so that it is skipped on execution. As we don't have a list of which
- groups are referenced, we cannot do this selectively.
-
- If the maximum is 1 or unlimited, we just have to stick in the BRAZERO
- and do no more at this point. However, we do need to adjust any
- OP_RECURSE calls inside the group that refer to the group itself or any
- internal or forward referenced group, because the offset is from the
- start of the whole regex. Temporarily terminate the pattern while doing
- this. */
-
- if (repeat_max <= 1) /* Covers 0, 1, and unlimited */
- {
- *code = OP_END;
- adjust_recurse(previous, 1, utf8, cd, save_hwm);
- memmove(previous+1, previous, len);
- code++;
- if (repeat_max == 0)
- {
- *previous++ = OP_SKIPZERO;
- goto END_REPEAT;
- }
- *previous++ = OP_BRAZERO + repeat_type;
- }
-
- /* If the maximum is greater than 1 and limited, we have to replicate
- in a nested fashion, sticking OP_BRAZERO before each set of brackets.
- The first one has to be handled carefully because it's the original
- copy, which has to be moved up. The remainder can be handled by code
- that is common with the non-zero minimum case below. We have to
- adjust the value or repeat_max, since one less copy is required. Once
- again, we may have to adjust any OP_RECURSE calls inside the group. */
-
- else
- {
- int offset;
- *code = OP_END;
- adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd, save_hwm);
- memmove(previous + 2 + LINK_SIZE, previous, len);
- code += 2 + LINK_SIZE;
- *previous++ = OP_BRAZERO + repeat_type;
- *previous++ = OP_BRA;
-
- /* We chain together the bracket offset fields that have to be
- filled in later when the ends of the brackets are reached. */
-
- offset = (bralink == NULL)? 0 : previous - bralink;
- bralink = previous;
- PUTINC(previous, 0, offset);
- }
-
- repeat_max--;
- }
-
- /* If the minimum is greater than zero, replicate the group as many
- times as necessary, and adjust the maximum to the number of subsequent
- copies that we need. If we set a first char from the group, and didn't
- set a required char, copy the latter from the former. If there are any
- forward reference subroutine calls in the group, there will be entries on
- the workspace list; replicate these with an appropriate increment. */
-
- else
- {
- if (repeat_min > 1)
- {
- /* In the pre-compile phase, we don't actually do the replication. We
- just adjust the length as if we had. Do some paranoid checks for
- potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
- integer type when available, otherwise double. */
-
- if (lengthptr != NULL)
- {
- int delta = (repeat_min - 1)*length_prevgroup;
- if ((INT64_OR_DOUBLE)(repeat_min - 1)*
- (INT64_OR_DOUBLE)length_prevgroup >
- (INT64_OR_DOUBLE)INT_MAX ||
- OFLOW_MAX - *lengthptr < delta)
- {
- *errorcodeptr = ERR20;
- goto FAILED;
- }
- *lengthptr += delta;
- }
-
- /* This is compiling for real */
-
- else
- {
- if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte;
- for (i = 1; i < repeat_min; i++)
- {
- uschar *hc;
- uschar *this_hwm = cd->hwm;
- memcpy(code, previous, len);
- for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE)
- {
- PUT(cd->hwm, 0, GET(hc, 0) + len);
- cd->hwm += LINK_SIZE;
- }
- save_hwm = this_hwm;
- code += len;
- }
- }
- }
-
- if (repeat_max > 0) repeat_max -= repeat_min;
- }
-
- /* This code is common to both the zero and non-zero minimum cases. If
- the maximum is limited, it replicates the group in a nested fashion,
- remembering the bracket starts on a stack. In the case of a zero minimum,
- the first one was set up above. In all cases the repeat_max now specifies
- the number of additional copies needed. Again, we must remember to
- replicate entries on the forward reference list. */
-
- if (repeat_max >= 0)
- {
- /* In the pre-compile phase, we don't actually do the replication. We
- just adjust the length as if we had. For each repetition we must add 1
- to the length for BRAZERO and for all but the last repetition we must
- add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some
- paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is
- a 64-bit integer type when available, otherwise double. */
-
- if (lengthptr != NULL && repeat_max > 0)
- {
- int delta = repeat_max * (length_prevgroup + 1 + 2 + 2*LINK_SIZE) -
- 2 - 2*LINK_SIZE; /* Last one doesn't nest */
- if ((INT64_OR_DOUBLE)repeat_max *
- (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE)
- > (INT64_OR_DOUBLE)INT_MAX ||
- OFLOW_MAX - *lengthptr < delta)
- {
- *errorcodeptr = ERR20;
- goto FAILED;
- }
- *lengthptr += delta;
- }
-
- /* This is compiling for real */
-
- else for (i = repeat_max - 1; i >= 0; i--)
- {
- uschar *hc;
- uschar *this_hwm = cd->hwm;
-
- *code++ = OP_BRAZERO + repeat_type;
-
- /* All but the final copy start a new nesting, maintaining the
- chain of brackets outstanding. */
-
- if (i != 0)
- {
- int offset;
- *code++ = OP_BRA;
- offset = (bralink == NULL)? 0 : code - bralink;
- bralink = code;
- PUTINC(code, 0, offset);
- }
-
- memcpy(code, previous, len);
- for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE)
- {
- PUT(cd->hwm, 0, GET(hc, 0) + len + ((i != 0)? 2+LINK_SIZE : 1));
- cd->hwm += LINK_SIZE;
- }
- save_hwm = this_hwm;
- code += len;
- }
-
- /* Now chain through the pending brackets, and fill in their length
- fields (which are holding the chain links pro tem). */
-
- while (bralink != NULL)
- {
- int oldlinkoffset;
- int offset = code - bralink + 1;
- uschar *bra = code - offset;
- oldlinkoffset = GET(bra, 1);
- bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
- *code++ = OP_KET;
- PUTINC(code, 0, offset);
- PUT(bra, 1, offset);
- }
- }
-
- /* If the maximum is unlimited, set a repeater in the final copy. We
- can't just offset backwards from the current code point, because we
- don't know if there's been an options resetting after the ket. The
- correct offset was computed above.
-
- Then, when we are doing the actual compile phase, check to see whether
- this group is a non-atomic one that could match an empty string. If so,
- convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so
- that runtime checking can be done. [This check is also applied to
- atomic groups at runtime, but in a different way.] */
-
- else
- {
- uschar *ketcode = code - ketoffset;
- uschar *bracode = ketcode - GET(ketcode, 1);
- *ketcode = OP_KETRMAX + repeat_type;
- if (lengthptr == NULL && *bracode != OP_ONCE)
- {
- uschar *scode = bracode;
- do
- {
- if (could_be_empty_branch(scode, ketcode, utf8, cd))
- {
- *bracode += OP_SBRA - OP_BRA;
- break;
- }
- scode += GET(scode, 1);
- }
- while (*scode == OP_ALT);
- }
- }
- }
-
- /* If previous is OP_FAIL, it was generated by an empty class [] in
- JavaScript mode. The other ways in which OP_FAIL can be generated, that is
- by (*FAIL) or (?!) set previous to NULL, which gives a "nothing to repeat"
- error above. We can just ignore the repeat in JS case. */
-
- else if (*previous == OP_FAIL) goto END_REPEAT;
-
- /* Else there's some kind of shambles */
-
- else
- {
- *errorcodeptr = ERR11;
- goto FAILED;
- }
-
- /* If the character following a repeat is '+', or if certain optimization
- tests above succeeded, possessive_quantifier is TRUE. For some of the
- simpler opcodes, there is an special alternative opcode for this. For
- anything else, we wrap the entire repeated item inside OP_ONCE brackets.
- The '+' notation is just syntactic sugar, taken from Sun's Java package,
- but the special opcodes can optimize it a bit. The repeated item starts at
- tempcode, not at previous, which might be the first part of a string whose
- (former) last char we repeated.
-
- Possessifying an 'exact' quantifier has no effect, so we can ignore it. But
- an 'upto' may follow. We skip over an 'exact' item, and then test the
- length of what remains before proceeding. */
-
- if (possessive_quantifier)
- {
- int len;
-
- if (*tempcode == OP_TYPEEXACT)
- tempcode += _pcre_OP_lengths[*tempcode] +
- ((tempcode[3] == OP_PROP || tempcode[3] == OP_NOTPROP)? 2 : 0);
-
- else if (*tempcode == OP_EXACT || *tempcode == OP_NOTEXACT)
- {
- tempcode += _pcre_OP_lengths[*tempcode];
-#ifdef SUPPORT_UTF8
- if (utf8 && tempcode[-1] >= 0xc0)
- tempcode += _pcre_utf8_table4[tempcode[-1] & 0x3f];
-#endif
- }
-
- len = code - tempcode;
- if (len > 0) switch (*tempcode)
- {
- case OP_STAR: *tempcode = OP_POSSTAR; break;
- case OP_PLUS: *tempcode = OP_POSPLUS; break;
- case OP_QUERY: *tempcode = OP_POSQUERY; break;
- case OP_UPTO: *tempcode = OP_POSUPTO; break;
-
- case OP_TYPESTAR: *tempcode = OP_TYPEPOSSTAR; break;
- case OP_TYPEPLUS: *tempcode = OP_TYPEPOSPLUS; break;
- case OP_TYPEQUERY: *tempcode = OP_TYPEPOSQUERY; break;
- case OP_TYPEUPTO: *tempcode = OP_TYPEPOSUPTO; break;
-
- case OP_NOTSTAR: *tempcode = OP_NOTPOSSTAR; break;
- case OP_NOTPLUS: *tempcode = OP_NOTPOSPLUS; break;
- case OP_NOTQUERY: *tempcode = OP_NOTPOSQUERY; break;
- case OP_NOTUPTO: *tempcode = OP_NOTPOSUPTO; break;
-
- /* Because we are moving code along, we must ensure that any
- pending recursive references are updated. */
-
- default:
- *code = OP_END;
- adjust_recurse(tempcode, 1 + LINK_SIZE, utf8, cd, save_hwm);
- memmove(tempcode + 1+LINK_SIZE, tempcode, len);
- code += 1 + LINK_SIZE;
- len += 1 + LINK_SIZE;
- tempcode[0] = OP_ONCE;
- *code++ = OP_KET;
- PUTINC(code, 0, len);
- PUT(tempcode, 1, len);
- break;
- }
- }
-
- /* In all case we no longer have a previous item. We also set the
- "follows varying string" flag for subsequently encountered reqbytes if
- it isn't already set and we have just passed a varying length item. */
-
- END_REPEAT:
- previous = NULL;
- cd->req_varyopt |= reqvary;
- break;
-
-
- /* ===================================================================*/
- /* Start of nested parenthesized sub-expression, or comment or lookahead or
- lookbehind or option setting or condition or all the other extended
- parenthesis forms. */
-
- case CHAR_LEFT_PARENTHESIS:
- newoptions = options;
- skipbytes = 0;
- bravalue = OP_CBRA;
- save_hwm = cd->hwm;
- reset_bracount = FALSE;
-
- /* First deal with various "verbs" that can be introduced by '*'. */
-
- if (*(++ptr) == CHAR_ASTERISK && (cd->ctypes[ptr[1]] & ctype_letter) != 0)
- {
- int i, namelen;
- const char *vn = verbnames;
- const uschar *name = ++ptr;
- previous = NULL;
- while ((cd->ctypes[*++ptr] & ctype_letter) != 0) {};
- if (*ptr == CHAR_COLON)
- {
- *errorcodeptr = ERR59; /* Not supported */
- goto FAILED;
- }
- if (*ptr != CHAR_RIGHT_PARENTHESIS)
- {
- *errorcodeptr = ERR60;
- goto FAILED;
- }
- namelen = ptr - name;
- for (i = 0; i < verbcount; i++)
- {
- if (namelen == verbs[i].len &&
- strncmp((char *)name, vn, namelen) == 0)
- {
- /* Check for open captures before ACCEPT */
-
- if (verbs[i].op == OP_ACCEPT)
- {
- open_capitem *oc;
- cd->had_accept = TRUE;
- for (oc = cd->open_caps; oc != NULL; oc = oc->next)
- {
- *code++ = OP_CLOSE;
- PUT2INC(code, 0, oc->number);
- }
- }
- *code++ = verbs[i].op;
- break;
- }
- vn += verbs[i].len + 1;
- }
- if (i < verbcount) continue;
- *errorcodeptr = ERR60;
- goto FAILED;
- }
-
- /* Deal with the extended parentheses; all are introduced by '?', and the
- appearance of any of them means that this is not a capturing group. */
-
- else if (*ptr == CHAR_QUESTION_MARK)
- {
- int i, set, unset, namelen;
- int *optset;
- const uschar *name;
- uschar *slot;
-
- switch (*(++ptr))
- {
- case CHAR_NUMBER_SIGN: /* Comment; skip to ket */
- ptr++;
- while (*ptr != 0 && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++;
- if (*ptr == 0)
- {
- *errorcodeptr = ERR18;
- goto FAILED;
- }
- continue;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */
- reset_bracount = TRUE;
- /* Fall through */
-
- /* ------------------------------------------------------------ */
- case CHAR_COLON: /* Non-capturing bracket */
- bravalue = OP_BRA;
- ptr++;
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_LEFT_PARENTHESIS:
- bravalue = OP_COND; /* Conditional group */
-
- /* A condition can be an assertion, a number (referring to a numbered
- group), a name (referring to a named group), or 'R', referring to
- recursion. R<digits> and R&name are also permitted for recursion tests.
-
- There are several syntaxes for testing a named group: (?(name)) is used
- by Python; Perl 5.10 onwards uses (?(<name>) or (?('name')).
-
- There are two unfortunate ambiguities, caused by history. (a) 'R' can
- be the recursive thing or the name 'R' (and similarly for 'R' followed
- by digits), and (b) a number could be a name that consists of digits.
- In both cases, we look for a name first; if not found, we try the other
- cases. */
-
- /* For conditions that are assertions, check the syntax, and then exit
- the switch. This will take control down to where bracketed groups,
- including assertions, are processed. */
-
- if (ptr[1] == CHAR_QUESTION_MARK && (ptr[2] == CHAR_EQUALS_SIGN ||
- ptr[2] == CHAR_EXCLAMATION_MARK || ptr[2] == CHAR_LESS_THAN_SIGN))
- break;
-
- /* Most other conditions use OP_CREF (a couple change to OP_RREF
- below), and all need to skip 3 bytes at the start of the group. */
-
- code[1+LINK_SIZE] = OP_CREF;
- skipbytes = 3;
- refsign = -1;
-
- /* Check for a test for recursion in a named group. */
-
- if (ptr[1] == CHAR_R && ptr[2] == CHAR_AMPERSAND)
- {
- terminator = -1;
- ptr += 2;
- code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */
- }
-
- /* Check for a test for a named group's having been set, using the Perl
- syntax (?(<name>) or (?('name') */
-
- else if (ptr[1] == CHAR_LESS_THAN_SIGN)
- {
- terminator = CHAR_GREATER_THAN_SIGN;
- ptr++;
- }
- else if (ptr[1] == CHAR_APOSTROPHE)
- {
- terminator = CHAR_APOSTROPHE;
- ptr++;
- }
- else
- {
- terminator = 0;
- if (ptr[1] == CHAR_MINUS || ptr[1] == CHAR_PLUS) refsign = *(++ptr);
- }
-
- /* We now expect to read a name; any thing else is an error */
-
- if ((cd->ctypes[ptr[1]] & ctype_word) == 0)
- {
- ptr += 1; /* To get the right offset */
- *errorcodeptr = ERR28;
- goto FAILED;
- }
-
- /* Read the name, but also get it as a number if it's all digits */
-
- recno = 0;
- name = ++ptr;
- while ((cd->ctypes[*ptr] & ctype_word) != 0)
- {
- if (recno >= 0)
- recno = (g_ascii_isdigit(*ptr) != 0)?
- recno * 10 + *ptr - CHAR_0 : -1;
- ptr++;
- }
- namelen = ptr - name;
-
- if ((terminator > 0 && *ptr++ != terminator) ||
- *ptr++ != CHAR_RIGHT_PARENTHESIS)
- {
- ptr--; /* Error offset */
- *errorcodeptr = ERR26;
- goto FAILED;
- }
-
- /* Do no further checking in the pre-compile phase. */
-
- if (lengthptr != NULL) break;
-
- /* In the real compile we do the work of looking for the actual
- reference. If the string started with "+" or "-" we require the rest to
- be digits, in which case recno will be set. */
-
- if (refsign > 0)
- {
- if (recno <= 0)
- {
- *errorcodeptr = ERR58;
- goto FAILED;
- }
- recno = (refsign == CHAR_MINUS)?
- cd->bracount - recno + 1 : recno +cd->bracount;
- if (recno <= 0 || recno > cd->final_bracount)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
- PUT2(code, 2+LINK_SIZE, recno);
- break;
- }
-
- /* Otherwise (did not start with "+" or "-"), start by looking for the
- name. If we find a name, add one to the opcode to change OP_CREF or
- OP_RREF into OP_NCREF or OP_NRREF. These behave exactly the same,
- except they record that the reference was originally to a name. The
- information is used to check duplicate names. */
-
- slot = cd->name_table;
- for (i = 0; i < cd->names_found; i++)
- {
- if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break;
- slot += cd->name_entry_size;
- }
-
- /* Found a previous named subpattern */
-
- if (i < cd->names_found)
- {
- recno = GET2(slot, 0);
- PUT2(code, 2+LINK_SIZE, recno);
- code[1+LINK_SIZE]++;
- }
-
- /* Search the pattern for a forward reference */
-
- else if ((i = find_parens(cd, name, namelen,
- (options & PCRE_EXTENDED) != 0)) > 0)
- {
- PUT2(code, 2+LINK_SIZE, i);
- code[1+LINK_SIZE]++;
- }
-
- /* If terminator == 0 it means that the name followed directly after
- the opening parenthesis [e.g. (?(abc)...] and in this case there are
- some further alternatives to try. For the cases where terminator != 0
- [things like (?(<name>... or (?('name')... or (?(R&name)... ] we have
- now checked all the possibilities, so give an error. */
-
- else if (terminator != 0)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
-
- /* Check for (?(R) for recursion. Allow digits after R to specify a
- specific group number. */
-
- else if (*name == CHAR_R)
- {
- recno = 0;
- for (i = 1; i < namelen; i++)
- {
- if (g_ascii_isdigit(name[i]) == 0)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
- recno = recno * 10 + name[i] - CHAR_0;
- }
- if (recno == 0) recno = RREF_ANY;
- code[1+LINK_SIZE] = OP_RREF; /* Change test type */
- PUT2(code, 2+LINK_SIZE, recno);
- }
-
- /* Similarly, check for the (?(DEFINE) "condition", which is always
- false. */
-
- else if (namelen == 6 && strncmp((char *)name, STRING_DEFINE, 6) == 0)
- {
- code[1+LINK_SIZE] = OP_DEF;
- skipbytes = 1;
- }
-
- /* Check for the "name" actually being a subpattern number. We are
- in the second pass here, so final_bracount is set. */
-
- else if (recno > 0 && recno <= cd->final_bracount)
- {
- PUT2(code, 2+LINK_SIZE, recno);
- }
-
- /* Either an unidentified subpattern, or a reference to (?(0) */
-
- else
- {
- *errorcodeptr = (recno == 0)? ERR35: ERR15;
- goto FAILED;
- }
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_EQUALS_SIGN: /* Positive lookahead */
- bravalue = OP_ASSERT;
- ptr++;
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_EXCLAMATION_MARK: /* Negative lookahead */
- ptr++;
- if (*ptr == CHAR_RIGHT_PARENTHESIS) /* Optimize (?!) */
- {
- *code++ = OP_FAIL;
- previous = NULL;
- continue;
- }
- bravalue = OP_ASSERT_NOT;
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_LESS_THAN_SIGN: /* Lookbehind or named define */
- switch (ptr[1])
- {
- case CHAR_EQUALS_SIGN: /* Positive lookbehind */
- bravalue = OP_ASSERTBACK;
- ptr += 2;
- break;
-
- case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */
- bravalue = OP_ASSERTBACK_NOT;
- ptr += 2;
- break;
-
- default: /* Could be name define, else bad */
- if ((cd->ctypes[ptr[1]] & ctype_word) != 0) goto DEFINE_NAME;
- ptr++; /* Correct offset for error */
- *errorcodeptr = ERR24;
- goto FAILED;
- }
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_GREATER_THAN_SIGN: /* One-time brackets */
- bravalue = OP_ONCE;
- ptr++;
- break;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_C: /* Callout - may be followed by digits; */
- previous_callout = code; /* Save for later completion */
- after_manual_callout = 1; /* Skip one item before completing */
- *code++ = OP_CALLOUT;
- {
- int n = 0;
- while (g_ascii_isdigit(*(++ptr)) != 0)
- n = n * 10 + *ptr - CHAR_0;
- if (*ptr != CHAR_RIGHT_PARENTHESIS)
- {
- *errorcodeptr = ERR39;
- goto FAILED;
- }
- if (n > 255)
- {
- *errorcodeptr = ERR38;
- goto FAILED;
- }
- *code++ = n;
- PUT(code, 0, ptr - cd->start_pattern + 1); /* Pattern offset */
- PUT(code, LINK_SIZE, 0); /* Default length */
- code += 2 * LINK_SIZE;
- }
- previous = NULL;
- continue;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_P: /* Python-style named subpattern handling */
- if (*(++ptr) == CHAR_EQUALS_SIGN ||
- *ptr == CHAR_GREATER_THAN_SIGN) /* Reference or recursion */
- {
- is_recurse = *ptr == CHAR_GREATER_THAN_SIGN;
- terminator = CHAR_RIGHT_PARENTHESIS;
- goto NAMED_REF_OR_RECURSE;
- }
- else if (*ptr != CHAR_LESS_THAN_SIGN) /* Test for Python-style defn */
- {
- *errorcodeptr = ERR41;
- goto FAILED;
- }
- /* Fall through to handle (?P< as (?< is handled */
-
-
- /* ------------------------------------------------------------ */
- DEFINE_NAME: /* Come here from (?< handling */
- case CHAR_APOSTROPHE:
- {
- terminator = (*ptr == CHAR_LESS_THAN_SIGN)?
- CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE;
- name = ++ptr;
-
- while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++;
- namelen = ptr - name;
-
- /* In the pre-compile phase, just do a syntax check. */
-
- if (lengthptr != NULL)
- {
- if (*ptr != terminator)
- {
- *errorcodeptr = ERR42;
- goto FAILED;
- }
- if (cd->names_found >= MAX_NAME_COUNT)
- {
- *errorcodeptr = ERR49;
- goto FAILED;
- }
- if (namelen + 3 > cd->name_entry_size)
- {
- cd->name_entry_size = namelen + 3;
- if (namelen > MAX_NAME_SIZE)
- {
- *errorcodeptr = ERR48;
- goto FAILED;
- }
- }
- }
-
- /* In the real compile, create the entry in the table, maintaining
- alphabetical order. Duplicate names for different numbers are
- permitted only if PCRE_DUPNAMES is set. Duplicate names for the same
- number are always OK. (An existing number can be re-used if (?|
- appears in the pattern.) In either event, a duplicate name results in
- a duplicate entry in the table, even if the number is the same. This
- is because the number of names, and hence the table size, is computed
- in the pre-compile, and it affects various numbers and pointers which
- would all have to be modified, and the compiled code moved down, if
- duplicates with the same number were omitted from the table. This
- doesn't seem worth the hassle. However, *different* names for the
- same number are not permitted. */
-
- else
- {
- BOOL dupname = FALSE;
- slot = cd->name_table;
-
- for (i = 0; i < cd->names_found; i++)
- {
- int crc = memcmp(name, slot+2, namelen);
- if (crc == 0)
- {
- if (slot[2+namelen] == 0)
- {
- if (GET2(slot, 0) != cd->bracount + 1 &&
- (options & PCRE_DUPNAMES) == 0)
- {
- *errorcodeptr = ERR43;
- goto FAILED;
- }
- else dupname = TRUE;
- }
- else crc = -1; /* Current name is a substring */
- }
-
- /* Make space in the table and break the loop for an earlier
- name. For a duplicate or later name, carry on. We do this for
- duplicates so that in the simple case (when ?(| is not used) they
- are in order of their numbers. */
-
- if (crc < 0)
- {
- memmove(slot + cd->name_entry_size, slot,
- (cd->names_found - i) * cd->name_entry_size);
- break;
- }
-
- /* Continue the loop for a later or duplicate name */
-
- slot += cd->name_entry_size;
- }
-
- /* For non-duplicate names, check for a duplicate number before
- adding the new name. */
-
- if (!dupname)
- {
- uschar *cslot = cd->name_table;
- for (i = 0; i < cd->names_found; i++)
- {
- if (cslot != slot)
- {
- if (GET2(cslot, 0) == cd->bracount + 1)
- {
- *errorcodeptr = ERR65;
- goto FAILED;
- }
- }
- else i--;
- cslot += cd->name_entry_size;
- }
- }
-
- PUT2(slot, 0, cd->bracount + 1);
- memcpy(slot + 2, name, namelen);
- slot[2+namelen] = 0;
- }
- }
-
- /* In both pre-compile and compile, count the number of names we've
- encountered. */
-
- cd->names_found++;
- ptr++; /* Move past > or ' */
- goto NUMBERED_GROUP;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */
- terminator = CHAR_RIGHT_PARENTHESIS;
- is_recurse = TRUE;
- /* Fall through */
-
- /* We come here from the Python syntax above that handles both
- references (?P=name) and recursion (?P>name), as well as falling
- through from the Perl recursion syntax (?&name). We also come here from
- the Perl \k<name> or \k'name' back reference syntax and the \k{name}
- .NET syntax, and the Oniguruma \g<...> and \g'...' subroutine syntax. */
-
- NAMED_REF_OR_RECURSE:
- name = ++ptr;
- while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++;
- namelen = ptr - name;
-
- /* In the pre-compile phase, do a syntax check and set a dummy
- reference number. */
-
- if (lengthptr != NULL)
- {
- if (namelen == 0)
- {
- *errorcodeptr = ERR62;
- goto FAILED;
- }
- if (*ptr != terminator)
- {
- *errorcodeptr = ERR42;
- goto FAILED;
- }
- if (namelen > MAX_NAME_SIZE)
- {
- *errorcodeptr = ERR48;
- goto FAILED;
- }
- recno = 0;
- }
-
- /* In the real compile, seek the name in the table. We check the name
- first, and then check that we have reached the end of the name in the
- table. That way, if the name that is longer than any in the table,
- the comparison will fail without reading beyond the table entry. */
-
- else
- {
- slot = cd->name_table;
- for (i = 0; i < cd->names_found; i++)
- {
- if (strncmp((char *)name, (char *)slot+2, namelen) == 0 &&
- slot[2+namelen] == 0)
- break;
- slot += cd->name_entry_size;
- }
-
- if (i < cd->names_found) /* Back reference */
- {
- recno = GET2(slot, 0);
- }
- else if ((recno = /* Forward back reference */
- find_parens(cd, name, namelen,
- (options & PCRE_EXTENDED) != 0)) <= 0)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
- }
-
- /* In both phases, we can now go to the code than handles numerical
- recursion or backreferences. */
-
- if (is_recurse) goto HANDLE_RECURSION;
- else goto HANDLE_REFERENCE;
-
-
- /* ------------------------------------------------------------ */
- case CHAR_R: /* Recursion */
- ptr++; /* Same as (?0) */
- /* Fall through */
-
-
- /* ------------------------------------------------------------ */
- case CHAR_MINUS: case CHAR_PLUS: /* Recursion or subroutine */
- case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4:
- case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
- {
- const uschar *called;
- terminator = CHAR_RIGHT_PARENTHESIS;
-
- /* Come here from the \g<...> and \g'...' code (Oniguruma
- compatibility). However, the syntax has been checked to ensure that
- the ... are a (signed) number, so that neither ERR63 nor ERR29 will
- be called on this path, nor with the jump to OTHER_CHAR_AFTER_QUERY
- ever be taken. */
-
- HANDLE_NUMERICAL_RECURSION:
-
- if ((refsign = *ptr) == CHAR_PLUS)
- {
- ptr++;
- if (g_ascii_isdigit(*ptr) == 0)
- {
- *errorcodeptr = ERR63;
- goto FAILED;
- }
- }
- else if (refsign == CHAR_MINUS)
- {
- if (g_ascii_isdigit(ptr[1]) == 0)
- goto OTHER_CHAR_AFTER_QUERY;
- ptr++;
- }
-
- recno = 0;
- while(g_ascii_isdigit(*ptr) != 0)
- recno = recno * 10 + *ptr++ - CHAR_0;
-
- if (*ptr != terminator)
- {
- *errorcodeptr = ERR29;
- goto FAILED;
- }
-
- if (refsign == CHAR_MINUS)
- {
- if (recno == 0)
- {
- *errorcodeptr = ERR58;
- goto FAILED;
- }
- recno = cd->bracount - recno + 1;
- if (recno <= 0)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
- }
- else if (refsign == CHAR_PLUS)
- {
- if (recno == 0)
- {
- *errorcodeptr = ERR58;
- goto FAILED;
- }
- recno += cd->bracount;
- }
-
- /* Come here from code above that handles a named recursion */
-
- HANDLE_RECURSION:
-
- previous = code;
- called = cd->start_code;
-
- /* When we are actually compiling, find the bracket that is being
- referenced. Temporarily end the regex in case it doesn't exist before
- this point. If we end up with a forward reference, first check that
- the bracket does occur later so we can give the error (and position)
- now. Then remember this forward reference in the workspace so it can
- be filled in at the end. */
-
- if (lengthptr == NULL)
- {
- *code = OP_END;
- if (recno != 0)
- called = _pcre_find_bracket(cd->start_code, utf8, recno);
-
- /* Forward reference */
-
- if (called == NULL)
- {
- if (find_parens(cd, NULL, recno,
- (options & PCRE_EXTENDED) != 0) < 0)
- {
- *errorcodeptr = ERR15;
- goto FAILED;
- }
-
- /* Fudge the value of "called" so that when it is inserted as an
- offset below, what it actually inserted is the reference number
- of the group. */
-
- called = cd->start_code + recno;
- PUTINC(cd->hwm, 0, code + 2 + LINK_SIZE - cd->start_code);
- }
-
- /* If not a forward reference, and the subpattern is still open,
- this is a recursive call. We check to see if this is a left
- recursion that could loop for ever, and diagnose that case. */
-
- else if (GET(called, 1) == 0 &&
- could_be_empty(called, code, bcptr, utf8, cd))
- {
- *errorcodeptr = ERR40;
- goto FAILED;
- }
- }
-
- /* Insert the recursion/subroutine item, automatically wrapped inside
- "once" brackets. Set up a "previous group" length so that a
- subsequent quantifier will work. */
-
- *code = OP_ONCE;
- PUT(code, 1, 2 + 2*LINK_SIZE);
- code += 1 + LINK_SIZE;
-
- *code = OP_RECURSE;
- PUT(code, 1, called - cd->start_code);
- code += 1 + LINK_SIZE;
-
- *code = OP_KET;
- PUT(code, 1, 2 + 2*LINK_SIZE);
- code += 1 + LINK_SIZE;
-
- length_prevgroup = 3 + 3*LINK_SIZE;
- }
-
- /* Can't determine a first byte now */
-
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- continue;
-
-
- /* ------------------------------------------------------------ */
- default: /* Other characters: check option setting */
- OTHER_CHAR_AFTER_QUERY:
- set = unset = 0;
- optset = &set;
-
- while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON)
- {
- switch (*ptr++)
- {
- case CHAR_MINUS: optset = &unset; break;
-
- case CHAR_J: /* Record that it changed in the external options */
- *optset |= PCRE_DUPNAMES;
- cd->external_flags |= PCRE_JCHANGED;
- break;
-
- case CHAR_i: *optset |= PCRE_CASELESS; break;
- case CHAR_m: *optset |= PCRE_MULTILINE; break;
- case CHAR_s: *optset |= PCRE_DOTALL; break;
- case CHAR_x: *optset |= PCRE_EXTENDED; break;
- case CHAR_U: *optset |= PCRE_UNGREEDY; break;
- case CHAR_X: *optset |= PCRE_EXTRA; break;
-
- default: *errorcodeptr = ERR12;
- ptr--; /* Correct the offset */
- goto FAILED;
- }
- }
-
- /* Set up the changed option bits, but don't change anything yet. */
-
- newoptions = (options | set) & (~unset);
-
- /* If the options ended with ')' this is not the start of a nested
- group with option changes, so the options change at this level. If this
- item is right at the start of the pattern, the options can be
- abstracted and made external in the pre-compile phase, and ignored in
- the compile phase. This can be helpful when matching -- for instance in
- caseless checking of required bytes.
-
- If the code pointer is not (cd->start_code + 1 + LINK_SIZE), we are
- definitely *not* at the start of the pattern because something has been
- compiled. In the pre-compile phase, however, the code pointer can have
- that value after the start, because it gets reset as code is discarded
- during the pre-compile. However, this can happen only at top level - if
- we are within parentheses, the starting BRA will still be present. At
- any parenthesis level, the length value can be used to test if anything
- has been compiled at that level. Thus, a test for both these conditions
- is necessary to ensure we correctly detect the start of the pattern in
- both phases.
-
- If we are not at the pattern start, compile code to change the ims
- options if this setting actually changes any of them, and reset the
- greedy defaults and the case value for firstbyte and reqbyte. */
-
- if (*ptr == CHAR_RIGHT_PARENTHESIS)
- {
- if (code == cd->start_code + 1 + LINK_SIZE &&
- (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE))
- {
- cd->external_options = newoptions;
- }
- else
- {
- if ((options & PCRE_IMS) != (newoptions & PCRE_IMS))
- {
- *code++ = OP_OPT;
- *code++ = newoptions & PCRE_IMS;
- }
- greedy_default = ((newoptions & PCRE_UNGREEDY) != 0);
- greedy_non_default = greedy_default ^ 1;
- req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
- }
-
- /* Change options at this level, and pass them back for use
- in subsequent branches. When not at the start of the pattern, this
- information is also necessary so that a resetting item can be
- compiled at the end of a group (if we are in a group). */
-
- *optionsptr = options = newoptions;
- previous = NULL; /* This item can't be repeated */
- continue; /* It is complete */
- }
-
- /* If the options ended with ':' we are heading into a nested group
- with possible change of options. Such groups are non-capturing and are
- not assertions of any kind. All we need to do is skip over the ':';
- the newoptions value is handled below. */
-
- bravalue = OP_BRA;
- ptr++;
- } /* End of switch for character following (? */
- } /* End of (? handling */
-
- /* Opening parenthesis not followed by '?'. If PCRE_NO_AUTO_CAPTURE is set,
- all unadorned brackets become non-capturing and behave like (?:...)
- brackets. */
-
- else if ((options & PCRE_NO_AUTO_CAPTURE) != 0)
- {
- bravalue = OP_BRA;
- }
-
- /* Else we have a capturing group. */
-
- else
- {
- NUMBERED_GROUP:
- cd->bracount += 1;
- PUT2(code, 1+LINK_SIZE, cd->bracount);
- skipbytes = 2;
- }
-
- /* Process nested bracketed regex. Assertions may not be repeated, but
- other kinds can be. All their opcodes are >= OP_ONCE. We copy code into a
- non-register variable in order to be able to pass its address because some
- compilers complain otherwise. Pass in a new setting for the ims options if
- they have changed. */
-
- previous = (bravalue >= OP_ONCE)? code : NULL;
- *code = bravalue;
- tempcode = code;
- tempreqvary = cd->req_varyopt; /* Save value before bracket */
- length_prevgroup = 0; /* Initialize for pre-compile phase */
-
- if (!compile_regex(
- newoptions, /* The complete new option state */
- options & PCRE_IMS, /* The previous ims option state */
- &tempcode, /* Where to put code (updated) */
- &ptr, /* Input pointer (updated) */
- errorcodeptr, /* Where to put an error message */
- (bravalue == OP_ASSERTBACK ||
- bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */
- reset_bracount, /* True if (?| group */
- skipbytes, /* Skip over bracket number */
- &subfirstbyte, /* For possible first char */
- &subreqbyte, /* For possible last char */
- bcptr, /* Current branch chain */
- cd, /* Tables block */
- (lengthptr == NULL)? NULL : /* Actual compile phase */
- &length_prevgroup /* Pre-compile phase */
- ))
- goto FAILED;
-
- /* At the end of compiling, code is still pointing to the start of the
- group, while tempcode has been updated to point past the end of the group
- and any option resetting that may follow it. The pattern pointer (ptr)
- is on the bracket. */
-
- /* If this is a conditional bracket, check that there are no more than
- two branches in the group, or just one if it's a DEFINE group. We do this
- in the real compile phase, not in the pre-pass, where the whole group may
- not be available. */
-
- if (bravalue == OP_COND && lengthptr == NULL)
- {
- uschar *tc = code;
- int condcount = 0;
-
- do {
- condcount++;
- tc += GET(tc,1);
- }
- while (*tc != OP_KET);
-
- /* A DEFINE group is never obeyed inline (the "condition" is always
- false). It must have only one branch. */
-
- if (code[LINK_SIZE+1] == OP_DEF)
- {
- if (condcount > 1)
- {
- *errorcodeptr = ERR54;
- goto FAILED;
- }
- bravalue = OP_DEF; /* Just a flag to suppress char handling below */
- }
-
- /* A "normal" conditional group. If there is just one branch, we must not
- make use of its firstbyte or reqbyte, because this is equivalent to an
- empty second branch. */
-
- else
- {
- if (condcount > 2)
- {
- *errorcodeptr = ERR27;
- goto FAILED;
- }
- if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE;
- }
- }
-
- /* Error if hit end of pattern */
-
- if (*ptr != CHAR_RIGHT_PARENTHESIS)
- {
- *errorcodeptr = ERR14;
- goto FAILED;
- }
-
- /* In the pre-compile phase, update the length by the length of the group,
- less the brackets at either end. Then reduce the compiled code to just a
- set of non-capturing brackets so that it doesn't use much memory if it is
- duplicated by a quantifier.*/
-
- if (lengthptr != NULL)
- {
- if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE)
- {
- *errorcodeptr = ERR20;
- goto FAILED;
- }
- *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE;
- *code++ = OP_BRA;
- PUTINC(code, 0, 1 + LINK_SIZE);
- *code++ = OP_KET;
- PUTINC(code, 0, 1 + LINK_SIZE);
- break; /* No need to waste time with special character handling */
- }
-
- /* Otherwise update the main code pointer to the end of the group. */
-
- code = tempcode;
-
- /* For a DEFINE group, required and first character settings are not
- relevant. */
-
- if (bravalue == OP_DEF) break;
-
- /* Handle updating of the required and first characters for other types of
- group. Update for normal brackets of all kinds, and conditions with two
- branches (see code above). If the bracket is followed by a quantifier with
- zero repeat, we have to back off. Hence the definition of zeroreqbyte and
- zerofirstbyte outside the main loop so that they can be accessed for the
- back off. */
-
- zeroreqbyte = reqbyte;
- zerofirstbyte = firstbyte;
- groupsetfirstbyte = FALSE;
-
- if (bravalue >= OP_ONCE)
- {
- /* If we have not yet set a firstbyte in this branch, take it from the
- subpattern, remembering that it was set here so that a repeat of more
- than one can replicate it as reqbyte if necessary. If the subpattern has
- no firstbyte, set "none" for the whole branch. In both cases, a zero
- repeat forces firstbyte to "none". */
-
- if (firstbyte == REQ_UNSET)
- {
- if (subfirstbyte >= 0)
- {
- firstbyte = subfirstbyte;
- groupsetfirstbyte = TRUE;
- }
- else firstbyte = REQ_NONE;
- zerofirstbyte = REQ_NONE;
- }
-
- /* If firstbyte was previously set, convert the subpattern's firstbyte
- into reqbyte if there wasn't one, using the vary flag that was in
- existence beforehand. */
-
- else if (subfirstbyte >= 0 && subreqbyte < 0)
- subreqbyte = subfirstbyte | tempreqvary;
-
- /* If the subpattern set a required byte (or set a first byte that isn't
- really the first byte - see above), set it. */
-
- if (subreqbyte >= 0) reqbyte = subreqbyte;
- }
-
- /* For a forward assertion, we take the reqbyte, if set. This can be
- helpful if the pattern that follows the assertion doesn't set a different
- char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte
- for an assertion, however because it leads to incorrect effect for patterns
- such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead
- of a firstbyte. This is overcome by a scan at the end if there's no
- firstbyte, looking for an asserted first char. */
-
- else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte;
- break; /* End of processing '(' */
-
-
- /* ===================================================================*/
- /* Handle metasequences introduced by \. For ones like \d, the ESC_ values
- are arranged to be the negation of the corresponding OP_values. For the
- back references, the values are ESC_REF plus the reference number. Only
- back references and those types that consume a character may be repeated.
- We can test for values between ESC_b and ESC_Z for the latter; this may
- have to change if any new ones are ever created. */
-
- case CHAR_BACKSLASH:
- tempptr = ptr;
- c = check_escape(&ptr, errorcodeptr, cd->bracount, options, FALSE);
- if (*errorcodeptr != 0) goto FAILED;
-
- if (c < 0)
- {
- if (-c == ESC_Q) /* Handle start of quoted string */
- {
- if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E)
- ptr += 2; /* avoid empty string */
- else inescq = TRUE;
- continue;
- }
-
- if (-c == ESC_E) continue; /* Perl ignores an orphan \E */
-
- /* For metasequences that actually match a character, we disable the
- setting of a first character if it hasn't already been set. */
-
- if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z)
- firstbyte = REQ_NONE;
-
- /* Set values to reset to if this is followed by a zero repeat. */
-
- zerofirstbyte = firstbyte;
- zeroreqbyte = reqbyte;
-
- /* \g<name> or \g'name' is a subroutine call by name and \g<n> or \g'n'
- is a subroutine call by number (Oniguruma syntax). In fact, the value
- -ESC_g is returned only for these cases. So we don't need to check for <
- or ' if the value is -ESC_g. For the Perl syntax \g{n} the value is
- -ESC_REF+n, and for the Perl syntax \g{name} the result is -ESC_k (as
- that is a synonym for a named back reference). */
-
- if (-c == ESC_g)
- {
- const uschar *p;
- save_hwm = cd->hwm; /* Normally this is set when '(' is read */
- terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)?
- CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE;
-
- /* These two statements stop the compiler for warning about possibly
- unset variables caused by the jump to HANDLE_NUMERICAL_RECURSION. In
- fact, because we actually check for a number below, the paths that
- would actually be in error are never taken. */
-
- skipbytes = 0;
- reset_bracount = FALSE;
-
- /* Test for a name */
-
- if (ptr[1] != CHAR_PLUS && ptr[1] != CHAR_MINUS)
- {
- BOOL isnumber = TRUE;
- for (p = ptr + 1; *p != 0 && *p != terminator; p++)
- {
- if ((cd->ctypes[*p] & ctype_digit) == 0) isnumber = FALSE;
- if ((cd->ctypes[*p] & ctype_word) == 0) break;
- }
- if (*p != terminator)
- {
- *errorcodeptr = ERR57;
- break;
- }
- if (isnumber)
- {
- ptr++;
- goto HANDLE_NUMERICAL_RECURSION;
- }
- is_recurse = TRUE;
- goto NAMED_REF_OR_RECURSE;
- }
-
- /* Test a signed number in angle brackets or quotes. */
-
- p = ptr + 2;
- while (g_ascii_isdigit(*p) != 0) p++;
- if (*p != terminator)
- {
- *errorcodeptr = ERR57;
- break;
- }
- ptr++;
- goto HANDLE_NUMERICAL_RECURSION;
- }
-
- /* \k<name> or \k'name' is a back reference by name (Perl syntax).
- We also support \k{name} (.NET syntax) */
-
- if (-c == ESC_k && (ptr[1] == CHAR_LESS_THAN_SIGN ||
- ptr[1] == CHAR_APOSTROPHE || ptr[1] == CHAR_LEFT_CURLY_BRACKET))
- {
- is_recurse = FALSE;
- terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)?
- CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)?
- CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET;
- goto NAMED_REF_OR_RECURSE;
- }
-
- /* Back references are handled specially; must disable firstbyte if
- not set to cope with cases like (?=(\w+))\1: which would otherwise set
- ':' later. */
-
- if (-c >= ESC_REF)
- {
- open_capitem *oc;
- recno = -c - ESC_REF;
-
- HANDLE_REFERENCE: /* Come here from named backref handling */
- if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
- previous = code;
- *code++ = OP_REF;
- PUT2INC(code, 0, recno);
- cd->backref_map |= (recno < 32)? (1 << recno) : 1;
- if (recno > cd->top_backref) cd->top_backref = recno;
-
- /* Check to see if this back reference is recursive, that it, it
- is inside the group that it references. A flag is set so that the
- group can be made atomic. */
-
- for (oc = cd->open_caps; oc != NULL; oc = oc->next)
- {
- if (oc->number == recno)
- {
- oc->flag = TRUE;
- break;
- }
- }
- }
-
- /* So are Unicode property matches, if supported. */
-
-#ifdef SUPPORT_UCP
- else if (-c == ESC_P || -c == ESC_p)
- {
- BOOL negated;
- int pdata;
- int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr);
- if (ptype < 0) goto FAILED;
- previous = code;
- *code++ = ((-c == ESC_p) != negated)? OP_PROP : OP_NOTPROP;
- *code++ = ptype;
- *code++ = pdata;
- }
-#else
-
- /* If Unicode properties are not supported, \X, \P, and \p are not
- allowed. */
-
- else if (-c == ESC_X || -c == ESC_P || -c == ESC_p)
- {
- *errorcodeptr = ERR45;
- goto FAILED;
- }
-#endif
-
- /* For the rest (including \X when Unicode properties are supported), we
- can obtain the OP value by negating the escape value. */
-
- else
- {
- previous = (-c > ESC_b && -c < ESC_Z)? code : NULL;
- *code++ = -c;
- }
- continue;
- }
-
- /* We have a data character whose value is in c. In UTF-8 mode it may have
- a value > 127. We set its representation in the length/buffer, and then
- handle it as a data character. */
-
-#ifdef SUPPORT_UTF8
- if (utf8 && c > 127)
- mclength = _pcre_ord2utf8(c, mcbuffer);
- else
-#endif
-
- {
- mcbuffer[0] = c;
- mclength = 1;
- }
- goto ONE_CHAR;
-
-
- /* ===================================================================*/
- /* Handle a literal character. It is guaranteed not to be whitespace or #
- when the extended flag is set. If we are in UTF-8 mode, it may be a
- multi-byte literal character. */
-
- default:
- NORMAL_CHAR:
- mclength = 1;
- mcbuffer[0] = c;
-
-#ifdef SUPPORT_UTF8
- if (utf8 && c >= 0xc0)
- {
- while ((ptr[1] & 0xc0) == 0x80)
- mcbuffer[mclength++] = *(++ptr);
- }
-#endif
-
- /* At this point we have the character's bytes in mcbuffer, and the length
- in mclength. When not in UTF-8 mode, the length is always 1. */
-
- ONE_CHAR:
- previous = code;
- *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARNC : OP_CHAR;
- for (c = 0; c < mclength; c++) *code++ = mcbuffer[c];
-
- /* Remember if \r or \n were seen */
-
- if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL)
- cd->external_flags |= PCRE_HASCRORLF;
-
- /* Set the first and required bytes appropriately. If no previous first
- byte, set it from this character, but revert to none on a zero repeat.
- Otherwise, leave the firstbyte value alone, and don't change it on a zero
- repeat. */
-
- if (firstbyte == REQ_UNSET)
- {
- zerofirstbyte = REQ_NONE;
- zeroreqbyte = reqbyte;
-
- /* If the character is more than one byte long, we can set firstbyte
- only if it is not to be matched caselessly. */
-
- if (mclength == 1 || req_caseopt == 0)
- {
- firstbyte = mcbuffer[0] | req_caseopt;
- if (mclength != 1) reqbyte = code[-1] | cd->req_varyopt;
- }
- else firstbyte = reqbyte = REQ_NONE;
- }
-
- /* firstbyte was previously set; we can set reqbyte only the length is
- 1 or the matching is caseful. */
-
- else
- {
- zerofirstbyte = firstbyte;
- zeroreqbyte = reqbyte;
- if (mclength == 1 || req_caseopt == 0)
- reqbyte = code[-1] | req_caseopt | cd->req_varyopt;
- }
-
- break; /* End of literal character handling */
- }
- } /* end of big loop */
-
-
-/* Control never reaches here by falling through, only by a goto for all the
-error states. Pass back the position in the pattern so that it can be displayed
-to the user for diagnosing the error. */
-
-FAILED:
-*ptrptr = ptr;
-return FALSE;
-}
-
-
-
-
-/*************************************************
-* Compile sequence of alternatives *
-*************************************************/
-
-/* On entry, ptr is pointing past the bracket character, but on return it
-points to the closing bracket, or vertical bar, or end of string. The code
-variable is pointing at the byte into which the BRA operator has been stored.
-If the ims options are changed at the start (for a (?ims: group) or during any
-branch, we need to insert an OP_OPT item at the start of every following branch
-to ensure they get set correctly at run time, and also pass the new options
-into every subsequent branch compile.
-
-This function is used during the pre-compile phase when we are trying to find
-out the amount of memory needed, as well as during the real compile phase. The
-value of lengthptr distinguishes the two phases.
-
-Arguments:
- options option bits, including any changes for this subpattern
- oldims previous settings of ims option bits
- codeptr -> the address of the current code pointer
- ptrptr -> the address of the current pattern pointer
- errorcodeptr -> pointer to error code variable
- lookbehind TRUE if this is a lookbehind assertion
- reset_bracount TRUE to reset the count for each branch
- skipbytes skip this many bytes at start (for brackets and OP_COND)
- firstbyteptr place to put the first required character, or a negative number
- reqbyteptr place to put the last required character, or a negative number
- bcptr pointer to the chain of currently open branches
- cd points to the data block with tables pointers etc.
- lengthptr NULL during the real compile phase
- points to length accumulator during pre-compile phase
-
-Returns: TRUE on success
-*/
-
-static BOOL
-compile_regex(int options, int oldims, uschar **codeptr, const uschar **ptrptr,
- int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, int skipbytes,
- int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd,
- int *lengthptr)
-{
-const uschar *ptr = *ptrptr;
-uschar *code = *codeptr;
-uschar *last_branch = code;
-uschar *start_bracket = code;
-uschar *reverse_count = NULL;
-open_capitem capitem;
-int capnumber = 0;
-int firstbyte, reqbyte;
-int branchfirstbyte, branchreqbyte;
-int length;
-int orig_bracount;
-int max_bracount;
-int old_external_options = cd->external_options;
-branch_chain bc;
-
-bc.outer = bcptr;
-bc.current_branch = code;
-
-firstbyte = reqbyte = REQ_UNSET;
-
-/* Accumulate the length for use in the pre-compile phase. Start with the
-length of the BRA and KET and any extra bytes that are required at the
-beginning. We accumulate in a local variable to save frequent testing of
-lenthptr for NULL. We cannot do this by looking at the value of code at the
-start and end of each alternative, because compiled items are discarded during
-the pre-compile phase so that the work space is not exceeded. */
-
-length = 2 + 2*LINK_SIZE + skipbytes;
-
-/* WARNING: If the above line is changed for any reason, you must also change
-the code that abstracts option settings at the start of the pattern and makes
-them global. It tests the value of length for (2 + 2*LINK_SIZE) in the
-pre-compile phase to find out whether anything has yet been compiled or not. */
-
-/* If this is a capturing subpattern, add to the chain of open capturing items
-so that we can detect them if (*ACCEPT) is encountered. This is also used to
-detect groups that contain recursive back references to themselves. */
-
-if (*code == OP_CBRA)
- {
- capnumber = GET2(code, 1 + LINK_SIZE);
- capitem.number = capnumber;
- capitem.next = cd->open_caps;
- capitem.flag = FALSE;
- cd->open_caps = &capitem;
- }
-
-/* Offset is set zero to mark that this bracket is still open */
-
-PUT(code, 1, 0);
-code += 1 + LINK_SIZE + skipbytes;
-
-/* Loop for each alternative branch */
-
-orig_bracount = max_bracount = cd->bracount;
-for (;;)
- {
- /* For a (?| group, reset the capturing bracket count so that each branch
- uses the same numbers. */
-
- if (reset_bracount) cd->bracount = orig_bracount;
-
- /* Handle a change of ims options at the start of the branch */
-
- if ((options & PCRE_IMS) != oldims)
- {
- *code++ = OP_OPT;
- *code++ = options & PCRE_IMS;
- length += 2;
- }
-
- /* Set up dummy OP_REVERSE if lookbehind assertion */
-
- if (lookbehind)
- {
- *code++ = OP_REVERSE;
- reverse_count = code;
- PUTINC(code, 0, 0);
- length += 1 + LINK_SIZE;
- }
-
- /* Now compile the branch; in the pre-compile phase its length gets added
- into the length. */
-
- if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstbyte,
- &branchreqbyte, &bc, cd, (lengthptr == NULL)? NULL : &length))
- {
- *ptrptr = ptr;
- return FALSE;
- }
-
- /* If the external options have changed during this branch, it means that we
- are at the top level, and a leading option setting has been encountered. We
- need to re-set the original option values to take account of this so that,
- during the pre-compile phase, we know to allow for a re-set at the start of
- subsequent branches. */
-
- if (old_external_options != cd->external_options)
- oldims = cd->external_options & PCRE_IMS;
-
- /* Keep the highest bracket count in case (?| was used and some branch
- has fewer than the rest. */
-
- if (cd->bracount > max_bracount) max_bracount = cd->bracount;
-
- /* In the real compile phase, there is some post-processing to be done. */
-
- if (lengthptr == NULL)
- {
- /* If this is the first branch, the firstbyte and reqbyte values for the
- branch become the values for the regex. */
-
- if (*last_branch != OP_ALT)
- {
- firstbyte = branchfirstbyte;
- reqbyte = branchreqbyte;
- }
-
- /* If this is not the first branch, the first char and reqbyte have to
- match the values from all the previous branches, except that if the
- previous value for reqbyte didn't have REQ_VARY set, it can still match,
- and we set REQ_VARY for the regex. */
-
- else
- {
- /* If we previously had a firstbyte, but it doesn't match the new branch,
- we have to abandon the firstbyte for the regex, but if there was
- previously no reqbyte, it takes on the value of the old firstbyte. */
-
- if (firstbyte >= 0 && firstbyte != branchfirstbyte)
- {
- if (reqbyte < 0) reqbyte = firstbyte;
- firstbyte = REQ_NONE;
- }
-
- /* If we (now or from before) have no firstbyte, a firstbyte from the
- branch becomes a reqbyte if there isn't a branch reqbyte. */
-
- if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0)
- branchreqbyte = branchfirstbyte;
-
- /* Now ensure that the reqbytes match */
-
- if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY))
- reqbyte = REQ_NONE;
- else reqbyte |= branchreqbyte; /* To "or" REQ_VARY */
- }
-
- /* If lookbehind, check that this branch matches a fixed-length string, and
- put the length into the OP_REVERSE item. Temporarily mark the end of the
- branch with OP_END. If the branch contains OP_RECURSE, the result is -3
- because there may be forward references that we can't check here. Set a
- flag to cause another lookbehind check at the end. Why not do it all at the
- end? Because common, erroneous checks are picked up here and the offset of
- the problem can be shown. */
-
- if (lookbehind)
- {
- int fixed_length;
- *code = OP_END;
- fixed_length = find_fixedlength(last_branch, options, FALSE, cd);
- DPRINTF(("fixed length = %d\n", fixed_length));
- if (fixed_length == -3)
- {
- cd->check_lookbehind = TRUE;
- }
- else if (fixed_length < 0)
- {
- *errorcodeptr = (fixed_length == -2)? ERR36 : ERR25;
- *ptrptr = ptr;
- return FALSE;
- }
- else { PUT(reverse_count, 0, fixed_length); }
- }
- }
-
- /* Reached end of expression, either ')' or end of pattern. In the real
- compile phase, go back through the alternative branches and reverse the chain
- of offsets, with the field in the BRA item now becoming an offset to the
- first alternative. If there are no alternatives, it points to the end of the
- group. The length in the terminating ket is always the length of the whole
- bracketed item. If any of the ims options were changed inside the group,
- compile a resetting op-code following, except at the very end of the pattern.
- Return leaving the pointer at the terminating char. */
-
- if (*ptr != CHAR_VERTICAL_LINE)
- {
- if (lengthptr == NULL)
- {
- int branch_length = code - last_branch;
- do
- {
- int prev_length = GET(last_branch, 1);
- PUT(last_branch, 1, branch_length);
- branch_length = prev_length;
- last_branch -= branch_length;
- }
- while (branch_length > 0);
- }
-
- /* Fill in the ket */
-
- *code = OP_KET;
- PUT(code, 1, code - start_bracket);
- code += 1 + LINK_SIZE;
-
- /* If it was a capturing subpattern, check to see if it contained any
- recursive back references. If so, we must wrap it in atomic brackets.
- In any event, remove the block from the chain. */
-
- if (capnumber > 0)
- {
- if (cd->open_caps->flag)
- {
- memmove(start_bracket + 1 + LINK_SIZE, start_bracket,
- code - start_bracket);
- *start_bracket = OP_ONCE;
- code += 1 + LINK_SIZE;
- PUT(start_bracket, 1, code - start_bracket);
- *code = OP_KET;
- PUT(code, 1, code - start_bracket);
- code += 1 + LINK_SIZE;
- length += 2 + 2*LINK_SIZE;
- }
- cd->open_caps = cd->open_caps->next;
- }
-
- /* Reset options if needed. */
-
- if ((options & PCRE_IMS) != oldims && *ptr == CHAR_RIGHT_PARENTHESIS)
- {
- *code++ = OP_OPT;
- *code++ = oldims;
- length += 2;
- }
-
- /* Retain the highest bracket number, in case resetting was used. */
-
- cd->bracount = max_bracount;
-
- /* Set values to pass back */
-
- *codeptr = code;
- *ptrptr = ptr;
- *firstbyteptr = firstbyte;
- *reqbyteptr = reqbyte;
- if (lengthptr != NULL)
- {
- if (OFLOW_MAX - *lengthptr < length)
- {
- *errorcodeptr = ERR20;
- return FALSE;
- }
- *lengthptr += length;
- }
- return TRUE;
- }
-
- /* Another branch follows. In the pre-compile phase, we can move the code
- pointer back to where it was for the start of the first branch. (That is,
- pretend that each branch is the only one.)
-
- In the real compile phase, insert an ALT node. Its length field points back
- to the previous branch while the bracket remains open. At the end the chain
- is reversed. It's done like this so that the start of the bracket has a
- zero offset until it is closed, making it possible to detect recursion. */
-
- if (lengthptr != NULL)
- {
- code = *codeptr + 1 + LINK_SIZE + skipbytes;
- length += 1 + LINK_SIZE;
- }
- else
- {
- *code = OP_ALT;
- PUT(code, 1, code - last_branch);
- bc.current_branch = last_branch = code;
- code += 1 + LINK_SIZE;
- }
-
- ptr++;
- }
-/* Control never reaches here */
-}
-
-
-
-
-/*************************************************
-* Check for anchored expression *
-*************************************************/
-
-/* Try to find out if this is an anchored regular expression. Consider each
-alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
-all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
-it's anchored. However, if this is a multiline pattern, then only OP_SOD
-counts, since OP_CIRC can match in the middle.
-
-We can also consider a regex to be anchored if OP_SOM starts all its branches.
-This is the code for \G, which means "match at start of match position, taking
-into account the match offset".
-
-A branch is also implicitly anchored if it starts with .* and DOTALL is set,
-because that will try the rest of the pattern at all possible matching points,
-so there is no point trying again.... er ....
-
-.... except when the .* appears inside capturing parentheses, and there is a
-subsequent back reference to those parentheses. We haven't enough information
-to catch that case precisely.
-
-At first, the best we could do was to detect when .* was in capturing brackets
-and the highest back reference was greater than or equal to that level.
-However, by keeping a bitmap of the first 31 back references, we can catch some
-of the more common cases more precisely.
-
-Arguments:
- code points to start of expression (the bracket)
- options points to the options setting
- bracket_map a bitmap of which brackets we are inside while testing; this
- handles up to substring 31; after that we just have to take
- the less precise approach
- backref_map the back reference bitmap
-
-Returns: TRUE or FALSE
-*/
-
-static BOOL
-is_anchored(register const uschar *code, int *options, unsigned int bracket_map,
- unsigned int backref_map)
-{
-do {
- const uschar *scode = first_significant_code(code + _pcre_OP_lengths[*code],
- options, PCRE_MULTILINE, FALSE);
- register int op = *scode;
-
- /* Non-capturing brackets */
-
- if (op == OP_BRA)
- {
- if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE;
- }
-
- /* Capturing brackets */
-
- else if (op == OP_CBRA)
- {
- int n = GET2(scode, 1+LINK_SIZE);
- int new_map = bracket_map | ((n < 32)? (1 << n) : 1);
- if (!is_anchored(scode, options, new_map, backref_map)) return FALSE;
- }
-
- /* Other brackets */
-
- else if (op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
- {
- if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE;
- }
-
- /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and
- it isn't in brackets that are or may be referenced. */
-
- else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR ||
- op == OP_TYPEPOSSTAR))
- {
- if (scode[1] != OP_ALLANY || (bracket_map & backref_map) != 0)
- return FALSE;
- }
-
- /* Check for explicit anchoring */
-
- else if (op != OP_SOD && op != OP_SOM &&
- ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC))
- return FALSE;
- code += GET(code, 1);
- }
-while (*code == OP_ALT); /* Loop for each alternative */
-return TRUE;
-}
-
-
-
-/*************************************************
-* Check for starting with ^ or .* *
-*************************************************/
-
-/* This is called to find out if every branch starts with ^ or .* so that
-"first char" processing can be done to speed things up in multiline
-matching and for non-DOTALL patterns that start with .* (which must start at
-the beginning or after \n). As in the case of is_anchored() (see above), we
-have to take account of back references to capturing brackets that contain .*
-because in that case we can't make the assumption.
-
-Arguments:
- code points to start of expression (the bracket)
- bracket_map a bitmap of which brackets we are inside while testing; this
- handles up to substring 31; after that we just have to take
- the less precise approach
- backref_map the back reference bitmap
-
-Returns: TRUE or FALSE
-*/
-
-static BOOL
-is_startline(const uschar *code, unsigned int bracket_map,
- unsigned int backref_map)
-{
-do {
- const uschar *scode = first_significant_code(code + _pcre_OP_lengths[*code],
- NULL, 0, FALSE);
- register int op = *scode;
-
- /* If we are at the start of a conditional assertion group, *both* the
- conditional assertion *and* what follows the condition must satisfy the test
- for start of line. Other kinds of condition fail. Note that there may be an
- auto-callout at the start of a condition. */
-
- if (op == OP_COND)
- {
- scode += 1 + LINK_SIZE;
- if (*scode == OP_CALLOUT) scode += _pcre_OP_lengths[OP_CALLOUT];
- switch (*scode)
- {
- case OP_CREF:
- case OP_NCREF:
- case OP_RREF:
- case OP_NRREF:
- case OP_DEF:
- return FALSE;
-
- default: /* Assertion */
- if (!is_startline(scode, bracket_map, backref_map)) return FALSE;
- do scode += GET(scode, 1); while (*scode == OP_ALT);
- scode += 1 + LINK_SIZE;
- break;
- }
- scode = first_significant_code(scode, NULL, 0, FALSE);
- op = *scode;
- }
-
- /* Non-capturing brackets */
-
- if (op == OP_BRA)
- {
- if (!is_startline(scode, bracket_map, backref_map)) return FALSE;
- }
-
- /* Capturing brackets */
-
- else if (op == OP_CBRA)
- {
- int n = GET2(scode, 1+LINK_SIZE);
- int new_map = bracket_map | ((n < 32)? (1 << n) : 1);
- if (!is_startline(scode, new_map, backref_map)) return FALSE;
- }
-
- /* Other brackets */
-
- else if (op == OP_ASSERT || op == OP_ONCE)
- {
- if (!is_startline(scode, bracket_map, backref_map)) return FALSE;
- }
-
- /* .* means "start at start or after \n" if it isn't in brackets that
- may be referenced. */
-
- else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)
- {
- if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE;
- }
-
- /* Check for explicit circumflex */
-
- else if (op != OP_CIRC) return FALSE;
-
- /* Move on to the next alternative */
-
- code += GET(code, 1);
- }
-while (*code == OP_ALT); /* Loop for each alternative */
-return TRUE;
-}
-
-
-
-/*************************************************
-* Check for asserted fixed first char *
-*************************************************/
-
-/* During compilation, the "first char" settings from forward assertions are
-discarded, because they can cause conflicts with actual literals that follow.
-However, if we end up without a first char setting for an unanchored pattern,
-it is worth scanning the regex to see if there is an initial asserted first
-char. If all branches start with the same asserted char, or with a bracket all
-of whose alternatives start with the same asserted char (recurse ad lib), then
-we return that char, otherwise -1.
-
-Arguments:
- code points to start of expression (the bracket)
- options pointer to the options (used to check casing changes)
- inassert TRUE if in an assertion
-
-Returns: -1 or the fixed first char
-*/
-
-static int
-find_firstassertedchar(const uschar *code, int *options, BOOL inassert)
-{
-register int c = -1;
-do {
- int d;
- const uschar *scode =
- first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS, TRUE);
- register int op = *scode;
-
- switch(op)
- {
- default:
- return -1;
-
- case OP_BRA:
- case OP_CBRA:
- case OP_ASSERT:
- case OP_ONCE:
- case OP_COND:
- if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0)
- return -1;
- if (c < 0) c = d; else if (c != d) return -1;
- break;
-
- case OP_EXACT: /* Fall through */
- scode += 2;
-
- case OP_CHAR:
- case OP_CHARNC:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- if (!inassert) return -1;
- if (c < 0)
- {
- c = scode[1];
- if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS;
- }
- else if (c != scode[1]) return -1;
- break;
- }
-
- code += GET(code, 1);
- }
-while (*code == OP_ALT);
-return c;
-}
-
-
-
-/*************************************************
-* Compile a Regular Expression *
-*************************************************/
-
-/* This function takes a string and returns a pointer to a block of store
-holding a compiled version of the expression. The original API for this
-function had no error code return variable; it is retained for backwards
-compatibility. The new function is given a new name.
-
-Arguments:
- pattern the regular expression
- options various option bits
- errorcodeptr pointer to error code variable (pcre_compile2() only)
- can be NULL if you don't want a code value
- errorptr pointer to pointer to error text
- erroroffset ptr offset in pattern where error was detected
- tables pointer to character tables or NULL
-
-Returns: pointer to compiled data block, or NULL on error,
- with errorptr and erroroffset set
-*/
-
-#ifdef NOT_USED_IN_GLIB
-
-PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION
-pcre_compile(const char *pattern, int options, const char **errorptr,
- int *erroroffset, const unsigned char *tables)
-{
-return pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables);
-}
-
-#endif
-
-PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION
-pcre_compile2(const char *pattern, int options, int *errorcodeptr,
- const char **errorptr, int *erroroffset, const unsigned char *tables)
-{
-real_pcre *re;
-int length = 1; /* For final END opcode */
-int firstbyte, reqbyte, newline;
-int errorcode = 0;
-int skipatstart = 0;
-BOOL utf8 = (options & PCRE_UTF8) != 0;
-size_t size;
-uschar *code;
-const uschar *codestart;
-const uschar *ptr;
-compile_data compile_block;
-compile_data *cd = &compile_block;
-
-/* This space is used for "compiling" into during the first phase, when we are
-computing the amount of memory that is needed. Compiled items are thrown away
-as soon as possible, so that a fairly large buffer should be sufficient for
-this purpose. The same space is used in the second phase for remembering where
-to fill in forward references to subpatterns. */
-
-uschar cworkspace[COMPILE_WORK_SIZE];
-
-/* Set this early so that early errors get offset 0. */
-
-ptr = (const uschar *)pattern;
-
-/* We can't pass back an error message if errorptr is NULL; I guess the best we
-can do is just return NULL, but we can set a code value if there is a code
-pointer. */
-
-if (errorptr == NULL)
- {
- if (errorcodeptr != NULL) *errorcodeptr = 99;
- return NULL;
- }
-
-*errorptr = NULL;
-if (errorcodeptr != NULL) *errorcodeptr = ERR0;
-
-/* However, we can give a message for this error */
-
-if (erroroffset == NULL)
- {
- errorcode = ERR16;
- goto PCRE_EARLY_ERROR_RETURN2;
- }
-
-*erroroffset = 0;
-
-/* Set up pointers to the individual character tables */
-
-if (tables == NULL) tables = _pcre_default_tables;
-cd->lcc = tables + lcc_offset;
-cd->fcc = tables + fcc_offset;
-cd->cbits = tables + cbits_offset;
-cd->ctypes = tables + ctypes_offset;
-
-/* Check that all undefined public option bits are zero */
-
-if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0)
- {
- errorcode = ERR17;
- goto PCRE_EARLY_ERROR_RETURN;
- }
-
-/* Check for global one-time settings at the start of the pattern, and remember
-the offset for later. */
-
-while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&
- ptr[skipatstart+1] == CHAR_ASTERISK)
- {
- int newnl = 0;
- int newbsr = 0;
-
- if (strncmp((char *)(ptr+skipatstart+2), STRING_UTF8_RIGHTPAR, 5) == 0)
- { skipatstart += 7; options |= PCRE_UTF8; continue; }
-
- if (strncmp((char *)(ptr+skipatstart+2), STRING_CR_RIGHTPAR, 3) == 0)
- { skipatstart += 5; newnl = PCRE_NEWLINE_CR; }
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_LF_RIGHTPAR, 3) == 0)
- { skipatstart += 5; newnl = PCRE_NEWLINE_LF; }
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_CRLF_RIGHTPAR, 5) == 0)
- { skipatstart += 7; newnl = PCRE_NEWLINE_CR + PCRE_NEWLINE_LF; }
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_ANY_RIGHTPAR, 4) == 0)
- { skipatstart += 6; newnl = PCRE_NEWLINE_ANY; }
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_ANYCRLF_RIGHTPAR, 8) == 0)
- { skipatstart += 10; newnl = PCRE_NEWLINE_ANYCRLF; }
-
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_BSR_ANYCRLF_RIGHTPAR, 12) == 0)
- { skipatstart += 14; newbsr = PCRE_BSR_ANYCRLF; }
- else if (strncmp((char *)(ptr+skipatstart+2), STRING_BSR_UNICODE_RIGHTPAR, 12) == 0)
- { skipatstart += 14; newbsr = PCRE_BSR_UNICODE; }
-
- if (newnl != 0)
- options = (options & ~PCRE_NEWLINE_BITS) | newnl;
- else if (newbsr != 0)
- options = (options & ~(PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) | newbsr;
- else break;
- }
-
-/* Can't support UTF8 unless PCRE has been compiled to include the code. */
-
-#ifdef SUPPORT_UTF8
-if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 &&
- (*erroroffset = _pcre_valid_utf8((USPTR)pattern, -1)) >= 0)
- {
- errorcode = ERR44;
- goto PCRE_EARLY_ERROR_RETURN2;
- }
-#else
-if (utf8)
- {
- errorcode = ERR32;
- goto PCRE_EARLY_ERROR_RETURN;
- }
-#endif
-
-/* Check validity of \R options. */
-
-switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE))
- {
- case 0:
- case PCRE_BSR_ANYCRLF:
- case PCRE_BSR_UNICODE:
- break;
- default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN;
- }
-
-/* Handle different types of newline. The three bits give seven cases. The
-current code allows for fixed one- or two-byte sequences, plus "any" and
-"anycrlf". */
-
-switch (options & PCRE_NEWLINE_BITS)
- {
- case 0: newline = NEWLINE; break; /* Build-time default */
- case PCRE_NEWLINE_CR: newline = CHAR_CR; break;
- case PCRE_NEWLINE_LF: newline = CHAR_NL; break;
- case PCRE_NEWLINE_CR+
- PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break;
- case PCRE_NEWLINE_ANY: newline = -1; break;
- case PCRE_NEWLINE_ANYCRLF: newline = -2; break;
- default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN;
- }
-
-if (newline == -2)
- {
- cd->nltype = NLTYPE_ANYCRLF;
- }
-else if (newline < 0)
- {
- cd->nltype = NLTYPE_ANY;
- }
-else
- {
- cd->nltype = NLTYPE_FIXED;
- if (newline > 255)
- {
- cd->nllen = 2;
- cd->nl[0] = (newline >> 8) & 255;
- cd->nl[1] = newline & 255;
- }
- else
- {
- cd->nllen = 1;
- cd->nl[0] = newline;
- }
- }
-
-/* Maximum back reference and backref bitmap. The bitmap records up to 31 back
-references to help in deciding whether (.*) can be treated as anchored or not.
-*/
-
-cd->top_backref = 0;
-cd->backref_map = 0;
-
-/* Reflect pattern for debugging output */
-
-DPRINTF(("------------------------------------------------------------------\n"));
-DPRINTF(("%s\n", pattern));
-
-/* Pretend to compile the pattern while actually just accumulating the length
-of memory required. This behaviour is triggered by passing a non-NULL final
-argument to compile_regex(). We pass a block of workspace (cworkspace) for it
-to compile parts of the pattern into; the compiled code is discarded when it is
-no longer needed, so hopefully this workspace will never overflow, though there
-is a test for its doing so. */
-
-cd->bracount = cd->final_bracount = 0;
-cd->names_found = 0;
-cd->name_entry_size = 0;
-cd->name_table = NULL;
-cd->start_workspace = cworkspace;
-cd->start_code = cworkspace;
-cd->hwm = cworkspace;
-cd->start_pattern = (const uschar *)pattern;
-cd->end_pattern = (const uschar *)(pattern + strlen(pattern));
-cd->req_varyopt = 0;
-cd->external_options = options;
-cd->external_flags = 0;
-cd->open_caps = NULL;
-
-/* Now do the pre-compile. On error, errorcode will be set non-zero, so we
-don't need to look at the result of the function here. The initial options have
-been put into the cd block so that they can be changed if an option setting is
-found within the regex right at the beginning. Bringing initial option settings
-outside can help speed up starting point checks. */
-
-ptr += skipatstart;
-code = cworkspace;
-*code = OP_BRA;
-(void)compile_regex(cd->external_options, cd->external_options & PCRE_IMS,
- &code, &ptr, &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd,
- &length);
-if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN;
-
-DPRINTF(("end pre-compile: length=%d workspace=%d\n", length,
- cd->hwm - cworkspace));
-
-if (length > MAX_PATTERN_SIZE)
- {
- errorcode = ERR20;
- goto PCRE_EARLY_ERROR_RETURN;
- }
-
-/* Compute the size of data block needed and get it, either from malloc or
-externally provided function. Integer overflow should no longer be possible
-because nowadays we limit the maximum value of cd->names_found and
-cd->name_entry_size. */
-
-size = length + sizeof(real_pcre) + cd->names_found * (cd->name_entry_size + 3);
-re = (real_pcre *)(pcre_malloc)(size);
-
-if (re == NULL)
- {
- errorcode = ERR21;
- goto PCRE_EARLY_ERROR_RETURN;
- }
-
-/* Put in the magic number, and save the sizes, initial options, internal
-flags, and character table pointer. NULL is used for the default character
-tables. The nullpad field is at the end; it's there to help in the case when a
-regex compiled on a system with 4-byte pointers is run on another with 8-byte
-pointers. */
-
-re->magic_number = MAGIC_NUMBER;
-re->size = size;
-re->options = cd->external_options;
-re->flags = cd->external_flags;
-re->dummy1 = 0;
-re->first_byte = 0;
-re->req_byte = 0;
-re->name_table_offset = sizeof(real_pcre);
-re->name_entry_size = cd->name_entry_size;
-re->name_count = cd->names_found;
-re->ref_count = 0;
-re->tables = (tables == _pcre_default_tables)? NULL : tables;
-re->nullpad = NULL;
-
-/* The starting points of the name/number translation table and of the code are
-passed around in the compile data block. The start/end pattern and initial
-options are already set from the pre-compile phase, as is the name_entry_size
-field. Reset the bracket count and the names_found field. Also reset the hwm
-field; this time it's used for remembering forward references to subpatterns.
-*/
-
-cd->final_bracount = cd->bracount; /* Save for checking forward references */
-cd->bracount = 0;
-cd->names_found = 0;
-cd->name_table = (uschar *)re + re->name_table_offset;
-codestart = cd->name_table + re->name_entry_size * re->name_count;
-cd->start_code = codestart;
-cd->hwm = cworkspace;
-cd->req_varyopt = 0;
-cd->had_accept = FALSE;
-cd->check_lookbehind = FALSE;
-cd->open_caps = NULL;
-
-/* Set up a starting, non-extracting bracket, then compile the expression. On
-error, errorcode will be set non-zero, so we don't need to look at the result
-of the function here. */
-
-ptr = (const uschar *)pattern + skipatstart;
-code = (uschar *)codestart;
-*code = OP_BRA;
-(void)compile_regex(re->options, re->options & PCRE_IMS, &code, &ptr,
- &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd, NULL);
-re->top_bracket = cd->bracount;
-re->top_backref = cd->top_backref;
-re->flags = cd->external_flags;
-
-if (cd->had_accept) reqbyte = -1; /* Must disable after (*ACCEPT) */
-
-/* If not reached end of pattern on success, there's an excess bracket. */
-
-if (errorcode == 0 && *ptr != 0) errorcode = ERR22;
-
-/* Fill in the terminating state and check for disastrous overflow, but
-if debugging, leave the test till after things are printed out. */
-
-*code++ = OP_END;
-
-#ifndef PCRE_DEBUG
-if (code - codestart > length) errorcode = ERR23;
-#endif
-
-/* Fill in any forward references that are required. */
-
-while (errorcode == 0 && cd->hwm > cworkspace)
- {
- int offset, recno;
- const uschar *groupptr;
- cd->hwm -= LINK_SIZE;
- offset = GET(cd->hwm, 0);
- recno = GET(codestart, offset);
- groupptr = _pcre_find_bracket(codestart, utf8, recno);
- if (groupptr == NULL) errorcode = ERR53;
- else PUT(((uschar *)codestart), offset, groupptr - codestart);
- }
-
-/* Give an error if there's back reference to a non-existent capturing
-subpattern. */
-
-if (errorcode == 0 && re->top_backref > re->top_bracket) errorcode = ERR15;
-
-/* If there were any lookbehind assertions that contained OP_RECURSE
-(recursions or subroutine calls), a flag is set for them to be checked here,
-because they may contain forward references. Actual recursions can't be fixed
-length, but subroutine calls can. It is done like this so that those without
-OP_RECURSE that are not fixed length get a diagnosic with a useful offset. The
-exceptional ones forgo this. We scan the pattern to check that they are fixed
-length, and set their lengths. */
-
-if (cd->check_lookbehind)
- {
- uschar *cc = (uschar *)codestart;
-
- /* Loop, searching for OP_REVERSE items, and process those that do not have
- their length set. (Actually, it will also re-process any that have a length
- of zero, but that is a pathological case, and it does no harm.) When we find
- one, we temporarily terminate the branch it is in while we scan it. */
-
- for (cc = (uschar *)_pcre_find_bracket(codestart, utf8, -1);
- cc != NULL;
- cc = (uschar *)_pcre_find_bracket(cc, utf8, -1))
- {
- if (GET(cc, 1) == 0)
- {
- int fixed_length;
- uschar *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE);
- int end_op = *be;
- *be = OP_END;
- fixed_length = find_fixedlength(cc, re->options, TRUE, cd);
- *be = end_op;
- DPRINTF(("fixed length = %d\n", fixed_length));
- if (fixed_length < 0)
- {
- errorcode = (fixed_length == -2)? ERR36 : ERR25;
- break;
- }
- PUT(cc, 1, fixed_length);
- }
- cc += 1 + LINK_SIZE;
- }
- }
-
-/* Failed to compile, or error while post-processing */
-
-if (errorcode != 0)
- {
- (pcre_free)(re);
- PCRE_EARLY_ERROR_RETURN:
- *erroroffset = ptr - (const uschar *)pattern;
- PCRE_EARLY_ERROR_RETURN2:
- *errorptr = find_error_text(errorcode);
- if (errorcodeptr != NULL) *errorcodeptr = errorcode;
- return NULL;
- }
-
-/* If the anchored option was not passed, set the flag if we can determine that
-the pattern is anchored by virtue of ^ characters or \A or anything else (such
-as starting with .* when DOTALL is set).
-
-Otherwise, if we know what the first byte has to be, save it, because that
-speeds up unanchored matches no end. If not, see if we can set the
-PCRE_STARTLINE flag. This is helpful for multiline matches when all branches
-start with ^. and also when all branches start with .* for non-DOTALL matches.
-*/
-
-if ((re->options & PCRE_ANCHORED) == 0)
- {
- int temp_options = re->options; /* May get changed during these scans */
- if (is_anchored(codestart, &temp_options, 0, cd->backref_map))
- re->options |= PCRE_ANCHORED;
- else
- {
- if (firstbyte < 0)
- firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE);
- if (firstbyte >= 0) /* Remove caseless flag for non-caseable chars */
- {
- int ch = firstbyte & 255;
- re->first_byte = ((firstbyte & REQ_CASELESS) != 0 &&
- cd->fcc[ch] == ch)? ch : firstbyte;
- re->flags |= PCRE_FIRSTSET;
- }
- else if (is_startline(codestart, 0, cd->backref_map))
- re->flags |= PCRE_STARTLINE;
- }
- }
-
-/* For an anchored pattern, we use the "required byte" only if it follows a
-variable length item in the regex. Remove the caseless flag for non-caseable
-bytes. */
-
-if (reqbyte >= 0 &&
- ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0))
- {
- int ch = reqbyte & 255;
- re->req_byte = ((reqbyte & REQ_CASELESS) != 0 &&
- cd->fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte;
- re->flags |= PCRE_REQCHSET;
- }
-
-/* Print out the compiled data if debugging is enabled. This is never the
-case when building a production library. */
-
-#ifdef PCRE_DEBUG
-printf("Length = %d top_bracket = %d top_backref = %d\n",
- length, re->top_bracket, re->top_backref);
-
-printf("Options=%08x\n", re->options);
-
-if ((re->flags & PCRE_FIRSTSET) != 0)
- {
- int ch = re->first_byte & 255;
- const char *caseless = ((re->first_byte & REQ_CASELESS) == 0)?
- "" : " (caseless)";
- if (isprint(ch)) printf("First char = %c%s\n", ch, caseless);
- else printf("First char = \\x%02x%s\n", ch, caseless);
- }
-
-if ((re->flags & PCRE_REQCHSET) != 0)
- {
- int ch = re->req_byte & 255;
- const char *caseless = ((re->req_byte & REQ_CASELESS) == 0)?
- "" : " (caseless)";
- if (isprint(ch)) printf("Req char = %c%s\n", ch, caseless);
- else printf("Req char = \\x%02x%s\n", ch, caseless);
- }
-
-pcre_printint(re, stdout, TRUE);
-
-/* This check is done here in the debugging case so that the code that
-was compiled can be seen. */
-
-if (code - codestart > length)
- {
- (pcre_free)(re);
- *errorptr = find_error_text(ERR23);
- *erroroffset = ptr - (uschar *)pattern;
- if (errorcodeptr != NULL) *errorcodeptr = ERR23;
- return NULL;
- }
-#endif /* PCRE_DEBUG */
-
-return (pcre *)re;
-}
-
-/* End of pcre_compile.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains the external function pcre_config(). */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Return info about what features are configured *
-*************************************************/
-
-/* This function has an extensible interface so that additional items can be
-added compatibly.
-
-Arguments:
- what what information is required
- where where to put the information
-
-Returns: 0 if data returned, negative on error
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_config(int what, void *where)
-{
-switch (what)
- {
- case PCRE_CONFIG_UTF8:
-#ifdef SUPPORT_UTF8
- *((int *)where) = 1;
-#else
- *((int *)where) = 0;
-#endif
- break;
-
- case PCRE_CONFIG_UNICODE_PROPERTIES:
-#ifdef SUPPORT_UCP
- *((int *)where) = 1;
-#else
- *((int *)where) = 0;
-#endif
- break;
-
- case PCRE_CONFIG_NEWLINE:
- *((int *)where) = NEWLINE;
- break;
-
- case PCRE_CONFIG_BSR:
-#ifdef BSR_ANYCRLF
- *((int *)where) = 1;
-#else
- *((int *)where) = 0;
-#endif
- break;
-
- case PCRE_CONFIG_LINK_SIZE:
- *((int *)where) = LINK_SIZE;
- break;
-
- case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD:
- *((int *)where) = POSIX_MALLOC_THRESHOLD;
- break;
-
- case PCRE_CONFIG_MATCH_LIMIT:
- *((unsigned long int *)where) = MATCH_LIMIT;
- break;
-
- case PCRE_CONFIG_MATCH_LIMIT_RECURSION:
- *((unsigned long int *)where) = MATCH_LIMIT_RECURSION;
- break;
-
- case PCRE_CONFIG_STACKRECURSE:
-#ifdef NO_RECURSE
- *((int *)where) = 0;
-#else
- *((int *)where) = 1;
-#endif
- break;
-
- default: return PCRE_ERROR_BADOPTION;
- }
-
-return 0;
-}
-
-/* End of pcre_config.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language (but see
-below for why this module is different).
-
- Written by Philip Hazel
- Copyright (c) 1997-2010 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains the external function pcre_dfa_exec(), which is an
-alternative matching function that uses a sort of DFA algorithm (not a true
-FSM). This is NOT Perl- compatible, but it has advantages in certain
-applications. */
-
-
-/* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved
-the performance of his patterns greatly. I could not use it as it stood, as it
-was not thread safe, and made assumptions about pattern sizes. Also, it caused
-test 7 to loop, and test 9 to crash with a segfault.
-
-The issue is the check for duplicate states, which is done by a simple linear
-search up the state list. (Grep for "duplicate" below to find the code.) For
-many patterns, there will never be many states active at one time, so a simple
-linear search is fine. In patterns that have many active states, it might be a
-bottleneck. The suggested code used an indexing scheme to remember which states
-had previously been used for each character, and avoided the linear search when
-it knew there was no chance of a duplicate. This was implemented when adding
-states to the state lists.
-
-I wrote some thread-safe, not-limited code to try something similar at the time
-of checking for duplicates (instead of when adding states), using index vectors
-on the stack. It did give a 13% improvement with one specially constructed
-pattern for certain subject strings, but on other strings and on many of the
-simpler patterns in the test suite it did worse. The major problem, I think,
-was the extra time to initialize the index. This had to be done for each call
-of internal_dfa_exec(). (The supplied patch used a static vector, initialized
-only once - I suspect this was the cause of the problems with the tests.)
-
-Overall, I concluded that the gains in some cases did not outweigh the losses
-in others, so I abandoned this code. */
-
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define NLBLOCK md /* Block containing newline information */
-#define PSSTART start_subject /* Field containing processed string start */
-#define PSEND end_subject /* Field containing processed string end */
-
-#include "pcre_internal.h"
-
-
-/* For use to indent debugging output */
-
-#define SP " "
-
-
-/*************************************************
-* Code parameters and static tables *
-*************************************************/
-
-/* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes
-into others, under special conditions. A gap of 20 between the blocks should be
-enough. The resulting opcodes don't have to be less than 256 because they are
-never stored, so we push them well clear of the normal opcodes. */
-
-#define OP_PROP_EXTRA 300
-#define OP_EXTUNI_EXTRA 320
-#define OP_ANYNL_EXTRA 340
-#define OP_HSPACE_EXTRA 360
-#define OP_VSPACE_EXTRA 380
-
-
-/* This table identifies those opcodes that are followed immediately by a
-character that is to be tested in some way. This makes is possible to
-centralize the loading of these characters. In the case of Type * etc, the
-"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a
-small value. Non-zero values in the table are the offsets from the opcode where
-the character is to be found. ***NOTE*** If the start of this table is
-modified, the three tables that follow must also be modified. */
-
-static const uschar coptable[] = {
- 0, /* End */
- 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */
- 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */
- 0, 0, 0, /* Any, AllAny, Anybyte */
- 0, 0, /* \P, \p */
- 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */
- 0, /* \X */
- 0, 0, 0, 0, 0, /* \Z, \z, Opt, ^, $ */
- 1, /* Char */
- 1, /* Charnc */
- 1, /* not */
- /* Positive single-char repeats */
- 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
- 3, 3, 3, /* upto, minupto, exact */
- 1, 1, 1, 3, /* *+, ++, ?+, upto+ */
- /* Negative single-char repeats - only for chars < 256 */
- 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
- 3, 3, 3, /* NOT upto, minupto, exact */
- 1, 1, 1, 3, /* NOT *+, ++, ?+, updo+ */
- /* Positive type repeats */
- 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
- 3, 3, 3, /* Type upto, minupto, exact */
- 1, 1, 1, 3, /* Type *+, ++, ?+, upto+ */
- /* Character class & ref repeats */
- 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */
- 0, 0, /* CRRANGE, CRMINRANGE */
- 0, /* CLASS */
- 0, /* NCLASS */
- 0, /* XCLASS - variable length */
- 0, /* REF */
- 0, /* RECURSE */
- 0, /* CALLOUT */
- 0, /* Alt */
- 0, /* Ket */
- 0, /* KetRmax */
- 0, /* KetRmin */
- 0, /* Assert */
- 0, /* Assert not */
- 0, /* Assert behind */
- 0, /* Assert behind not */
- 0, /* Reverse */
- 0, 0, 0, 0, /* ONCE, BRA, CBRA, COND */
- 0, 0, 0, /* SBRA, SCBRA, SCOND */
- 0, 0, /* CREF, NCREF */
- 0, 0, /* RREF, NRREF */
- 0, /* DEF */
- 0, 0, /* BRAZERO, BRAMINZERO */
- 0, 0, 0, 0, /* PRUNE, SKIP, THEN, COMMIT */
- 0, 0, 0, 0 /* FAIL, ACCEPT, CLOSE, SKIPZERO */
-};
-
-/* This table identifies those opcodes that inspect a character. It is used to
-remember the fact that a character could have been inspected when the end of
-the subject is reached. ***NOTE*** If the start of this table is modified, the
-two tables that follow must also be modified. */
-
-static const uschar poptable[] = {
- 0, /* End */
- 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */
- 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */
- 1, 1, 1, /* Any, AllAny, Anybyte */
- 1, 1, /* \P, \p */
- 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */
- 1, /* \X */
- 0, 0, 0, 0, 0, /* \Z, \z, Opt, ^, $ */
- 1, /* Char */
- 1, /* Charnc */
- 1, /* not */
- /* Positive single-char repeats */
- 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
- 1, 1, 1, /* upto, minupto, exact */
- 1, 1, 1, 1, /* *+, ++, ?+, upto+ */
- /* Negative single-char repeats - only for chars < 256 */
- 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
- 1, 1, 1, /* NOT upto, minupto, exact */
- 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */
- /* Positive type repeats */
- 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
- 1, 1, 1, /* Type upto, minupto, exact */
- 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */
- /* Character class & ref repeats */
- 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
- 1, 1, /* CRRANGE, CRMINRANGE */
- 1, /* CLASS */
- 1, /* NCLASS */
- 1, /* XCLASS - variable length */
- 0, /* REF */
- 0, /* RECURSE */
- 0, /* CALLOUT */
- 0, /* Alt */
- 0, /* Ket */
- 0, /* KetRmax */
- 0, /* KetRmin */
- 0, /* Assert */
- 0, /* Assert not */
- 0, /* Assert behind */
- 0, /* Assert behind not */
- 0, /* Reverse */
- 0, 0, 0, 0, /* ONCE, BRA, CBRA, COND */
- 0, 0, 0, /* SBRA, SCBRA, SCOND */
- 0, 0, /* CREF, NCREF */
- 0, 0, /* RREF, NRREF */
- 0, /* DEF */
- 0, 0, /* BRAZERO, BRAMINZERO */
- 0, 0, 0, 0, /* PRUNE, SKIP, THEN, COMMIT */
- 0, 0, 0, 0 /* FAIL, ACCEPT, CLOSE, SKIPZERO */
-};
-
-/* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W,
-and \w */
-
-static const uschar toptable1[] = {
- 0, 0, 0, 0, 0, 0,
- ctype_digit, ctype_digit,
- ctype_space, ctype_space,
- ctype_word, ctype_word,
- 0, 0 /* OP_ANY, OP_ALLANY */
-};
-
-static const uschar toptable2[] = {
- 0, 0, 0, 0, 0, 0,
- ctype_digit, 0,
- ctype_space, 0,
- ctype_word, 0,
- 1, 1 /* OP_ANY, OP_ALLANY */
-};
-
-
-/* Structure for holding data about a particular state, which is in effect the
-current data for an active path through the match tree. It must consist
-entirely of ints because the working vector we are passed, and which we put
-these structures in, is a vector of ints. */
-
-typedef struct stateblock {
- int offset; /* Offset to opcode */
- int count; /* Count for repeats */
- int ims; /* ims flag bits */
- int data; /* Some use extra data */
-} stateblock;
-
-#define INTS_PER_STATEBLOCK (sizeof(stateblock)/sizeof(int))
-
-
-#ifdef PCRE_DEBUG
-/*************************************************
-* Print character string *
-*************************************************/
-
-/* Character string printing function for debugging.
-
-Arguments:
- p points to string
- length number of bytes
- f where to print
-
-Returns: nothing
-*/
-
-static void
-pchars(unsigned char *p, int length, FILE *f)
-{
-int c;
-while (length-- > 0)
- {
- if (isprint(c = *(p++)))
- fprintf(f, "%c", c);
- else
- fprintf(f, "\\x%02x", c);
- }
-}
-#endif
-
-
-
-/*************************************************
-* Execute a Regular Expression - DFA engine *
-*************************************************/
-
-/* This internal function applies a compiled pattern to a subject string,
-starting at a given point, using a DFA engine. This function is called from the
-external one, possibly multiple times if the pattern is not anchored. The
-function calls itself recursively for some kinds of subpattern.
-
-Arguments:
- md the match_data block with fixed information
- this_start_code the opening bracket of this subexpression's code
- current_subject where we currently are in the subject string
- start_offset start offset in the subject string
- offsets vector to contain the matching string offsets
- offsetcount size of same
- workspace vector of workspace
- wscount size of same
- ims the current ims flags
- rlevel function call recursion level
- recursing regex recursive call level
-
-Returns: > 0 => number of match offset pairs placed in offsets
- = 0 => offsets overflowed; longest matches are present
- -1 => failed to match
- < -1 => some kind of unexpected problem
-
-The following macros are used for adding states to the two state vectors (one
-for the current character, one for the following character). */
-
-#define ADD_ACTIVE(x,y) \
- if (active_count++ < wscount) \
- { \
- next_active_state->offset = (x); \
- next_active_state->count = (y); \
- next_active_state->ims = ims; \
- next_active_state++; \
- DPRINTF(("%.*sADD_ACTIVE(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \
- } \
- else return PCRE_ERROR_DFA_WSSIZE
-
-#define ADD_ACTIVE_DATA(x,y,z) \
- if (active_count++ < wscount) \
- { \
- next_active_state->offset = (x); \
- next_active_state->count = (y); \
- next_active_state->ims = ims; \
- next_active_state->data = (z); \
- next_active_state++; \
- DPRINTF(("%.*sADD_ACTIVE_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \
- } \
- else return PCRE_ERROR_DFA_WSSIZE
-
-#define ADD_NEW(x,y) \
- if (new_count++ < wscount) \
- { \
- next_new_state->offset = (x); \
- next_new_state->count = (y); \
- next_new_state->ims = ims; \
- next_new_state++; \
- DPRINTF(("%.*sADD_NEW(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \
- } \
- else return PCRE_ERROR_DFA_WSSIZE
-
-#define ADD_NEW_DATA(x,y,z) \
- if (new_count++ < wscount) \
- { \
- next_new_state->offset = (x); \
- next_new_state->count = (y); \
- next_new_state->ims = ims; \
- next_new_state->data = (z); \
- next_new_state++; \
- DPRINTF(("%.*sADD_NEW_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \
- } \
- else return PCRE_ERROR_DFA_WSSIZE
-
-/* And now, here is the code */
-
-static int
-internal_dfa_exec(
- dfa_match_data *md,
- const uschar *this_start_code,
- const uschar *current_subject,
- int start_offset,
- int *offsets,
- int offsetcount,
- int *workspace,
- int wscount,
- int ims,
- int rlevel,
- int recursing)
-{
-stateblock *active_states, *new_states, *temp_states;
-stateblock *next_active_state, *next_new_state;
-
-const uschar *ctypes, *lcc, *fcc;
-const uschar *ptr;
-const uschar *end_code, *first_op;
-
-int active_count, new_count, match_count;
-
-/* Some fields in the md block are frequently referenced, so we load them into
-independent variables in the hope that this will perform better. */
-
-const uschar *start_subject = md->start_subject;
-const uschar *end_subject = md->end_subject;
-const uschar *start_code = md->start_code;
-
-#ifdef SUPPORT_UTF8
-BOOL utf8 = (md->poptions & PCRE_UTF8) != 0;
-#else
-BOOL utf8 = FALSE;
-#endif
-
-rlevel++;
-offsetcount &= (-2);
-
-wscount -= 2;
-wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) /
- (2 * INTS_PER_STATEBLOCK);
-
-DPRINTF(("\n%.*s---------------------\n"
- "%.*sCall to internal_dfa_exec f=%d r=%d\n",
- rlevel*2-2, SP, rlevel*2-2, SP, rlevel, recursing));
-
-ctypes = md->tables + ctypes_offset;
-lcc = md->tables + lcc_offset;
-fcc = md->tables + fcc_offset;
-
-match_count = PCRE_ERROR_NOMATCH; /* A negative number */
-
-active_states = (stateblock *)(workspace + 2);
-next_new_state = new_states = active_states + wscount;
-new_count = 0;
-
-first_op = this_start_code + 1 + LINK_SIZE +
- ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0);
-
-/* The first thing in any (sub) pattern is a bracket of some sort. Push all
-the alternative states onto the list, and find out where the end is. This
-makes is possible to use this function recursively, when we want to stop at a
-matching internal ket rather than at the end.
-
-If the first opcode in the first alternative is OP_REVERSE, we are dealing with
-a backward assertion. In that case, we have to find out the maximum amount to
-move back, and set up each alternative appropriately. */
-
-if (*first_op == OP_REVERSE)
- {
- int max_back = 0;
- int gone_back;
-
- end_code = this_start_code;
- do
- {
- int back = GET(end_code, 2+LINK_SIZE);
- if (back > max_back) max_back = back;
- end_code += GET(end_code, 1);
- }
- while (*end_code == OP_ALT);
-
- /* If we can't go back the amount required for the longest lookbehind
- pattern, go back as far as we can; some alternatives may still be viable. */
-
-#ifdef SUPPORT_UTF8
- /* In character mode we have to step back character by character */
-
- if (utf8)
- {
- for (gone_back = 0; gone_back < max_back; gone_back++)
- {
- if (current_subject <= start_subject) break;
- current_subject--;
- while (current_subject > start_subject &&
- (*current_subject & 0xc0) == 0x80)
- current_subject--;
- }
- }
- else
-#endif
-
- /* In byte-mode we can do this quickly. */
-
- {
- gone_back = (current_subject - max_back < start_subject)?
- current_subject - start_subject : max_back;
- current_subject -= gone_back;
- }
-
- /* Save the earliest consulted character */
-
- if (current_subject < md->start_used_ptr)
- md->start_used_ptr = current_subject;
-
- /* Now we can process the individual branches. */
-
- end_code = this_start_code;
- do
- {
- int back = GET(end_code, 2+LINK_SIZE);
- if (back <= gone_back)
- {
- int bstate = end_code - start_code + 2 + 2*LINK_SIZE;
- ADD_NEW_DATA(-bstate, 0, gone_back - back);
- }
- end_code += GET(end_code, 1);
- }
- while (*end_code == OP_ALT);
- }
-
-/* This is the code for a "normal" subpattern (not a backward assertion). The
-start of a whole pattern is always one of these. If we are at the top level,
-we may be asked to restart matching from the same point that we reached for a
-previous partial match. We still have to scan through the top-level branches to
-find the end state. */
-
-else
- {
- end_code = this_start_code;
-
- /* Restarting */
-
- if (rlevel == 1 && (md->moptions & PCRE_DFA_RESTART) != 0)
- {
- do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT);
- new_count = workspace[1];
- if (!workspace[0])
- memcpy(new_states, active_states, new_count * sizeof(stateblock));
- }
-
- /* Not restarting */
-
- else
- {
- int length = 1 + LINK_SIZE +
- ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0);
- do
- {
- ADD_NEW(end_code - start_code + length, 0);
- end_code += GET(end_code, 1);
- length = 1 + LINK_SIZE;
- }
- while (*end_code == OP_ALT);
- }
- }
-
-workspace[0] = 0; /* Bit indicating which vector is current */
-
-DPRINTF(("%.*sEnd state = %d\n", rlevel*2-2, SP, end_code - start_code));
-
-/* Loop for scanning the subject */
-
-ptr = current_subject;
-for (;;)
- {
- int i, j;
- int clen, dlen;
- unsigned int c, d;
- int forced_fail = 0;
- BOOL could_continue = FALSE;
-
- /* Make the new state list into the active state list and empty the
- new state list. */
-
- temp_states = active_states;
- active_states = new_states;
- new_states = temp_states;
- active_count = new_count;
- new_count = 0;
-
- workspace[0] ^= 1; /* Remember for the restarting feature */
- workspace[1] = active_count;
-
-#ifdef PCRE_DEBUG
- printf("%.*sNext character: rest of subject = \"", rlevel*2-2, SP);
- pchars((uschar *)ptr, strlen((char *)ptr), stdout);
- printf("\"\n");
-
- printf("%.*sActive states: ", rlevel*2-2, SP);
- for (i = 0; i < active_count; i++)
- printf("%d/%d ", active_states[i].offset, active_states[i].count);
- printf("\n");
-#endif
-
- /* Set the pointers for adding new states */
-
- next_active_state = active_states + active_count;
- next_new_state = new_states;
-
- /* Load the current character from the subject outside the loop, as many
- different states may want to look at it, and we assume that at least one
- will. */
-
- if (ptr < end_subject)
- {
- clen = 1; /* Number of bytes in the character */
-#ifdef SUPPORT_UTF8
- if (utf8) { GETCHARLEN(c, ptr, clen); } else
-#endif /* SUPPORT_UTF8 */
- c = *ptr;
- }
- else
- {
- clen = 0; /* This indicates the end of the subject */
- c = NOTACHAR; /* This value should never actually be used */
- }
-
- /* Scan up the active states and act on each one. The result of an action
- may be to add more states to the currently active list (e.g. on hitting a
- parenthesis) or it may be to put states on the new list, for considering
- when we move the character pointer on. */
-
- for (i = 0; i < active_count; i++)
- {
- stateblock *current_state = active_states + i;
- const uschar *code;
- int state_offset = current_state->offset;
- int count, codevalue, rrc;
-
-#ifdef PCRE_DEBUG
- printf ("%.*sProcessing state %d c=", rlevel*2-2, SP, state_offset);
- if (clen == 0) printf("EOL\n");
- else if (c > 32 && c < 127) printf("'%c'\n", c);
- else printf("0x%02x\n", c);
-#endif
-
- /* This variable is referred to implicity in the ADD_xxx macros. */
-
- ims = current_state->ims;
-
- /* A negative offset is a special case meaning "hold off going to this
- (negated) state until the number of characters in the data field have
- been skipped". */
-
- if (state_offset < 0)
- {
- if (current_state->data > 0)
- {
- DPRINTF(("%.*sSkipping this character\n", rlevel*2-2, SP));
- ADD_NEW_DATA(state_offset, current_state->count,
- current_state->data - 1);
- continue;
- }
- else
- {
- current_state->offset = state_offset = -state_offset;
- }
- }
-
- /* Check for a duplicate state with the same count, and skip if found.
- See the note at the head of this module about the possibility of improving
- performance here. */
-
- for (j = 0; j < i; j++)
- {
- if (active_states[j].offset == state_offset &&
- active_states[j].count == current_state->count)
- {
- DPRINTF(("%.*sDuplicate state: skipped\n", rlevel*2-2, SP));
- goto NEXT_ACTIVE_STATE;
- }
- }
-
- /* The state offset is the offset to the opcode */
-
- code = start_code + state_offset;
- codevalue = *code;
-
- /* If this opcode inspects a character, but we are at the end of the
- subject, remember the fact for use when testing for a partial match. */
-
- if (clen == 0 && poptable[codevalue] != 0)
- could_continue = TRUE;
-
- /* If this opcode is followed by an inline character, load it. It is
- tempting to test for the presence of a subject character here, but that
- is wrong, because sometimes zero repetitions of the subject are
- permitted.
-
- We also use this mechanism for opcodes such as OP_TYPEPLUS that take an
- argument that is not a data character - but is always one byte long. We
- have to take special action to deal with \P, \p, \H, \h, \V, \v and \X in
- this case. To keep the other cases fast, convert these ones to new opcodes.
- */
-
- if (coptable[codevalue] > 0)
- {
- dlen = 1;
-#ifdef SUPPORT_UTF8
- if (utf8) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else
-#endif /* SUPPORT_UTF8 */
- d = code[coptable[codevalue]];
- if (codevalue >= OP_TYPESTAR)
- {
- switch(d)
- {
- case OP_ANYBYTE: return PCRE_ERROR_DFA_UITEM;
- case OP_NOTPROP:
- case OP_PROP: codevalue += OP_PROP_EXTRA; break;
- case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break;
- case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break;
- case OP_NOT_HSPACE:
- case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break;
- case OP_NOT_VSPACE:
- case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break;
- default: break;
- }
- }
- }
- else
- {
- dlen = 0; /* Not strictly necessary, but compilers moan */
- d = NOTACHAR; /* if these variables are not set. */
- }
-
-
- /* Now process the individual opcodes */
-
- switch (codevalue)
- {
-/* ========================================================================== */
- /* These cases are never obeyed. This is a fudge that causes a compile-
- time error if the vectors coptable or poptable, which are indexed by
- opcode, are not the correct length. It seems to be the only way to do
- such a check at compile time, as the sizeof() operator does not work
- in the C preprocessor. */
-
- case OP_TABLE_LENGTH:
- case OP_TABLE_LENGTH +
- ((sizeof(coptable) == OP_TABLE_LENGTH) &&
- (sizeof(poptable) == OP_TABLE_LENGTH)):
- break;
-
-/* ========================================================================== */
- /* Reached a closing bracket. If not at the end of the pattern, carry
- on with the next opcode. Otherwise, unless we have an empty string and
- PCRE_NOTEMPTY is set, or PCRE_NOTEMPTY_ATSTART is set and we are at the
- start of the subject, save the match data, shifting up all previous
- matches so we always have the longest first. */
-
- case OP_KET:
- case OP_KETRMIN:
- case OP_KETRMAX:
- if (code != end_code)
- {
- ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0);
- if (codevalue != OP_KET)
- {
- ADD_ACTIVE(state_offset - GET(code, 1), 0);
- }
- }
- else
- {
- if (ptr > current_subject ||
- ((md->moptions & PCRE_NOTEMPTY) == 0 &&
- ((md->moptions & PCRE_NOTEMPTY_ATSTART) == 0 ||
- current_subject > start_subject + md->start_offset)))
- {
- if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0;
- else if (match_count > 0 && ++match_count * 2 >= offsetcount)
- match_count = 0;
- count = ((match_count == 0)? offsetcount : match_count * 2) - 2;
- if (count > 0) memmove(offsets + 2, offsets, count * sizeof(int));
- if (offsetcount >= 2)
- {
- offsets[0] = current_subject - start_subject;
- offsets[1] = ptr - start_subject;
- DPRINTF(("%.*sSet matched string = \"%.*s\"\n", rlevel*2-2, SP,
- offsets[1] - offsets[0], current_subject));
- }
- if ((md->moptions & PCRE_DFA_SHORTEST) != 0)
- {
- DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n"
- "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel,
- match_count, rlevel*2-2, SP));
- return match_count;
- }
- }
- }
- break;
-
-/* ========================================================================== */
- /* These opcodes add to the current list of states without looking
- at the current character. */
-
- /*-----------------------------------------------------------------*/
- case OP_ALT:
- do { code += GET(code, 1); } while (*code == OP_ALT);
- ADD_ACTIVE(code - start_code, 0);
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_BRA:
- case OP_SBRA:
- do
- {
- ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0);
- code += GET(code, 1);
- }
- while (*code == OP_ALT);
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_CBRA:
- case OP_SCBRA:
- ADD_ACTIVE(code - start_code + 3 + LINK_SIZE, 0);
- code += GET(code, 1);
- while (*code == OP_ALT)
- {
- ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0);
- code += GET(code, 1);
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_BRAZERO:
- case OP_BRAMINZERO:
- ADD_ACTIVE(state_offset + 1, 0);
- code += 1 + GET(code, 2);
- while (*code == OP_ALT) code += GET(code, 1);
- ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0);
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_SKIPZERO:
- code += 1 + GET(code, 2);
- while (*code == OP_ALT) code += GET(code, 1);
- ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0);
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_CIRC:
- if ((ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) ||
- ((ims & PCRE_MULTILINE) != 0 &&
- ptr != end_subject &&
- WAS_NEWLINE(ptr)))
- { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EOD:
- if (ptr >= end_subject) { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_OPT:
- ims = code[1];
- ADD_ACTIVE(state_offset + 2, 0);
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_SOD:
- if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_SOM:
- if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
-
-/* ========================================================================== */
- /* These opcodes inspect the next subject character, and sometimes
- the previous one as well, but do not have an argument. The variable
- clen contains the length of the current character and is zero if we are
- at the end of the subject. */
-
- /*-----------------------------------------------------------------*/
- case OP_ANY:
- if (clen > 0 && !IS_NEWLINE(ptr))
- { ADD_NEW(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_ALLANY:
- if (clen > 0)
- { ADD_NEW(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EODN:
- if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - md->nllen))
- { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_DOLL:
- if ((md->moptions & PCRE_NOTEOL) == 0)
- {
- if (clen == 0 ||
- ((md->poptions & PCRE_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) &&
- ((ims & PCRE_MULTILINE) != 0 || ptr == end_subject - md->nllen)
- ))
- { ADD_ACTIVE(state_offset + 1, 0); }
- }
- else if ((ims & PCRE_MULTILINE) != 0 && IS_NEWLINE(ptr))
- { ADD_ACTIVE(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
-
- case OP_DIGIT:
- case OP_WHITESPACE:
- case OP_WORDCHAR:
- if (clen > 0 && c < 256 &&
- ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)
- { ADD_NEW(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_NOT_DIGIT:
- case OP_NOT_WHITESPACE:
- case OP_NOT_WORDCHAR:
- if (clen > 0 && (c >= 256 ||
- ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0))
- { ADD_NEW(state_offset + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_WORD_BOUNDARY:
- case OP_NOT_WORD_BOUNDARY:
- {
- int left_word, right_word;
-
- if (ptr > start_subject)
- {
- const uschar *temp = ptr - 1;
- if (temp < md->start_used_ptr) md->start_used_ptr = temp;
-#ifdef SUPPORT_UTF8
- if (utf8) BACKCHAR(temp);
-#endif
- GETCHARTEST(d, temp);
- left_word = d < 256 && (ctypes[d] & ctype_word) != 0;
- }
- else left_word = 0;
-
- if (clen > 0)
- right_word = c < 256 && (ctypes[c] & ctype_word) != 0;
- else right_word = 0;
-
- if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY))
- { ADD_ACTIVE(state_offset + 1, 0); }
- }
- break;
-
-
- /*-----------------------------------------------------------------*/
- /* Check the next character by Unicode property. We will get here only
- if the support is in the binary; otherwise a compile-time error occurs.
- */
-
-#ifdef SUPPORT_UCP
- case OP_PROP:
- case OP_NOTPROP:
- if (clen > 0)
- {
- BOOL OK;
- int chartype = UCD_CHARTYPE(c);
- switch(code[1])
- {
- case PT_ANY:
- OK = TRUE;
- break;
-
- case PT_LAMP:
- OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt;
- break;
-
- case PT_GC:
- OK = _pcre_ucp_gentype[chartype] == code[2];
- break;
-
- case PT_PC:
- OK = chartype == code[2];
- break;
-
- case PT_SC:
- OK = UCD_SCRIPT(c) == code[2];
- break;
-
- /* Should never occur, but keep compilers from grumbling. */
-
- default:
- OK = codevalue != OP_PROP;
- break;
- }
-
- if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); }
- }
- break;
-#endif
-
-
-
-/* ========================================================================== */
- /* These opcodes likewise inspect the subject character, but have an
- argument that is not a data character. It is one of these opcodes:
- OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE,
- OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */
-
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
- if (clen > 0)
- {
- if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
- (c < 256 &&
- (d != OP_ANY || !IS_NEWLINE(ptr)) &&
- ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
- {
- if (count > 0 && codevalue == OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW(state_offset, count);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSQUERY:
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0)
- {
- if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
- (c < 256 &&
- (d != OP_ANY || !IS_NEWLINE(ptr)) &&
- ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
- {
- if (codevalue == OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW(state_offset + 2, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPOSSTAR:
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0)
- {
- if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
- (c < 256 &&
- (d != OP_ANY || !IS_NEWLINE(ptr)) &&
- ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
- {
- if (codevalue == OP_TYPEPOSSTAR)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW(state_offset, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_TYPEEXACT:
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
- (c < 256 &&
- (d != OP_ANY || !IS_NEWLINE(ptr)) &&
- ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
- {
- if (++count >= GET2(code, 1))
- { ADD_NEW(state_offset + 4, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEPOSUPTO:
- ADD_ACTIVE(state_offset + 4, 0);
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
- (c < 256 &&
- (d != OP_ANY || !IS_NEWLINE(ptr)) &&
- ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
- {
- if (codevalue == OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW(state_offset + 4, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- }
- break;
-
-/* ========================================================================== */
- /* These are virtual opcodes that are used when something like
- OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its
- argument. It keeps the code above fast for the other cases. The argument
- is in the d variable. */
-
-#ifdef SUPPORT_UCP
- case OP_PROP_EXTRA + OP_TYPEPLUS:
- case OP_PROP_EXTRA + OP_TYPEMINPLUS:
- case OP_PROP_EXTRA + OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); }
- if (clen > 0)
- {
- BOOL OK;
- int chartype = UCD_CHARTYPE(c);
- switch(code[2])
- {
- case PT_ANY:
- OK = TRUE;
- break;
-
- case PT_LAMP:
- OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt;
- break;
-
- case PT_GC:
- OK = _pcre_ucp_gentype[chartype] == code[3];
- break;
-
- case PT_PC:
- OK = chartype == code[3];
- break;
-
- case PT_SC:
- OK = UCD_SCRIPT(c) == code[3];
- break;
-
- /* Should never occur, but keep compilers from grumbling. */
-
- default:
- OK = codevalue != OP_PROP;
- break;
- }
-
- if (OK == (d == OP_PROP))
- {
- if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW(state_offset, count);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EXTUNI_EXTRA + OP_TYPEPLUS:
- case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS:
- case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
- if (clen > 0 && UCD_CATEGORY(c) != ucp_M)
- {
- const uschar *nptr = ptr + clen;
- int ncount = 0;
- if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- while (nptr < end_subject)
- {
- int nd;
- int ndlen = 1;
- GETCHARLEN(nd, nptr, ndlen);
- if (UCD_CATEGORY(nd) != ucp_M) break;
- ncount++;
- nptr += ndlen;
- }
- count++;
- ADD_NEW_DATA(-state_offset, count, ncount);
- }
- break;
-#endif
-
- /*-----------------------------------------------------------------*/
- case OP_ANYNL_EXTRA + OP_TYPEPLUS:
- case OP_ANYNL_EXTRA + OP_TYPEMINPLUS:
- case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
- if (clen > 0)
- {
- int ncount = 0;
- switch (c)
- {
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break;
- goto ANYNL01;
-
- case 0x000d:
- if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1;
- /* Fall through */
-
- ANYNL01:
- case 0x000a:
- if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW_DATA(-state_offset, count, ncount);
- break;
-
- default:
- break;
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_VSPACE_EXTRA + OP_TYPEPLUS:
- case OP_VSPACE_EXTRA + OP_TYPEMINPLUS:
- case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x000a:
- case 0x000b:
- case 0x000c:
- case 0x000d:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- break;
- }
-
- if (OK == (d == OP_VSPACE))
- {
- if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW_DATA(-state_offset, count, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_HSPACE_EXTRA + OP_TYPEPLUS:
- case OP_HSPACE_EXTRA + OP_TYPEMINPLUS:
- case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- break;
- }
-
- if (OK == (d == OP_HSPACE))
- {
- if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW_DATA(-state_offset, count, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
-#ifdef SUPPORT_UCP
- case OP_PROP_EXTRA + OP_TYPEQUERY:
- case OP_PROP_EXTRA + OP_TYPEMINQUERY:
- case OP_PROP_EXTRA + OP_TYPEPOSQUERY:
- count = 4;
- goto QS1;
-
- case OP_PROP_EXTRA + OP_TYPESTAR:
- case OP_PROP_EXTRA + OP_TYPEMINSTAR:
- case OP_PROP_EXTRA + OP_TYPEPOSSTAR:
- count = 0;
-
- QS1:
-
- ADD_ACTIVE(state_offset + 4, 0);
- if (clen > 0)
- {
- BOOL OK;
- int chartype = UCD_CHARTYPE(c);
- switch(code[2])
- {
- case PT_ANY:
- OK = TRUE;
- break;
-
- case PT_LAMP:
- OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt;
- break;
-
- case PT_GC:
- OK = _pcre_ucp_gentype[chartype] == code[3];
- break;
-
- case PT_PC:
- OK = chartype == code[3];
- break;
-
- case PT_SC:
- OK = UCD_SCRIPT(c) == code[3];
- break;
-
- /* Should never occur, but keep compilers from grumbling. */
-
- default:
- OK = codevalue != OP_PROP;
- break;
- }
-
- if (OK == (d == OP_PROP))
- {
- if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR ||
- codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW(state_offset + count, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EXTUNI_EXTRA + OP_TYPEQUERY:
- case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY:
- case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY:
- count = 2;
- goto QS2;
-
- case OP_EXTUNI_EXTRA + OP_TYPESTAR:
- case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR:
- case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR:
- count = 0;
-
- QS2:
-
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0 && UCD_CATEGORY(c) != ucp_M)
- {
- const uschar *nptr = ptr + clen;
- int ncount = 0;
- if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR ||
- codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- while (nptr < end_subject)
- {
- int nd;
- int ndlen = 1;
- GETCHARLEN(nd, nptr, ndlen);
- if (UCD_CATEGORY(nd) != ucp_M) break;
- ncount++;
- nptr += ndlen;
- }
- ADD_NEW_DATA(-(state_offset + count), 0, ncount);
- }
- break;
-#endif
-
- /*-----------------------------------------------------------------*/
- case OP_ANYNL_EXTRA + OP_TYPEQUERY:
- case OP_ANYNL_EXTRA + OP_TYPEMINQUERY:
- case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY:
- count = 2;
- goto QS3;
-
- case OP_ANYNL_EXTRA + OP_TYPESTAR:
- case OP_ANYNL_EXTRA + OP_TYPEMINSTAR:
- case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR:
- count = 0;
-
- QS3:
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0)
- {
- int ncount = 0;
- switch (c)
- {
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break;
- goto ANYNL02;
-
- case 0x000d:
- if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1;
- /* Fall through */
-
- ANYNL02:
- case 0x000a:
- if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR ||
- codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW_DATA(-(state_offset + count), 0, ncount);
- break;
-
- default:
- break;
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_VSPACE_EXTRA + OP_TYPEQUERY:
- case OP_VSPACE_EXTRA + OP_TYPEMINQUERY:
- case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY:
- count = 2;
- goto QS4;
-
- case OP_VSPACE_EXTRA + OP_TYPESTAR:
- case OP_VSPACE_EXTRA + OP_TYPEMINSTAR:
- case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR:
- count = 0;
-
- QS4:
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x000a:
- case 0x000b:
- case 0x000c:
- case 0x000d:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- break;
- }
- if (OK == (d == OP_VSPACE))
- {
- if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR ||
- codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW_DATA(-(state_offset + count), 0, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_HSPACE_EXTRA + OP_TYPEQUERY:
- case OP_HSPACE_EXTRA + OP_TYPEMINQUERY:
- case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY:
- count = 2;
- goto QS5;
-
- case OP_HSPACE_EXTRA + OP_TYPESTAR:
- case OP_HSPACE_EXTRA + OP_TYPEMINSTAR:
- case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR:
- count = 0;
-
- QS5:
- ADD_ACTIVE(state_offset + 2, 0);
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- break;
- }
-
- if (OK == (d == OP_HSPACE))
- {
- if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR ||
- codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW_DATA(-(state_offset + count), 0, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
-#ifdef SUPPORT_UCP
- case OP_PROP_EXTRA + OP_TYPEEXACT:
- case OP_PROP_EXTRA + OP_TYPEUPTO:
- case OP_PROP_EXTRA + OP_TYPEMINUPTO:
- case OP_PROP_EXTRA + OP_TYPEPOSUPTO:
- if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT)
- { ADD_ACTIVE(state_offset + 6, 0); }
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- BOOL OK;
- int chartype = UCD_CHARTYPE(c);
- switch(code[4])
- {
- case PT_ANY:
- OK = TRUE;
- break;
-
- case PT_LAMP:
- OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt;
- break;
-
- case PT_GC:
- OK = _pcre_ucp_gentype[chartype] == code[5];
- break;
-
- case PT_PC:
- OK = chartype == code[5];
- break;
-
- case PT_SC:
- OK = UCD_SCRIPT(c) == code[5];
- break;
-
- /* Should never occur, but keep compilers from grumbling. */
-
- default:
- OK = codevalue != OP_PROP;
- break;
- }
-
- if (OK == (d == OP_PROP))
- {
- if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW(state_offset + 6, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EXTUNI_EXTRA + OP_TYPEEXACT:
- case OP_EXTUNI_EXTRA + OP_TYPEUPTO:
- case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO:
- case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO:
- if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT)
- { ADD_ACTIVE(state_offset + 4, 0); }
- count = current_state->count; /* Number already matched */
- if (clen > 0 && UCD_CATEGORY(c) != ucp_M)
- {
- const uschar *nptr = ptr + clen;
- int ncount = 0;
- if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- while (nptr < end_subject)
- {
- int nd;
- int ndlen = 1;
- GETCHARLEN(nd, nptr, ndlen);
- if (UCD_CATEGORY(nd) != ucp_M) break;
- ncount++;
- nptr += ndlen;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); }
- else
- { ADD_NEW_DATA(-state_offset, count, ncount); }
- }
- break;
-#endif
-
- /*-----------------------------------------------------------------*/
- case OP_ANYNL_EXTRA + OP_TYPEEXACT:
- case OP_ANYNL_EXTRA + OP_TYPEUPTO:
- case OP_ANYNL_EXTRA + OP_TYPEMINUPTO:
- case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO:
- if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT)
- { ADD_ACTIVE(state_offset + 4, 0); }
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- int ncount = 0;
- switch (c)
- {
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break;
- goto ANYNL03;
-
- case 0x000d:
- if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1;
- /* Fall through */
-
- ANYNL03:
- case 0x000a:
- if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); }
- else
- { ADD_NEW_DATA(-state_offset, count, ncount); }
- break;
-
- default:
- break;
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_VSPACE_EXTRA + OP_TYPEEXACT:
- case OP_VSPACE_EXTRA + OP_TYPEUPTO:
- case OP_VSPACE_EXTRA + OP_TYPEMINUPTO:
- case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO:
- if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT)
- { ADD_ACTIVE(state_offset + 4, 0); }
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x000a:
- case 0x000b:
- case 0x000c:
- case 0x000d:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- }
-
- if (OK == (d == OP_VSPACE))
- {
- if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW_DATA(-(state_offset + 4), 0, 0); }
- else
- { ADD_NEW_DATA(-state_offset, count, 0); }
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_HSPACE_EXTRA + OP_TYPEEXACT:
- case OP_HSPACE_EXTRA + OP_TYPEUPTO:
- case OP_HSPACE_EXTRA + OP_TYPEMINUPTO:
- case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO:
- if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT)
- { ADD_ACTIVE(state_offset + 4, 0); }
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- BOOL OK;
- switch (c)
- {
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- OK = TRUE;
- break;
-
- default:
- OK = FALSE;
- break;
- }
-
- if (OK == (d == OP_HSPACE))
- {
- if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW_DATA(-(state_offset + 4), 0, 0); }
- else
- { ADD_NEW_DATA(-state_offset, count, 0); }
- }
- }
- break;
-
-/* ========================================================================== */
- /* These opcodes are followed by a character that is usually compared
- to the current subject character; it is loaded into d. We still get
- here even if there is no subject character, because in some cases zero
- repetitions are permitted. */
-
- /*-----------------------------------------------------------------*/
- case OP_CHAR:
- if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_CHARNC:
- if (clen == 0) break;
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else
- {
- unsigned int othercase;
- if (c < 128) othercase = fcc[c]; else
-
- /* If we have Unicode property support, we can use it to test the
- other case of the character. */
-
-#ifdef SUPPORT_UCP
- othercase = UCD_OTHERCASE(c);
-#else
- othercase = NOTACHAR;
-#endif
-
- if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); }
- }
- }
- else
-#endif /* SUPPORT_UTF8 */
-
- /* Non-UTF-8 mode */
- {
- if (lcc[c] == lcc[d]) { ADD_NEW(state_offset + 2, 0); }
- }
- break;
-
-
-#ifdef SUPPORT_UCP
- /*-----------------------------------------------------------------*/
- /* This is a tricky one because it can match more than one character.
- Find out how many characters to skip, and then set up a negative state
- to wait for them to pass before continuing. */
-
- case OP_EXTUNI:
- if (clen > 0 && UCD_CATEGORY(c) != ucp_M)
- {
- const uschar *nptr = ptr + clen;
- int ncount = 0;
- while (nptr < end_subject)
- {
- int nclen = 1;
- GETCHARLEN(c, nptr, nclen);
- if (UCD_CATEGORY(c) != ucp_M) break;
- ncount++;
- nptr += nclen;
- }
- ADD_NEW_DATA(-(state_offset + 1), 0, ncount);
- }
- break;
-#endif
-
- /*-----------------------------------------------------------------*/
- /* This is a tricky like EXTUNI because it too can match more than one
- character (when CR is followed by LF). In this case, set up a negative
- state to wait for one character to pass before continuing. */
-
- case OP_ANYNL:
- if (clen > 0) switch(c)
- {
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break;
-
- case 0x000a:
- ADD_NEW(state_offset + 1, 0);
- break;
-
- case 0x000d:
- if (ptr + 1 < end_subject && ptr[1] == 0x0a)
- {
- ADD_NEW_DATA(-(state_offset + 1), 0, 1);
- }
- else
- {
- ADD_NEW(state_offset + 1, 0);
- }
- break;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_NOT_VSPACE:
- if (clen > 0) switch(c)
- {
- case 0x000a:
- case 0x000b:
- case 0x000c:
- case 0x000d:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- break;
-
- default:
- ADD_NEW(state_offset + 1, 0);
- break;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_VSPACE:
- if (clen > 0) switch(c)
- {
- case 0x000a:
- case 0x000b:
- case 0x000c:
- case 0x000d:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- ADD_NEW(state_offset + 1, 0);
- break;
-
- default: break;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_NOT_HSPACE:
- if (clen > 0) switch(c)
- {
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- break;
-
- default:
- ADD_NEW(state_offset + 1, 0);
- break;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_HSPACE:
- if (clen > 0) switch(c)
- {
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- ADD_NEW(state_offset + 1, 0);
- break;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- /* Match a negated single character. This is only used for one-byte
- characters, that is, we know that d < 256. The character we are
- checking (c) can be multibyte. */
-
- case OP_NOT:
- if (clen > 0)
- {
- unsigned int otherd = ((ims & PCRE_CASELESS) != 0)? fcc[d] : d;
- if (c != d && c != otherd) { ADD_NEW(state_offset + dlen + 1, 0); }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_NOTPLUS:
- case OP_NOTMINPLUS:
- case OP_NOTPOSPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); }
- if (clen > 0)
- {
- unsigned int otherd = NOTACHAR;
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && d >= 128)
- {
-#ifdef SUPPORT_UCP
- otherd = UCD_OTHERCASE(d);
-#endif /* SUPPORT_UCP */
- }
- else
-#endif /* SUPPORT_UTF8 */
- otherd = fcc[d];
- }
- if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
- {
- if (count > 0 &&
- (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS))
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- count++;
- ADD_NEW(state_offset, count);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- case OP_NOTQUERY:
- case OP_NOTMINQUERY:
- case OP_NOTPOSQUERY:
- ADD_ACTIVE(state_offset + dlen + 1, 0);
- if (clen > 0)
- {
- unsigned int otherd = NOTACHAR;
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && d >= 128)
- {
-#ifdef SUPPORT_UCP
- otherd = UCD_OTHERCASE(d);
-#endif /* SUPPORT_UCP */
- }
- else
-#endif /* SUPPORT_UTF8 */
- otherd = fcc[d];
- }
- if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
- {
- if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW(state_offset + dlen + 1, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_NOTSTAR:
- case OP_NOTMINSTAR:
- case OP_NOTPOSSTAR:
- ADD_ACTIVE(state_offset + dlen + 1, 0);
- if (clen > 0)
- {
- unsigned int otherd = NOTACHAR;
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && d >= 128)
- {
-#ifdef SUPPORT_UCP
- otherd = UCD_OTHERCASE(d);
-#endif /* SUPPORT_UCP */
- }
- else
-#endif /* SUPPORT_UTF8 */
- otherd = fcc[d];
- }
- if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
- {
- if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- ADD_NEW(state_offset, 0);
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_EXACT:
- case OP_NOTEXACT:
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- unsigned int otherd = NOTACHAR;
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && d >= 128)
- {
-#ifdef SUPPORT_UCP
- otherd = UCD_OTHERCASE(d);
-#endif /* SUPPORT_UCP */
- }
- else
-#endif /* SUPPORT_UTF8 */
- otherd = fcc[d];
- }
- if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
- {
- if (++count >= GET2(code, 1))
- { ADD_NEW(state_offset + dlen + 3, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- case OP_NOTUPTO:
- case OP_NOTMINUPTO:
- case OP_NOTPOSUPTO:
- ADD_ACTIVE(state_offset + dlen + 3, 0);
- count = current_state->count; /* Number already matched */
- if (clen > 0)
- {
- unsigned int otherd = NOTACHAR;
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (utf8 && d >= 128)
- {
-#ifdef SUPPORT_UCP
- otherd = UCD_OTHERCASE(d);
-#endif /* SUPPORT_UCP */
- }
- else
-#endif /* SUPPORT_UTF8 */
- otherd = fcc[d];
- }
- if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
- {
- if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO)
- {
- active_count--; /* Remove non-match possibility */
- next_active_state--;
- }
- if (++count >= GET2(code, 1))
- { ADD_NEW(state_offset + dlen + 3, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- }
- break;
-
-
-/* ========================================================================== */
- /* These are the class-handling opcodes */
-
- case OP_CLASS:
- case OP_NCLASS:
- case OP_XCLASS:
- {
- BOOL isinclass = FALSE;
- int next_state_offset;
- const uschar *ecode;
-
- /* For a simple class, there is always just a 32-byte table, and we
- can set isinclass from it. */
-
- if (codevalue != OP_XCLASS)
- {
- ecode = code + 33;
- if (clen > 0)
- {
- isinclass = (c > 255)? (codevalue == OP_NCLASS) :
- ((code[1 + c/8] & (1 << (c&7))) != 0);
- }
- }
-
- /* An extended class may have a table or a list of single characters,
- ranges, or both, and it may be positive or negative. There's a
- function that sorts all this out. */
-
- else
- {
- ecode = code + GET(code, 1);
- if (clen > 0) isinclass = _pcre_xclass(c, code + 1 + LINK_SIZE);
- }
-
- /* At this point, isinclass is set for all kinds of class, and ecode
- points to the byte after the end of the class. If there is a
- quantifier, this is where it will be. */
-
- next_state_offset = ecode - start_code;
-
- switch (*ecode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- ADD_ACTIVE(next_state_offset + 1, 0);
- if (isinclass) { ADD_NEW(state_offset, 0); }
- break;
-
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- count = current_state->count; /* Already matched */
- if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); }
- if (isinclass) { count++; ADD_NEW(state_offset, count); }
- break;
-
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- ADD_ACTIVE(next_state_offset + 1, 0);
- if (isinclass) { ADD_NEW(next_state_offset + 1, 0); }
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- count = current_state->count; /* Already matched */
- if (count >= GET2(ecode, 1))
- { ADD_ACTIVE(next_state_offset + 5, 0); }
- if (isinclass)
- {
- int max = GET2(ecode, 3);
- if (++count >= max && max != 0) /* Max 0 => no limit */
- { ADD_NEW(next_state_offset + 5, 0); }
- else
- { ADD_NEW(state_offset, count); }
- }
- break;
-
- default:
- if (isinclass) { ADD_NEW(next_state_offset, 0); }
- break;
- }
- }
- break;
-
-/* ========================================================================== */
- /* These are the opcodes for fancy brackets of various kinds. We have
- to use recursion in order to handle them. The "always failing" assertion
- (?!) is optimised to OP_FAIL when compiling, so we have to support that,
- though the other "backtracking verbs" are not supported. */
-
- case OP_FAIL:
- forced_fail++; /* Count FAILs for multiple states */
- break;
-
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- {
- int rc;
- int local_offsets[2];
- int local_workspace[1000];
- const uschar *endasscode = code + GET(code, 1);
-
- while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
-
- rc = internal_dfa_exec(
- md, /* static match data */
- code, /* this subexpression's code */
- ptr, /* where we currently are */
- ptr - start_subject, /* start offset */
- local_offsets, /* offset vector */
- sizeof(local_offsets)/sizeof(int), /* size of same */
- local_workspace, /* workspace vector */
- sizeof(local_workspace)/sizeof(int), /* size of same */
- ims, /* the current ims flags */
- rlevel, /* function recursion level */
- recursing); /* pass on regex recursion */
-
- if (rc == PCRE_ERROR_DFA_UITEM) return rc;
- if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK))
- { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_COND:
- case OP_SCOND:
- {
- int local_offsets[1000];
- int local_workspace[1000];
- int codelink = GET(code, 1);
- int condcode;
-
- /* Because of the way auto-callout works during compile, a callout item
- is inserted between OP_COND and an assertion condition. This does not
- happen for the other conditions. */
-
- if (code[LINK_SIZE+1] == OP_CALLOUT)
- {
- rrc = 0;
- if (pcre_callout != NULL)
- {
- pcre_callout_block cb;
- cb.version = 1; /* Version 1 of the callout block */
- cb.callout_number = code[LINK_SIZE+2];
- cb.offset_vector = offsets;
- cb.subject = (PCRE_SPTR)start_subject;
- cb.subject_length = end_subject - start_subject;
- cb.start_match = current_subject - start_subject;
- cb.current_position = ptr - start_subject;
- cb.pattern_position = GET(code, LINK_SIZE + 3);
- cb.next_item_length = GET(code, 3 + 2*LINK_SIZE);
- cb.capture_top = 1;
- cb.capture_last = -1;
- cb.callout_data = md->callout_data;
- if ((rrc = (*pcre_callout)(&cb)) < 0) return rrc; /* Abandon */
- }
- if (rrc > 0) break; /* Fail this thread */
- code += _pcre_OP_lengths[OP_CALLOUT]; /* Skip callout data */
- }
-
- condcode = code[LINK_SIZE+1];
-
- /* Back reference conditions are not supported */
-
- if (condcode == OP_CREF || condcode == OP_NCREF)
- return PCRE_ERROR_DFA_UCOND;
-
- /* The DEFINE condition is always false */
-
- if (condcode == OP_DEF)
- { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
-
- /* The only supported version of OP_RREF is for the value RREF_ANY,
- which means "test if in any recursion". We can't test for specifically
- recursed groups. */
-
- else if (condcode == OP_RREF || condcode == OP_NRREF)
- {
- int value = GET2(code, LINK_SIZE+2);
- if (value != RREF_ANY) return PCRE_ERROR_DFA_UCOND;
- if (recursing > 0)
- { ADD_ACTIVE(state_offset + LINK_SIZE + 4, 0); }
- else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
- }
-
- /* Otherwise, the condition is an assertion */
-
- else
- {
- int rc;
- const uschar *asscode = code + LINK_SIZE + 1;
- const uschar *endasscode = asscode + GET(asscode, 1);
-
- while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
-
- rc = internal_dfa_exec(
- md, /* fixed match data */
- asscode, /* this subexpression's code */
- ptr, /* where we currently are */
- ptr - start_subject, /* start offset */
- local_offsets, /* offset vector */
- sizeof(local_offsets)/sizeof(int), /* size of same */
- local_workspace, /* workspace vector */
- sizeof(local_workspace)/sizeof(int), /* size of same */
- ims, /* the current ims flags */
- rlevel, /* function recursion level */
- recursing); /* pass on regex recursion */
-
- if (rc == PCRE_ERROR_DFA_UITEM) return rc;
- if ((rc >= 0) ==
- (condcode == OP_ASSERT || condcode == OP_ASSERTBACK))
- { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); }
- else
- { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
- }
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_RECURSE:
- {
- int local_offsets[1000];
- int local_workspace[1000];
- int rc;
-
- DPRINTF(("%.*sStarting regex recursion %d\n", rlevel*2-2, SP,
- recursing + 1));
-
- rc = internal_dfa_exec(
- md, /* fixed match data */
- start_code + GET(code, 1), /* this subexpression's code */
- ptr, /* where we currently are */
- ptr - start_subject, /* start offset */
- local_offsets, /* offset vector */
- sizeof(local_offsets)/sizeof(int), /* size of same */
- local_workspace, /* workspace vector */
- sizeof(local_workspace)/sizeof(int), /* size of same */
- ims, /* the current ims flags */
- rlevel, /* function recursion level */
- recursing + 1); /* regex recurse level */
-
- DPRINTF(("%.*sReturn from regex recursion %d: rc=%d\n", rlevel*2-2, SP,
- recursing + 1, rc));
-
- /* Ran out of internal offsets */
-
- if (rc == 0) return PCRE_ERROR_DFA_RECURSE;
-
- /* For each successful matched substring, set up the next state with a
- count of characters to skip before trying it. Note that the count is in
- characters, not bytes. */
-
- if (rc > 0)
- {
- for (rc = rc*2 - 2; rc >= 0; rc -= 2)
- {
- const uschar *p = start_subject + local_offsets[rc];
- const uschar *pp = start_subject + local_offsets[rc+1];
- int charcount = local_offsets[rc+1] - local_offsets[rc];
- while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--;
- if (charcount > 0)
- {
- ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0, (charcount - 1));
- }
- else
- {
- ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0);
- }
- }
- }
- else if (rc != PCRE_ERROR_NOMATCH) return rc;
- }
- break;
-
- /*-----------------------------------------------------------------*/
- case OP_ONCE:
- {
- int local_offsets[2];
- int local_workspace[1000];
-
- int rc = internal_dfa_exec(
- md, /* fixed match data */
- code, /* this subexpression's code */
- ptr, /* where we currently are */
- ptr - start_subject, /* start offset */
- local_offsets, /* offset vector */
- sizeof(local_offsets)/sizeof(int), /* size of same */
- local_workspace, /* workspace vector */
- sizeof(local_workspace)/sizeof(int), /* size of same */
- ims, /* the current ims flags */
- rlevel, /* function recursion level */
- recursing); /* pass on regex recursion */
-
- if (rc >= 0)
- {
- const uschar *end_subpattern = code;
- int charcount = local_offsets[1] - local_offsets[0];
- int next_state_offset, repeat_state_offset;
-
- do { end_subpattern += GET(end_subpattern, 1); }
- while (*end_subpattern == OP_ALT);
- next_state_offset = end_subpattern - start_code + LINK_SIZE + 1;
-
- /* If the end of this subpattern is KETRMAX or KETRMIN, we must
- arrange for the repeat state also to be added to the relevant list.
- Calculate the offset, or set -1 for no repeat. */
-
- repeat_state_offset = (*end_subpattern == OP_KETRMAX ||
- *end_subpattern == OP_KETRMIN)?
- end_subpattern - start_code - GET(end_subpattern, 1) : -1;
-
- /* If we have matched an empty string, add the next state at the
- current character pointer. This is important so that the duplicate
- checking kicks in, which is what breaks infinite loops that match an
- empty string. */
-
- if (charcount == 0)
- {
- ADD_ACTIVE(next_state_offset, 0);
- }
-
- /* Optimization: if there are no more active states, and there
- are no new states yet set up, then skip over the subject string
- right here, to save looping. Otherwise, set up the new state to swing
- into action when the end of the substring is reached. */
-
- else if (i + 1 >= active_count && new_count == 0)
- {
- ptr += charcount;
- clen = 0;
- ADD_NEW(next_state_offset, 0);
-
- /* If we are adding a repeat state at the new character position,
- we must fudge things so that it is the only current state.
- Otherwise, it might be a duplicate of one we processed before, and
- that would cause it to be skipped. */
-
- if (repeat_state_offset >= 0)
- {
- next_active_state = active_states;
- active_count = 0;
- i = -1;
- ADD_ACTIVE(repeat_state_offset, 0);
- }
- }
- else
- {
- const uschar *p = start_subject + local_offsets[0];
- const uschar *pp = start_subject + local_offsets[1];
- while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--;
- ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1));
- if (repeat_state_offset >= 0)
- { ADD_NEW_DATA(-repeat_state_offset, 0, (charcount - 1)); }
- }
-
- }
- else if (rc != PCRE_ERROR_NOMATCH) return rc;
- }
- break;
-
-
-/* ========================================================================== */
- /* Handle callouts */
-
- case OP_CALLOUT:
- rrc = 0;
- if (pcre_callout != NULL)
- {
- pcre_callout_block cb;
- cb.version = 1; /* Version 1 of the callout block */
- cb.callout_number = code[1];
- cb.offset_vector = offsets;
- cb.subject = (PCRE_SPTR)start_subject;
- cb.subject_length = end_subject - start_subject;
- cb.start_match = current_subject - start_subject;
- cb.current_position = ptr - start_subject;
- cb.pattern_position = GET(code, 2);
- cb.next_item_length = GET(code, 2 + LINK_SIZE);
- cb.capture_top = 1;
- cb.capture_last = -1;
- cb.callout_data = md->callout_data;
- if ((rrc = (*pcre_callout)(&cb)) < 0) return rrc; /* Abandon */
- }
- if (rrc == 0)
- { ADD_ACTIVE(state_offset + _pcre_OP_lengths[OP_CALLOUT], 0); }
- break;
-
-
-/* ========================================================================== */
- default: /* Unsupported opcode */
- return PCRE_ERROR_DFA_UITEM;
- }
-
- NEXT_ACTIVE_STATE: continue;
-
- } /* End of loop scanning active states */
-
- /* We have finished the processing at the current subject character. If no
- new states have been set for the next character, we have found all the
- matches that we are going to find. If we are at the top level and partial
- matching has been requested, check for appropriate conditions.
-
- The "forced_ fail" variable counts the number of (*F) encountered for the
- character. If it is equal to the original active_count (saved in
- workspace[1]) it means that (*F) was found on every active state. In this
- case we don't want to give a partial match.
-
- The "could_continue" variable is true if a state could have continued but
- for the fact that the end of the subject was reached. */
-
- if (new_count <= 0)
- {
- if (rlevel == 1 && /* Top level, and */
- could_continue && /* Some could go on */
- forced_fail != workspace[1] && /* Not all forced fail & */
- ( /* either... */
- (md->moptions & PCRE_PARTIAL_HARD) != 0 /* Hard partial */
- || /* or... */
- ((md->moptions & PCRE_PARTIAL_SOFT) != 0 && /* Soft partial and */
- match_count < 0) /* no matches */
- ) && /* And... */
- ptr >= end_subject && /* Reached end of subject */
- ptr > current_subject) /* Matched non-empty string */
- {
- if (offsetcount >= 2)
- {
- offsets[0] = md->start_used_ptr - start_subject;
- offsets[1] = end_subject - start_subject;
- }
- match_count = PCRE_ERROR_PARTIAL;
- }
-
- DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n"
- "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, match_count,
- rlevel*2-2, SP));
- break; /* In effect, "return", but see the comment below */
- }
-
- /* One or more states are active for the next character. */
-
- ptr += clen; /* Advance to next subject character */
- } /* Loop to move along the subject string */
-
-/* Control gets here from "break" a few lines above. We do it this way because
-if we use "return" above, we have compiler trouble. Some compilers warn if
-there's nothing here because they think the function doesn't return a value. On
-the other hand, if we put a dummy statement here, some more clever compilers
-complain that it can't be reached. Sigh. */
-
-return match_count;
-}
-
-
-
-
-/*************************************************
-* Execute a Regular Expression - DFA engine *
-*************************************************/
-
-/* This external function applies a compiled re to a subject string using a DFA
-engine. This function calls the internal function multiple times if the pattern
-is not anchored.
-
-Arguments:
- argument_re points to the compiled expression
- extra_data points to extra data or is NULL
- subject points to the subject string
- length length of subject string (may contain binary zeros)
- start_offset where to start in the subject string
- options option bits
- offsets vector of match offsets
- offsetcount size of same
- workspace workspace vector
- wscount size of same
-
-Returns: > 0 => number of match offset pairs placed in offsets
- = 0 => offsets overflowed; longest matches are present
- -1 => failed to match
- < -1 => some kind of unexpected problem
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data,
- const char *subject, int length, int start_offset, int options, int *offsets,
- int offsetcount, int *workspace, int wscount)
-{
-real_pcre *re = (real_pcre *)argument_re;
-dfa_match_data match_block;
-dfa_match_data *md = &match_block;
-BOOL utf8, anchored, startline, firstline;
-const uschar *current_subject, *end_subject, *lcc;
-
-pcre_study_data internal_study;
-const pcre_study_data *study = NULL;
-real_pcre internal_re;
-
-const uschar *req_byte_ptr;
-const uschar *start_bits = NULL;
-BOOL first_byte_caseless = FALSE;
-BOOL req_byte_caseless = FALSE;
-int first_byte = -1;
-int req_byte = -1;
-int req_byte2 = -1;
-int newline;
-
-/* Plausibility checks */
-
-if ((options & ~PUBLIC_DFA_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
-if (re == NULL || subject == NULL || workspace == NULL ||
- (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
-if (offsetcount < 0) return PCRE_ERROR_BADCOUNT;
-if (wscount < 20) return PCRE_ERROR_DFA_WSSIZE;
-
-/* We need to find the pointer to any study data before we test for byte
-flipping, so we scan the extra_data block first. This may set two fields in the
-match block, so we must initialize them beforehand. However, the other fields
-in the match block must not be set until after the byte flipping. */
-
-md->tables = re->tables;
-md->callout_data = NULL;
-
-if (extra_data != NULL)
- {
- unsigned int flags = extra_data->flags;
- if ((flags & PCRE_EXTRA_STUDY_DATA) != 0)
- study = (const pcre_study_data *)extra_data->study_data;
- if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) return PCRE_ERROR_DFA_UMLIMIT;
- if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0)
- return PCRE_ERROR_DFA_UMLIMIT;
- if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0)
- md->callout_data = extra_data->callout_data;
- if ((flags & PCRE_EXTRA_TABLES) != 0)
- md->tables = extra_data->tables;
- }
-
-/* Check that the first field in the block is the magic number. If it is not,
-test for a regex that was compiled on a host of opposite endianness. If this is
-the case, flipped values are put in internal_re and internal_study if there was
-study data too. */
-
-if (re->magic_number != MAGIC_NUMBER)
- {
- re = _pcre_try_flipped(re, &internal_re, study, &internal_study);
- if (re == NULL) return PCRE_ERROR_BADMAGIC;
- if (study != NULL) study = &internal_study;
- }
-
-/* Set some local values */
-
-current_subject = (const unsigned char *)subject + start_offset;
-end_subject = (const unsigned char *)subject + length;
-req_byte_ptr = current_subject - 1;
-
-#ifdef SUPPORT_UTF8
-utf8 = (re->options & PCRE_UTF8) != 0;
-#else
-utf8 = FALSE;
-#endif
-
-anchored = (options & (PCRE_ANCHORED|PCRE_DFA_RESTART)) != 0 ||
- (re->options & PCRE_ANCHORED) != 0;
-
-/* The remaining fixed data for passing around. */
-
-md->start_code = (const uschar *)argument_re +
- re->name_table_offset + re->name_count * re->name_entry_size;
-md->start_subject = (const unsigned char *)subject;
-md->end_subject = end_subject;
-md->start_offset = start_offset;
-md->moptions = options;
-md->poptions = re->options;
-
-/* If the BSR option is not set at match time, copy what was set
-at compile time. */
-
-if ((md->moptions & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) == 0)
- {
- if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0)
- md->moptions |= re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE);
-#ifdef BSR_ANYCRLF
- else md->moptions |= PCRE_BSR_ANYCRLF;
-#endif
- }
-
-/* Handle different types of newline. The three bits give eight cases. If
-nothing is set at run time, whatever was used at compile time applies. */
-
-switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : (pcre_uint32)options) &
- PCRE_NEWLINE_BITS)
- {
- case 0: newline = NEWLINE; break; /* Compile-time default */
- case PCRE_NEWLINE_CR: newline = CHAR_CR; break;
- case PCRE_NEWLINE_LF: newline = CHAR_NL; break;
- case PCRE_NEWLINE_CR+
- PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break;
- case PCRE_NEWLINE_ANY: newline = -1; break;
- case PCRE_NEWLINE_ANYCRLF: newline = -2; break;
- default: return PCRE_ERROR_BADNEWLINE;
- }
-
-if (newline == -2)
- {
- md->nltype = NLTYPE_ANYCRLF;
- }
-else if (newline < 0)
- {
- md->nltype = NLTYPE_ANY;
- }
-else
- {
- md->nltype = NLTYPE_FIXED;
- if (newline > 255)
- {
- md->nllen = 2;
- md->nl[0] = (newline >> 8) & 255;
- md->nl[1] = newline & 255;
- }
- else
- {
- md->nllen = 1;
- md->nl[0] = newline;
- }
- }
-
-/* Check a UTF-8 string if required. Unfortunately there's no way of passing
-back the character offset. */
-
-#ifdef SUPPORT_UTF8
-if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0)
- {
- if (_pcre_valid_utf8((uschar *)subject, length) >= 0)
- return PCRE_ERROR_BADUTF8;
- if (start_offset > 0 && start_offset < length)
- {
- int tb = ((uschar *)subject)[start_offset];
- if (tb > 127)
- {
- tb &= 0xc0;
- if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET;
- }
- }
- }
-#endif
-
-/* If the exec call supplied NULL for tables, use the inbuilt ones. This
-is a feature that makes it possible to save compiled regex and re-use them
-in other programs later. */
-
-if (md->tables == NULL) md->tables = _pcre_default_tables;
-
-/* The lower casing table and the "must be at the start of a line" flag are
-used in a loop when finding where to start. */
-
-lcc = md->tables + lcc_offset;
-startline = (re->flags & PCRE_STARTLINE) != 0;
-firstline = (re->options & PCRE_FIRSTLINE) != 0;
-
-/* Set up the first character to match, if available. The first_byte value is
-never set for an anchored regular expression, but the anchoring may be forced
-at run time, so we have to test for anchoring. The first char may be unset for
-an unanchored pattern, of course. If there's no first char and the pattern was
-studied, there may be a bitmap of possible first characters. */
-
-if (!anchored)
- {
- if ((re->flags & PCRE_FIRSTSET) != 0)
- {
- first_byte = re->first_byte & 255;
- if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE)
- first_byte = lcc[first_byte];
- }
- else
- {
- if (!startline && study != NULL &&
- (study->flags & PCRE_STUDY_MAPPED) != 0)
- start_bits = study->start_bits;
- }
- }
-
-/* For anchored or unanchored matches, there may be a "last known required
-character" set. */
-
-if ((re->flags & PCRE_REQCHSET) != 0)
- {
- req_byte = re->req_byte & 255;
- req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0;
- req_byte2 = (md->tables + fcc_offset)[req_byte]; /* case flipped */
- }
-
-/* Call the main matching function, looping for a non-anchored regex after a
-failed match. If not restarting, perform certain optimizations at the start of
-a match. */
-
-for (;;)
- {
- int rc;
-
- if ((options & PCRE_DFA_RESTART) == 0)
- {
- const uschar *save_end_subject = end_subject;
-
- /* If firstline is TRUE, the start of the match is constrained to the first
- line of a multiline string. Implement this by temporarily adjusting
- end_subject so that we stop scanning at a newline. If the match fails at
- the newline, later code breaks this loop. */
-
- if (firstline)
- {
- USPTR t = current_subject;
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- while (t < md->end_subject && !IS_NEWLINE(t))
- {
- t++;
- while (t < end_subject && (*t & 0xc0) == 0x80) t++;
- }
- }
- else
-#endif
- while (t < md->end_subject && !IS_NEWLINE(t)) t++;
- end_subject = t;
- }
-
- /* There are some optimizations that avoid running the match if a known
- starting point is not found. However, there is an option that disables
- these, for testing and for ensuring that all callouts do actually occur. */
-
- if ((options & PCRE_NO_START_OPTIMIZE) == 0)
- {
- /* Advance to a known first byte. */
-
- if (first_byte >= 0)
- {
- if (first_byte_caseless)
- while (current_subject < end_subject &&
- lcc[*current_subject] != first_byte)
- current_subject++;
- else
- while (current_subject < end_subject &&
- *current_subject != first_byte)
- current_subject++;
- }
-
- /* Or to just after a linebreak for a multiline match if possible */
-
- else if (startline)
- {
- if (current_subject > md->start_subject + start_offset)
- {
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- while (current_subject < end_subject &&
- !WAS_NEWLINE(current_subject))
- {
- current_subject++;
- while(current_subject < end_subject &&
- (*current_subject & 0xc0) == 0x80)
- current_subject++;
- }
- }
- else
-#endif
- while (current_subject < end_subject && !WAS_NEWLINE(current_subject))
- current_subject++;
-
- /* If we have just passed a CR and the newline option is ANY or
- ANYCRLF, and we are now at a LF, advance the match position by one
- more character. */
-
- if (current_subject[-1] == CHAR_CR &&
- (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) &&
- current_subject < end_subject &&
- *current_subject == CHAR_NL)
- current_subject++;
- }
- }
-
- /* Or to a non-unique first char after study */
-
- else if (start_bits != NULL)
- {
- while (current_subject < end_subject)
- {
- register unsigned int c = *current_subject;
- if ((start_bits[c/8] & (1 << (c&7))) == 0) current_subject++;
- else break;
- }
- }
- }
-
- /* Restore fudged end_subject */
-
- end_subject = save_end_subject;
-
- /* The following two optimizations are disabled for partial matching or if
- disabling is explicitly requested (and of course, by the test above, this
- code is not obeyed when restarting after a partial match). */
-
- if ((options & PCRE_NO_START_OPTIMIZE) == 0 &&
- (options & (PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT)) == 0)
- {
- /* If the pattern was studied, a minimum subject length may be set. This
- is a lower bound; no actual string of that length may actually match the
- pattern. Although the value is, strictly, in characters, we treat it as
- bytes to avoid spending too much time in this optimization. */
-
- if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 &&
- (pcre_uint32)(end_subject - current_subject) < study->minlength)
- return PCRE_ERROR_NOMATCH;
-
- /* If req_byte is set, we know that that character must appear in the
- subject for the match to succeed. If the first character is set, req_byte
- must be later in the subject; otherwise the test starts at the match
- point. This optimization can save a huge amount of work in patterns with
- nested unlimited repeats that aren't going to match. Writing separate
- code for cased/caseless versions makes it go faster, as does using an
- autoincrement and backing off on a match.
-
- HOWEVER: when the subject string is very, very long, searching to its end
- can take a long time, and give bad performance on quite ordinary
- patterns. This showed up when somebody was matching /^C/ on a 32-megabyte
- string... so we don't do this when the string is sufficiently long. */
-
- if (req_byte >= 0 && end_subject - current_subject < REQ_BYTE_MAX)
- {
- register const uschar *p = current_subject + ((first_byte >= 0)? 1 : 0);
-
- /* We don't need to repeat the search if we haven't yet reached the
- place we found it at last time. */
-
- if (p > req_byte_ptr)
- {
- if (req_byte_caseless)
- {
- while (p < end_subject)
- {
- register int pp = *p++;
- if (pp == req_byte || pp == req_byte2) { p--; break; }
- }
- }
- else
- {
- while (p < end_subject)
- {
- if (*p++ == req_byte) { p--; break; }
- }
- }
-
- /* If we can't find the required character, break the matching loop,
- which will cause a return or PCRE_ERROR_NOMATCH. */
-
- if (p >= end_subject) break;
-
- /* If we have found the required character, save the point where we
- found it, so that we don't search again next time round the loop if
- the start hasn't passed this character yet. */
-
- req_byte_ptr = p;
- }
- }
- }
- } /* End of optimizations that are done when not restarting */
-
- /* OK, now we can do the business */
-
- md->start_used_ptr = current_subject;
-
- rc = internal_dfa_exec(
- md, /* fixed match data */
- md->start_code, /* this subexpression's code */
- current_subject, /* where we currently are */
- start_offset, /* start offset in subject */
- offsets, /* offset vector */
- offsetcount, /* size of same */
- workspace, /* workspace vector */
- wscount, /* size of same */
- re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL), /* ims flags */
- 0, /* function recurse level */
- 0); /* regex recurse level */
-
- /* Anything other than "no match" means we are done, always; otherwise, carry
- on only if not anchored. */
-
- if (rc != PCRE_ERROR_NOMATCH || anchored) return rc;
-
- /* Advance to the next subject character unless we are at the end of a line
- and firstline is set. */
-
- if (firstline && IS_NEWLINE(current_subject)) break;
- current_subject++;
- if (utf8)
- {
- while (current_subject < end_subject && (*current_subject & 0xc0) == 0x80)
- current_subject++;
- }
- if (current_subject > end_subject) break;
-
- /* If we have just passed a CR and we are now at a LF, and the pattern does
- not contain any explicit matches for \r or \n, and the newline option is CRLF
- or ANY or ANYCRLF, advance the match position by one more character. */
-
- if (current_subject[-1] == CHAR_CR &&
- current_subject < end_subject &&
- *current_subject == CHAR_NL &&
- (re->flags & PCRE_HASCRORLF) == 0 &&
- (md->nltype == NLTYPE_ANY ||
- md->nltype == NLTYPE_ANYCRLF ||
- md->nllen == 2))
- current_subject++;
-
- } /* "Bumpalong" loop */
-
-return PCRE_ERROR_NOMATCH;
-}
-
-/* End of pcre_dfa_exec.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2010 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains pcre_exec(), the externally visible function that does
-pattern matching using an NFA algorithm, trying to mimic Perl as closely as
-possible. There are also some static supporting functions. */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define NLBLOCK md /* Block containing newline information */
-#define PSSTART start_subject /* Field containing processed string start */
-#define PSEND end_subject /* Field containing processed string end */
-
-#include "pcre_internal.h"
-
-/* Undefine some potentially clashing cpp symbols */
-
-#undef min
-#undef max
-
-/* Flag bits for the match() function */
-
-#define match_condassert 0x01 /* Called to check a condition assertion */
-#define match_cbegroup 0x02 /* Could-be-empty unlimited repeat group */
-
-/* Non-error returns from the match() function. Error returns are externally
-defined PCRE_ERROR_xxx codes, which are all negative. */
-
-#define MATCH_MATCH 1
-#define MATCH_NOMATCH 0
-
-/* Special internal returns from the match() function. Make them sufficiently
-negative to avoid the external error codes. */
-
-#define MATCH_COMMIT (-999)
-#define MATCH_PRUNE (-998)
-#define MATCH_SKIP (-997)
-#define MATCH_THEN (-996)
-
-/* Maximum number of ints of offset to save on the stack for recursive calls.
-If the offset vector is bigger, malloc is used. This should be a multiple of 3,
-because the offset vector is always a multiple of 3 long. */
-
-#define REC_STACK_SAVE_MAX 30
-
-/* Min and max values for the common repeats; for the maxima, 0 => infinity */
-
-static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
-static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
-
-
-
-#ifdef PCRE_DEBUG
-/*************************************************
-* Debugging function to print chars *
-*************************************************/
-
-/* Print a sequence of chars in printable format, stopping at the end of the
-subject if the requested.
-
-Arguments:
- p points to characters
- length number to print
- is_subject TRUE if printing from within md->start_subject
- md pointer to matching data block, if is_subject is TRUE
-
-Returns: nothing
-*/
-
-static void
-pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
-{
-unsigned int c;
-if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
-while (length-- > 0)
- if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c);
-}
-#endif
-
-
-
-/*************************************************
-* Match a back-reference *
-*************************************************/
-
-/* If a back reference hasn't been set, the length that is passed is greater
-than the number of characters left in the string, so the match fails.
-
-Arguments:
- offset index into the offset vector
- eptr points into the subject
- length length to be matched
- md points to match data block
- ims the ims flags
-
-Returns: TRUE if matched
-*/
-
-static BOOL
-match_ref(int offset, register USPTR eptr, int length, match_data *md,
- unsigned long int ims)
-{
-USPTR p = md->start_subject + md->offset_vector[offset];
-
-#ifdef PCRE_DEBUG
-if (eptr >= md->end_subject)
- printf("matching subject <null>");
-else
- {
- printf("matching subject ");
- pchars(eptr, length, TRUE, md);
- }
-printf(" against backref ");
-pchars(p, length, FALSE, md);
-printf("\n");
-#endif
-
-/* Always fail if not enough characters left */
-
-if (length > md->end_subject - eptr) return FALSE;
-
-/* Separate the caseless case for speed. In UTF-8 mode we can only do this
-properly if Unicode properties are supported. Otherwise, we can check only
-ASCII characters. */
-
-if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
-#ifdef SUPPORT_UCP
- if (md->utf8)
- {
- USPTR endptr = eptr + length;
- while (eptr < endptr)
- {
- int c, d;
- GETCHARINC(c, eptr);
- GETCHARINC(d, p);
- if (c != d && c != UCD_OTHERCASE(d)) return FALSE;
- }
- }
- else
-#endif
-#endif
-
- /* The same code works when not in UTF-8 mode and in UTF-8 mode when there
- is no UCP support. */
-
- while (length-- > 0)
- { if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; }
- }
-
-/* In the caseful case, we can just compare the bytes, whether or not we
-are in UTF-8 mode. */
-
-else
- { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
-
-return TRUE;
-}
-
-
-
-/***************************************************************************
-****************************************************************************
- RECURSION IN THE match() FUNCTION
-
-The match() function is highly recursive, though not every recursive call
-increases the recursive depth. Nevertheless, some regular expressions can cause
-it to recurse to a great depth. I was writing for Unix, so I just let it call
-itself recursively. This uses the stack for saving everything that has to be
-saved for a recursive call. On Unix, the stack can be large, and this works
-fine.
-
-It turns out that on some non-Unix-like systems there are problems with
-programs that use a lot of stack. (This despite the fact that every last chip
-has oodles of memory these days, and techniques for extending the stack have
-been known for decades.) So....
-
-There is a fudge, triggered by defining NO_RECURSE, which avoids recursive
-calls by keeping local variables that need to be preserved in blocks of memory
-obtained from malloc() instead instead of on the stack. Macros are used to
-achieve this so that the actual code doesn't look very different to what it
-always used to.
-
-The original heap-recursive code used longjmp(). However, it seems that this
-can be very slow on some operating systems. Following a suggestion from Stan
-Switzer, the use of longjmp() has been abolished, at the cost of having to
-provide a unique number for each call to RMATCH. There is no way of generating
-a sequence of numbers at compile time in C. I have given them names, to make
-them stand out more clearly.
-
-Crude tests on x86 Linux show a small speedup of around 5-8%. However, on
-FreeBSD, avoiding longjmp() more than halves the time taken to run the standard
-tests. Furthermore, not using longjmp() means that local dynamic variables
-don't have indeterminate values; this has meant that the frame size can be
-reduced because the result can be "passed back" by straight setting of the
-variable instead of being passed in the frame.
-****************************************************************************
-***************************************************************************/
-
-/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN
-below must be updated in sync. */
-
-enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10,
- RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,
- RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,
- RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40,
- RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50,
- RM51, RM52, RM53, RM54 };
-
-/* These versions of the macros use the stack, as normal. There are debugging
-versions and production versions. Note that the "rw" argument of RMATCH isn't
-actually used in this definition. */
-
-#ifndef NO_RECURSE
-#define REGISTER register
-
-#ifdef PCRE_DEBUG
-#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \
- { \
- printf("match() called in line %d\n", __LINE__); \
- rrc = match(ra,rb,mstart,markptr,rc,rd,re,rf,rg,rdepth+1); \
- printf("to line %d\n", __LINE__); \
- }
-#define RRETURN(ra) \
- { \
- printf("match() returned %d from line %d ", ra, __LINE__); \
- return ra; \
- }
-#else
-#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \
- rrc = match(ra,rb,mstart,markptr,rc,rd,re,rf,rg,rdepth+1)
-#define RRETURN(ra) return ra
-#endif
-
-#else
-
-
-/* These versions of the macros manage a private stack on the heap. Note that
-the "rd" argument of RMATCH isn't actually used in this definition. It's the md
-argument of match(), which never changes. */
-
-#define REGISTER
-
-#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw)\
- {\
- heapframe *newframe = (pcre_stack_malloc)(sizeof(heapframe));\
- frame->Xwhere = rw; \
- newframe->Xeptr = ra;\
- newframe->Xecode = rb;\
- newframe->Xmstart = mstart;\
- newframe->Xmarkptr = markptr;\
- newframe->Xoffset_top = rc;\
- newframe->Xims = re;\
- newframe->Xeptrb = rf;\
- newframe->Xflags = rg;\
- newframe->Xrdepth = frame->Xrdepth + 1;\
- newframe->Xprevframe = frame;\
- frame = newframe;\
- DPRINTF(("restarting from line %d\n", __LINE__));\
- goto HEAP_RECURSE;\
- L_##rw:\
- DPRINTF(("jumped back to line %d\n", __LINE__));\
- }
-
-#define RRETURN(ra)\
- {\
- heapframe *newframe = frame;\
- frame = newframe->Xprevframe;\
- (pcre_stack_free)(newframe);\
- if (frame != NULL)\
- {\
- rrc = ra;\
- goto HEAP_RETURN;\
- }\
- return ra;\
- }
-
-
-/* Structure for remembering the local variables in a private frame */
-
-typedef struct heapframe {
- struct heapframe *Xprevframe;
-
- /* Function arguments that may change */
-
- USPTR Xeptr;
- const uschar *Xecode;
- USPTR Xmstart;
- USPTR Xmarkptr;
- int Xoffset_top;
- long int Xims;
- eptrblock *Xeptrb;
- int Xflags;
- unsigned int Xrdepth;
-
- /* Function local variables */
-
- USPTR Xcallpat;
-#ifdef SUPPORT_UTF8
- USPTR Xcharptr;
-#endif
- USPTR Xdata;
- USPTR Xnext;
- USPTR Xpp;
- USPTR Xprev;
- USPTR Xsaved_eptr;
-
- recursion_info Xnew_recursive;
-
- BOOL Xcur_is_word;
- BOOL Xcondition;
- BOOL Xprev_is_word;
-
- unsigned long int Xoriginal_ims;
-
-#ifdef SUPPORT_UCP
- int Xprop_type;
- int Xprop_value;
- int Xprop_fail_result;
- int Xprop_category;
- int Xprop_chartype;
- int Xprop_script;
- int Xoclength;
- uschar Xocchars[8];
-#endif
-
- int Xcodelink;
- int Xctype;
- unsigned int Xfc;
- int Xfi;
- int Xlength;
- int Xmax;
- int Xmin;
- int Xnumber;
- int Xoffset;
- int Xop;
- int Xsave_capture_last;
- int Xsave_offset1, Xsave_offset2, Xsave_offset3;
- int Xstacksave[REC_STACK_SAVE_MAX];
-
- eptrblock Xnewptrb;
-
- /* Where to jump back to */
-
- int Xwhere;
-
-} heapframe;
-
-#endif
-
-
-/***************************************************************************
-***************************************************************************/
-
-
-
-/*************************************************
-* Match from current position *
-*************************************************/
-
-/* This function is called recursively in many circumstances. Whenever it
-returns a negative (error) response, the outer incarnation must also return the
-same response. */
-
-/* These macros pack up tests that are used for partial matching, and which
-appears several times in the code. We set the "hit end" flag if the pointer is
-at the end of the subject and also past the start of the subject (i.e.
-something has been matched). For hard partial matching, we then return
-immediately. The second one is used when we already know we are past the end of
-the subject. */
-
-#define CHECK_PARTIAL()\
- if (md->partial != 0 && eptr >= md->end_subject && eptr > mstart)\
- {\
- md->hitend = TRUE;\
- if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL);\
- }
-
-#define SCHECK_PARTIAL()\
- if (md->partial != 0 && eptr > mstart)\
- {\
- md->hitend = TRUE;\
- if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL);\
- }
-
-
-/* Performance note: It might be tempting to extract commonly used fields from
-the md structure (e.g. utf8, end_subject) into individual variables to improve
-performance. Tests using gcc on a SPARC disproved this; in the first case, it
-made performance worse.
-
-Arguments:
- eptr pointer to current character in subject
- ecode pointer to current position in compiled code
- mstart pointer to the current match start position (can be modified
- by encountering \K)
- markptr pointer to the most recent MARK name, or NULL
- offset_top current top pointer
- md pointer to "static" info for the match
- ims current /i, /m, and /s options
- eptrb pointer to chain of blocks containing eptr at start of
- brackets - for testing for empty matches
- flags can contain
- match_condassert - this is an assertion condition
- match_cbegroup - this is the start of an unlimited repeat
- group that can match an empty string
- rdepth the recursion depth
-
-Returns: MATCH_MATCH if matched ) these values are >= 0
- MATCH_NOMATCH if failed to match )
- a negative PCRE_ERROR_xxx value if aborted by an error condition
- (e.g. stopped by repeated call or recursion limit)
-*/
-
-static int
-match(REGISTER USPTR eptr, REGISTER const uschar *ecode, USPTR mstart, USPTR
- markptr, int offset_top, match_data *md, unsigned long int ims,
- eptrblock *eptrb, int flags, unsigned int rdepth)
-{
-/* These variables do not need to be preserved over recursion in this function,
-so they can be ordinary variables in all cases. Mark some of them with
-"register" because they are used a lot in loops. */
-
-register int rrc; /* Returns from recursive calls */
-register int i; /* Used for loops not involving calls to RMATCH() */
-register unsigned int c; /* Character values not kept over RMATCH() calls */
-register BOOL utf8; /* Local copy of UTF-8 flag for speed */
-
-BOOL minimize, possessive; /* Quantifier options */
-int condcode;
-
-/* When recursion is not being used, all "local" variables that have to be
-preserved over calls to RMATCH() are part of a "frame" which is obtained from
-heap storage. Set up the top-level frame here; others are obtained from the
-heap whenever RMATCH() does a "recursion". See the macro definitions above. */
-
-#ifdef NO_RECURSE
-heapframe *frame = (pcre_stack_malloc)(sizeof(heapframe));
-frame->Xprevframe = NULL; /* Marks the top level */
-
-/* Copy in the original argument variables */
-
-frame->Xeptr = eptr;
-frame->Xecode = ecode;
-frame->Xmstart = mstart;
-frame->Xmarkptr = markptr;
-frame->Xoffset_top = offset_top;
-frame->Xims = ims;
-frame->Xeptrb = eptrb;
-frame->Xflags = flags;
-frame->Xrdepth = rdepth;
-
-/* This is where control jumps back to to effect "recursion" */
-
-HEAP_RECURSE:
-
-/* Macros make the argument variables come from the current frame */
-
-#define eptr frame->Xeptr
-#define ecode frame->Xecode
-#define mstart frame->Xmstart
-#define markptr frame->Xmarkptr
-#define offset_top frame->Xoffset_top
-#define ims frame->Xims
-#define eptrb frame->Xeptrb
-#define flags frame->Xflags
-#define rdepth frame->Xrdepth
-
-/* Ditto for the local variables */
-
-#ifdef SUPPORT_UTF8
-#define charptr frame->Xcharptr
-#endif
-#define callpat frame->Xcallpat
-#define codelink frame->Xcodelink
-#define data frame->Xdata
-#define next frame->Xnext
-#define pp frame->Xpp
-#define prev frame->Xprev
-#define saved_eptr frame->Xsaved_eptr
-
-#define new_recursive frame->Xnew_recursive
-
-#define cur_is_word frame->Xcur_is_word
-#define condition frame->Xcondition
-#define prev_is_word frame->Xprev_is_word
-
-#define original_ims frame->Xoriginal_ims
-
-#ifdef SUPPORT_UCP
-#define prop_type frame->Xprop_type
-#define prop_value frame->Xprop_value
-#define prop_fail_result frame->Xprop_fail_result
-#define prop_category frame->Xprop_category
-#define prop_chartype frame->Xprop_chartype
-#define prop_script frame->Xprop_script
-#define oclength frame->Xoclength
-#define occhars frame->Xocchars
-#endif
-
-#define ctype frame->Xctype
-#define fc frame->Xfc
-#define fi frame->Xfi
-#define length frame->Xlength
-#define max frame->Xmax
-#define min frame->Xmin
-#define number frame->Xnumber
-#define offset frame->Xoffset
-#define op frame->Xop
-#define save_capture_last frame->Xsave_capture_last
-#define save_offset1 frame->Xsave_offset1
-#define save_offset2 frame->Xsave_offset2
-#define save_offset3 frame->Xsave_offset3
-#define stacksave frame->Xstacksave
-
-#define newptrb frame->Xnewptrb
-
-/* When recursion is being used, local variables are allocated on the stack and
-get preserved during recursion in the normal way. In this environment, fi and
-i, and fc and c, can be the same variables. */
-
-#else /* NO_RECURSE not defined */
-#define fi i
-#define fc c
-
-
-#ifdef SUPPORT_UTF8 /* Many of these variables are used only */
-const uschar *charptr; /* in small blocks of the code. My normal */
-#endif /* style of coding would have declared */
-const uschar *callpat; /* them within each of those blocks. */
-const uschar *data; /* However, in order to accommodate the */
-const uschar *next; /* version of this code that uses an */
-USPTR pp; /* external "stack" implemented on the */
-const uschar *prev; /* heap, it is easier to declare them all */
-USPTR saved_eptr; /* here, so the declarations can be cut */
- /* out in a block. The only declarations */
-recursion_info new_recursive; /* within blocks below are for variables */
- /* that do not have to be preserved over */
-BOOL cur_is_word; /* a recursive call to RMATCH(). */
-BOOL condition;
-BOOL prev_is_word;
-
-unsigned long int original_ims;
-
-#ifdef SUPPORT_UCP
-int prop_type;
-int prop_value;
-int prop_fail_result;
-int prop_category;
-int prop_chartype;
-int prop_script;
-int oclength;
-uschar occhars[8];
-#endif
-
-int codelink;
-int ctype;
-int length;
-int max;
-int min;
-int number;
-int offset;
-int op;
-int save_capture_last;
-int save_offset1, save_offset2, save_offset3;
-int stacksave[REC_STACK_SAVE_MAX];
-
-eptrblock newptrb;
-#endif /* NO_RECURSE */
-
-/* These statements are here to stop the compiler complaining about unitialized
-variables. */
-
-#ifdef SUPPORT_UCP
-prop_value = 0;
-prop_fail_result = 0;
-#endif
-
-
-/* This label is used for tail recursion, which is used in a few cases even
-when NO_RECURSE is not defined, in order to reduce the amount of stack that is
-used. Thanks to Ian Taylor for noticing this possibility and sending the
-original patch. */
-
-TAIL_RECURSE:
-
-/* OK, now we can get on with the real code of the function. Recursive calls
-are specified by the macro RMATCH and RRETURN is used to return. When
-NO_RECURSE is *not* defined, these just turn into a recursive call to match()
-and a "return", respectively (possibly with some debugging if PCRE_DEBUG is
-defined). However, RMATCH isn't like a function call because it's quite a
-complicated macro. It has to be used in one particular way. This shouldn't,
-however, impact performance when true recursion is being used. */
-
-#ifdef SUPPORT_UTF8
-utf8 = md->utf8; /* Local copy of the flag */
-#else
-utf8 = FALSE;
-#endif
-
-/* First check that we haven't called match() too many times, or that we
-haven't exceeded the recursive call limit. */
-
-if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT);
-if (rdepth >= md->match_limit_recursion) RRETURN(PCRE_ERROR_RECURSIONLIMIT);
-
-original_ims = ims; /* Save for resetting on ')' */
-
-/* At the start of a group with an unlimited repeat that may match an empty
-string, the match_cbegroup flag is set. When this is the case, add the current
-subject pointer to the chain of such remembered pointers, to be checked when we
-hit the closing ket, in order to break infinite loops that match no characters.
-When match() is called in other circumstances, don't add to the chain. The
-match_cbegroup flag must NOT be used with tail recursion, because the memory
-block that is used is on the stack, so a new one may be required for each
-match(). */
-
-if ((flags & match_cbegroup) != 0)
- {
- newptrb.epb_saved_eptr = eptr;
- newptrb.epb_prev = eptrb;
- eptrb = &newptrb;
- }
-
-/* Now start processing the opcodes. */
-
-for (;;)
- {
- minimize = possessive = FALSE;
- op = *ecode;
-
- switch(op)
- {
- case OP_FAIL:
- RRETURN(MATCH_NOMATCH);
-
- case OP_PRUNE:
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
- ims, eptrb, flags, RM51);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_PRUNE);
-
- case OP_COMMIT:
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
- ims, eptrb, flags, RM52);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_COMMIT);
-
- case OP_SKIP:
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
- ims, eptrb, flags, RM53);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- md->start_match_ptr = eptr; /* Pass back current position */
- RRETURN(MATCH_SKIP);
-
- case OP_THEN:
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
- ims, eptrb, flags, RM54);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- RRETURN(MATCH_THEN);
-
- /* Handle a capturing bracket. If there is space in the offset vector, save
- the current subject position in the working slot at the top of the vector.
- We mustn't change the current values of the data slot, because they may be
- set from a previous iteration of this group, and be referred to by a
- reference inside the group.
-
- If the bracket fails to match, we need to restore this value and also the
- values of the final offsets, in case they were set by a previous iteration
- of the same bracket.
-
- If there isn't enough space in the offset vector, treat this as if it were
- a non-capturing bracket. Don't worry about setting the flag for the error
- case here; that is handled in the code for KET. */
-
- case OP_CBRA:
- case OP_SCBRA:
- number = GET2(ecode, 1+LINK_SIZE);
- offset = number << 1;
-
-#ifdef PCRE_DEBUG
- printf("start bracket %d\n", number);
- printf("subject=");
- pchars(eptr, 16, TRUE, md);
- printf("\n");
-#endif
-
- if (offset < md->offset_max)
- {
- save_offset1 = md->offset_vector[offset];
- save_offset2 = md->offset_vector[offset+1];
- save_offset3 = md->offset_vector[md->offset_end - number];
- save_capture_last = md->capture_last;
-
- DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
- md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
-
- flags = (op == OP_SCBRA)? match_cbegroup : 0;
- do
- {
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
- ims, eptrb, flags, RM1);
- if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
- md->capture_last = save_capture_last;
- ecode += GET(ecode, 1);
- }
- while (*ecode == OP_ALT);
-
- DPRINTF(("bracket %d failed\n", number));
-
- md->offset_vector[offset] = save_offset1;
- md->offset_vector[offset+1] = save_offset2;
- md->offset_vector[md->offset_end - number] = save_offset3;
-
- RRETURN(MATCH_NOMATCH);
- }
-
- /* FALL THROUGH ... Insufficient room for saving captured contents. Treat
- as a non-capturing bracket. */
-
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
-
- DPRINTF(("insufficient capture room: treat as non-capturing\n"));
-
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
- /* VVVVVVVVVVVVVVVVVVVVVVVVV */
-
- /* Non-capturing bracket. Loop for all the alternatives. When we get to the
- final alternative within the brackets, we would return the result of a
- recursive call to match() whatever happened. We can reduce stack usage by
- turning this into a tail recursion, except in the case when match_cbegroup
- is set.*/
-
- case OP_BRA:
- case OP_SBRA:
- DPRINTF(("start non-capturing bracket\n"));
- flags = (op >= OP_SBRA)? match_cbegroup : 0;
- for (;;)
- {
- if (ecode[GET(ecode, 1)] != OP_ALT) /* Final alternative */
- {
- if (flags == 0) /* Not a possibly empty group */
- {
- ecode += _pcre_OP_lengths[*ecode];
- DPRINTF(("bracket 0 tail recursion\n"));
- goto TAIL_RECURSE;
- }
-
- /* Possibly empty group; can't use tail recursion. */
-
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims,
- eptrb, flags, RM48);
- RRETURN(rrc);
- }
-
- /* For non-final alternatives, continue the loop for a NOMATCH result;
- otherwise return. */
-
- RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims,
- eptrb, flags, RM2);
- if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
- ecode += GET(ecode, 1);
- }
- /* Control never reaches here. */
-
- /* Conditional group: compilation checked that there are no more than
- two branches. If the condition is false, skipping the first branch takes us
- past the end if there is only one branch, but that's OK because that is
- exactly what going to the ket would do. As there is only one branch to be
- obeyed, we can use tail recursion to avoid using another stack frame. */
-
- case OP_COND:
- case OP_SCOND:
- codelink= GET(ecode, 1);
-
- /* Because of the way auto-callout works during compile, a callout item is
- inserted between OP_COND and an assertion condition. */
-
- if (ecode[LINK_SIZE+1] == OP_CALLOUT)
- {
- if (pcre_callout != NULL)
- {
- pcre_callout_block cb;
- cb.version = 1; /* Version 1 of the callout block */
- cb.callout_number = ecode[LINK_SIZE+2];
- cb.offset_vector = md->offset_vector;
- cb.subject = (PCRE_SPTR)md->start_subject;
- cb.subject_length = md->end_subject - md->start_subject;
- cb.start_match = mstart - md->start_subject;
- cb.current_position = eptr - md->start_subject;
- cb.pattern_position = GET(ecode, LINK_SIZE + 3);
- cb.next_item_length = GET(ecode, 3 + 2*LINK_SIZE);
- cb.capture_top = offset_top/2;
- cb.capture_last = md->capture_last;
- cb.callout_data = md->callout_data;
- if ((rrc = (*pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH);
- if (rrc < 0) RRETURN(rrc);
- }
- ecode += _pcre_OP_lengths[OP_CALLOUT];
- }
-
- condcode = ecode[LINK_SIZE+1];
-
- /* Now see what the actual condition is */
-
- if (condcode == OP_RREF || condcode == OP_NRREF) /* Recursion test */
- {
- if (md->recursive == NULL) /* Not recursing => FALSE */
- {
- condition = FALSE;
- ecode += GET(ecode, 1);
- }
- else
- {
- int recno = GET2(ecode, LINK_SIZE + 2); /* Recursion group number*/
- condition = (recno == RREF_ANY || recno == md->recursive->group_num);
-
- /* If the test is for recursion into a specific subpattern, and it is
- false, but the test was set up by name, scan the table to see if the
- name refers to any other numbers, and test them. The condition is true
- if any one is set. */
-
- if (!condition && condcode == OP_NRREF && recno != RREF_ANY)
- {
- uschar *slotA = md->name_table;
- for (i = 0; i < md->name_count; i++)
- {
- if (GET2(slotA, 0) == recno) break;
- slotA += md->name_entry_size;
- }
-
- /* Found a name for the number - there can be only one; duplicate
- names for different numbers are allowed, but not vice versa. First
- scan down for duplicates. */
-
- if (i < md->name_count)
- {
- uschar *slotB = slotA;
- while (slotB > md->name_table)
- {
- slotB -= md->name_entry_size;
- if (strcmp((char *)slotA + 2, (char *)slotB + 2) == 0)
- {
- condition = GET2(slotB, 0) == md->recursive->group_num;
- if (condition) break;
- }
- else break;
- }
-
- /* Scan up for duplicates */
-
- if (!condition)
- {
- slotB = slotA;
- for (i++; i < md->name_count; i++)
- {
- slotB += md->name_entry_size;
- if (strcmp((char *)slotA + 2, (char *)slotB + 2) == 0)
- {
- condition = GET2(slotB, 0) == md->recursive->group_num;
- if (condition) break;
- }
- else break;
- }
- }
- }
- }
-
- /* Chose branch according to the condition */
-
- ecode += condition? 3 : GET(ecode, 1);
- }
- }
-
- else if (condcode == OP_CREF || condcode == OP_NCREF) /* Group used test */
- {
- offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */
- condition = offset < offset_top && md->offset_vector[offset] >= 0;
-
- /* If the numbered capture is unset, but the reference was by name,
- scan the table to see if the name refers to any other numbers, and test
- them. The condition is true if any one is set. This is tediously similar
- to the code above, but not close enough to try to amalgamate. */
-
- if (!condition && condcode == OP_NCREF)
- {
- int refno = offset >> 1;
- uschar *slotA = md->name_table;
-
- for (i = 0; i < md->name_count; i++)
- {
- if (GET2(slotA, 0) == refno) break;
- slotA += md->name_entry_size;
- }
-
- /* Found a name for the number - there can be only one; duplicate names
- for different numbers are allowed, but not vice versa. First scan down
- for duplicates. */
-
- if (i < md->name_count)
- {
- uschar *slotB = slotA;
- while (slotB > md->name_table)
- {
- slotB -= md->name_entry_size;
- if (strcmp((char *)slotA + 2, (char *)slotB + 2) == 0)
- {
- offset = GET2(slotB, 0) << 1;
- condition = offset < offset_top &&
- md->offset_vector[offset] >= 0;
- if (condition) break;
- }
- else break;
- }
-
- /* Scan up for duplicates */
-
- if (!condition)
- {
- slotB = slotA;
- for (i++; i < md->name_count; i++)
- {
- slotB += md->name_entry_size;
- if (strcmp((char *)slotA + 2, (char *)slotB + 2) == 0)
- {
- offset = GET2(slotB, 0) << 1;
- condition = offset < offset_top &&
- md->offset_vector[offset] >= 0;
- if (condition) break;
- }
- else break;
- }
- }
- }
- }
-
- /* Chose branch according to the condition */
-
- ecode += condition? 3 : GET(ecode, 1);
- }
-
- else if (condcode == OP_DEF) /* DEFINE - always false */
- {
- condition = FALSE;
- ecode += GET(ecode, 1);
- }
-
- /* The condition is an assertion. Call match() to evaluate it - setting
- the final argument match_condassert causes it to stop at the end of an
- assertion. */
-
- else
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
- match_condassert, RM3);
- if (rrc == MATCH_MATCH)
- {
- condition = TRUE;
- ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2);
- while (*ecode == OP_ALT) ecode += GET(ecode, 1);
- }
- else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN)
- {
- RRETURN(rrc); /* Need braces because of following else */
- }
- else
- {
- condition = FALSE;
- ecode += codelink;
- }
- }
-
- /* We are now at the branch that is to be obeyed. As there is only one,
- we can use tail recursion to avoid using another stack frame, except when
- match_cbegroup is required for an unlimited repeat of a possibly empty
- group. If the second alternative doesn't exist, we can just plough on. */
-
- if (condition || *ecode == OP_ALT)
- {
- ecode += 1 + LINK_SIZE;
- if (op == OP_SCOND) /* Possibly empty group */
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, match_cbegroup, RM49);
- RRETURN(rrc);
- }
- else /* Group must match something */
- {
- flags = 0;
- goto TAIL_RECURSE;
- }
- }
- else /* Condition false & no alternative */
- {
- ecode += 1 + LINK_SIZE;
- }
- break;
-
-
- /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes,
- to close any currently open capturing brackets. */
-
- case OP_CLOSE:
- number = GET2(ecode, 1);
- offset = number << 1;
-
-#ifdef PCRE_DEBUG
- printf("end bracket %d at *ACCEPT", number);
- printf("\n");
-#endif
-
- md->capture_last = number;
- if (offset >= md->offset_max) md->offset_overflow = TRUE; else
- {
- md->offset_vector[offset] =
- md->offset_vector[md->offset_end - number];
- md->offset_vector[offset+1] = eptr - md->start_subject;
- if (offset_top <= offset) offset_top = offset + 2;
- }
- ecode += 3;
- break;
-
-
- /* End of the pattern, either real or forced. If we are in a top-level
- recursion, we should restore the offsets appropriately and continue from
- after the call. */
-
- case OP_ACCEPT:
- case OP_END:
- if (md->recursive != NULL && md->recursive->group_num == 0)
- {
- recursion_info *rec = md->recursive;
- DPRINTF(("End of pattern in a (?0) recursion\n"));
- md->recursive = rec->prevrec;
- memmove(md->offset_vector, rec->offset_save,
- rec->saved_max * sizeof(int));
- offset_top = rec->save_offset_top;
- ims = original_ims;
- ecode = rec->after_call;
- break;
- }
-
- /* Otherwise, if we have matched an empty string, fail if PCRE_NOTEMPTY is
- set, or if PCRE_NOTEMPTY_ATSTART is set and we have matched at the start of
- the subject. In both cases, backtracking will then try other alternatives,
- if any. */
-
- if (eptr == mstart &&
- (md->notempty ||
- (md->notempty_atstart &&
- mstart == md->start_subject + md->start_offset)))
- RRETURN(MATCH_NOMATCH);
-
- /* Otherwise, we have a match. */
-
- md->end_match_ptr = eptr; /* Record where we ended */
- md->end_offset_top = offset_top; /* and how many extracts were taken */
- md->start_match_ptr = mstart; /* and the start (\K can modify) */
- RRETURN(MATCH_MATCH);
-
- /* Change option settings */
-
- case OP_OPT:
- ims = ecode[1];
- ecode += 2;
- DPRINTF(("ims set to %02lx\n", ims));
- break;
-
- /* Assertion brackets. Check the alternative branches in turn - the
- matching won't pass the KET for an assertion. If any one branch matches,
- the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
- start of each branch to move the current point backwards, so the code at
- this level is identical to the lookahead case. */
-
- case OP_ASSERT:
- case OP_ASSERTBACK:
- do
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0,
- RM4);
- if (rrc == MATCH_MATCH)
- {
- mstart = md->start_match_ptr; /* In case \K reset it */
- break;
- }
- if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
- ecode += GET(ecode, 1);
- }
- while (*ecode == OP_ALT);
- if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH);
-
- /* If checking an assertion for a condition, return MATCH_MATCH. */
-
- if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
-
- /* Continue from after the assertion, updating the offsets high water
- mark, since extracts may have been taken during the assertion. */
-
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- ecode += 1 + LINK_SIZE;
- offset_top = md->end_offset_top;
- continue;
-
- /* Negative assertion: all branches must fail to match. Encountering SKIP,
- PRUNE, or COMMIT means we must assume failure without checking subsequent
- branches. */
-
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK_NOT:
- do
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0,
- RM5);
- if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH);
- if (rrc == MATCH_SKIP || rrc == MATCH_PRUNE || rrc == MATCH_COMMIT)
- {
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- break;
- }
- if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
- ecode += GET(ecode,1);
- }
- while (*ecode == OP_ALT);
-
- if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
-
- ecode += 1 + LINK_SIZE;
- continue;
-
- /* Move the subject pointer back. This occurs only at the start of
- each branch of a lookbehind assertion. If we are too close to the start to
- move back, this match function fails. When working with UTF-8 we move
- back a number of characters, not bytes. */
-
- case OP_REVERSE:
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- i = GET(ecode, 1);
- while (i-- > 0)
- {
- eptr--;
- if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
- BACKCHAR(eptr);
- }
- }
- else
-#endif
-
- /* No UTF-8 support, or not in UTF-8 mode: count is byte count */
-
- {
- eptr -= GET(ecode, 1);
- if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
- }
-
- /* Save the earliest consulted character, then skip to next op code */
-
- if (eptr < md->start_used_ptr) md->start_used_ptr = eptr;
- ecode += 1 + LINK_SIZE;
- break;
-
- /* The callout item calls an external function, if one is provided, passing
- details of the match so far. This is mainly for debugging, though the
- function is able to force a failure. */
-
- case OP_CALLOUT:
- if (pcre_callout != NULL)
- {
- pcre_callout_block cb;
- cb.version = 1; /* Version 1 of the callout block */
- cb.callout_number = ecode[1];
- cb.offset_vector = md->offset_vector;
- cb.subject = (PCRE_SPTR)md->start_subject;
- cb.subject_length = md->end_subject - md->start_subject;
- cb.start_match = mstart - md->start_subject;
- cb.current_position = eptr - md->start_subject;
- cb.pattern_position = GET(ecode, 2);
- cb.next_item_length = GET(ecode, 2 + LINK_SIZE);
- cb.capture_top = offset_top/2;
- cb.capture_last = md->capture_last;
- cb.callout_data = md->callout_data;
- if ((rrc = (*pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH);
- if (rrc < 0) RRETURN(rrc);
- }
- ecode += 2 + 2*LINK_SIZE;
- break;
-
- /* Recursion either matches the current regex, or some subexpression. The
- offset data is the offset to the starting bracket from the start of the
- whole pattern. (This is so that it works from duplicated subpatterns.)
-
- If there are any capturing brackets started but not finished, we have to
- save their starting points and reinstate them after the recursion. However,
- we don't know how many such there are (offset_top records the completed
- total) so we just have to save all the potential data. There may be up to
- 65535 such values, which is too large to put on the stack, but using malloc
- for small numbers seems expensive. As a compromise, the stack is used when
- there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc
- is used. A problem is what to do if the malloc fails ... there is no way of
- returning to the top level with an error. Save the top REC_STACK_SAVE_MAX
- values on the stack, and accept that the rest may be wrong.
-
- There are also other values that have to be saved. We use a chained
- sequence of blocks that actually live on the stack. Thanks to Robin Houston
- for the original version of this logic. */
-
- case OP_RECURSE:
- {
- callpat = md->start_code + GET(ecode, 1);
- new_recursive.group_num = (callpat == md->start_code)? 0 :
- GET2(callpat, 1 + LINK_SIZE);
-
- /* Add to "recursing stack" */
-
- new_recursive.prevrec = md->recursive;
- md->recursive = &new_recursive;
-
- /* Find where to continue from afterwards */
-
- ecode += 1 + LINK_SIZE;
- new_recursive.after_call = ecode;
-
- /* Now save the offset data. */
-
- new_recursive.saved_max = md->offset_end;
- if (new_recursive.saved_max <= REC_STACK_SAVE_MAX)
- new_recursive.offset_save = stacksave;
- else
- {
- new_recursive.offset_save =
- (int *)(pcre_malloc)(new_recursive.saved_max * sizeof(int));
- if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY);
- }
-
- memcpy(new_recursive.offset_save, md->offset_vector,
- new_recursive.saved_max * sizeof(int));
- new_recursive.save_offset_top = offset_top;
-
- /* OK, now we can do the recursion. For each top-level alternative we
- restore the offset and recursion data. */
-
- DPRINTF(("Recursing into group %d\n", new_recursive.group_num));
- flags = (*callpat >= OP_SBRA)? match_cbegroup : 0;
- do
- {
- RMATCH(eptr, callpat + _pcre_OP_lengths[*callpat], offset_top,
- md, ims, eptrb, flags, RM6);
- if (rrc == MATCH_MATCH)
- {
- DPRINTF(("Recursion matched\n"));
- md->recursive = new_recursive.prevrec;
- if (new_recursive.offset_save != stacksave)
- (pcre_free)(new_recursive.offset_save);
- RRETURN(MATCH_MATCH);
- }
- else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN)
- {
- DPRINTF(("Recursion gave error %d\n", rrc));
- if (new_recursive.offset_save != stacksave)
- (pcre_free)(new_recursive.offset_save);
- RRETURN(rrc);
- }
-
- md->recursive = &new_recursive;
- memcpy(md->offset_vector, new_recursive.offset_save,
- new_recursive.saved_max * sizeof(int));
- callpat += GET(callpat, 1);
- }
- while (*callpat == OP_ALT);
-
- DPRINTF(("Recursion didn't match\n"));
- md->recursive = new_recursive.prevrec;
- if (new_recursive.offset_save != stacksave)
- (pcre_free)(new_recursive.offset_save);
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never reaches here */
-
- /* "Once" brackets are like assertion brackets except that after a match,
- the point in the subject string is not moved back. Thus there can never be
- a move back into the brackets. Friedl calls these "atomic" subpatterns.
- Check the alternative branches in turn - the matching won't pass the KET
- for this kind of subpattern. If any one branch matches, we carry on as at
- the end of a normal bracket, leaving the subject pointer, but resetting
- the start-of-match value in case it was changed by \K. */
-
- case OP_ONCE:
- prev = ecode;
- saved_eptr = eptr;
-
- do
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM7);
- if (rrc == MATCH_MATCH)
- {
- mstart = md->start_match_ptr;
- break;
- }
- if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
- ecode += GET(ecode,1);
- }
- while (*ecode == OP_ALT);
-
- /* If hit the end of the group (which could be repeated), fail */
-
- if (*ecode != OP_ONCE && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH);
-
- /* Continue as from after the assertion, updating the offsets high water
- mark, since extracts may have been taken. */
-
- do ecode += GET(ecode, 1); while (*ecode == OP_ALT);
-
- offset_top = md->end_offset_top;
- eptr = md->end_match_ptr;
-
- /* For a non-repeating ket, just continue at this level. This also
- happens for a repeating ket if no characters were matched in the group.
- This is the forcible breaking of infinite loops as implemented in Perl
- 5.005. If there is an options reset, it will get obeyed in the normal
- course of events. */
-
- if (*ecode == OP_KET || eptr == saved_eptr)
- {
- ecode += 1+LINK_SIZE;
- break;
- }
-
- /* The repeating kets try the rest of the pattern or restart from the
- preceding bracket, in the appropriate order. The second "call" of match()
- uses tail recursion, to avoid using another stack frame. We need to reset
- any options that changed within the bracket before re-running it, so
- check the next opcode. */
-
- if (ecode[1+LINK_SIZE] == OP_OPT)
- {
- ims = (ims & ~PCRE_IMS) | ecode[4];
- DPRINTF(("ims set to %02lx at group repeat\n", ims));
- }
-
- if (*ecode == OP_KETRMIN)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM8);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode = prev;
- flags = 0;
- goto TAIL_RECURSE;
- }
- else /* OP_KETRMAX */
- {
- RMATCH(eptr, prev, offset_top, md, ims, eptrb, match_cbegroup, RM9);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += 1 + LINK_SIZE;
- flags = 0;
- goto TAIL_RECURSE;
- }
- /* Control never gets here */
-
- /* An alternation is the end of a branch; scan along to find the end of the
- bracketed group and go to there. */
-
- case OP_ALT:
- do ecode += GET(ecode,1); while (*ecode == OP_ALT);
- break;
-
- /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group,
- indicating that it may occur zero times. It may repeat infinitely, or not
- at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets
- with fixed upper repeat limits are compiled as a number of copies, with the
- optional ones preceded by BRAZERO or BRAMINZERO. */
-
- case OP_BRAZERO:
- {
- next = ecode+1;
- RMATCH(eptr, next, offset_top, md, ims, eptrb, 0, RM10);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- do next += GET(next,1); while (*next == OP_ALT);
- ecode = next + 1 + LINK_SIZE;
- }
- break;
-
- case OP_BRAMINZERO:
- {
- next = ecode+1;
- do next += GET(next, 1); while (*next == OP_ALT);
- RMATCH(eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0, RM11);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode++;
- }
- break;
-
- case OP_SKIPZERO:
- {
- next = ecode+1;
- do next += GET(next,1); while (*next == OP_ALT);
- ecode = next + 1 + LINK_SIZE;
- }
- break;
-
- /* End of a group, repeated or non-repeating. */
-
- case OP_KET:
- case OP_KETRMIN:
- case OP_KETRMAX:
- prev = ecode - GET(ecode, 1);
-
- /* If this was a group that remembered the subject start, in order to break
- infinite repeats of empty string matches, retrieve the subject start from
- the chain. Otherwise, set it NULL. */
-
- if (*prev >= OP_SBRA)
- {
- saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */
- eptrb = eptrb->epb_prev; /* Backup to previous group */
- }
- else saved_eptr = NULL;
-
- /* If we are at the end of an assertion group or an atomic group, stop
- matching and return MATCH_MATCH, but record the current high water mark for
- use by positive assertions. We also need to record the match start in case
- it was changed by \K. */
-
- if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
- *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
- *prev == OP_ONCE)
- {
- md->end_match_ptr = eptr; /* For ONCE */
- md->end_offset_top = offset_top;
- md->start_match_ptr = mstart;
- RRETURN(MATCH_MATCH);
- }
-
- /* For capturing groups we have to check the group number back at the start
- and if necessary complete handling an extraction by setting the offsets and
- bumping the high water mark. Note that whole-pattern recursion is coded as
- a recurse into group 0, so it won't be picked up here. Instead, we catch it
- when the OP_END is reached. Other recursion is handled here. */
-
- if (*prev == OP_CBRA || *prev == OP_SCBRA)
- {
- number = GET2(prev, 1+LINK_SIZE);
- offset = number << 1;
-
-#ifdef PCRE_DEBUG
- printf("end bracket %d", number);
- printf("\n");
-#endif
-
- md->capture_last = number;
- if (offset >= md->offset_max) md->offset_overflow = TRUE; else
- {
- md->offset_vector[offset] =
- md->offset_vector[md->offset_end - number];
- md->offset_vector[offset+1] = eptr - md->start_subject;
- if (offset_top <= offset) offset_top = offset + 2;
- }
-
- /* Handle a recursively called group. Restore the offsets
- appropriately and continue from after the call. */
-
- if (md->recursive != NULL && md->recursive->group_num == number)
- {
- recursion_info *rec = md->recursive;
- DPRINTF(("Recursion (%d) succeeded - continuing\n", number));
- md->recursive = rec->prevrec;
- memcpy(md->offset_vector, rec->offset_save,
- rec->saved_max * sizeof(int));
- offset_top = rec->save_offset_top;
- ecode = rec->after_call;
- ims = original_ims;
- break;
- }
- }
-
- /* For both capturing and non-capturing groups, reset the value of the ims
- flags, in case they got changed during the group. */
-
- ims = original_ims;
- DPRINTF(("ims reset to %02lx\n", ims));
-
- /* For a non-repeating ket, just continue at this level. This also
- happens for a repeating ket if no characters were matched in the group.
- This is the forcible breaking of infinite loops as implemented in Perl
- 5.005. If there is an options reset, it will get obeyed in the normal
- course of events. */
-
- if (*ecode == OP_KET || eptr == saved_eptr)
- {
- ecode += 1 + LINK_SIZE;
- break;
- }
-
- /* The repeating kets try the rest of the pattern or restart from the
- preceding bracket, in the appropriate order. In the second case, we can use
- tail recursion to avoid using another stack frame, unless we have an
- unlimited repeat of a group that can match an empty string. */
-
- flags = (*prev >= OP_SBRA)? match_cbegroup : 0;
-
- if (*ecode == OP_KETRMIN)
- {
- RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM12);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (flags != 0) /* Could match an empty string */
- {
- RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM50);
- RRETURN(rrc);
- }
- ecode = prev;
- goto TAIL_RECURSE;
- }
- else /* OP_KETRMAX */
- {
- RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM13);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- ecode += 1 + LINK_SIZE;
- flags = 0;
- goto TAIL_RECURSE;
- }
- /* Control never gets here */
-
- /* Start of subject unless notbol, or after internal newline if multiline */
-
- case OP_CIRC:
- if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH);
- if ((ims & PCRE_MULTILINE) != 0)
- {
- if (eptr != md->start_subject &&
- (eptr == md->end_subject || !WAS_NEWLINE(eptr)))
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
- }
- /* ... else fall through */
-
- /* Start of subject assertion */
-
- case OP_SOD:
- if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Start of match assertion */
-
- case OP_SOM:
- if (eptr != md->start_subject + md->start_offset) RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Reset the start of match point */
-
- case OP_SET_SOM:
- mstart = eptr;
- ecode++;
- break;
-
- /* Assert before internal newline if multiline, or before a terminating
- newline unless endonly is set, else end of subject unless noteol is set. */
-
- case OP_DOLL:
- if ((ims & PCRE_MULTILINE) != 0)
- {
- if (eptr < md->end_subject)
- { if (!IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); }
- else
- { if (md->noteol) RRETURN(MATCH_NOMATCH); }
- ecode++;
- break;
- }
- else
- {
- if (md->noteol) RRETURN(MATCH_NOMATCH);
- if (!md->endonly)
- {
- if (eptr != md->end_subject &&
- (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen))
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
- }
- }
- /* ... else fall through for endonly */
-
- /* End of subject assertion (\z) */
-
- case OP_EOD:
- if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* End of subject or ending \n assertion (\Z) */
-
- case OP_EODN:
- if (eptr != md->end_subject &&
- (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen))
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- /* Word boundary assertions */
-
- case OP_NOT_WORD_BOUNDARY:
- case OP_WORD_BOUNDARY:
- {
-
- /* Find out if the previous and current characters are "word" characters.
- It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to
- be "non-word" characters. Remember the earliest consulted character for
- partial matching. */
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- if (eptr == md->start_subject) prev_is_word = FALSE; else
- {
- USPTR lastptr = eptr - 1;
- while((*lastptr & 0xc0) == 0x80) lastptr--;
- if (lastptr < md->start_used_ptr) md->start_used_ptr = lastptr;
- GETCHAR(c, lastptr);
- prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
- }
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- cur_is_word = FALSE;
- }
- else
- {
- GETCHAR(c, eptr);
- cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
- }
- }
- else
-#endif
-
- /* Not in UTF-8 mode */
-
- {
- if (eptr == md->start_subject) prev_is_word = FALSE; else
- {
- if (eptr <= md->start_used_ptr) md->start_used_ptr = eptr - 1;
- prev_is_word = ((md->ctypes[eptr[-1]] & ctype_word) != 0);
- }
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- cur_is_word = FALSE;
- }
- else cur_is_word = ((md->ctypes[*eptr] & ctype_word) != 0);
- }
-
- /* Now see if the situation is what we want */
-
- if ((*ecode++ == OP_WORD_BOUNDARY)?
- cur_is_word == prev_is_word : cur_is_word != prev_is_word)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- /* Match a single character type; inline for speed */
-
- case OP_ANY:
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
- /* Fall through */
-
- case OP_ALLANY:
- if (eptr++ >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (utf8) while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- ecode++;
- break;
-
- /* Match a single byte, even in UTF-8 mode. This opcode really does match
- any byte, even newline, independent of the setting of PCRE_DOTALL. */
-
- case OP_ANYBYTE:
- if (eptr++ >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- break;
-
- case OP_NOT_DIGIT:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c < 256 &&
-#endif
- (md->ctypes[c] & ctype_digit) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_DIGIT:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c >= 256 ||
-#endif
- (md->ctypes[c] & ctype_digit) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_NOT_WHITESPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c < 256 &&
-#endif
- (md->ctypes[c] & ctype_space) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_WHITESPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c >= 256 ||
-#endif
- (md->ctypes[c] & ctype_space) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_NOT_WORDCHAR:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c < 256 &&
-#endif
- (md->ctypes[c] & ctype_word) != 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_WORDCHAR:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (
-#ifdef SUPPORT_UTF8
- c >= 256 ||
-#endif
- (md->ctypes[c] & ctype_word) == 0
- )
- RRETURN(MATCH_NOMATCH);
- ecode++;
- break;
-
- case OP_ANYNL:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x000d:
- if (eptr < md->end_subject && *eptr == 0x0a) eptr++;
- break;
-
- case 0x000a:
- break;
-
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH);
- break;
- }
- ecode++;
- break;
-
- case OP_NOT_HSPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- break;
-
- case OP_HSPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- break;
- }
- ecode++;
- break;
-
- case OP_NOT_VSPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- break;
-
- case OP_VSPACE:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- break;
- }
- ecode++;
- break;
-
-#ifdef SUPPORT_UCP
- /* Check the next character by Unicode property. We will get here only
- if the support is in the binary; otherwise a compile-time error occurs. */
-
- case OP_PROP:
- case OP_NOTPROP:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- {
- int chartype = UCD_CHARTYPE(c);
- switch(ecode[1])
- {
- case PT_ANY:
- if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_LAMP:
- if ((chartype == ucp_Lu ||
- chartype == ucp_Ll ||
- chartype == ucp_Lt) == (op == OP_NOTPROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_GC:
- if ((ecode[2] != _pcre_ucp_gentype[chartype]) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_PC:
- if ((ecode[2] != chartype) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- case PT_SC:
- if ((ecode[2] != UCD_SCRIPT(c)) == (op == OP_PROP))
- RRETURN(MATCH_NOMATCH);
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
-
- ecode += 3;
- }
- break;
-
- /* Match an extended Unicode sequence. We will get here only if the support
- is in the binary; otherwise a compile-time error occurs. */
-
- case OP_EXTUNI:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- {
- int category = UCD_CATEGORY(c);
- if (category == ucp_M) RRETURN(MATCH_NOMATCH);
- while (eptr < md->end_subject)
- {
- int len = 1;
- if (!utf8) c = *eptr; else
- {
- GETCHARLEN(c, eptr, len);
- }
- category = UCD_CATEGORY(c);
- if (category != ucp_M) break;
- eptr += len;
- }
- }
- ecode++;
- break;
-#endif
-
-
- /* Match a back reference, possibly repeatedly. Look past the end of the
- item to see if there is repeat information following. The code is similar
- to that for character classes, but repeated for efficiency. Then obey
- similar code to character type repeats - written out again for speed.
- However, if the referenced string is the empty string, always treat
- it as matched, any number of times (otherwise there could be infinite
- loops). */
-
- case OP_REF:
- {
- offset = GET2(ecode, 1) << 1; /* Doubled ref number */
- ecode += 3;
-
- /* If the reference is unset, there are two possibilities:
-
- (a) In the default, Perl-compatible state, set the length to be longer
- than the amount of subject left; this ensures that every attempt at a
- match fails. We can't just fail here, because of the possibility of
- quantifiers with zero minima.
-
- (b) If the JavaScript compatibility flag is set, set the length to zero
- so that the back reference matches an empty string.
-
- Otherwise, set the length to the length of what was matched by the
- referenced subpattern. */
-
- if (offset >= offset_top || md->offset_vector[offset] < 0)
- length = (md->jscript_compat)? 0 : md->end_subject - eptr + 1;
- else
- length = md->offset_vector[offset+1] - md->offset_vector[offset];
-
- /* Set up for repetition, or handle the non-repeated case */
-
- switch (*ecode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- c = *ecode++ - OP_CRSTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 3);
- if (max == 0) max = INT_MAX;
- ecode += 5;
- break;
-
- default: /* No repeat follows */
- if (!match_ref(offset, eptr, length, md, ims))
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += length;
- continue; /* With the main loop */
- }
-
- /* If the length of the reference is zero, just continue with the
- main loop. */
-
- if (length == 0) continue;
-
- /* First, ensure the minimum number of matches are present. We get back
- the length of the reference string explicitly rather than passing the
- address of eptr, so that eptr can be a register variable. */
-
- for (i = 1; i <= min; i++)
- {
- if (!match_ref(offset, eptr, length, md, ims))
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += length;
- }
-
- /* If min = max, continue at the same level without recursion.
- They are not both allowed to be zero. */
-
- if (min == max) continue;
-
- /* If minimizing, keep trying and advancing the pointer */
-
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM14);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (!match_ref(offset, eptr, length, md, ims))
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += length;
- }
- /* Control never gets here */
- }
-
- /* If maximizing, find the longest string and work backwards */
-
- else
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (!match_ref(offset, eptr, length, md, ims))
- {
- CHECK_PARTIAL();
- break;
- }
- eptr += length;
- }
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM15);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr -= length;
- }
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
-
- /* Match a bit-mapped character class, possibly repeatedly. This op code is
- used when all the characters in the class have values in the range 0-255,
- and either the matching is caseful, or the characters are in the range
- 0-127 when UTF-8 processing is enabled. The only difference between
- OP_CLASS and OP_NCLASS occurs when a data character outside the range is
- encountered.
-
- First, look past the end of the item to see if there is repeat information
- following. Then obey similar code to character type repeats - written out
- again for speed. */
-
- case OP_NCLASS:
- case OP_CLASS:
- {
- data = ecode + 1; /* Save for matching */
- ecode += 33; /* Advance past the item */
-
- switch (*ecode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- c = *ecode++ - OP_CRSTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 3);
- if (max == 0) max = INT_MAX;
- ecode += 5;
- break;
-
- default: /* No repeat follows */
- min = max = 1;
- break;
- }
-
- /* First, ensure the minimum number of matches are present. */
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- if (c > 255)
- {
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
- }
- else
- {
- if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
- }
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- c = *eptr++;
- if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
- }
- }
-
- /* If max == min we can continue with the main loop without the
- need to recurse. */
-
- if (min == max) continue;
-
- /* If minimizing, keep testing the rest of the expression and advancing
- the pointer while it matches the class. */
-
- if (minimize)
- {
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM16);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- if (c > 255)
- {
- if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
- }
- else
- {
- if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
- }
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM17);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- c = *eptr++;
- if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- /* If maximizing, find the longest possible run, then work backwards. */
-
- else
- {
- pp = eptr;
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c > 255)
- {
- if (op == OP_CLASS) break;
- }
- else
- {
- if ((data[c/8] & (1 << (c&7))) == 0) break;
- }
- eptr += len;
- }
- for (;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM18);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- BACKCHAR(eptr);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if ((data[c/8] & (1 << (c&7))) == 0) break;
- eptr++;
- }
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM19);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- }
- }
-
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
-
-
- /* Match an extended character class. This opcode is encountered only
- when UTF-8 mode mode is supported. Nevertheless, we may not be in UTF-8
- mode, because Unicode properties are supported in non-UTF-8 mode. */
-
-#ifdef SUPPORT_UTF8
- case OP_XCLASS:
- {
- data = ecode + 1 + LINK_SIZE; /* Save for matching */
- ecode += GET(ecode, 1); /* Advance past the item */
-
- switch (*ecode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- c = *ecode++ - OP_CRSTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- minimize = (*ecode == OP_CRMINRANGE);
- min = GET2(ecode, 1);
- max = GET2(ecode, 3);
- if (max == 0) max = INT_MAX;
- ecode += 5;
- break;
-
- default: /* No repeat follows */
- min = max = 1;
- break;
- }
-
- /* First, ensure the minimum number of matches are present. */
-
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (!_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH);
- }
-
- /* If max == min we can continue with the main loop without the
- need to recurse. */
-
- if (min == max) continue;
-
- /* If minimizing, keep testing the rest of the expression and advancing
- the pointer while it matches the class. */
-
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM20);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- if (!_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
-
- /* If maximizing, find the longest possible run, then work backwards. */
-
- else
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLENTEST(c, eptr, len);
- if (!_pcre_xclass(c, data)) break;
- eptr += len;
- }
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM21);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- if (utf8) BACKCHAR(eptr);
- }
- RRETURN(MATCH_NOMATCH);
- }
-
- /* Control never gets here */
- }
-#endif /* End of XCLASS */
-
- /* Match a single character, casefully */
-
- case OP_CHAR:
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- length = 1;
- ecode++;
- GETCHARLEN(fc, ecode, length);
- if (length > md->end_subject - eptr)
- {
- CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
- while (length-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH);
- }
- else
-#endif
-
- /* Non-UTF-8 mode */
- {
- if (md->end_subject - eptr < 1)
- {
- SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
- if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH);
- ecode += 2;
- }
- break;
-
- /* Match a single character, caselessly */
-
- case OP_CHARNC:
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- length = 1;
- ecode++;
- GETCHARLEN(fc, ecode, length);
-
- if (length > md->end_subject - eptr)
- {
- CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
-
- /* If the pattern character's value is < 128, we have only one byte, and
- can use the fast lookup table. */
-
- if (fc < 128)
- {
- if (md->lcc[*ecode++] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- }
-
- /* Otherwise we must pick up the subject character */
-
- else
- {
- unsigned int dc;
- GETCHARINC(dc, eptr);
- ecode += length;
-
- /* If we have Unicode property support, we can use it to test the other
- case of the character, if there is one. */
-
- if (fc != dc)
- {
-#ifdef SUPPORT_UCP
- if (dc != UCD_OTHERCASE(fc))
-#endif
- RRETURN(MATCH_NOMATCH);
- }
- }
- }
- else
-#endif /* SUPPORT_UTF8 */
-
- /* Non-UTF-8 mode */
- {
- if (md->end_subject - eptr < 1)
- {
- SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
- RRETURN(MATCH_NOMATCH);
- }
- if (md->lcc[ecode[1]] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- ecode += 2;
- }
- break;
-
- /* Match a single character repeatedly. */
-
- case OP_EXACT:
- min = max = GET2(ecode, 1);
- ecode += 3;
- goto REPEATCHAR;
-
- case OP_POSUPTO:
- possessive = TRUE;
- /* Fall through */
-
- case OP_UPTO:
- case OP_MINUPTO:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_MINUPTO;
- ecode += 3;
- goto REPEATCHAR;
-
- case OP_POSSTAR:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
- goto REPEATCHAR;
-
- case OP_POSPLUS:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
- goto REPEATCHAR;
-
- case OP_POSQUERY:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
- goto REPEATCHAR;
-
- case OP_STAR:
- case OP_MINSTAR:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_QUERY:
- case OP_MINQUERY:
- c = *ecode++ - OP_STAR;
- minimize = (c & 1) != 0;
-
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
-
- /* Common code for all repeated single-character matches. */
-
- REPEATCHAR:
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- length = 1;
- charptr = ecode;
- GETCHARLEN(fc, ecode, length);
- ecode += length;
-
- /* Handle multibyte character matching specially here. There is
- support for caseless matching if UCP support is present. */
-
- if (length > 1)
- {
-#ifdef SUPPORT_UCP
- unsigned int othercase;
- if ((ims & PCRE_CASELESS) != 0 &&
- (othercase = UCD_OTHERCASE(fc)) != fc)
- oclength = _pcre_ord2utf8(othercase, occhars);
- else oclength = 0;
-#endif /* SUPPORT_UCP */
-
- for (i = 1; i <= min; i++)
- {
- if (eptr <= md->end_subject - length &&
- memcmp(eptr, charptr, length) == 0) eptr += length;
-#ifdef SUPPORT_UCP
- else if (oclength > 0 &&
- eptr <= md->end_subject - oclength &&
- memcmp(eptr, occhars, oclength) == 0) eptr += oclength;
-#endif /* SUPPORT_UCP */
- else
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM22);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr <= md->end_subject - length &&
- memcmp(eptr, charptr, length) == 0) eptr += length;
-#ifdef SUPPORT_UCP
- else if (oclength > 0 &&
- eptr <= md->end_subject - oclength &&
- memcmp(eptr, occhars, oclength) == 0) eptr += oclength;
-#endif /* SUPPORT_UCP */
- else
- {
- CHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (eptr <= md->end_subject - length &&
- memcmp(eptr, charptr, length) == 0) eptr += length;
-#ifdef SUPPORT_UCP
- else if (oclength > 0 &&
- eptr <= md->end_subject - oclength &&
- memcmp(eptr, occhars, oclength) == 0) eptr += oclength;
-#endif /* SUPPORT_UCP */
- else
- {
- CHECK_PARTIAL();
- break;
- }
- }
-
- if (possessive) continue;
-
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM23);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr == pp) { RRETURN(MATCH_NOMATCH); }
-#ifdef SUPPORT_UCP
- eptr--;
- BACKCHAR(eptr);
-#else /* without SUPPORT_UCP */
- eptr -= length;
-#endif /* SUPPORT_UCP */
- }
- }
- /* Control never gets here */
- }
-
- /* If the length of a UTF-8 character is 1, we fall through here, and
- obey the code as for non-UTF-8 characters below, though in this case the
- value of fc will always be < 128. */
- }
- else
-#endif /* SUPPORT_UTF8 */
-
- /* When not in UTF-8 mode, load a single-byte character. */
-
- fc = *ecode++;
-
- /* The value of fc at this point is always less than 256, though we may or
- may not be in UTF-8 mode. The code is duplicated for the caseless and
- caseful cases, for speed, since matching characters is likely to be quite
- common. First, ensure the minimum number of matches are present. If min =
- max, continue at the same level without recursing. Otherwise, if
- minimizing, keep trying the rest of the expression and advancing one
- matching character if failing, up to the maximum. Alternatively, if
- maximizing, find the maximum number of characters and work backwards. */
-
- DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max,
- max, eptr));
-
- if ((ims & PCRE_CASELESS) != 0)
- {
- fc = md->lcc[fc];
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- }
- if (min == max) continue;
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM24);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc != md->lcc[*eptr]) break;
- eptr++;
- }
-
- if (possessive) continue;
-
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM25);
- eptr--;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- }
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
-
- /* Caseful comparisons (includes all multi-byte characters) */
-
- else
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != *eptr++) RRETURN(MATCH_NOMATCH);
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM26);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc != *eptr++) RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
- else /* Maximize */
- {
- pp = eptr;
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc != *eptr) break;
- eptr++;
- }
- if (possessive) continue;
-
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM27);
- eptr--;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- }
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
-
- /* Match a negated single one-byte character. The character we are
- checking can be multibyte. */
-
- case OP_NOT:
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- ecode++;
- GETCHARINCTEST(c, eptr);
- if ((ims & PCRE_CASELESS) != 0)
- {
-#ifdef SUPPORT_UTF8
- if (c < 256)
-#endif
- c = md->lcc[c];
- if (md->lcc[*ecode++] == c) RRETURN(MATCH_NOMATCH);
- }
- else
- {
- if (*ecode++ == c) RRETURN(MATCH_NOMATCH);
- }
- break;
-
- /* Match a negated single one-byte character repeatedly. This is almost a
- repeat of the code for a repeated single character, but I haven't found a
- nice way of commoning these up that doesn't require a test of the
- positive/negative option for each character match. Maybe that wouldn't add
- very much to the time taken, but character matching *is* what this is all
- about... */
-
- case OP_NOTEXACT:
- min = max = GET2(ecode, 1);
- ecode += 3;
- goto REPEATNOTCHAR;
-
- case OP_NOTUPTO:
- case OP_NOTMINUPTO:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_NOTMINUPTO;
- ecode += 3;
- goto REPEATNOTCHAR;
-
- case OP_NOTPOSSTAR:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
- goto REPEATNOTCHAR;
-
- case OP_NOTPOSPLUS:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
- goto REPEATNOTCHAR;
-
- case OP_NOTPOSQUERY:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
- goto REPEATNOTCHAR;
-
- case OP_NOTPOSUPTO:
- possessive = TRUE;
- min = 0;
- max = GET2(ecode, 1);
- ecode += 3;
- goto REPEATNOTCHAR;
-
- case OP_NOTSTAR:
- case OP_NOTMINSTAR:
- case OP_NOTPLUS:
- case OP_NOTMINPLUS:
- case OP_NOTQUERY:
- case OP_NOTMINQUERY:
- c = *ecode++ - OP_NOTSTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
-
- /* Common code for all repeated single-byte matches. */
-
- REPEATNOTCHAR:
- fc = *ecode++;
-
- /* The code is duplicated for the caseless and caseful cases, for speed,
- since matching characters is likely to be quite common. First, ensure the
- minimum number of matches are present. If min = max, continue at the same
- level without recursing. Otherwise, if minimizing, keep trying the rest of
- the expression and advancing one matching character if failing, up to the
- maximum. Alternatively, if maximizing, find the maximum number of
- characters and work backwards. */
-
- DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max,
- max, eptr));
-
- if ((ims & PCRE_CASELESS) != 0)
- {
- fc = md->lcc[fc];
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (d < 256) d = md->lcc[d];
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
-
- /* Not UTF-8 mode */
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- }
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM28);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (d < 256) d = md->lcc[d];
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM29);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- /* Maximize case */
-
- else
- {
- pp = eptr;
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(d, eptr, len);
- if (d < 256) d = md->lcc[d];
- if (fc == d) break;
- eptr += len;
- }
- if (possessive) continue;
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM30);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- BACKCHAR(eptr);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc == md->lcc[*eptr]) break;
- eptr++;
- }
- if (possessive) continue;
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM31);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- }
- }
-
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
- }
-
- /* Caseful comparisons */
-
- else
- {
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
- }
- }
-
- if (min == max) continue;
-
- if (minimize)
- {
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM32);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(d, eptr);
- if (fc == d) RRETURN(MATCH_NOMATCH);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM33);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
- }
-
- /* Maximize case */
-
- else
- {
- pp = eptr;
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- register unsigned int d;
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(d, eptr, len);
- if (fc == d) break;
- eptr += len;
- }
- if (possessive) continue;
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM34);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- BACKCHAR(eptr);
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (fc == *eptr) break;
- eptr++;
- }
- if (possessive) continue;
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM35);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- eptr--;
- }
- }
-
- RRETURN(MATCH_NOMATCH);
- }
- }
- /* Control never gets here */
-
- /* Match a single character type repeatedly; several different opcodes
- share code. This is very similar to the code for single characters, but we
- repeat it in the interests of efficiency. */
-
- case OP_TYPEEXACT:
- min = max = GET2(ecode, 1);
- minimize = TRUE;
- ecode += 3;
- goto REPEATTYPE;
-
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- min = 0;
- max = GET2(ecode, 1);
- minimize = *ecode == OP_TYPEMINUPTO;
- ecode += 3;
- goto REPEATTYPE;
-
- case OP_TYPEPOSSTAR:
- possessive = TRUE;
- min = 0;
- max = INT_MAX;
- ecode++;
- goto REPEATTYPE;
-
- case OP_TYPEPOSPLUS:
- possessive = TRUE;
- min = 1;
- max = INT_MAX;
- ecode++;
- goto REPEATTYPE;
-
- case OP_TYPEPOSQUERY:
- possessive = TRUE;
- min = 0;
- max = 1;
- ecode++;
- goto REPEATTYPE;
-
- case OP_TYPEPOSUPTO:
- possessive = TRUE;
- min = 0;
- max = GET2(ecode, 1);
- ecode += 3;
- goto REPEATTYPE;
-
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- c = *ecode++ - OP_TYPESTAR;
- minimize = (c & 1) != 0;
- min = rep_min[c]; /* Pick up values from tables; */
- max = rep_max[c]; /* zero for max => infinity */
- if (max == 0) max = INT_MAX;
-
- /* Common code for all repeated single character type matches. Note that
- in UTF-8 mode, '.' matches a character of any length, but for the other
- character types, the valid characters are all one-byte long. */
-
- REPEATTYPE:
- ctype = *ecode++; /* Code for the character type */
-
-#ifdef SUPPORT_UCP
- if (ctype == OP_PROP || ctype == OP_NOTPROP)
- {
- prop_fail_result = ctype == OP_NOTPROP;
- prop_type = *ecode++;
- prop_value = *ecode++;
- }
- else prop_type = -1;
-#endif
-
- /* First, ensure the minimum number of matches are present. Use inline
- code for maximizing the speed, and do the type test once at the start
- (i.e. keep it out of the loop). Separate the UTF-8 code completely as that
- is tidier. Also separate the UCP code, which can be the same for both UTF-8
- and single-bytes. */
-
- if (min > 0)
- {
-#ifdef SUPPORT_UCP
- if (prop_type >= 0)
- {
- switch(prop_type)
- {
- case PT_ANY:
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- }
- break;
-
- case PT_LAMP:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == ucp_Lu ||
- prop_chartype == ucp_Ll ||
- prop_chartype == ucp_Lt) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case PT_GC:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_category = UCD_CATEGORY(c);
- if ((prop_category == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case PT_PC:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case PT_SC:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_script = UCD_SCRIPT(c);
- if ((prop_script == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
- }
-
- /* Match extended Unicode sequences. We will get here only if the
- support is in the binary; otherwise a compile-time error occurs. */
-
- else if (ctype == OP_EXTUNI)
- {
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_category = UCD_CATEGORY(c);
- if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH);
- while (eptr < md->end_subject)
- {
- int len = 1;
- if (!utf8) c = *eptr;
- else { GETCHARLEN(c, eptr, len); }
- prop_category = UCD_CATEGORY(c);
- if (prop_category != ucp_M) break;
- eptr += len;
- }
- }
- }
-
- else
-#endif /* SUPPORT_UCP */
-
-/* Handle all other cases when the coding is UTF-8 */
-
-#ifdef SUPPORT_UTF8
- if (utf8) switch(ctype)
- {
- case OP_ANY:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
- eptr++;
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- }
- break;
-
- case OP_ALLANY:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr++;
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- }
- break;
-
- case OP_ANYBYTE:
- if (eptr > md->end_subject - min) RRETURN(MATCH_NOMATCH);
- eptr += min;
- break;
-
- case OP_ANYNL:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x000d:
- if (eptr < md->end_subject && *eptr == 0x0a) eptr++;
- break;
-
- case 0x000a:
- break;
-
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH);
- break;
- }
- }
- break;
-
- case OP_NOT_HSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- switch(c)
- {
- default: break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- RRETURN(MATCH_NOMATCH);
- }
- }
- break;
-
- case OP_HSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- break;
- }
- }
- break;
-
- case OP_NOT_VSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- switch(c)
- {
- default: break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- RRETURN(MATCH_NOMATCH);
- }
- }
- break;
-
- case OP_VSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- break;
- }
- }
- break;
-
- case OP_NOT_DIGIT:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- if (c < 128 && (md->ctypes[c] & ctype_digit) != 0)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_DIGIT:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (*eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0)
- RRETURN(MATCH_NOMATCH);
- /* No need to skip more bytes - we know it's a 1-byte character */
- }
- break;
-
- case OP_NOT_WHITESPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (*eptr < 128 && (md->ctypes[*eptr] & ctype_space) != 0)
- RRETURN(MATCH_NOMATCH);
- while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80);
- }
- break;
-
- case OP_WHITESPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (*eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0)
- RRETURN(MATCH_NOMATCH);
- /* No need to skip more bytes - we know it's a 1-byte character */
- }
- break;
-
- case OP_NOT_WORDCHAR:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (*eptr < 128 && (md->ctypes[*eptr] & ctype_word) != 0)
- RRETURN(MATCH_NOMATCH);
- while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80);
- }
- break;
-
- case OP_WORDCHAR:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (*eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0)
- RRETURN(MATCH_NOMATCH);
- /* No need to skip more bytes - we know it's a 1-byte character */
- }
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- } /* End switch(ctype) */
-
- else
-#endif /* SUPPORT_UTF8 */
-
- /* Code for the non-UTF-8 case for minimum matching of operators other
- than OP_PROP and OP_NOTPROP. */
-
- switch(ctype)
- {
- case OP_ANY:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH);
- eptr++;
- }
- break;
-
- case OP_ALLANY:
- if (eptr > md->end_subject - min)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += min;
- break;
-
- case OP_ANYBYTE:
- if (eptr > md->end_subject - min)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- eptr += min;
- break;
-
- case OP_ANYNL:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- switch(*eptr++)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x000d:
- if (eptr < md->end_subject && *eptr == 0x0a) eptr++;
- break;
- case 0x000a:
- break;
-
- case 0x000b:
- case 0x000c:
- case 0x0085:
- if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH);
- break;
- }
- }
- break;
-
- case OP_NOT_HSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- switch(*eptr++)
- {
- default: break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- RRETURN(MATCH_NOMATCH);
- }
- }
- break;
-
- case OP_HSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- switch(*eptr++)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- break;
- }
- }
- break;
-
- case OP_NOT_VSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- switch(*eptr++)
- {
- default: break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- RRETURN(MATCH_NOMATCH);
- }
- }
- break;
-
- case OP_VSPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- switch(*eptr++)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- break;
- }
- }
- break;
-
- case OP_NOT_DIGIT:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_DIGIT:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_NOT_WHITESPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_WHITESPACE:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_NOT_WORDCHAR:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_word) != 0)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_WORDCHAR:
- for (i = 1; i <= min; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if ((md->ctypes[*eptr++] & ctype_word) == 0)
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
- }
-
- /* If min = max, continue at the same level without recursing */
-
- if (min == max) continue;
-
- /* If minimizing, we have to test the rest of the pattern before each
- subsequent match. Again, separate the UTF-8 case for speed, and also
- separate the UCP cases. */
-
- if (minimize)
- {
-#ifdef SUPPORT_UCP
- if (prop_type >= 0)
- {
- switch(prop_type)
- {
- case PT_ANY:
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM36);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- if (prop_fail_result) RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- case PT_LAMP:
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM37);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == ucp_Lu ||
- prop_chartype == ucp_Ll ||
- prop_chartype == ucp_Lt) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- case PT_GC:
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM38);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- prop_category = UCD_CATEGORY(c);
- if ((prop_category == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- case PT_PC:
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM39);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- case PT_SC:
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM40);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINC(c, eptr);
- prop_script = UCD_SCRIPT(c);
- if ((prop_script == prop_value) == prop_fail_result)
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
- }
-
- /* Match extended Unicode sequences. We will get here only if the
- support is in the binary; otherwise a compile-time error occurs. */
-
- else if (ctype == OP_EXTUNI)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM41);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- GETCHARINCTEST(c, eptr);
- prop_category = UCD_CATEGORY(c);
- if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH);
- while (eptr < md->end_subject)
- {
- int len = 1;
- if (!utf8) c = *eptr;
- else { GETCHARLEN(c, eptr, len); }
- prop_category = UCD_CATEGORY(c);
- if (prop_category != ucp_M) break;
- eptr += len;
- }
- }
- }
-
- else
-#endif /* SUPPORT_UCP */
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
- if (utf8)
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM42);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (ctype == OP_ANY && IS_NEWLINE(eptr))
- RRETURN(MATCH_NOMATCH);
- GETCHARINC(c, eptr);
- switch(ctype)
- {
- case OP_ANY: /* This is the non-NL case */
- case OP_ALLANY:
- case OP_ANYBYTE:
- break;
-
- case OP_ANYNL:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x000d:
- if (eptr < md->end_subject && *eptr == 0x0a) eptr++;
- break;
- case 0x000a:
- break;
-
- case 0x000b:
- case 0x000c:
- case 0x0085:
- case 0x2028:
- case 0x2029:
- if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH);
- break;
- }
- break;
-
- case OP_NOT_HSPACE:
- switch(c)
- {
- default: break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_HSPACE:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- break;
- }
- break;
-
- case OP_NOT_VSPACE:
- switch(c)
- {
- default: break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_VSPACE:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- break;
- }
- break;
-
- case OP_NOT_DIGIT:
- if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_DIGIT:
- if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_NOT_WHITESPACE:
- if (c < 256 && (md->ctypes[c] & ctype_space) != 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_WHITESPACE:
- if (c >= 256 || (md->ctypes[c] & ctype_space) == 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_NOT_WORDCHAR:
- if (c < 256 && (md->ctypes[c] & ctype_word) != 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_WORDCHAR:
- if (c >= 256 || (md->ctypes[c] & ctype_word) == 0)
- RRETURN(MATCH_NOMATCH);
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
- }
- }
- else
-#endif
- /* Not UTF-8 mode */
- {
- for (fi = min;; fi++)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM43);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (fi >= max) RRETURN(MATCH_NOMATCH);
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- RRETURN(MATCH_NOMATCH);
- }
- if (ctype == OP_ANY && IS_NEWLINE(eptr))
- RRETURN(MATCH_NOMATCH);
- c = *eptr++;
- switch(ctype)
- {
- case OP_ANY: /* This is the non-NL case */
- case OP_ALLANY:
- case OP_ANYBYTE:
- break;
-
- case OP_ANYNL:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x000d:
- if (eptr < md->end_subject && *eptr == 0x0a) eptr++;
- break;
-
- case 0x000a:
- break;
-
- case 0x000b:
- case 0x000c:
- case 0x0085:
- if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH);
- break;
- }
- break;
-
- case OP_NOT_HSPACE:
- switch(c)
- {
- default: break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_HSPACE:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- break;
- }
- break;
-
- case OP_NOT_VSPACE:
- switch(c)
- {
- default: break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- RRETURN(MATCH_NOMATCH);
- }
- break;
-
- case OP_VSPACE:
- switch(c)
- {
- default: RRETURN(MATCH_NOMATCH);
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- break;
- }
- break;
-
- case OP_NOT_DIGIT:
- if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_DIGIT:
- if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_NOT_WHITESPACE:
- if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_WHITESPACE:
- if ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_NOT_WORDCHAR:
- if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH);
- break;
-
- case OP_WORDCHAR:
- if ((md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH);
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
- }
- }
- /* Control never gets here */
- }
-
- /* If maximizing, it is worth using inline code for speed, doing the type
- test once at the start (i.e. keep it out of the loop). Again, keep the
- UTF-8 and UCP stuff separate. */
-
- else
- {
- pp = eptr; /* Remember where we started */
-
-#ifdef SUPPORT_UCP
- if (prop_type >= 0)
- {
- switch(prop_type)
- {
- case PT_ANY:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (prop_fail_result) break;
- eptr+= len;
- }
- break;
-
- case PT_LAMP:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == ucp_Lu ||
- prop_chartype == ucp_Ll ||
- prop_chartype == ucp_Lt) == prop_fail_result)
- break;
- eptr+= len;
- }
- break;
-
- case PT_GC:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- prop_category = UCD_CATEGORY(c);
- if ((prop_category == prop_value) == prop_fail_result)
- break;
- eptr+= len;
- }
- break;
-
- case PT_PC:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- prop_chartype = UCD_CHARTYPE(c);
- if ((prop_chartype == prop_value) == prop_fail_result)
- break;
- eptr+= len;
- }
- break;
-
- case PT_SC:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- prop_script = UCD_SCRIPT(c);
- if ((prop_script == prop_value) == prop_fail_result)
- break;
- eptr+= len;
- }
- break;
- }
-
- /* eptr is now past the end of the maximum run */
-
- if (possessive) continue;
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM44);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- if (utf8) BACKCHAR(eptr);
- }
- }
-
- /* Match extended Unicode sequences. We will get here only if the
- support is in the binary; otherwise a compile-time error occurs. */
-
- else if (ctype == OP_EXTUNI)
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARINCTEST(c, eptr);
- prop_category = UCD_CATEGORY(c);
- if (prop_category == ucp_M) break;
- while (eptr < md->end_subject)
- {
- int len = 1;
- if (!utf8) c = *eptr; else
- {
- GETCHARLEN(c, eptr, len);
- }
- prop_category = UCD_CATEGORY(c);
- if (prop_category != ucp_M) break;
- eptr += len;
- }
- }
-
- /* eptr is now past the end of the maximum run */
-
- if (possessive) continue;
-
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM45);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- for (;;) /* Move back over one extended */
- {
- int len = 1;
- if (!utf8) c = *eptr; else
- {
- BACKCHAR(eptr);
- GETCHARLEN(c, eptr, len);
- }
- prop_category = UCD_CATEGORY(c);
- if (prop_category != ucp_M) break;
- eptr--;
- }
- }
- }
-
- else
-#endif /* SUPPORT_UCP */
-
-#ifdef SUPPORT_UTF8
- /* UTF-8 mode */
-
- if (utf8)
- {
- switch(ctype)
- {
- case OP_ANY:
- if (max < INT_MAX)
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (IS_NEWLINE(eptr)) break;
- eptr++;
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- }
- }
-
- /* Handle unlimited UTF-8 repeat */
-
- else
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (IS_NEWLINE(eptr)) break;
- eptr++;
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- }
- }
- break;
-
- case OP_ALLANY:
- if (max < INT_MAX)
- {
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- eptr++;
- while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
- }
- }
- else eptr = md->end_subject; /* Unlimited UTF-8 repeat */
- break;
-
- /* The byte case is the same as non-UTF8 */
-
- case OP_ANYBYTE:
- c = max - min;
- if (c > (unsigned int)(md->end_subject - eptr))
- {
- eptr = md->end_subject;
- SCHECK_PARTIAL();
- }
- else eptr += c;
- break;
-
- case OP_ANYNL:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c == 0x000d)
- {
- if (++eptr >= md->end_subject) break;
- if (*eptr == 0x000a) eptr++;
- }
- else
- {
- if (c != 0x000a &&
- (md->bsr_anycrlf ||
- (c != 0x000b && c != 0x000c &&
- c != 0x0085 && c != 0x2028 && c != 0x2029)))
- break;
- eptr += len;
- }
- }
- break;
-
- case OP_NOT_HSPACE:
- case OP_HSPACE:
- for (i = min; i < max; i++)
- {
- BOOL gotspace;
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- switch(c)
- {
- default: gotspace = FALSE; break;
- case 0x09: /* HT */
- case 0x20: /* SPACE */
- case 0xa0: /* NBSP */
- case 0x1680: /* OGHAM SPACE MARK */
- case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */
- case 0x2000: /* EN QUAD */
- case 0x2001: /* EM QUAD */
- case 0x2002: /* EN SPACE */
- case 0x2003: /* EM SPACE */
- case 0x2004: /* THREE-PER-EM SPACE */
- case 0x2005: /* FOUR-PER-EM SPACE */
- case 0x2006: /* SIX-PER-EM SPACE */
- case 0x2007: /* FIGURE SPACE */
- case 0x2008: /* PUNCTUATION SPACE */
- case 0x2009: /* THIN SPACE */
- case 0x200A: /* HAIR SPACE */
- case 0x202f: /* NARROW NO-BREAK SPACE */
- case 0x205f: /* MEDIUM MATHEMATICAL SPACE */
- case 0x3000: /* IDEOGRAPHIC SPACE */
- gotspace = TRUE;
- break;
- }
- if (gotspace == (ctype == OP_NOT_HSPACE)) break;
- eptr += len;
- }
- break;
-
- case OP_NOT_VSPACE:
- case OP_VSPACE:
- for (i = min; i < max; i++)
- {
- BOOL gotspace;
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- switch(c)
- {
- default: gotspace = FALSE; break;
- case 0x0a: /* LF */
- case 0x0b: /* VT */
- case 0x0c: /* FF */
- case 0x0d: /* CR */
- case 0x85: /* NEL */
- case 0x2028: /* LINE SEPARATOR */
- case 0x2029: /* PARAGRAPH SEPARATOR */
- gotspace = TRUE;
- break;
- }
- if (gotspace == (ctype == OP_NOT_VSPACE)) break;
- eptr += len;
- }
- break;
-
- case OP_NOT_DIGIT:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break;
- eptr+= len;
- }
- break;
-
- case OP_DIGIT:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break;
- eptr+= len;
- }
- break;
-
- case OP_NOT_WHITESPACE:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break;
- eptr+= len;
- }
- break;
-
- case OP_WHITESPACE:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break;
- eptr+= len;
- }
- break;
-
- case OP_NOT_WORDCHAR:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break;
- eptr+= len;
- }
- break;
-
- case OP_WORDCHAR:
- for (i = min; i < max; i++)
- {
- int len = 1;
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- GETCHARLEN(c, eptr, len);
- if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break;
- eptr+= len;
- }
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
-
- /* eptr is now past the end of the maximum run */
-
- if (possessive) continue;
- for(;;)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM46);
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- if (eptr-- == pp) break; /* Stop if tried at original pos */
- BACKCHAR(eptr);
- }
- }
- else
-#endif /* SUPPORT_UTF8 */
-
- /* Not UTF-8 mode */
- {
- switch(ctype)
- {
- case OP_ANY:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if (IS_NEWLINE(eptr)) break;
- eptr++;
- }
- break;
-
- case OP_ALLANY:
- case OP_ANYBYTE:
- c = max - min;
- if (c > (unsigned int)(md->end_subject - eptr))
- {
- eptr = md->end_subject;
- SCHECK_PARTIAL();
- }
- else eptr += c;
- break;
-
- case OP_ANYNL:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if (c == 0x000d)
- {
- if (++eptr >= md->end_subject) break;
- if (*eptr == 0x000a) eptr++;
- }
- else
- {
- if (c != 0x000a &&
- (md->bsr_anycrlf ||
- (c != 0x000b && c != 0x000c && c != 0x0085)))
- break;
- eptr++;
- }
- }
- break;
-
- case OP_NOT_HSPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if (c == 0x09 || c == 0x20 || c == 0xa0) break;
- eptr++;
- }
- break;
-
- case OP_HSPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if (c != 0x09 && c != 0x20 && c != 0xa0) break;
- eptr++;
- }
- break;
-
- case OP_NOT_VSPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if (c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d || c == 0x85)
- break;
- eptr++;
- }
- break;
-
- case OP_VSPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- c = *eptr;
- if (c != 0x0a && c != 0x0b && c != 0x0c && c != 0x0d && c != 0x85)
- break;
- eptr++;
- }
- break;
-
- case OP_NOT_DIGIT:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_digit) != 0) break;
- eptr++;
- }
- break;
-
- case OP_DIGIT:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_digit) == 0) break;
- eptr++;
- }
- break;
-
- case OP_NOT_WHITESPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_space) != 0) break;
- eptr++;
- }
- break;
-
- case OP_WHITESPACE:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_space) == 0) break;
- eptr++;
- }
- break;
-
- case OP_NOT_WORDCHAR:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_word) != 0) break;
- eptr++;
- }
- break;
-
- case OP_WORDCHAR:
- for (i = min; i < max; i++)
- {
- if (eptr >= md->end_subject)
- {
- SCHECK_PARTIAL();
- break;
- }
- if ((md->ctypes[*eptr] & ctype_word) == 0) break;
- eptr++;
- }
- break;
-
- default:
- RRETURN(PCRE_ERROR_INTERNAL);
- }
-
- /* eptr is now past the end of the maximum run */
-
- if (possessive) continue;
- while (eptr >= pp)
- {
- RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM47);
- eptr--;
- if (rrc != MATCH_NOMATCH) RRETURN(rrc);
- }
- }
-
- /* Get here if we can't make it match with any permitted repetitions */
-
- RRETURN(MATCH_NOMATCH);
- }
- /* Control never gets here */
-
- /* There's been some horrible disaster. Arrival here can only mean there is
- something seriously wrong in the code above or the OP_xxx definitions. */
-
- default:
- DPRINTF(("Unknown opcode %d\n", *ecode));
- RRETURN(PCRE_ERROR_UNKNOWN_OPCODE);
- }
-
- /* Do not stick any code in here without much thought; it is assumed
- that "continue" in the code above comes out to here to repeat the main
- loop. */
-
- } /* End of main loop */
-/* Control never reaches here */
-
-
-/* When compiling to use the heap rather than the stack for recursive calls to
-match(), the RRETURN() macro jumps here. The number that is saved in
-frame->Xwhere indicates which label we actually want to return to. */
-
-#ifdef NO_RECURSE
-#define LBL(val) case val: goto L_RM##val;
-HEAP_RETURN:
-switch (frame->Xwhere)
- {
- LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)
- LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17)
- LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33)
- LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52)
- LBL(53) LBL(54)
-#ifdef SUPPORT_UTF8
- LBL(16) LBL(18) LBL(20) LBL(21) LBL(22) LBL(23) LBL(28) LBL(30)
- LBL(32) LBL(34) LBL(42) LBL(46)
-#ifdef SUPPORT_UCP
- LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45)
-#endif /* SUPPORT_UCP */
-#endif /* SUPPORT_UTF8 */
- default:
- DPRINTF(("jump error in pcre match: label %d non-existent\n", frame->Xwhere));
- return PCRE_ERROR_INTERNAL;
- }
-#undef LBL
-#endif /* NO_RECURSE */
-}
-
-
-/***************************************************************************
-****************************************************************************
- RECURSION IN THE match() FUNCTION
-
-Undefine all the macros that were defined above to handle this. */
-
-#ifdef NO_RECURSE
-#undef eptr
-#undef ecode
-#undef mstart
-#undef offset_top
-#undef ims
-#undef eptrb
-#undef flags
-
-#undef callpat
-#undef charptr
-#undef data
-#undef next
-#undef pp
-#undef prev
-#undef saved_eptr
-
-#undef new_recursive
-
-#undef cur_is_word
-#undef condition
-#undef prev_is_word
-
-#undef original_ims
-
-#undef ctype
-#undef length
-#undef max
-#undef min
-#undef number
-#undef offset
-#undef op
-#undef save_capture_last
-#undef save_offset1
-#undef save_offset2
-#undef save_offset3
-#undef stacksave
-
-#undef newptrb
-
-#endif
-
-/* These two are defined as macros in both cases */
-
-#undef fc
-#undef fi
-
-/***************************************************************************
-***************************************************************************/
-
-
-
-/*************************************************
-* Execute a Regular Expression *
-*************************************************/
-
-/* This function applies a compiled re to a subject string and picks out
-portions of the string if it matches. Two elements in the vector are set for
-each substring: the offsets to the start and end of the substring.
-
-Arguments:
- argument_re points to the compiled expression
- extra_data points to extra data or is NULL
- subject points to the subject string
- length length of subject string (may contain binary zeros)
- start_offset where to start in the subject string
- options option bits
- offsets points to a vector of ints to be filled in with offsets
- offsetcount the number of elements in the vector
-
-Returns: > 0 => success; value is the number of elements filled in
- = 0 => success, but offsets is not big enough
- -1 => failed to match
- < -1 => some kind of unexpected problem
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_exec(const pcre *argument_re, const pcre_extra *extra_data,
- PCRE_SPTR subject, int length, int start_offset, int options, int *offsets,
- int offsetcount)
-{
-int rc, resetcount, ocount;
-int first_byte = -1;
-int req_byte = -1;
-int req_byte2 = -1;
-int newline;
-unsigned long int ims;
-BOOL using_temporary_offsets = FALSE;
-BOOL anchored;
-BOOL startline;
-BOOL firstline;
-BOOL first_byte_caseless = FALSE;
-BOOL req_byte_caseless = FALSE;
-BOOL utf8;
-match_data match_block;
-match_data *md = &match_block;
-const uschar *tables;
-const uschar *start_bits = NULL;
-USPTR start_match = (USPTR)subject + start_offset;
-USPTR end_subject;
-USPTR start_partial = NULL;
-USPTR req_byte_ptr = start_match - 1;
-
-pcre_study_data internal_study;
-const pcre_study_data *study;
-
-real_pcre internal_re;
-const real_pcre *external_re = (const real_pcre *)argument_re;
-const real_pcre *re = external_re;
-
-/* Plausibility checks */
-
-if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
-if (re == NULL || subject == NULL ||
- (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
-if (offsetcount < 0) return PCRE_ERROR_BADCOUNT;
-
-/* This information is for finding all the numbers associated with a given
-name, for condition testing. */
-
-md->name_table = (uschar *)re + re->name_table_offset;
-md->name_count = re->name_count;
-md->name_entry_size = re->name_entry_size;
-
-/* Fish out the optional data from the extra_data structure, first setting
-the default values. */
-
-study = NULL;
-md->match_limit = MATCH_LIMIT;
-md->match_limit_recursion = MATCH_LIMIT_RECURSION;
-md->callout_data = NULL;
-
-/* The table pointer is always in native byte order. */
-
-tables = external_re->tables;
-
-if (extra_data != NULL)
- {
- register unsigned int flags = extra_data->flags;
- if ((flags & PCRE_EXTRA_STUDY_DATA) != 0)
- study = (const pcre_study_data *)extra_data->study_data;
- if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0)
- md->match_limit = extra_data->match_limit;
- if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0)
- md->match_limit_recursion = extra_data->match_limit_recursion;
- if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0)
- md->callout_data = extra_data->callout_data;
- if ((flags & PCRE_EXTRA_TABLES) != 0) tables = extra_data->tables;
- }
-
-/* If the exec call supplied NULL for tables, use the inbuilt ones. This
-is a feature that makes it possible to save compiled regex and re-use them
-in other programs later. */
-
-if (tables == NULL) tables = _pcre_default_tables;
-
-/* Check that the first field in the block is the magic number. If it is not,
-test for a regex that was compiled on a host of opposite endianness. If this is
-the case, flipped values are put in internal_re and internal_study if there was
-study data too. */
-
-if (re->magic_number != MAGIC_NUMBER)
- {
- re = _pcre_try_flipped(re, &internal_re, study, &internal_study);
- if (re == NULL) return PCRE_ERROR_BADMAGIC;
- if (study != NULL) study = &internal_study;
- }
-
-/* Set up other data */
-
-anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
-startline = (re->flags & PCRE_STARTLINE) != 0;
-firstline = (re->options & PCRE_FIRSTLINE) != 0;
-
-/* The code starts after the real_pcre block and the capture name table. */
-
-md->start_code = (const uschar *)external_re + re->name_table_offset +
- re->name_count * re->name_entry_size;
-
-md->start_subject = (USPTR)subject;
-md->start_offset = start_offset;
-md->end_subject = md->start_subject + length;
-end_subject = md->end_subject;
-
-md->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
-utf8 = md->utf8 = (re->options & PCRE_UTF8) != 0;
-md->jscript_compat = (re->options & PCRE_JAVASCRIPT_COMPAT) != 0;
-
-md->notbol = (options & PCRE_NOTBOL) != 0;
-md->noteol = (options & PCRE_NOTEOL) != 0;
-md->notempty = (options & PCRE_NOTEMPTY) != 0;
-md->notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0;
-md->partial = ((options & PCRE_PARTIAL_HARD) != 0)? 2 :
- ((options & PCRE_PARTIAL_SOFT) != 0)? 1 : 0;
-md->hitend = FALSE;
-
-md->recursive = NULL; /* No recursion at top level */
-
-md->lcc = tables + lcc_offset;
-md->ctypes = tables + ctypes_offset;
-
-/* Handle different \R options. */
-
-switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE))
- {
- case 0:
- if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0)
- md->bsr_anycrlf = (re->options & PCRE_BSR_ANYCRLF) != 0;
- else
-#ifdef BSR_ANYCRLF
- md->bsr_anycrlf = TRUE;
-#else
- md->bsr_anycrlf = FALSE;
-#endif
- break;
-
- case PCRE_BSR_ANYCRLF:
- md->bsr_anycrlf = TRUE;
- break;
-
- case PCRE_BSR_UNICODE:
- md->bsr_anycrlf = FALSE;
- break;
-
- default: return PCRE_ERROR_BADNEWLINE;
- }
-
-/* Handle different types of newline. The three bits give eight cases. If
-nothing is set at run time, whatever was used at compile time applies. */
-
-switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options :
- (pcre_uint32)options) & PCRE_NEWLINE_BITS)
- {
- case 0: newline = NEWLINE; break; /* Compile-time default */
- case PCRE_NEWLINE_CR: newline = CHAR_CR; break;
- case PCRE_NEWLINE_LF: newline = CHAR_NL; break;
- case PCRE_NEWLINE_CR+
- PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break;
- case PCRE_NEWLINE_ANY: newline = -1; break;
- case PCRE_NEWLINE_ANYCRLF: newline = -2; break;
- default: return PCRE_ERROR_BADNEWLINE;
- }
-
-if (newline == -2)
- {
- md->nltype = NLTYPE_ANYCRLF;
- }
-else if (newline < 0)
- {
- md->nltype = NLTYPE_ANY;
- }
-else
- {
- md->nltype = NLTYPE_FIXED;
- if (newline > 255)
- {
- md->nllen = 2;
- md->nl[0] = (newline >> 8) & 255;
- md->nl[1] = newline & 255;
- }
- else
- {
- md->nllen = 1;
- md->nl[0] = newline;
- }
- }
-
-/* Partial matching was originally supported only for a restricted set of
-regexes; from release 8.00 there are no restrictions, but the bits are still
-defined (though never set). So there's no harm in leaving this code. */
-
-if (md->partial && (re->flags & PCRE_NOPARTIAL) != 0)
- return PCRE_ERROR_BADPARTIAL;
-
-/* Check a UTF-8 string if required. Unfortunately there's no way of passing
-back the character offset. */
-
-#ifdef SUPPORT_UTF8
-if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0)
- {
- if (_pcre_valid_utf8((USPTR)subject, length) >= 0)
- return PCRE_ERROR_BADUTF8;
- if (start_offset > 0 && start_offset < length)
- {
- int tb = ((USPTR)subject)[start_offset];
- if (tb > 127)
- {
- tb &= 0xc0;
- if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET;
- }
- }
- }
-#endif
-
-/* The ims options can vary during the matching as a result of the presence
-of (?ims) items in the pattern. They are kept in a local variable so that
-restoring at the exit of a group is easy. */
-
-ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
-
-/* If the expression has got more back references than the offsets supplied can
-hold, we get a temporary chunk of working store to use during the matching.
-Otherwise, we can use the vector supplied, rounding down its size to a multiple
-of 3. */
-
-ocount = offsetcount - (offsetcount % 3);
-
-if (re->top_backref > 0 && re->top_backref >= ocount/3)
- {
- ocount = re->top_backref * 3 + 3;
- md->offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
- if (md->offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
- using_temporary_offsets = TRUE;
- DPRINTF(("Got memory to hold back references\n"));
- }
-else md->offset_vector = offsets;
-
-md->offset_end = ocount;
-md->offset_max = (2*ocount)/3;
-md->offset_overflow = FALSE;
-md->capture_last = -1;
-
-/* Compute the minimum number of offsets that we need to reset each time. Doing
-this makes a huge difference to execution time when there aren't many brackets
-in the pattern. */
-
-resetcount = 2 + re->top_bracket * 2;
-if (resetcount > offsetcount) resetcount = ocount;
-
-/* Reset the working variable associated with each extraction. These should
-never be used unless previously set, but they get saved and restored, and so we
-initialize them to avoid reading uninitialized locations. */
-
-if (md->offset_vector != NULL)
- {
- register int *iptr = md->offset_vector + ocount;
- register int *iend = iptr - resetcount/2 + 1;
- while (--iptr >= iend) *iptr = -1;
- }
-
-/* Set up the first character to match, if available. The first_byte value is
-never set for an anchored regular expression, but the anchoring may be forced
-at run time, so we have to test for anchoring. The first char may be unset for
-an unanchored pattern, of course. If there's no first char and the pattern was
-studied, there may be a bitmap of possible first characters. */
-
-if (!anchored)
- {
- if ((re->flags & PCRE_FIRSTSET) != 0)
- {
- first_byte = re->first_byte & 255;
- if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE)
- first_byte = md->lcc[first_byte];
- }
- else
- if (!startline && study != NULL &&
- (study->flags & PCRE_STUDY_MAPPED) != 0)
- start_bits = study->start_bits;
- }
-
-/* For anchored or unanchored matches, there may be a "last known required
-character" set. */
-
-if ((re->flags & PCRE_REQCHSET) != 0)
- {
- req_byte = re->req_byte & 255;
- req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0;
- req_byte2 = (tables + fcc_offset)[req_byte]; /* case flipped */
- }
-
-
-/* ==========================================================================*/
-
-/* Loop for handling unanchored repeated matching attempts; for anchored regexs
-the loop runs just once. */
-
-for(;;)
- {
- USPTR save_end_subject = end_subject;
- USPTR new_start_match;
-
- /* Reset the maximum number of extractions we might see. */
-
- if (md->offset_vector != NULL)
- {
- register int *iptr = md->offset_vector;
- register int *iend = iptr + resetcount;
- while (iptr < iend) *iptr++ = -1;
- }
-
- /* If firstline is TRUE, the start of the match is constrained to the first
- line of a multiline string. That is, the match must be before or at the first
- newline. Implement this by temporarily adjusting end_subject so that we stop
- scanning at a newline. If the match fails at the newline, later code breaks
- this loop. */
-
- if (firstline)
- {
- USPTR t = start_match;
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- while (t < md->end_subject && !IS_NEWLINE(t))
- {
- t++;
- while (t < end_subject && (*t & 0xc0) == 0x80) t++;
- }
- }
- else
-#endif
- while (t < md->end_subject && !IS_NEWLINE(t)) t++;
- end_subject = t;
- }
-
- /* There are some optimizations that avoid running the match if a known
- starting point is not found, or if a known later character is not present.
- However, there is an option that disables these, for testing and for ensuring
- that all callouts do actually occur. */
-
- if ((options & PCRE_NO_START_OPTIMIZE) == 0)
- {
- /* Advance to a unique first byte if there is one. */
-
- if (first_byte >= 0)
- {
- if (first_byte_caseless)
- while (start_match < end_subject && md->lcc[*start_match] != first_byte)
- start_match++;
- else
- while (start_match < end_subject && *start_match != first_byte)
- start_match++;
- }
-
- /* Or to just after a linebreak for a multiline match */
-
- else if (startline)
- {
- if (start_match > md->start_subject + start_offset)
- {
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- {
- start_match++;
- while(start_match < end_subject && (*start_match & 0xc0) == 0x80)
- start_match++;
- }
- }
- else
-#endif
- while (start_match < end_subject && !WAS_NEWLINE(start_match))
- start_match++;
-
- /* If we have just passed a CR and the newline option is ANY or ANYCRLF,
- and we are now at a LF, advance the match position by one more character.
- */
-
- if (start_match[-1] == CHAR_CR &&
- (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) &&
- start_match < end_subject &&
- *start_match == CHAR_NL)
- start_match++;
- }
- }
-
- /* Or to a non-unique first byte after study */
-
- else if (start_bits != NULL)
- {
- while (start_match < end_subject)
- {
- register unsigned int c = *start_match;
- if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++;
- else break;
- }
- }
- } /* Starting optimizations */
-
- /* Restore fudged end_subject */
-
- end_subject = save_end_subject;
-
- /* The following two optimizations are disabled for partial matching or if
- disabling is explicitly requested. */
-
- if ((options & PCRE_NO_START_OPTIMIZE) == 0 && !md->partial)
- {
- /* If the pattern was studied, a minimum subject length may be set. This is
- a lower bound; no actual string of that length may actually match the
- pattern. Although the value is, strictly, in characters, we treat it as
- bytes to avoid spending too much time in this optimization. */
-
- if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 &&
- (pcre_uint32)(end_subject - start_match) < study->minlength)
- {
- rc = MATCH_NOMATCH;
- break;
- }
-
- /* If req_byte is set, we know that that character must appear in the
- subject for the match to succeed. If the first character is set, req_byte
- must be later in the subject; otherwise the test starts at the match point.
- This optimization can save a huge amount of backtracking in patterns with
- nested unlimited repeats that aren't going to match. Writing separate code
- for cased/caseless versions makes it go faster, as does using an
- autoincrement and backing off on a match.
-
- HOWEVER: when the subject string is very, very long, searching to its end
- can take a long time, and give bad performance on quite ordinary patterns.
- This showed up when somebody was matching something like /^\d+C/ on a
- 32-megabyte string... so we don't do this when the string is sufficiently
- long. */
-
- if (req_byte >= 0 && end_subject - start_match < REQ_BYTE_MAX)
- {
- register USPTR p = start_match + ((first_byte >= 0)? 1 : 0);
-
- /* We don't need to repeat the search if we haven't yet reached the
- place we found it at last time. */
-
- if (p > req_byte_ptr)
- {
- if (req_byte_caseless)
- {
- while (p < end_subject)
- {
- register int pp = *p++;
- if (pp == req_byte || pp == req_byte2) { p--; break; }
- }
- }
- else
- {
- while (p < end_subject)
- {
- if (*p++ == req_byte) { p--; break; }
- }
- }
-
- /* If we can't find the required character, break the matching loop,
- forcing a match failure. */
-
- if (p >= end_subject)
- {
- rc = MATCH_NOMATCH;
- break;
- }
-
- /* If we have found the required character, save the point where we
- found it, so that we don't search again next time round the loop if
- the start hasn't passed this character yet. */
-
- req_byte_ptr = p;
- }
- }
- }
-
-#ifdef PCRE_DEBUG /* Sigh. Some compilers never learn. */
- printf(">>>> Match against: ");
- pchars(start_match, end_subject - start_match, TRUE, md);
- printf("\n");
-#endif
-
- /* OK, we can now run the match. If "hitend" is set afterwards, remember the
- first starting point for which a partial match was found. */
-
- md->start_match_ptr = start_match;
- md->start_used_ptr = start_match;
- md->match_call_count = 0;
- rc = match(start_match, md->start_code, start_match, NULL, 2, md, ims, NULL,
- 0, 0);
- if (md->hitend && start_partial == NULL) start_partial = md->start_used_ptr;
-
- switch(rc)
- {
- /* NOMATCH and PRUNE advance by one character. THEN at this level acts
- exactly like PRUNE. */
-
- case MATCH_NOMATCH:
- case MATCH_PRUNE:
- case MATCH_THEN:
- new_start_match = start_match + 1;
-#ifdef SUPPORT_UTF8
- if (utf8)
- while(new_start_match < end_subject && (*new_start_match & 0xc0) == 0x80)
- new_start_match++;
-#endif
- break;
-
- /* SKIP passes back the next starting point explicitly. */
-
- case MATCH_SKIP:
- new_start_match = md->start_match_ptr;
- break;
-
- /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */
-
- case MATCH_COMMIT:
- rc = MATCH_NOMATCH;
- goto ENDLOOP;
-
- /* Any other return is either a match, or some kind of error. */
-
- default:
- goto ENDLOOP;
- }
-
- /* Control reaches here for the various types of "no match at this point"
- result. Reset the code to MATCH_NOMATCH for subsequent checking. */
-
- rc = MATCH_NOMATCH;
-
- /* If PCRE_FIRSTLINE is set, the match must happen before or at the first
- newline in the subject (though it may continue over the newline). Therefore,
- if we have just failed to match, starting at a newline, do not continue. */
-
- if (firstline && IS_NEWLINE(start_match)) break;
-
- /* Advance to new matching position */
-
- start_match = new_start_match;
-
- /* Break the loop if the pattern is anchored or if we have passed the end of
- the subject. */
-
- if (anchored || start_match > end_subject) break;
-
- /* If we have just passed a CR and we are now at a LF, and the pattern does
- not contain any explicit matches for \r or \n, and the newline option is CRLF
- or ANY or ANYCRLF, advance the match position by one more character. */
-
- if (start_match[-1] == CHAR_CR &&
- start_match < end_subject &&
- *start_match == CHAR_NL &&
- (re->flags & PCRE_HASCRORLF) == 0 &&
- (md->nltype == NLTYPE_ANY ||
- md->nltype == NLTYPE_ANYCRLF ||
- md->nllen == 2))
- start_match++;
-
- } /* End of for(;;) "bumpalong" loop */
-
-/* ==========================================================================*/
-
-/* We reach here when rc is not MATCH_NOMATCH, or if one of the stopping
-conditions is true:
-
-(1) The pattern is anchored or the match was failed by (*COMMIT);
-
-(2) We are past the end of the subject;
-
-(3) PCRE_FIRSTLINE is set and we have failed to match at a newline, because
- this option requests that a match occur at or before the first newline in
- the subject.
-
-When we have a match and the offset vector is big enough to deal with any
-backreferences, captured substring offsets will already be set up. In the case
-where we had to get some local store to hold offsets for backreference
-processing, copy those that we can. In this case there need not be overflow if
-certain parts of the pattern were not used, even though there are more
-capturing parentheses than vector slots. */
-
-ENDLOOP:
-
-if (rc == MATCH_MATCH)
- {
- if (using_temporary_offsets)
- {
- if (offsetcount >= 4)
- {
- memcpy(offsets + 2, md->offset_vector + 2,
- (offsetcount - 2) * sizeof(int));
- DPRINTF(("Copied offsets from temporary memory\n"));
- }
- if (md->end_offset_top > offsetcount) md->offset_overflow = TRUE;
- DPRINTF(("Freeing temporary memory\n"));
- (pcre_free)(md->offset_vector);
- }
-
- /* Set the return code to the number of captured strings, or 0 if there are
- too many to fit into the vector. */
-
- rc = md->offset_overflow? 0 : md->end_offset_top/2;
-
- /* If there is space, set up the whole thing as substring 0. The value of
- md->start_match_ptr might be modified if \K was encountered on the success
- matching path. */
-
- if (offsetcount < 2) rc = 0; else
- {
- offsets[0] = md->start_match_ptr - md->start_subject;
- offsets[1] = md->end_match_ptr - md->start_subject;
- }
-
- DPRINTF((">>>> returning %d\n", rc));
- return rc;
- }
-
-/* Control gets here if there has been an error, or if the overall match
-attempt has failed at all permitted starting positions. */
-
-if (using_temporary_offsets)
- {
- DPRINTF(("Freeing temporary memory\n"));
- (pcre_free)(md->offset_vector);
- }
-
-if (rc != MATCH_NOMATCH && rc != PCRE_ERROR_PARTIAL)
- {
- DPRINTF((">>>> error: returning %d\n", rc));
- return rc;
- }
-else if (start_partial != NULL)
- {
- DPRINTF((">>>> returning PCRE_ERROR_PARTIAL\n"));
- if (offsetcount > 1)
- {
- offsets[0] = start_partial - (USPTR)subject;
- offsets[1] = end_subject - (USPTR)subject;
- }
- return PCRE_ERROR_PARTIAL;
- }
-else
- {
- DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n"));
- return PCRE_ERROR_NOMATCH;
- }
-}
-
-/* End of pcre_exec.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains the external function pcre_fullinfo(), which returns
-information about a compiled pattern. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Return info about compiled pattern *
-*************************************************/
-
-/* This is a newer "info" function which has an extensible interface so
-that additional items can be added compatibly.
-
-Arguments:
- argument_re points to compiled code
- extra_data points extra data, or NULL
- what what information is required
- where where to put the information
-
-Returns: 0 if data returned, negative on error
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, int what,
- void *where)
-{
-real_pcre internal_re;
-pcre_study_data internal_study;
-const real_pcre *re = (const real_pcre *)argument_re;
-const pcre_study_data *study = NULL;
-
-if (re == NULL || where == NULL) return PCRE_ERROR_NULL;
-
-if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0)
- study = (const pcre_study_data *)extra_data->study_data;
-
-if (re->magic_number != MAGIC_NUMBER)
- {
- re = _pcre_try_flipped(re, &internal_re, study, &internal_study);
- if (re == NULL) return PCRE_ERROR_BADMAGIC;
- if (study != NULL) study = &internal_study;
- }
-
-switch (what)
- {
- case PCRE_INFO_OPTIONS:
- *((unsigned long int *)where) = re->options & PUBLIC_COMPILE_OPTIONS;
- break;
-
- case PCRE_INFO_SIZE:
- *((size_t *)where) = re->size;
- break;
-
- case PCRE_INFO_STUDYSIZE:
- *((size_t *)where) = (study == NULL)? 0 : study->size;
- break;
-
- case PCRE_INFO_CAPTURECOUNT:
- *((int *)where) = re->top_bracket;
- break;
-
- case PCRE_INFO_BACKREFMAX:
- *((int *)where) = re->top_backref;
- break;
-
- case PCRE_INFO_FIRSTBYTE:
- *((int *)where) =
- ((re->flags & PCRE_FIRSTSET) != 0)? re->first_byte :
- ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2;
- break;
-
- /* Make sure we pass back the pointer to the bit vector in the external
- block, not the internal copy (with flipped integer fields). */
-
- case PCRE_INFO_FIRSTTABLE:
- *((const uschar **)where) =
- (study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0)?
- ((const pcre_study_data *)extra_data->study_data)->start_bits : NULL;
- break;
-
- case PCRE_INFO_MINLENGTH:
- *((int *)where) =
- (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0)?
- study->minlength : -1;
- break;
-
- case PCRE_INFO_LASTLITERAL:
- *((int *)where) =
- ((re->flags & PCRE_REQCHSET) != 0)? re->req_byte : -1;
- break;
-
- case PCRE_INFO_NAMEENTRYSIZE:
- *((int *)where) = re->name_entry_size;
- break;
-
- case PCRE_INFO_NAMECOUNT:
- *((int *)where) = re->name_count;
- break;
-
- case PCRE_INFO_NAMETABLE:
- *((const uschar **)where) = (const uschar *)re + re->name_table_offset;
- break;
-
- case PCRE_INFO_DEFAULT_TABLES:
- *((const uschar **)where) = (const uschar *)(_pcre_default_tables);
- break;
-
- /* From release 8.00 this will always return TRUE because NOPARTIAL is
- no longer ever set (the restrictions have been removed). */
-
- case PCRE_INFO_OKPARTIAL:
- *((int *)where) = (re->flags & PCRE_NOPARTIAL) == 0;
- break;
-
- case PCRE_INFO_JCHANGED:
- *((int *)where) = (re->flags & PCRE_JCHANGED) != 0;
- break;
-
- case PCRE_INFO_HASCRORLF:
- *((int *)where) = (re->flags & PCRE_HASCRORLF) != 0;
- break;
-
- default: return PCRE_ERROR_BADOPTION;
- }
-
-return 0;
-}
-
-/* End of pcre_fullinfo.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2008 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains some convenience functions for extracting substrings
-from the subject string after a regex match has succeeded. The original idea
-for these functions came from Scott Wimer. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Find number for named string *
-*************************************************/
-
-/* This function is used by the get_first_set() function below, as well
-as being generally available. It assumes that names are unique.
-
-Arguments:
- code the compiled regex
- stringname the name whose number is required
-
-Returns: the number of the named parentheses, or a negative number
- (PCRE_ERROR_NOSUBSTRING) if not found
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_get_stringnumber(const pcre *code, const char *stringname)
-{
-int rc;
-int entrysize;
-int top, bot;
-uschar *nametable;
-
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0)
- return rc;
-if (top <= 0) return PCRE_ERROR_NOSUBSTRING;
-
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0)
- return rc;
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0)
- return rc;
-
-bot = 0;
-while (top > bot)
- {
- int mid = (top + bot) / 2;
- uschar *entry = nametable + entrysize*mid;
- int c = strcmp(stringname, (char *)(entry + 2));
- if (c == 0) return (entry[0] << 8) + entry[1];
- if (c > 0) bot = mid + 1; else top = mid;
- }
-
-return PCRE_ERROR_NOSUBSTRING;
-}
-
-
-
-/*************************************************
-* Find (multiple) entries for named string *
-*************************************************/
-
-/* This is used by the get_first_set() function below, as well as being
-generally available. It is used when duplicated names are permitted.
-
-Arguments:
- code the compiled regex
- stringname the name whose entries required
- firstptr where to put the pointer to the first entry
- lastptr where to put the pointer to the last entry
-
-Returns: the length of each entry, or a negative number
- (PCRE_ERROR_NOSUBSTRING) if not found
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_get_stringtable_entries(const pcre *code, const char *stringname,
- char **firstptr, char **lastptr)
-{
-int rc;
-int entrysize;
-int top, bot;
-uschar *nametable, *lastentry;
-
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0)
- return rc;
-if (top <= 0) return PCRE_ERROR_NOSUBSTRING;
-
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0)
- return rc;
-if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0)
- return rc;
-
-lastentry = nametable + entrysize * (top - 1);
-bot = 0;
-while (top > bot)
- {
- int mid = (top + bot) / 2;
- uschar *entry = nametable + entrysize*mid;
- int c = strcmp(stringname, (char *)(entry + 2));
- if (c == 0)
- {
- uschar *first = entry;
- uschar *last = entry;
- while (first > nametable)
- {
- if (strcmp(stringname, (char *)(first - entrysize + 2)) != 0) break;
- first -= entrysize;
- }
- while (last < lastentry)
- {
- if (strcmp(stringname, (char *)(last + entrysize + 2)) != 0) break;
- last += entrysize;
- }
- *firstptr = (char *)first;
- *lastptr = (char *)last;
- return entrysize;
- }
- if (c > 0) bot = mid + 1; else top = mid;
- }
-
-return PCRE_ERROR_NOSUBSTRING;
-}
-
-
-#ifdef NOT_USED_IN_GLIB
-
-/*************************************************
-* Find first set of multiple named strings *
-*************************************************/
-
-/* This function allows for duplicate names in the table of named substrings.
-It returns the number of the first one that was set in a pattern match.
-
-Arguments:
- code the compiled regex
- stringname the name of the capturing substring
- ovector the vector of matched substrings
-
-Returns: the number of the first that is set,
- or the number of the last one if none are set,
- or a negative number on error
-*/
-
-static int
-get_first_set(const pcre *code, const char *stringname, int *ovector)
-{
-const real_pcre *re = (const real_pcre *)code;
-int entrysize;
-char *first, *last;
-uschar *entry;
-if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0)
- return pcre_get_stringnumber(code, stringname);
-entrysize = pcre_get_stringtable_entries(code, stringname, &first, &last);
-if (entrysize <= 0) return entrysize;
-for (entry = (uschar *)first; entry <= (uschar *)last; entry += entrysize)
- {
- int n = (entry[0] << 8) + entry[1];
- if (ovector[n*2] >= 0) return n;
- }
-return (first[0] << 8) + first[1];
-}
-
-
-
-
-/*************************************************
-* Copy captured string to given buffer *
-*************************************************/
-
-/* This function copies a single captured substring into a given buffer.
-Note that we use memcpy() rather than strncpy() in case there are binary zeros
-in the string.
-
-Arguments:
- subject the subject string that was matched
- ovector pointer to the offsets table
- stringcount the number of substrings that were captured
- (i.e. the yield of the pcre_exec call, unless
- that was zero, in which case it should be 1/3
- of the offset table size)
- stringnumber the number of the required substring
- buffer where to put the substring
- size the size of the buffer
-
-Returns: if successful:
- the length of the copied string, not including the zero
- that is put on the end; can be zero
- if not successful:
- PCRE_ERROR_NOMEMORY (-6) buffer too small
- PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_copy_substring(const char *subject, int *ovector, int stringcount,
- int stringnumber, char *buffer, int size)
-{
-int yield;
-if (stringnumber < 0 || stringnumber >= stringcount)
- return PCRE_ERROR_NOSUBSTRING;
-stringnumber *= 2;
-yield = ovector[stringnumber+1] - ovector[stringnumber];
-if (size < yield + 1) return PCRE_ERROR_NOMEMORY;
-memcpy(buffer, subject + ovector[stringnumber], yield);
-buffer[yield] = 0;
-return yield;
-}
-
-
-
-/*************************************************
-* Copy named captured string to given buffer *
-*************************************************/
-
-/* This function copies a single captured substring into a given buffer,
-identifying it by name. If the regex permits duplicate names, the first
-substring that is set is chosen.
-
-Arguments:
- code the compiled regex
- subject the subject string that was matched
- ovector pointer to the offsets table
- stringcount the number of substrings that were captured
- (i.e. the yield of the pcre_exec call, unless
- that was zero, in which case it should be 1/3
- of the offset table size)
- stringname the name of the required substring
- buffer where to put the substring
- size the size of the buffer
-
-Returns: if successful:
- the length of the copied string, not including the zero
- that is put on the end; can be zero
- if not successful:
- PCRE_ERROR_NOMEMORY (-6) buffer too small
- PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector,
- int stringcount, const char *stringname, char *buffer, int size)
-{
-int n = get_first_set(code, stringname, ovector);
-if (n <= 0) return n;
-return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size);
-}
-
-
-
-/*************************************************
-* Copy all captured strings to new store *
-*************************************************/
-
-/* This function gets one chunk of store and builds a list of pointers and all
-of the captured substrings in it. A NULL pointer is put on the end of the list.
-
-Arguments:
- subject the subject string that was matched
- ovector pointer to the offsets table
- stringcount the number of substrings that were captured
- (i.e. the yield of the pcre_exec call, unless
- that was zero, in which case it should be 1/3
- of the offset table size)
- listptr set to point to the list of pointers
-
-Returns: if successful: 0
- if not successful:
- PCRE_ERROR_NOMEMORY (-6) failed to get store
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_get_substring_list(const char *subject, int *ovector, int stringcount,
- const char ***listptr)
-{
-int i;
-int size = sizeof(char *);
-int double_count = stringcount * 2;
-char **stringlist;
-char *p;
-
-for (i = 0; i < double_count; i += 2)
- size += sizeof(char *) + ovector[i+1] - ovector[i] + 1;
-
-stringlist = (char **)(pcre_malloc)(size);
-if (stringlist == NULL) return PCRE_ERROR_NOMEMORY;
-
-*listptr = (const char **)stringlist;
-p = (char *)(stringlist + stringcount + 1);
-
-for (i = 0; i < double_count; i += 2)
- {
- int len = ovector[i+1] - ovector[i];
- memcpy(p, subject + ovector[i], len);
- *stringlist++ = p;
- p += len;
- *p++ = 0;
- }
-
-*stringlist = NULL;
-return 0;
-}
-
-
-
-/*************************************************
-* Free store obtained by get_substring_list *
-*************************************************/
-
-/* This function exists for the benefit of people calling PCRE from non-C
-programs that can call its functions, but not free() or (pcre_free)() directly.
-
-Argument: the result of a previous pcre_get_substring_list()
-Returns: nothing
-*/
-
-PCRE_EXP_DEFN void PCRE_CALL_CONVENTION
-pcre_free_substring_list(const char **pointer)
-{
-(pcre_free)((void *)pointer);
-}
-
-
-
-/*************************************************
-* Copy captured string to new store *
-*************************************************/
-
-/* This function copies a single captured substring into a piece of new
-store
-
-Arguments:
- subject the subject string that was matched
- ovector pointer to the offsets table
- stringcount the number of substrings that were captured
- (i.e. the yield of the pcre_exec call, unless
- that was zero, in which case it should be 1/3
- of the offset table size)
- stringnumber the number of the required substring
- stringptr where to put a pointer to the substring
-
-Returns: if successful:
- the length of the string, not including the zero that
- is put on the end; can be zero
- if not successful:
- PCRE_ERROR_NOMEMORY (-6) failed to get store
- PCRE_ERROR_NOSUBSTRING (-7) substring not present
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_get_substring(const char *subject, int *ovector, int stringcount,
- int stringnumber, const char **stringptr)
-{
-int yield;
-char *substring;
-if (stringnumber < 0 || stringnumber >= stringcount)
- return PCRE_ERROR_NOSUBSTRING;
-stringnumber *= 2;
-yield = ovector[stringnumber+1] - ovector[stringnumber];
-substring = (char *)(pcre_malloc)(yield + 1);
-if (substring == NULL) return PCRE_ERROR_NOMEMORY;
-memcpy(substring, subject + ovector[stringnumber], yield);
-substring[yield] = 0;
-*stringptr = substring;
-return yield;
-}
-
-
-
-/*************************************************
-* Copy named captured string to new store *
-*************************************************/
-
-/* This function copies a single captured substring, identified by name, into
-new store. If the regex permits duplicate names, the first substring that is
-set is chosen.
-
-Arguments:
- code the compiled regex
- subject the subject string that was matched
- ovector pointer to the offsets table
- stringcount the number of substrings that were captured
- (i.e. the yield of the pcre_exec call, unless
- that was zero, in which case it should be 1/3
- of the offset table size)
- stringname the name of the required substring
- stringptr where to put the pointer
-
-Returns: if successful:
- the length of the copied string, not including the zero
- that is put on the end; can be zero
- if not successful:
- PCRE_ERROR_NOMEMORY (-6) couldn't get memory
- PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
-*/
-
-PCRE_EXP_DEFN int PCRE_CALL_CONVENTION
-pcre_get_named_substring(const pcre *code, const char *subject, int *ovector,
- int stringcount, const char *stringname, const char **stringptr)
-{
-int n = get_first_set(code, stringname, ovector);
-if (n <= 0) return n;
-return pcre_get_substring(subject, ovector, stringcount, n, stringptr);
-}
-
-
-
-
-/*************************************************
-* Free store obtained by get_substring *
-*************************************************/
-
-/* This function exists for the benefit of people calling PCRE from non-C
-programs that can call its functions, but not free() or (pcre_free)() directly.
-
-Argument: the result of a previous pcre_get_substring()
-Returns: nothing
-*/
-
-PCRE_EXP_DEFN void PCRE_CALL_CONVENTION
-pcre_free_substring(const char *pointer)
-{
-(pcre_free)((void *)pointer);
-}
-
-#endif
-
-/* End of pcre_get.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2008 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains global variables that are exported by the PCRE library.
-PCRE is thread-clean and doesn't use any global variables in the normal sense.
-However, it calls memory allocation and freeing functions via the four
-indirections below, and it can optionally do callouts, using the fifth
-indirection. These values can be changed by the caller, but are shared between
-all threads.
-
-For MS Visual Studio and Symbian OS, there are problems in initializing these
-variables to non-local functions. In these cases, therefore, an indirection via
-a local function is used.
-
-Also, when compiling for Virtual Pascal, things are done differently, and
-global variables are not used. */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-#if defined _MSC_VER || defined __SYMBIAN32__
-static void* LocalPcreMalloc(size_t aSize)
- {
- return malloc(aSize);
- }
-static void LocalPcreFree(void* aPtr)
- {
- free(aPtr);
- }
-PCRE_EXP_DATA_DEFN int (*pcre_callout)(pcre_callout_block *) = NULL;
-
-#elif !defined VPCOMPAT
-PCRE_EXP_DATA_DEFN int (*pcre_callout)(pcre_callout_block *) = NULL;
-#endif
-
-/* End of pcre_globals.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2010 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-/* This header contains definitions that are shared between the different
-modules, but which are not relevant to the exported API. This includes some
-functions whose names all begin with "_pcre_". */
-
-#ifndef PCRE_INTERNAL_H
-#define PCRE_INTERNAL_H
-
-/* Define PCRE_DEBUG to get debugging output on stdout. */
-
-#if 0
-#define PCRE_DEBUG
-#endif
-
-/* We do not support both EBCDIC and UTF-8 at the same time. The "configure"
-script prevents both being selected, but not everybody uses "configure". */
-
-#if defined EBCDIC && defined SUPPORT_UTF8
-#error The use of both EBCDIC and SUPPORT_UTF8 is not supported.
-#endif
-
-/* If SUPPORT_UCP is defined, SUPPORT_UTF8 must also be defined. The
-"configure" script ensures this, but not everybody uses "configure". */
-
-#if defined SUPPORT_UCP && !defined SUPPORT_UTF8
-#define SUPPORT_UTF8 1
-#endif
-
-/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
-inline, and there are *still* stupid compilers about that don't like indented
-pre-processor statements, or at least there were when I first wrote this. After
-all, it had only been about 10 years then...
-
-It turns out that the Mac Debugging.h header also defines the macro DPRINTF, so
-be absolutely sure we get our version. */
-
-#undef DPRINTF
-#ifdef PCRE_DEBUG
-#define DPRINTF(p) printf p
-#else
-#define DPRINTF(p) /* Nothing */
-#endif
-
-
-/* Standard C headers plus the external interface definition. The only time
-setjmp and stdarg are used is when NO_RECURSE is set. */
-
-#include <ctype.h>
-#include <limits.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* When compiling a DLL for Windows, the exported symbols have to be declared
-using some MS magic. I found some useful information on this web page:
-http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the
-information there, using __declspec(dllexport) without "extern" we have a
-definition; with "extern" we have a declaration. The settings here override the
-setting in pcre.h (which is included below); it defines only PCRE_EXP_DECL,
-which is all that is needed for applications (they just import the symbols). We
-use:
-
- PCRE_EXP_DECL for declarations
- PCRE_EXP_DEFN for definitions of exported functions
- PCRE_EXP_DATA_DEFN for definitions of exported variables
-
-The reason for the two DEFN macros is that in non-Windows environments, one
-does not want to have "extern" before variable definitions because it leads to
-compiler warnings. So we distinguish between functions and variables. In
-Windows, the two should always be the same.
-
-The reason for wrapping this in #ifndef PCRE_EXP_DECL is so that pcretest,
-which is an application, but needs to import this file in order to "peek" at
-internals, can #include pcre.h first to get an application's-eye view.
-
-In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon,
-special-purpose environments) might want to stick other stuff in front of
-exported symbols. That's why, in the non-Windows case, we set PCRE_EXP_DEFN and
-PCRE_EXP_DATA_DEFN only if they are not already set. */
-
-#ifndef PCRE_EXP_DECL
-# ifdef _WIN32
-# ifndef PCRE_STATIC
-# define PCRE_EXP_DECL extern __declspec(dllexport)
-# define PCRE_EXP_DEFN __declspec(dllexport)
-# define PCRE_EXP_DATA_DEFN __declspec(dllexport)
-# else
-# define PCRE_EXP_DECL extern
-# define PCRE_EXP_DEFN
-# define PCRE_EXP_DATA_DEFN
-# endif
-# else
-# ifdef __cplusplus
-# define PCRE_EXP_DECL extern "C"
-# else
-# define PCRE_EXP_DECL extern
-# endif
-# ifndef PCRE_EXP_DEFN
-# define PCRE_EXP_DEFN PCRE_EXP_DECL
-# endif
-# ifndef PCRE_EXP_DATA_DEFN
-# define PCRE_EXP_DATA_DEFN
-# endif
-# endif
-#endif
-
-/* When compiling with the MSVC compiler, it is sometimes necessary to include
-a "calling convention" before exported function names. (This is secondhand
-information; I know nothing about MSVC myself). For example, something like
-
- void __cdecl function(....)
-
-might be needed. In order so make this easy, all the exported functions have
-PCRE_CALL_CONVENTION just before their names. It is rarely needed; if not
-set, we ensure here that it has no effect. */
-
-#ifndef PCRE_CALL_CONVENTION
-#define PCRE_CALL_CONVENTION
-#endif
-
-/* We need to have types that specify unsigned 16-bit and 32-bit integers. We
-cannot determine these outside the compilation (e.g. by running a program as
-part of "configure") because PCRE is often cross-compiled for use on other
-systems. Instead we make use of the maximum sizes that are available at
-preprocessor time in standard C environments. */
-
-#if USHRT_MAX == 65535
- typedef unsigned short pcre_uint16;
- typedef short pcre_int16;
-#elif UINT_MAX == 65535
- typedef unsigned int pcre_uint16;
- typedef int pcre_int16;
-#else
- #error Cannot determine a type for 16-bit unsigned integers
-#endif
-
-#if UINT_MAX == 4294967295
- typedef unsigned int pcre_uint32;
- typedef int pcre_int32;
-#elif ULONG_MAX == 4294967295
- typedef unsigned long int pcre_uint32;
- typedef long int pcre_int32;
-#else
- #error Cannot determine a type for 32-bit unsigned integers
-#endif
-
-/* When checking for integer overflow in pcre_compile(), we need to handle
-large integers. If a 64-bit integer type is available, we can use that.
-Otherwise we have to cast to double, which of course requires floating point
-arithmetic. Handle this by defining a macro for the appropriate type. If
-stdint.h is available, include it; it may define INT64_MAX. Systems that do not
-have stdint.h (e.g. Solaris) may have inttypes.h. The macro int64_t may be set
-by "configure". */
-
-#if HAVE_STDINT_H
-#include <stdint.h>
-#elif HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-
-#if defined INT64_MAX || defined int64_t
-#define INT64_OR_DOUBLE int64_t
-#else
-#define INT64_OR_DOUBLE double
-#endif
-
-/* All character handling must be done as unsigned characters. Otherwise there
-are problems with top-bit-set characters and functions such as isspace().
-However, we leave the interface to the outside world as char *, because that
-should make things easier for callers. We define a short type for unsigned char
-to save lots of typing. I tried "uchar", but it causes problems on Digital
-Unix, where it is defined in sys/types, so use "uschar" instead. */
-
-typedef unsigned char uschar;
-
-/* This is an unsigned int value that no character can ever have. UTF-8
-characters only go up to 0x7fffffff (though Unicode doesn't go beyond
-0x0010ffff). */
-
-#define NOTACHAR 0xffffffff
-
-/* PCRE is able to support several different kinds of newline (CR, LF, CRLF,
-"any" and "anycrlf" at present). The following macros are used to package up
-testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various
-modules to indicate in which datablock the parameters exist, and what the
-start/end of string field names are. */
-
-#define NLTYPE_FIXED 0 /* Newline is a fixed length string */
-#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */
-#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */
-
-/* This macro checks for a newline at the given position */
-
-#define IS_NEWLINE(p) \
- ((NLBLOCK->nltype != NLTYPE_FIXED)? \
- ((p) < NLBLOCK->PSEND && \
- _pcre_is_newline((p), NLBLOCK->nltype, NLBLOCK->PSEND, &(NLBLOCK->nllen),\
- utf8)) \
- : \
- ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \
- (p)[0] == NLBLOCK->nl[0] && \
- (NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \
- ) \
- )
-
-/* This macro checks for a newline immediately preceding the given position */
-
-#define WAS_NEWLINE(p) \
- ((NLBLOCK->nltype != NLTYPE_FIXED)? \
- ((p) > NLBLOCK->PSSTART && \
- _pcre_was_newline((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \
- &(NLBLOCK->nllen), utf8)) \
- : \
- ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \
- (p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \
- (NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \
- ) \
- )
-
-/* When PCRE is compiled as a C++ library, the subject pointer can be replaced
-with a custom type. This makes it possible, for example, to allow pcre_exec()
-to process subject strings that are discontinuous by using a smart pointer
-class. It must always be possible to inspect all of the subject string in
-pcre_exec() because of the way it backtracks. Two macros are required in the
-normal case, for sign-unspecified and unsigned char pointers. The former is
-used for the external interface and appears in pcre.h, which is why its name
-must begin with PCRE_. */
-
-#ifdef CUSTOM_SUBJECT_PTR
-#define PCRE_SPTR CUSTOM_SUBJECT_PTR
-#define USPTR CUSTOM_SUBJECT_PTR
-#else
-#define PCRE_SPTR const char *
-#define USPTR const unsigned char *
-#endif
-
-
-
-/* Include the public PCRE header and the definitions of UCP character property
-values. */
-
-#include "pcre.h"
-#include "ucp.h"
-
-/* When compiling for use with the Virtual Pascal compiler, these functions
-need to have their names changed. PCRE must be compiled with the -DVPCOMPAT
-option on the command line. */
-
-#ifdef VPCOMPAT
-#define strlen(s) _strlen(s)
-#define strncmp(s1,s2,m) _strncmp(s1,s2,m)
-#define memcmp(s,c,n) _memcmp(s,c,n)
-#define memcpy(d,s,n) _memcpy(d,s,n)
-#define memmove(d,s,n) _memmove(d,s,n)
-#define memset(s,c,n) _memset(s,c,n)
-#else /* VPCOMPAT */
-
-/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(),
-define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY
-is set. Otherwise, include an emulating function for those systems that have
-neither (there some non-Unix environments where this is the case). */
-
-#ifndef HAVE_MEMMOVE
-#undef memmove /* some systems may have a macro */
-#ifdef HAVE_BCOPY
-#define memmove(a, b, c) bcopy(b, a, c)
-#else /* HAVE_BCOPY */
-static void *
-pcre_memmove(void *d, const void *s, size_t n)
-{
-size_t i;
-unsigned char *dest = (unsigned char *)d;
-const unsigned char *src = (const unsigned char *)s;
-if (dest > src)
- {
- dest += n;
- src += n;
- for (i = 0; i < n; ++i) *(--dest) = *(--src);
- return (void *)dest;
- }
-else
- {
- for (i = 0; i < n; ++i) *dest++ = *src++;
- return (void *)(dest - n);
- }
-}
-#define memmove(a, b, c) pcre_memmove(a, b, c)
-#endif /* not HAVE_BCOPY */
-#endif /* not HAVE_MEMMOVE */
-#endif /* not VPCOMPAT */
-
-
-/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored
-in big-endian order) by default. These are used, for example, to link from the
-start of a subpattern to its alternatives and its end. The use of 2 bytes per
-offset limits the size of the compiled regex to around 64K, which is big enough
-for almost everybody. However, I received a request for an even bigger limit.
-For this reason, and also to make the code easier to maintain, the storing and
-loading of offsets from the byte string is now handled by the macros that are
-defined here.
-
-The macros are controlled by the value of LINK_SIZE. This defaults to 2 in
-the config.h file, but can be overridden by using -D on the command line. This
-is automated on Unix systems via the "configure" command. */
-
-#if LINK_SIZE == 2
-
-#define PUT(a,n,d) \
- (a[n] = (d) >> 8), \
- (a[(n)+1] = (d) & 255)
-
-#define GET(a,n) \
- (((a)[n] << 8) | (a)[(n)+1])
-
-#define MAX_PATTERN_SIZE (1 << 16)
-
-
-#elif LINK_SIZE == 3
-
-#define PUT(a,n,d) \
- (a[n] = (d) >> 16), \
- (a[(n)+1] = (d) >> 8), \
- (a[(n)+2] = (d) & 255)
-
-#define GET(a,n) \
- (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])
-
-#define MAX_PATTERN_SIZE (1 << 24)
-
-
-#elif LINK_SIZE == 4
-
-#define PUT(a,n,d) \
- (a[n] = (d) >> 24), \
- (a[(n)+1] = (d) >> 16), \
- (a[(n)+2] = (d) >> 8), \
- (a[(n)+3] = (d) & 255)
-
-#define GET(a,n) \
- (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])
-
-#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
-
-
-#else
-#error LINK_SIZE must be either 2, 3, or 4
-#endif
-
-
-/* Convenience macro defined in terms of the others */
-
-#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE
-
-
-/* PCRE uses some other 2-byte quantities that do not change when the size of
-offsets changes. There are used for repeat counts and for other things such as
-capturing parenthesis numbers in back references. */
-
-#define PUT2(a,n,d) \
- a[n] = (d) >> 8; \
- a[(n)+1] = (d) & 255
-
-#define GET2(a,n) \
- (((a)[n] << 8) | (a)[(n)+1])
-
-#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2
-
-
-/* When UTF-8 encoding is being used, a character is no longer just a single
-byte. The macros for character handling generate simple sequences when used in
-byte-mode, and more complicated ones for UTF-8 characters. BACKCHAR should
-never be called in byte mode. To make sure it can never even appear when UTF-8
-support is omitted, we don't even define it. */
-
-#ifndef SUPPORT_UTF8
-#define GETCHAR(c, eptr) c = *eptr;
-#define GETCHARTEST(c, eptr) c = *eptr;
-#define GETCHARINC(c, eptr) c = *eptr++;
-#define GETCHARINCTEST(c, eptr) c = *eptr++;
-#define GETCHARLEN(c, eptr, len) c = *eptr;
-/* #define BACKCHAR(eptr) */
-
-#else /* SUPPORT_UTF8 */
-
-/* Get the next UTF-8 character, not advancing the pointer. This is called when
-we know we are in UTF-8 mode. */
-
-#define GETCHAR(c, eptr) \
- c = *eptr; \
- if (c >= 0xc0) \
- { \
- int gcii; \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- for (gcii = 1; gcii <= gcaa; gcii++) \
- { \
- gcss -= 6; \
- c |= (eptr[gcii] & 0x3f) << gcss; \
- } \
- }
-
-/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the
-pointer. */
-
-#define GETCHARTEST(c, eptr) \
- c = *eptr; \
- if (utf8 && c >= 0xc0) \
- { \
- int gcii; \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- for (gcii = 1; gcii <= gcaa; gcii++) \
- { \
- gcss -= 6; \
- c |= (eptr[gcii] & 0x3f) << gcss; \
- } \
- }
-
-/* Get the next UTF-8 character, advancing the pointer. This is called when we
-know we are in UTF-8 mode. */
-
-#define GETCHARINC(c, eptr) \
- c = *eptr++; \
- if (c >= 0xc0) \
- { \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- while (gcaa-- > 0) \
- { \
- gcss -= 6; \
- c |= (*eptr++ & 0x3f) << gcss; \
- } \
- }
-
-/* Get the next character, testing for UTF-8 mode, and advancing the pointer */
-
-#define GETCHARINCTEST(c, eptr) \
- c = *eptr++; \
- if (utf8 && c >= 0xc0) \
- { \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- while (gcaa-- > 0) \
- { \
- gcss -= 6; \
- c |= (*eptr++ & 0x3f) << gcss; \
- } \
- }
-
-/* Get the next UTF-8 character, not advancing the pointer, incrementing length
-if there are extra bytes. This is called when we know we are in UTF-8 mode. */
-
-#define GETCHARLEN(c, eptr, len) \
- c = *eptr; \
- if (c >= 0xc0) \
- { \
- int gcii; \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- for (gcii = 1; gcii <= gcaa; gcii++) \
- { \
- gcss -= 6; \
- c |= (eptr[gcii] & 0x3f) << gcss; \
- } \
- len += gcaa; \
- }
-
-/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the
-pointer, incrementing length if there are extra bytes. This is called when we
-know we are in UTF-8 mode. */
-
-#define GETCHARLENTEST(c, eptr, len) \
- c = *eptr; \
- if (utf8 && c >= 0xc0) \
- { \
- int gcii; \
- int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
- int gcss = 6*gcaa; \
- c = (c & _pcre_utf8_table3[gcaa]) << gcss; \
- for (gcii = 1; gcii <= gcaa; gcii++) \
- { \
- gcss -= 6; \
- c |= (eptr[gcii] & 0x3f) << gcss; \
- } \
- len += gcaa; \
- }
-
-/* If the pointer is not at the start of a character, move it back until
-it is. This is called only in UTF-8 mode - we don't put a test within the macro
-because almost all calls are already within a block of UTF-8 only code. */
-
-#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--
-
-#endif
-
-
-/* In case there is no definition of offsetof() provided - though any proper
-Standard C system should have one. */
-
-#ifndef offsetof
-#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
-#endif
-
-
-/* These are the public options that can change during matching. */
-
-#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
-
-/* Private flags containing information about the compiled regex. They used to
-live at the top end of the options word, but that got almost full, so now they
-are in a 16-bit flags word. From release 8.00, PCRE_NOPARTIAL is unused, as
-the restrictions on partial matching have been lifted. It remains for backwards
-compatibility. */
-
-#define PCRE_NOPARTIAL 0x0001 /* can't use partial with this regex */
-#define PCRE_FIRSTSET 0x0002 /* first_byte is set */
-#define PCRE_REQCHSET 0x0004 /* req_byte is set */
-#define PCRE_STARTLINE 0x0008 /* start after \n for multiline */
-#define PCRE_JCHANGED 0x0010 /* j option used in regex */
-#define PCRE_HASCRORLF 0x0020 /* explicit \r or \n in pattern */
-
-/* Options for the "extra" block produced by pcre_study(). */
-
-#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */
-#define PCRE_STUDY_MINLEN 0x02 /* a minimum length field exists */
-
-/* Masks for identifying the public options that are permitted at compile
-time, run time, or study time, respectively. */
-
-#define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY| \
- PCRE_NEWLINE_ANYCRLF)
-
-#define PUBLIC_COMPILE_OPTIONS \
- (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \
- PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \
- PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE| \
- PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \
- PCRE_JAVASCRIPT_COMPAT)
-
-#define PUBLIC_EXEC_OPTIONS \
- (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \
- PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_NEWLINE_BITS| \
- PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE|PCRE_NO_START_OPTIMIZE)
-
-#define PUBLIC_DFA_EXEC_OPTIONS \
- (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \
- PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_DFA_SHORTEST| \
- PCRE_DFA_RESTART|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \
- PCRE_NO_START_OPTIMIZE)
-
-#define PUBLIC_STUDY_OPTIONS 0 /* None defined */
-
-/* Magic number to provide a small check against being handed junk. Also used
-to detect whether a pattern was compiled on a host of different endianness. */
-
-#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
-
-/* Negative values for the firstchar and reqchar variables */
-
-#define REQ_UNSET (-2)
-#define REQ_NONE (-1)
-
-/* The maximum remaining length of subject we are prepared to search for a
-req_byte match. */
-
-#define REQ_BYTE_MAX 1000
-
-/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a
-variable-length repeat, or a anything other than literal characters. */
-
-#define REQ_CASELESS 0x0100 /* indicates caselessness */
-#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */
-
-/* Miscellaneous definitions. The #ifndef is to pacify compiler warnings in
-environments where these macros are defined elsewhere. Unfortunately, there
-is no way to do the same for the typedef. */
-
-typedef gboolean BOOL;
-
-/* If PCRE is to support UTF-8 on EBCDIC platforms, we cannot use normal
-character constants like '*' because the compiler would emit their EBCDIC code,
-which is different from their ASCII/UTF-8 code. Instead we define macros for
-the characters so that they always use the ASCII/UTF-8 code when UTF-8 support
-is enabled. When UTF-8 support is not enabled, the definitions use character
-literals. Both character and string versions of each character are needed, and
-there are some longer strings as well.
-
-This means that, on EBCDIC platforms, the PCRE library can handle either
-EBCDIC, or UTF-8, but not both. To support both in the same compiled library
-would need different lookups depending on whether PCRE_UTF8 was set or not.
-This would make it impossible to use characters in switch/case statements,
-which would reduce performance. For a theoretical use (which nobody has asked
-for) in a minority area (EBCDIC platforms), this is not sensible. Any
-application that did need both could compile two versions of the library, using
-macros to give the functions distinct names. */
-
-#ifndef SUPPORT_UTF8
-
-/* UTF-8 support is not enabled; use the platform-dependent character literals
-so that PCRE works on both ASCII and EBCDIC platforms, in non-UTF-mode only. */
-
-#define CHAR_HT '\t'
-#define CHAR_VT '\v'
-#define CHAR_FF '\f'
-#define CHAR_CR '\r'
-#define CHAR_NL '\n'
-#define CHAR_BS '\b'
-#define CHAR_BEL '\a'
-#ifdef EBCDIC
-#define CHAR_ESC '\047'
-#define CHAR_DEL '\007'
-#else
-#define CHAR_ESC '\033'
-#define CHAR_DEL '\177'
-#endif
-
-#define CHAR_SPACE ' '
-#define CHAR_EXCLAMATION_MARK '!'
-#define CHAR_QUOTATION_MARK '"'
-#define CHAR_NUMBER_SIGN '#'
-#define CHAR_DOLLAR_SIGN '$'
-#define CHAR_PERCENT_SIGN '%'
-#define CHAR_AMPERSAND '&'
-#define CHAR_APOSTROPHE '\''
-#define CHAR_LEFT_PARENTHESIS '('
-#define CHAR_RIGHT_PARENTHESIS ')'
-#define CHAR_ASTERISK '*'
-#define CHAR_PLUS '+'
-#define CHAR_COMMA ','
-#define CHAR_MINUS '-'
-#define CHAR_DOT '.'
-#define CHAR_SLASH '/'
-#define CHAR_0 '0'
-#define CHAR_1 '1'
-#define CHAR_2 '2'
-#define CHAR_3 '3'
-#define CHAR_4 '4'
-#define CHAR_5 '5'
-#define CHAR_6 '6'
-#define CHAR_7 '7'
-#define CHAR_8 '8'
-#define CHAR_9 '9'
-#define CHAR_COLON ':'
-#define CHAR_SEMICOLON ';'
-#define CHAR_LESS_THAN_SIGN '<'
-#define CHAR_EQUALS_SIGN '='
-#define CHAR_GREATER_THAN_SIGN '>'
-#define CHAR_QUESTION_MARK '?'
-#define CHAR_COMMERCIAL_AT '@'
-#define CHAR_A 'A'
-#define CHAR_B 'B'
-#define CHAR_C 'C'
-#define CHAR_D 'D'
-#define CHAR_E 'E'
-#define CHAR_F 'F'
-#define CHAR_G 'G'
-#define CHAR_H 'H'
-#define CHAR_I 'I'
-#define CHAR_J 'J'
-#define CHAR_K 'K'
-#define CHAR_L 'L'
-#define CHAR_M 'M'
-#define CHAR_N 'N'
-#define CHAR_O 'O'
-#define CHAR_P 'P'
-#define CHAR_Q 'Q'
-#define CHAR_R 'R'
-#define CHAR_S 'S'
-#define CHAR_T 'T'
-#define CHAR_U 'U'
-#define CHAR_V 'V'
-#define CHAR_W 'W'
-#define CHAR_X 'X'
-#define CHAR_Y 'Y'
-#define CHAR_Z 'Z'
-#define CHAR_LEFT_SQUARE_BRACKET '['
-#define CHAR_BACKSLASH '\\'
-#define CHAR_RIGHT_SQUARE_BRACKET ']'
-#define CHAR_CIRCUMFLEX_ACCENT '^'
-#define CHAR_UNDERSCORE '_'
-#define CHAR_GRAVE_ACCENT '`'
-#define CHAR_a 'a'
-#define CHAR_b 'b'
-#define CHAR_c 'c'
-#define CHAR_d 'd'
-#define CHAR_e 'e'
-#define CHAR_f 'f'
-#define CHAR_g 'g'
-#define CHAR_h 'h'
-#define CHAR_i 'i'
-#define CHAR_j 'j'
-#define CHAR_k 'k'
-#define CHAR_l 'l'
-#define CHAR_m 'm'
-#define CHAR_n 'n'
-#define CHAR_o 'o'
-#define CHAR_p 'p'
-#define CHAR_q 'q'
-#define CHAR_r 'r'
-#define CHAR_s 's'
-#define CHAR_t 't'
-#define CHAR_u 'u'
-#define CHAR_v 'v'
-#define CHAR_w 'w'
-#define CHAR_x 'x'
-#define CHAR_y 'y'
-#define CHAR_z 'z'
-#define CHAR_LEFT_CURLY_BRACKET '{'
-#define CHAR_VERTICAL_LINE '|'
-#define CHAR_RIGHT_CURLY_BRACKET '}'
-#define CHAR_TILDE '~'
-
-#define STR_HT "\t"
-#define STR_VT "\v"
-#define STR_FF "\f"
-#define STR_CR "\r"
-#define STR_NL "\n"
-#define STR_BS "\b"
-#define STR_BEL "\a"
-#ifdef EBCDIC
-#define STR_ESC "\047"
-#define STR_DEL "\007"
-#else
-#define STR_ESC "\033"
-#define STR_DEL "\177"
-#endif
-
-#define STR_SPACE " "
-#define STR_EXCLAMATION_MARK "!"
-#define STR_QUOTATION_MARK "\""
-#define STR_NUMBER_SIGN "#"
-#define STR_DOLLAR_SIGN "$"
-#define STR_PERCENT_SIGN "%"
-#define STR_AMPERSAND "&"
-#define STR_APOSTROPHE "'"
-#define STR_LEFT_PARENTHESIS "("
-#define STR_RIGHT_PARENTHESIS ")"
-#define STR_ASTERISK "*"
-#define STR_PLUS "+"
-#define STR_COMMA ","
-#define STR_MINUS "-"
-#define STR_DOT "."
-#define STR_SLASH "/"
-#define STR_0 "0"
-#define STR_1 "1"
-#define STR_2 "2"
-#define STR_3 "3"
-#define STR_4 "4"
-#define STR_5 "5"
-#define STR_6 "6"
-#define STR_7 "7"
-#define STR_8 "8"
-#define STR_9 "9"
-#define STR_COLON ":"
-#define STR_SEMICOLON ";"
-#define STR_LESS_THAN_SIGN "<"
-#define STR_EQUALS_SIGN "="
-#define STR_GREATER_THAN_SIGN ">"
-#define STR_QUESTION_MARK "?"
-#define STR_COMMERCIAL_AT "@"
-#define STR_A "A"
-#define STR_B "B"
-#define STR_C "C"
-#define STR_D "D"
-#define STR_E "E"
-#define STR_F "F"
-#define STR_G "G"
-#define STR_H "H"
-#define STR_I "I"
-#define STR_J "J"
-#define STR_K "K"
-#define STR_L "L"
-#define STR_M "M"
-#define STR_N "N"
-#define STR_O "O"
-#define STR_P "P"
-#define STR_Q "Q"
-#define STR_R "R"
-#define STR_S "S"
-#define STR_T "T"
-#define STR_U "U"
-#define STR_V "V"
-#define STR_W "W"
-#define STR_X "X"
-#define STR_Y "Y"
-#define STR_Z "Z"
-#define STR_LEFT_SQUARE_BRACKET "["
-#define STR_BACKSLASH "\\"
-#define STR_RIGHT_SQUARE_BRACKET "]"
-#define STR_CIRCUMFLEX_ACCENT "^"
-#define STR_UNDERSCORE "_"
-#define STR_GRAVE_ACCENT "`"
-#define STR_a "a"
-#define STR_b "b"
-#define STR_c "c"
-#define STR_d "d"
-#define STR_e "e"
-#define STR_f "f"
-#define STR_g "g"
-#define STR_h "h"
-#define STR_i "i"
-#define STR_j "j"
-#define STR_k "k"
-#define STR_l "l"
-#define STR_m "m"
-#define STR_n "n"
-#define STR_o "o"
-#define STR_p "p"
-#define STR_q "q"
-#define STR_r "r"
-#define STR_s "s"
-#define STR_t "t"
-#define STR_u "u"
-#define STR_v "v"
-#define STR_w "w"
-#define STR_x "x"
-#define STR_y "y"
-#define STR_z "z"
-#define STR_LEFT_CURLY_BRACKET "{"
-#define STR_VERTICAL_LINE "|"
-#define STR_RIGHT_CURLY_BRACKET "}"
-#define STR_TILDE "~"
-
-#define STRING_ACCEPT0 "ACCEPT\0"
-#define STRING_COMMIT0 "COMMIT\0"
-#define STRING_F0 "F\0"
-#define STRING_FAIL0 "FAIL\0"
-#define STRING_PRUNE0 "PRUNE\0"
-#define STRING_SKIP0 "SKIP\0"
-#define STRING_THEN "THEN"
-
-#define STRING_alpha0 "alpha\0"
-#define STRING_lower0 "lower\0"
-#define STRING_upper0 "upper\0"
-#define STRING_alnum0 "alnum\0"
-#define STRING_ascii0 "ascii\0"
-#define STRING_blank0 "blank\0"
-#define STRING_cntrl0 "cntrl\0"
-#define STRING_digit0 "digit\0"
-#define STRING_graph0 "graph\0"
-#define STRING_print0 "print\0"
-#define STRING_punct0 "punct\0"
-#define STRING_space0 "space\0"
-#define STRING_word0 "word\0"
-#define STRING_xdigit "xdigit"
-
-#define STRING_DEFINE "DEFINE"
-
-#define STRING_CR_RIGHTPAR "CR)"
-#define STRING_LF_RIGHTPAR "LF)"
-#define STRING_CRLF_RIGHTPAR "CRLF)"
-#define STRING_ANY_RIGHTPAR "ANY)"
-#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)"
-#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)"
-#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)"
-#define STRING_UTF8_RIGHTPAR "UTF8)"
-
-#else /* SUPPORT_UTF8 */
-
-/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This
-works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode
-only. */
-
-#define CHAR_HT '\011'
-#define CHAR_VT '\013'
-#define CHAR_FF '\014'
-#define CHAR_CR '\015'
-#define CHAR_NL '\012'
-#define CHAR_BS '\010'
-#define CHAR_BEL '\007'
-#define CHAR_ESC '\033'
-#define CHAR_DEL '\177'
-
-#define CHAR_SPACE '\040'
-#define CHAR_EXCLAMATION_MARK '\041'
-#define CHAR_QUOTATION_MARK '\042'
-#define CHAR_NUMBER_SIGN '\043'
-#define CHAR_DOLLAR_SIGN '\044'
-#define CHAR_PERCENT_SIGN '\045'
-#define CHAR_AMPERSAND '\046'
-#define CHAR_APOSTROPHE '\047'
-#define CHAR_LEFT_PARENTHESIS '\050'
-#define CHAR_RIGHT_PARENTHESIS '\051'
-#define CHAR_ASTERISK '\052'
-#define CHAR_PLUS '\053'
-#define CHAR_COMMA '\054'
-#define CHAR_MINUS '\055'
-#define CHAR_DOT '\056'
-#define CHAR_SLASH '\057'
-#define CHAR_0 '\060'
-#define CHAR_1 '\061'
-#define CHAR_2 '\062'
-#define CHAR_3 '\063'
-#define CHAR_4 '\064'
-#define CHAR_5 '\065'
-#define CHAR_6 '\066'
-#define CHAR_7 '\067'
-#define CHAR_8 '\070'
-#define CHAR_9 '\071'
-#define CHAR_COLON '\072'
-#define CHAR_SEMICOLON '\073'
-#define CHAR_LESS_THAN_SIGN '\074'
-#define CHAR_EQUALS_SIGN '\075'
-#define CHAR_GREATER_THAN_SIGN '\076'
-#define CHAR_QUESTION_MARK '\077'
-#define CHAR_COMMERCIAL_AT '\100'
-#define CHAR_A '\101'
-#define CHAR_B '\102'
-#define CHAR_C '\103'
-#define CHAR_D '\104'
-#define CHAR_E '\105'
-#define CHAR_F '\106'
-#define CHAR_G '\107'
-#define CHAR_H '\110'
-#define CHAR_I '\111'
-#define CHAR_J '\112'
-#define CHAR_K '\113'
-#define CHAR_L '\114'
-#define CHAR_M '\115'
-#define CHAR_N '\116'
-#define CHAR_O '\117'
-#define CHAR_P '\120'
-#define CHAR_Q '\121'
-#define CHAR_R '\122'
-#define CHAR_S '\123'
-#define CHAR_T '\124'
-#define CHAR_U '\125'
-#define CHAR_V '\126'
-#define CHAR_W '\127'
-#define CHAR_X '\130'
-#define CHAR_Y '\131'
-#define CHAR_Z '\132'
-#define CHAR_LEFT_SQUARE_BRACKET '\133'
-#define CHAR_BACKSLASH '\134'
-#define CHAR_RIGHT_SQUARE_BRACKET '\135'
-#define CHAR_CIRCUMFLEX_ACCENT '\136'
-#define CHAR_UNDERSCORE '\137'
-#define CHAR_GRAVE_ACCENT '\140'
-#define CHAR_a '\141'
-#define CHAR_b '\142'
-#define CHAR_c '\143'
-#define CHAR_d '\144'
-#define CHAR_e '\145'
-#define CHAR_f '\146'
-#define CHAR_g '\147'
-#define CHAR_h '\150'
-#define CHAR_i '\151'
-#define CHAR_j '\152'
-#define CHAR_k '\153'
-#define CHAR_l '\154'
-#define CHAR_m '\155'
-#define CHAR_n '\156'
-#define CHAR_o '\157'
-#define CHAR_p '\160'
-#define CHAR_q '\161'
-#define CHAR_r '\162'
-#define CHAR_s '\163'
-#define CHAR_t '\164'
-#define CHAR_u '\165'
-#define CHAR_v '\166'
-#define CHAR_w '\167'
-#define CHAR_x '\170'
-#define CHAR_y '\171'
-#define CHAR_z '\172'
-#define CHAR_LEFT_CURLY_BRACKET '\173'
-#define CHAR_VERTICAL_LINE '\174'
-#define CHAR_RIGHT_CURLY_BRACKET '\175'
-#define CHAR_TILDE '\176'
-
-#define STR_HT "\011"
-#define STR_VT "\013"
-#define STR_FF "\014"
-#define STR_CR "\015"
-#define STR_NL "\012"
-#define STR_BS "\010"
-#define STR_BEL "\007"
-#define STR_ESC "\033"
-#define STR_DEL "\177"
-
-#define STR_SPACE "\040"
-#define STR_EXCLAMATION_MARK "\041"
-#define STR_QUOTATION_MARK "\042"
-#define STR_NUMBER_SIGN "\043"
-#define STR_DOLLAR_SIGN "\044"
-#define STR_PERCENT_SIGN "\045"
-#define STR_AMPERSAND "\046"
-#define STR_APOSTROPHE "\047"
-#define STR_LEFT_PARENTHESIS "\050"
-#define STR_RIGHT_PARENTHESIS "\051"
-#define STR_ASTERISK "\052"
-#define STR_PLUS "\053"
-#define STR_COMMA "\054"
-#define STR_MINUS "\055"
-#define STR_DOT "\056"
-#define STR_SLASH "\057"
-#define STR_0 "\060"
-#define STR_1 "\061"
-#define STR_2 "\062"
-#define STR_3 "\063"
-#define STR_4 "\064"
-#define STR_5 "\065"
-#define STR_6 "\066"
-#define STR_7 "\067"
-#define STR_8 "\070"
-#define STR_9 "\071"
-#define STR_COLON "\072"
-#define STR_SEMICOLON "\073"
-#define STR_LESS_THAN_SIGN "\074"
-#define STR_EQUALS_SIGN "\075"
-#define STR_GREATER_THAN_SIGN "\076"
-#define STR_QUESTION_MARK "\077"
-#define STR_COMMERCIAL_AT "\100"
-#define STR_A "\101"
-#define STR_B "\102"
-#define STR_C "\103"
-#define STR_D "\104"
-#define STR_E "\105"
-#define STR_F "\106"
-#define STR_G "\107"
-#define STR_H "\110"
-#define STR_I "\111"
-#define STR_J "\112"
-#define STR_K "\113"
-#define STR_L "\114"
-#define STR_M "\115"
-#define STR_N "\116"
-#define STR_O "\117"
-#define STR_P "\120"
-#define STR_Q "\121"
-#define STR_R "\122"
-#define STR_S "\123"
-#define STR_T "\124"
-#define STR_U "\125"
-#define STR_V "\126"
-#define STR_W "\127"
-#define STR_X "\130"
-#define STR_Y "\131"
-#define STR_Z "\132"
-#define STR_LEFT_SQUARE_BRACKET "\133"
-#define STR_BACKSLASH "\134"
-#define STR_RIGHT_SQUARE_BRACKET "\135"
-#define STR_CIRCUMFLEX_ACCENT "\136"
-#define STR_UNDERSCORE "\137"
-#define STR_GRAVE_ACCENT "\140"
-#define STR_a "\141"
-#define STR_b "\142"
-#define STR_c "\143"
-#define STR_d "\144"
-#define STR_e "\145"
-#define STR_f "\146"
-#define STR_g "\147"
-#define STR_h "\150"
-#define STR_i "\151"
-#define STR_j "\152"
-#define STR_k "\153"
-#define STR_l "\154"
-#define STR_m "\155"
-#define STR_n "\156"
-#define STR_o "\157"
-#define STR_p "\160"
-#define STR_q "\161"
-#define STR_r "\162"
-#define STR_s "\163"
-#define STR_t "\164"
-#define STR_u "\165"
-#define STR_v "\166"
-#define STR_w "\167"
-#define STR_x "\170"
-#define STR_y "\171"
-#define STR_z "\172"
-#define STR_LEFT_CURLY_BRACKET "\173"
-#define STR_VERTICAL_LINE "\174"
-#define STR_RIGHT_CURLY_BRACKET "\175"
-#define STR_TILDE "\176"
-
-#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0"
-#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0"
-#define STRING_F0 STR_F "\0"
-#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0"
-#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0"
-#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0"
-#define STRING_THEN STR_T STR_H STR_E STR_N
-
-#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0"
-#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0"
-#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0"
-#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0"
-#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0"
-#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0"
-#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0"
-#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0"
-#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0"
-#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0"
-#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0"
-#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0"
-#define STRING_word0 STR_w STR_o STR_r STR_d "\0"
-#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t
-
-#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E
-
-#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS
-#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS
-#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
-#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS
-#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
-#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
-#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS
-#define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS
-
-#endif /* SUPPORT_UTF8 */
-
-/* Escape items that are just an encoding of a particular data value. */
-
-#ifndef ESC_e
-#define ESC_e CHAR_ESC
-#endif
-
-#ifndef ESC_f
-#define ESC_f CHAR_FF
-#endif
-
-#ifndef ESC_n
-#define ESC_n CHAR_NL
-#endif
-
-#ifndef ESC_r
-#define ESC_r CHAR_CR
-#endif
-
-/* We can't officially use ESC_t because it is a POSIX reserved identifier
-(presumably because of all the others like size_t). */
-
-#ifndef ESC_tee
-#define ESC_tee CHAR_HT
-#endif
-
-/* Codes for different types of Unicode property */
-
-#define PT_ANY 0 /* Any property - matches all chars */
-#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */
-#define PT_GC 2 /* General characteristic (e.g. L) */
-#define PT_PC 3 /* Particular characteristic (e.g. Lu) */
-#define PT_SC 4 /* Script (e.g. Han) */
-
-/* Flag bits and data types for the extended class (OP_XCLASS) for classes that
-contain UTF-8 characters with values greater than 255. */
-
-#define XCL_NOT 0x01 /* Flag: this is a negative class */
-#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */
-
-#define XCL_END 0 /* Marks end of individual items */
-#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */
-#define XCL_RANGE 2 /* A range (two multibyte chars) follows */
-#define XCL_PROP 3 /* Unicode property (2-byte property code follows) */
-#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */
-
-/* These are escaped items that aren't just an encoding of a particular data
-value such as \n. They must have non-zero values, as check_escape() returns
-their negation. Also, they must appear in the same order as in the opcode
-definitions below, up to ESC_z. There's a dummy for OP_ANY because it
-corresponds to "." rather than an escape sequence, and another for OP_ALLANY
-(which is used for [^] in JavaScript compatibility mode).
-
-The final escape must be ESC_REF as subsequent values are used for
-backreferences (\1, \2, \3, etc). There are two tests in the code for an escape
-greater than ESC_b and less than ESC_Z to detect the types that may be
-repeated. These are the types that consume characters. If any new escapes are
-put in between that don't consume a character, that code will have to change.
-*/
-
-enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s,
- ESC_W, ESC_w, ESC_dum1, ESC_dum2, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H,
- ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_g, ESC_k,
- ESC_REF };
-
-
-/* Opcode table: Starting from 1 (i.e. after OP_END), the values up to
-OP_EOD must correspond in order to the list of escapes immediately above.
-
-*** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definitions
-that follow must also be updated to match. There are also tables called
-"coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */
-
-enum {
- OP_END, /* 0 End of pattern */
-
- /* Values corresponding to backslashed metacharacters */
-
- OP_SOD, /* 1 Start of data: \A */
- OP_SOM, /* 2 Start of match (subject + offset): \G */
- OP_SET_SOM, /* 3 Set start of match (\K) */
- OP_NOT_WORD_BOUNDARY, /* 4 \B */
- OP_WORD_BOUNDARY, /* 5 \b */
- OP_NOT_DIGIT, /* 6 \D */
- OP_DIGIT, /* 7 \d */
- OP_NOT_WHITESPACE, /* 8 \S */
- OP_WHITESPACE, /* 9 \s */
- OP_NOT_WORDCHAR, /* 10 \W */
- OP_WORDCHAR, /* 11 \w */
- OP_ANY, /* 12 Match any character (subject to DOTALL) */
- OP_ALLANY, /* 13 Match any character (not subject to DOTALL) */
- OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */
- OP_NOTPROP, /* 15 \P (not Unicode property) */
- OP_PROP, /* 16 \p (Unicode property) */
- OP_ANYNL, /* 17 \R (any newline sequence) */
- OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */
- OP_HSPACE, /* 19 \h (horizontal whitespace) */
- OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */
- OP_VSPACE, /* 21 \v (vertical whitespace) */
- OP_EXTUNI, /* 22 \X (extended Unicode sequence */
- OP_EODN, /* 23 End of data or \n at end of data: \Z. */
- OP_EOD, /* 24 End of data: \z */
-
- OP_OPT, /* 25 Set runtime options */
- OP_CIRC, /* 26 Start of line - varies with multiline switch */
- OP_DOLL, /* 27 End of line - varies with multiline switch */
- OP_CHAR, /* 28 Match one character, casefully */
- OP_CHARNC, /* 29 Match one character, caselessly */
- OP_NOT, /* 30 Match one character, not the following one */
-
- OP_STAR, /* 31 The maximizing and minimizing versions of */
- OP_MINSTAR, /* 32 these six opcodes must come in pairs, with */
- OP_PLUS, /* 33 the minimizing one second. */
- OP_MINPLUS, /* 34 This first set applies to single characters.*/
- OP_QUERY, /* 35 */
- OP_MINQUERY, /* 36 */
-
- OP_UPTO, /* 37 From 0 to n matches */
- OP_MINUPTO, /* 38 */
- OP_EXACT, /* 39 Exactly n matches */
-
- OP_POSSTAR, /* 40 Possessified star */
- OP_POSPLUS, /* 41 Possessified plus */
- OP_POSQUERY, /* 42 Posesssified query */
- OP_POSUPTO, /* 43 Possessified upto */
-
- OP_NOTSTAR, /* 44 The maximizing and minimizing versions of */
- OP_NOTMINSTAR, /* 45 these six opcodes must come in pairs, with */
- OP_NOTPLUS, /* 46 the minimizing one second. They must be in */
- OP_NOTMINPLUS, /* 47 exactly the same order as those above. */
- OP_NOTQUERY, /* 48 This set applies to "not" single characters. */
- OP_NOTMINQUERY, /* 49 */
-
- OP_NOTUPTO, /* 50 From 0 to n matches */
- OP_NOTMINUPTO, /* 51 */
- OP_NOTEXACT, /* 52 Exactly n matches */
-
- OP_NOTPOSSTAR, /* 53 Possessified versions */
- OP_NOTPOSPLUS, /* 54 */
- OP_NOTPOSQUERY, /* 55 */
- OP_NOTPOSUPTO, /* 56 */
-
- OP_TYPESTAR, /* 57 The maximizing and minimizing versions of */
- OP_TYPEMINSTAR, /* 58 these six opcodes must come in pairs, with */
- OP_TYPEPLUS, /* 59 the minimizing one second. These codes must */
- OP_TYPEMINPLUS, /* 60 be in exactly the same order as those above. */
- OP_TYPEQUERY, /* 61 This set applies to character types such as \d */
- OP_TYPEMINQUERY, /* 62 */
-
- OP_TYPEUPTO, /* 63 From 0 to n matches */
- OP_TYPEMINUPTO, /* 64 */
- OP_TYPEEXACT, /* 65 Exactly n matches */
-
- OP_TYPEPOSSTAR, /* 66 Possessified versions */
- OP_TYPEPOSPLUS, /* 67 */
- OP_TYPEPOSQUERY, /* 68 */
- OP_TYPEPOSUPTO, /* 69 */
-
- OP_CRSTAR, /* 70 The maximizing and minimizing versions of */
- OP_CRMINSTAR, /* 71 all these opcodes must come in pairs, with */
- OP_CRPLUS, /* 72 the minimizing one second. These codes must */
- OP_CRMINPLUS, /* 73 be in exactly the same order as those above. */
- OP_CRQUERY, /* 74 These are for character classes and back refs */
- OP_CRMINQUERY, /* 75 */
- OP_CRRANGE, /* 76 These are different to the three sets above. */
- OP_CRMINRANGE, /* 77 */
-
- OP_CLASS, /* 78 Match a character class, chars < 256 only */
- OP_NCLASS, /* 79 Same, but the bitmap was created from a negative
- class - the difference is relevant only when a UTF-8
- character > 255 is encountered. */
-
- OP_XCLASS, /* 80 Extended class for handling UTF-8 chars within the
- class. This does both positive and negative. */
-
- OP_REF, /* 81 Match a back reference */
- OP_RECURSE, /* 82 Match a numbered subpattern (possibly recursive) */
- OP_CALLOUT, /* 83 Call out to external function if provided */
-
- OP_ALT, /* 84 Start of alternation */
- OP_KET, /* 85 End of group that doesn't have an unbounded repeat */
- OP_KETRMAX, /* 86 These two must remain together and in this */
- OP_KETRMIN, /* 87 order. They are for groups the repeat for ever. */
-
- /* The assertions must come before BRA, CBRA, ONCE, and COND.*/
-
- OP_ASSERT, /* 88 Positive lookahead */
- OP_ASSERT_NOT, /* 89 Negative lookahead */
- OP_ASSERTBACK, /* 90 Positive lookbehind */
- OP_ASSERTBACK_NOT, /* 91 Negative lookbehind */
- OP_REVERSE, /* 92 Move pointer back - used in lookbehind assertions */
-
- /* ONCE, BRA, CBRA, and COND must come after the assertions, with ONCE first,
- as there's a test for >= ONCE for a subpattern that isn't an assertion. */
-
- OP_ONCE, /* 93 Atomic group */
- OP_BRA, /* 94 Start of non-capturing bracket */
- OP_CBRA, /* 95 Start of capturing bracket */
- OP_COND, /* 96 Conditional group */
-
- /* These three must follow the previous three, in the same order. There's a
- check for >= SBRA to distinguish the two sets. */
-
- OP_SBRA, /* 97 Start of non-capturing bracket, check empty */
- OP_SCBRA, /* 98 Start of capturing bracket, check empty */
- OP_SCOND, /* 99 Conditional group, check empty */
-
- /* The next two pairs must (respectively) be kept together. */
-
- OP_CREF, /* 100 Used to hold a capture number as condition */
- OP_NCREF, /* 101 Same, but generaged by a name reference*/
- OP_RREF, /* 102 Used to hold a recursion number as condition */
- OP_NRREF, /* 103 Same, but generaged by a name reference*/
- OP_DEF, /* 104 The DEFINE condition */
-
- OP_BRAZERO, /* 105 These two must remain together and in this */
- OP_BRAMINZERO, /* 106 order. */
-
- /* These are backtracking control verbs */
-
- OP_PRUNE, /* 107 */
- OP_SKIP, /* 108 */
- OP_THEN, /* 109 */
- OP_COMMIT, /* 110 */
-
- /* These are forced failure and success verbs */
-
- OP_FAIL, /* 111 */
- OP_ACCEPT, /* 112 */
- OP_CLOSE, /* 113 Used before OP_ACCEPT to close open captures */
-
- /* This is used to skip a subpattern with a {0} quantifier */
-
- OP_SKIPZERO, /* 114 */
-
- /* This is not an opcode, but is used to check that tables indexed by opcode
- are the correct length, in order to catch updating errors - there have been
- some in the past. */
-
- OP_TABLE_LENGTH
-};
-
-/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro
-definitions that follow must also be updated to match. There are also tables
-called "coptable" cna "poptable" in pcre_dfa_exec.c that must be updated. */
-
-
-/* This macro defines textual names for all the opcodes. These are used only
-for debugging. The macro is referenced only in pcre_printint.c. */
-
-#define OP_NAME_LIST \
- "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \
- "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \
- "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \
- "extuni", "\\Z", "\\z", \
- "Opt", "^", "$", "char", "charnc", "not", \
- "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
- "*+","++", "?+", "{", \
- "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
- "*+","++", "?+", "{", \
- "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
- "*+","++", "?+", "{", \
- "*", "*?", "+", "+?", "?", "??", "{", "{", \
- "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \
- "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \
- "AssertB", "AssertB not", "Reverse", \
- "Once", "Bra", "CBra", "Cond", "SBra", "SCBra", "SCond", \
- "Cond ref", "Cond nref", "Cond rec", "Cond nrec", "Cond def", \
- "Brazero", "Braminzero", \
- "*PRUNE", "*SKIP", "*THEN", "*COMMIT", "*FAIL", "*ACCEPT", \
- "Close", "Skip zero"
-
-
-/* This macro defines the length of fixed length operations in the compiled
-regex. The lengths are used when searching for specific things, and also in the
-debugging printing of a compiled regex. We use a macro so that it can be
-defined close to the definitions of the opcodes themselves.
-
-As things have been extended, some of these are no longer fixed lenths, but are
-minima instead. For example, the length of a single-character repeat may vary
-in UTF-8 mode. The code that uses this table must know about such things. */
-
-#define OP_LENGTHS \
- 1, /* End */ \
- 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \
- 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \
- 1, 1, 1, /* Any, AllAny, Anybyte */ \
- 3, 3, /* \P, \p */ \
- 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \
- 1, /* \X */ \
- 1, 1, 2, 1, 1, /* \Z, \z, Opt, ^, $ */ \
- 2, /* Char - the minimum length */ \
- 2, /* Charnc - the minimum length */ \
- 2, /* not */ \
- /* Positive single-char repeats ** These are */ \
- 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \
- 4, 4, 4, /* upto, minupto, exact ** UTF-8 mode */ \
- 2, 2, 2, 4, /* *+, ++, ?+, upto+ */ \
- /* Negative single-char repeats - only for chars < 256 */ \
- 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \
- 4, 4, 4, /* NOT upto, minupto, exact */ \
- 2, 2, 2, 4, /* Possessive *, +, ?, upto */ \
- /* Positive type repeats */ \
- 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \
- 4, 4, 4, /* Type upto, minupto, exact */ \
- 2, 2, 2, 4, /* Possessive *+, ++, ?+, upto+ */ \
- /* Character class & ref repeats */ \
- 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \
- 5, 5, /* CRRANGE, CRMINRANGE */ \
- 33, /* CLASS */ \
- 33, /* NCLASS */ \
- 0, /* XCLASS - variable length */ \
- 3, /* REF */ \
- 1+LINK_SIZE, /* RECURSE */ \
- 2+2*LINK_SIZE, /* CALLOUT */ \
- 1+LINK_SIZE, /* Alt */ \
- 1+LINK_SIZE, /* Ket */ \
- 1+LINK_SIZE, /* KetRmax */ \
- 1+LINK_SIZE, /* KetRmin */ \
- 1+LINK_SIZE, /* Assert */ \
- 1+LINK_SIZE, /* Assert not */ \
- 1+LINK_SIZE, /* Assert behind */ \
- 1+LINK_SIZE, /* Assert behind not */ \
- 1+LINK_SIZE, /* Reverse */ \
- 1+LINK_SIZE, /* ONCE */ \
- 1+LINK_SIZE, /* BRA */ \
- 3+LINK_SIZE, /* CBRA */ \
- 1+LINK_SIZE, /* COND */ \
- 1+LINK_SIZE, /* SBRA */ \
- 3+LINK_SIZE, /* SCBRA */ \
- 1+LINK_SIZE, /* SCOND */ \
- 3, 3, /* CREF, NCREF */ \
- 3, 3, /* RREF, NRREF */ \
- 1, /* DEF */ \
- 1, 1, /* BRAZERO, BRAMINZERO */ \
- 1, 1, 1, 1, /* PRUNE, SKIP, THEN, COMMIT, */ \
- 1, 1, 3, 1 /* FAIL, ACCEPT, CLOSE, SKIPZERO */
-
-
-/* A magic value for OP_RREF and OP_NRREF to indicate the "any recursion"
-condition. */
-
-#define RREF_ANY 0xffff
-
-/* Compile time error code numbers. They are given names so that they can more
-easily be tracked. When a new number is added, the table called eint in
-pcreposix.c must be updated. */
-
-enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9,
- ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19,
- ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29,
- ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39,
- ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49,
- ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59,
- ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERRCOUNT };
-
-/* The real format of the start of the pcre block; the index of names and the
-code vector run on as long as necessary after the end. We store an explicit
-offset to the name table so that if a regex is compiled on one host, saved, and
-then run on another where the size of pointers is different, all might still
-be well. For the case of compiled-on-4 and run-on-8, we include an extra
-pointer that is always NULL. For future-proofing, a few dummy fields were
-originally included - even though you can never get this planning right - but
-there is only one left now.
-
-NOTE NOTE NOTE:
-Because people can now save and re-use compiled patterns, any additions to this
-structure should be made at the end, and something earlier (e.g. a new
-flag in the options or one of the dummy fields) should indicate that the new
-fields are present. Currently PCRE always sets the dummy fields to zero.
-NOTE NOTE NOTE
-*/
-
-typedef struct real_pcre {
- pcre_uint32 magic_number;
- pcre_uint32 size; /* Total that was malloced */
- pcre_uint32 options; /* Public options */
- pcre_uint16 flags; /* Private flags */
- pcre_uint16 dummy1; /* For future use */
- pcre_uint16 top_bracket;
- pcre_uint16 top_backref;
- pcre_uint16 first_byte;
- pcre_uint16 req_byte;
- pcre_uint16 name_table_offset; /* Offset to name table that follows */
- pcre_uint16 name_entry_size; /* Size of any name items */
- pcre_uint16 name_count; /* Number of name items */
- pcre_uint16 ref_count; /* Reference count */
-
- const unsigned char *tables; /* Pointer to tables or NULL for std */
- const unsigned char *nullpad; /* NULL padding */
-} real_pcre;
-
-/* The format of the block used to store data from pcre_study(). The same
-remark (see NOTE above) about extending this structure applies. */
-
-typedef struct pcre_study_data {
- pcre_uint32 size; /* Total that was malloced */
- pcre_uint32 flags; /* Private flags */
- uschar start_bits[32]; /* Starting char bits */
- pcre_uint32 minlength; /* Minimum subject length */
-} pcre_study_data;
-
-/* Structure for building a chain of open capturing subpatterns during
-compiling, so that instructions to close them can be compiled when (*ACCEPT) is
-encountered. This is also used to identify subpatterns that contain recursive
-back references to themselves, so that they can be made atomic. */
-
-typedef struct open_capitem {
- struct open_capitem *next; /* Chain link */
- pcre_uint16 number; /* Capture number */
- pcre_uint16 flag; /* Set TRUE if recursive back ref */
-} open_capitem;
-
-/* Structure for passing "static" information around between the functions
-doing the compiling, so that they are thread-safe. */
-
-typedef struct compile_data {
- const uschar *lcc; /* Points to lower casing table */
- const uschar *fcc; /* Points to case-flipping table */
- const uschar *cbits; /* Points to character type table */
- const uschar *ctypes; /* Points to table of type maps */
- const uschar *start_workspace;/* The start of working space */
- const uschar *start_code; /* The start of the compiled code */
- const uschar *start_pattern; /* The start of the pattern */
- const uschar *end_pattern; /* The end of the pattern */
- open_capitem *open_caps; /* Chain of open capture items */
- uschar *hwm; /* High watermark of workspace */
- uschar *name_table; /* The name/number table */
- int names_found; /* Number of entries so far */
- int name_entry_size; /* Size of each entry */
- int bracount; /* Count of capturing parens as we compile */
- int final_bracount; /* Saved value after first pass */
- int top_backref; /* Maximum back reference */
- unsigned int backref_map; /* Bitmap of low back refs */
- int external_options; /* External (initial) options */
- int external_flags; /* External flag bits to be set */
- int req_varyopt; /* "After variable item" flag for reqbyte */
- BOOL had_accept; /* (*ACCEPT) encountered */
- BOOL check_lookbehind; /* Lookbehinds need later checking */
- int nltype; /* Newline type */
- int nllen; /* Newline string length */
- uschar nl[4]; /* Newline string when fixed length */
-} compile_data;
-
-/* Structure for maintaining a chain of pointers to the currently incomplete
-branches, for testing for left recursion. */
-
-typedef struct branch_chain {
- struct branch_chain *outer;
- uschar *current_branch;
-} branch_chain;
-
-/* Structure for items in a linked list that represents an explicit recursive
-call within the pattern. */
-
-typedef struct recursion_info {
- struct recursion_info *prevrec; /* Previous recursion record (or NULL) */
- int group_num; /* Number of group that was called */
- const uschar *after_call; /* "Return value": points after the call in the expr */
- int *offset_save; /* Pointer to start of saved offsets */
- int saved_max; /* Number of saved offsets */
- int save_offset_top; /* Current value of offset_top */
-} recursion_info;
-
-/* Structure for building a chain of data for holding the values of the subject
-pointer at the start of each subpattern, so as to detect when an empty string
-has been matched by a subpattern - to break infinite loops. */
-
-typedef struct eptrblock {
- struct eptrblock *epb_prev;
- USPTR epb_saved_eptr;
-} eptrblock;
-
-
-/* Structure for passing "static" information around between the functions
-doing traditional NFA matching, so that they are thread-safe. */
-
-typedef struct match_data {
- unsigned long int match_call_count; /* As it says */
- unsigned long int match_limit; /* As it says */
- unsigned long int match_limit_recursion; /* As it says */
- int *offset_vector; /* Offset vector */
- int offset_end; /* One past the end */
- int offset_max; /* The maximum usable for return data */
- int nltype; /* Newline type */
- int nllen; /* Newline string length */
- int name_count; /* Number of names in name table */
- int name_entry_size; /* Size of entry in names table */
- uschar *name_table; /* Table of names */
- uschar nl[4]; /* Newline string when fixed */
- const uschar *lcc; /* Points to lower casing table */
- const uschar *ctypes; /* Points to table of type maps */
- BOOL offset_overflow; /* Set if too many extractions */
- BOOL notbol; /* NOTBOL flag */
- BOOL noteol; /* NOTEOL flag */
- BOOL utf8; /* UTF8 flag */
- BOOL jscript_compat; /* JAVASCRIPT_COMPAT flag */
- BOOL endonly; /* Dollar not before final \n */
- BOOL notempty; /* Empty string match not wanted */
- BOOL notempty_atstart; /* Empty string match at start not wanted */
- BOOL hitend; /* Hit the end of the subject at some point */
- BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */
- const uschar *start_code; /* For use when recursing */
- USPTR start_subject; /* Start of the subject string */
- USPTR end_subject; /* End of the subject string */
- USPTR start_match_ptr; /* Start of matched string */
- USPTR end_match_ptr; /* Subject position at end match */
- USPTR start_used_ptr; /* Earliest consulted character */
- int partial; /* PARTIAL options */
- int end_offset_top; /* Highwater mark at end of match */
- int capture_last; /* Most recent capture number */
- int start_offset; /* The start offset value */
- eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */
- int eptrn; /* Next free eptrblock */
- recursion_info *recursive; /* Linked list of recursion data */
- void *callout_data; /* To pass back to callouts */
-} match_data;
-
-/* A similar structure is used for the same purpose by the DFA matching
-functions. */
-
-typedef struct dfa_match_data {
- const uschar *start_code; /* Start of the compiled pattern */
- const uschar *start_subject; /* Start of the subject string */
- const uschar *end_subject; /* End of subject string */
- const uschar *start_used_ptr; /* Earliest consulted character */
- const uschar *tables; /* Character tables */
- int start_offset; /* The start offset value */
- int moptions; /* Match options */
- int poptions; /* Pattern options */
- int nltype; /* Newline type */
- int nllen; /* Newline string length */
- uschar nl[4]; /* Newline string when fixed */
- void *callout_data; /* To pass back to callouts */
-} dfa_match_data;
-
-/* Bit definitions for entries in the pcre_ctypes table. */
-
-#define ctype_space 0x01
-#define ctype_letter 0x02
-#define ctype_digit 0x04
-#define ctype_xdigit 0x08
-#define ctype_word 0x10 /* alphanumeric or '_' */
-#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */
-
-/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
-of bits for a class map. Some classes are built by combining these tables. */
-
-#define cbit_space 0 /* [:space:] or \s */
-#define cbit_xdigit 32 /* [:xdigit:] */
-#define cbit_digit 64 /* [:digit:] or \d */
-#define cbit_upper 96 /* [:upper:] */
-#define cbit_lower 128 /* [:lower:] */
-#define cbit_word 160 /* [:word:] or \w */
-#define cbit_graph 192 /* [:graph:] */
-#define cbit_print 224 /* [:print:] */
-#define cbit_punct 256 /* [:punct:] */
-#define cbit_cntrl 288 /* [:cntrl:] */
-#define cbit_length 320 /* Length of the cbits table */
-
-/* Offsets of the various tables from the base tables pointer, and
-total length. */
-
-#define lcc_offset 0
-#define fcc_offset 256
-#define cbits_offset 512
-#define ctypes_offset (cbits_offset + cbit_length)
-#define tables_length (ctypes_offset + 256)
-
-/* Layout of the UCP type table that translates property names into types and
-codes. Each entry used to point directly to a name, but to reduce the number of
-relocations in shared libraries, it now has an offset into a single string
-instead. */
-
-typedef struct {
- pcre_uint16 name_offset;
- pcre_uint16 type;
- pcre_uint16 value;
-} ucp_type_table;
-
-
-/* Internal shared data tables. These are tables that are used by more than one
-of the exported public functions. They have to be "external" in the C sense,
-but are not part of the PCRE public API. The data for these tables is in the
-pcre_tables.c module. */
-
-extern const int _pcre_utf8_table1[];
-extern const int _pcre_utf8_table2[];
-extern const int _pcre_utf8_table3[];
-extern const uschar _pcre_utf8_table4[];
-
-extern const int _pcre_utf8_table1_size;
-
-extern const char _pcre_utt_names[];
-extern const ucp_type_table _pcre_utt[];
-extern const int _pcre_utt_size;
-
-extern const uschar _pcre_default_tables[];
-
-extern const uschar _pcre_OP_lengths[];
-
-
-/* Internal shared functions. These are functions that are used by more than
-one of the exported public functions. They have to be "external" in the C
-sense, but are not part of the PCRE public API. */
-
-extern const uschar *_pcre_find_bracket(const uschar *, BOOL, int);
-extern BOOL _pcre_is_newline(USPTR, int, USPTR, int *, BOOL);
-extern int _pcre_ord2utf8(int, uschar *);
-extern real_pcre *_pcre_try_flipped(const real_pcre *, real_pcre *,
- const pcre_study_data *, pcre_study_data *);
-#define _pcre_valid_utf8(u, i) TRUE
-extern BOOL _pcre_was_newline(USPTR, int, USPTR, int *, BOOL);
-extern BOOL _pcre_xclass(int, const uschar *);
-
-
-/* Unicode character database (UCD) */
-
-typedef struct {
- uschar script;
- uschar chartype;
- pcre_int32 other_case;
-} ucd_record;
-
-extern const ucd_record _pcre_ucd_records[];
-extern const uschar _pcre_ucd_stage1[];
-extern const pcre_uint16 _pcre_ucd_stage2[];
-extern const int _pcre_ucp_gentype[];
-
-extern unsigned int _pcre_ucp_othercase (const unsigned int);
-
-/* UCD access macros */
-
-#include "../glib.h"
-
-#define UCD_CHARTYPE(ch) g_unichar_type(ch)
-#define UCD_SCRIPT(ch) g_unichar_get_script(ch)
-#define UCD_CATEGORY(ch) _pcre_ucp_gentype[UCD_CHARTYPE(ch)]
-#define UCD_OTHERCASE(ch) _pcre_ucp_othercase(ch)
-
-#endif
-
-/* End of pcre_internal.h */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains internal functions for testing newlines when more than
-one kind of newline is to be recognized. When a newline is found, its length is
-returned. In principle, we could implement several newline "types", each
-referring to a different set of newline characters. At present, PCRE supports
-only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF,
-and NLTYPE_ANY. The full list of Unicode newline characters is taken from
-http://unicode.org/unicode/reports/tr18/. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-
-/*************************************************
-* Check for newline at given position *
-*************************************************/
-
-/* It is guaranteed that the initial value of ptr is less than the end of the
-string that is being processed.
-
-Arguments:
- ptr pointer to possible newline
- type the newline type
- endptr pointer to the end of the string
- lenptr where to return the length
- utf8 TRUE if in utf8 mode
-
-Returns: TRUE or FALSE
-*/
-
-BOOL
-_pcre_is_newline(USPTR ptr, int type, USPTR endptr, int *lenptr, BOOL utf8)
-{
-int c;
-if (utf8) { GETCHAR(c, ptr); } else c = *ptr;
-
-if (type == NLTYPE_ANYCRLF) switch(c)
- {
- case 0x000a: *lenptr = 1; return TRUE; /* LF */
- case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1;
- return TRUE; /* CR */
- default: return FALSE;
- }
-
-/* NLTYPE_ANY */
-
-else switch(c)
- {
- case 0x000a: /* LF */
- case 0x000b: /* VT */
- case 0x000c: *lenptr = 1; return TRUE; /* FF */
- case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1;
- return TRUE; /* CR */
- case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */
- case 0x2028: /* LS */
- case 0x2029: *lenptr = 3; return TRUE; /* PS */
- default: return FALSE;
- }
-}
-
-
-
-/*************************************************
-* Check for newline at previous position *
-*************************************************/
-
-/* It is guaranteed that the initial value of ptr is greater than the start of
-the string that is being processed.
-
-Arguments:
- ptr pointer to possible newline
- type the newline type
- startptr pointer to the start of the string
- lenptr where to return the length
- utf8 TRUE if in utf8 mode
-
-Returns: TRUE or FALSE
-*/
-
-BOOL
-_pcre_was_newline(USPTR ptr, int type, USPTR startptr, int *lenptr, BOOL utf8)
-{
-int c;
-ptr--;
-#ifdef SUPPORT_UTF8
-if (utf8)
- {
- BACKCHAR(ptr);
- GETCHAR(c, ptr);
- }
-else c = *ptr;
-#else /* no UTF-8 support */
-c = *ptr;
-#endif /* SUPPORT_UTF8 */
-
-if (type == NLTYPE_ANYCRLF) switch(c)
- {
- case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1;
- return TRUE; /* LF */
- case 0x000d: *lenptr = 1; return TRUE; /* CR */
- default: return FALSE;
- }
-
-else switch(c)
- {
- case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1;
- return TRUE; /* LF */
- case 0x000b: /* VT */
- case 0x000c: /* FF */
- case 0x000d: *lenptr = 1; return TRUE; /* CR */
- case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */
- case 0x2028: /* LS */
- case 0x2029: *lenptr = 3; return TRUE; /* PS */
- default: return FALSE;
- }
-}
-
-/* End of pcre_newline.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2008 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This file contains a private PCRE function that converts an ordinal
-character value into a UTF8 string. */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Convert character value to UTF-8 *
-*************************************************/
-
-/* This function takes an integer value in the range 0 - 0x7fffffff
-and encodes it as a UTF-8 character in 0 to 6 bytes.
-
-Arguments:
- cvalue the character value
- buffer pointer to buffer for result - at least 6 bytes long
-
-Returns: number of characters placed in the buffer
-*/
-
-int
-_pcre_ord2utf8(int cvalue, uschar *buffer)
-{
-#ifdef SUPPORT_UTF8
-register int i, j;
-for (i = 0; i < _pcre_utf8_table1_size; i++)
- if (cvalue <= _pcre_utf8_table1[i]) break;
-buffer += i;
-for (j = i; j > 0; j--)
- {
- *buffer-- = 0x80 | (cvalue & 0x3f);
- cvalue >>= 6;
- }
-*buffer = _pcre_utf8_table2[i] | cvalue;
-return i + 1;
-#else
-(void)(cvalue); /* Keep compiler happy; this function won't ever be */
-(void)(buffer); /* called when SUPPORT_UTF8 is not defined. */
-return 0;
-#endif
-}
-
-/* End of pcre_ord2utf8.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2010 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains the external function pcre_study(), along with local
-supporting functions. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/* Returns from set_start_bits() */
-
-enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE };
-
-
-
-/*************************************************
-* Find the minimum subject length for a group *
-*************************************************/
-
-/* Scan a parenthesized group and compute the minimum length of subject that
-is needed to match it. This is a lower bound; it does not mean there is a
-string of that length that matches. In UTF8 mode, the result is in characters
-rather than bytes.
-
-Arguments:
- code pointer to start of group (the bracket)
- startcode pointer to start of the whole pattern
- options the compiling options
-
-Returns: the minimum length
- -1 if \C was encountered
- -2 internal error (missing capturing bracket)
-*/
-
-static int
-find_minlength(const uschar *code, const uschar *startcode, int options)
-{
-int length = -1;
-BOOL utf8 = (options & PCRE_UTF8) != 0;
-BOOL had_recurse = FALSE;
-register int branchlength = 0;
-register uschar *cc = (uschar *)code + 1 + LINK_SIZE;
-
-if (*code == OP_CBRA || *code == OP_SCBRA) cc += 2;
-
-/* Scan along the opcodes for this branch. If we get to the end of the
-branch, check the length against that of the other branches. */
-
-for (;;)
- {
- int d, min;
- uschar *cs, *ce;
- register int op = *cc;
-
- switch (op)
- {
- case OP_COND:
- case OP_SCOND:
-
- /* If there is only one branch in a condition, the implied branch has zero
- length, so we don't add anything. This covers the DEFINE "condition"
- automatically. */
-
- cs = cc + GET(cc, 1);
- if (*cs != OP_ALT)
- {
- cc = cs + 1 + LINK_SIZE;
- break;
- }
-
- /* Otherwise we can fall through and treat it the same as any other
- subpattern. */
-
- case OP_CBRA:
- case OP_SCBRA:
- case OP_BRA:
- case OP_SBRA:
- case OP_ONCE:
- d = find_minlength(cc, startcode, options);
- if (d < 0) return d;
- branchlength += d;
- do cc += GET(cc, 1); while (*cc == OP_ALT);
- cc += 1 + LINK_SIZE;
- break;
-
- /* Reached end of a branch; if it's a ket it is the end of a nested
- call. If it's ALT it is an alternation in a nested call. If it is
- END it's the end of the outer call. All can be handled by the same code. */
-
- case OP_ALT:
- case OP_KET:
- case OP_KETRMAX:
- case OP_KETRMIN:
- case OP_END:
- if (length < 0 || (!had_recurse && branchlength < length))
- length = branchlength;
- if (*cc != OP_ALT) return length;
- cc += 1 + LINK_SIZE;
- branchlength = 0;
- had_recurse = FALSE;
- break;
-
- /* Skip over assertive subpatterns */
-
- case OP_ASSERT:
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- do cc += GET(cc, 1); while (*cc == OP_ALT);
- /* Fall through */
-
- /* Skip over things that don't match chars */
-
- case OP_REVERSE:
- case OP_CREF:
- case OP_NCREF:
- case OP_RREF:
- case OP_NRREF:
- case OP_DEF:
- case OP_OPT:
- case OP_CALLOUT:
- case OP_SOD:
- case OP_SOM:
- case OP_EOD:
- case OP_EODN:
- case OP_CIRC:
- case OP_DOLL:
- case OP_NOT_WORD_BOUNDARY:
- case OP_WORD_BOUNDARY:
- cc += _pcre_OP_lengths[*cc];
- break;
-
- /* Skip over a subpattern that has a {0} or {0,x} quantifier */
-
- case OP_BRAZERO:
- case OP_BRAMINZERO:
- case OP_SKIPZERO:
- cc += _pcre_OP_lengths[*cc];
- do cc += GET(cc, 1); while (*cc == OP_ALT);
- cc += 1 + LINK_SIZE;
- break;
-
- /* Handle literal characters and + repetitions */
-
- case OP_CHAR:
- case OP_CHARNC:
- case OP_NOT:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- case OP_NOTPLUS:
- case OP_NOTMINPLUS:
- case OP_NOTPOSPLUS:
- branchlength++;
- cc += 2;
-#ifdef SUPPORT_UTF8
- if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];
-#endif
- break;
-
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- case OP_TYPEPOSPLUS:
- branchlength++;
- cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;
- break;
-
- /* Handle exact repetitions. The count is already in characters, but we
- need to skip over a multibyte character in UTF8 mode. */
-
- case OP_EXACT:
- case OP_NOTEXACT:
- branchlength += GET2(cc,1);
- cc += 4;
-#ifdef SUPPORT_UTF8
- if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];
-#endif
- break;
-
- case OP_TYPEEXACT:
- branchlength += GET2(cc,1);
- cc += (cc[3] == OP_PROP || cc[3] == OP_NOTPROP)? 6 : 4;
- break;
-
- /* Handle single-char non-literal matchers */
-
- case OP_PROP:
- case OP_NOTPROP:
- cc += 2;
- /* Fall through */
-
- case OP_NOT_DIGIT:
- case OP_DIGIT:
- case OP_NOT_WHITESPACE:
- case OP_WHITESPACE:
- case OP_NOT_WORDCHAR:
- case OP_WORDCHAR:
- case OP_ANY:
- case OP_ALLANY:
- case OP_EXTUNI:
- case OP_HSPACE:
- case OP_NOT_HSPACE:
- case OP_VSPACE:
- case OP_NOT_VSPACE:
- branchlength++;
- cc++;
- break;
-
- /* "Any newline" might match two characters */
-
- case OP_ANYNL:
- branchlength += 2;
- cc++;
- break;
-
- /* The single-byte matcher means we can't proceed in UTF-8 mode */
-
- case OP_ANYBYTE:
-#ifdef SUPPORT_UTF8
- if (utf8) return -1;
-#endif
- branchlength++;
- cc++;
- break;
-
- /* For repeated character types, we have to test for \p and \P, which have
- an extra two bytes of parameters. */
-
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSSTAR:
- case OP_TYPEPOSQUERY:
- if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
- cc += _pcre_OP_lengths[op];
- break;
-
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEPOSUPTO:
- if (cc[3] == OP_PROP || cc[3] == OP_NOTPROP) cc += 2;
- cc += _pcre_OP_lengths[op];
- break;
-
- /* Check a class for variable quantification */
-
-#ifdef SUPPORT_UTF8
- case OP_XCLASS:
- cc += GET(cc, 1) - 33;
- /* Fall through */
-#endif
-
- case OP_CLASS:
- case OP_NCLASS:
- cc += 33;
-
- switch (*cc)
- {
- case OP_CRPLUS:
- case OP_CRMINPLUS:
- branchlength++;
- /* Fall through */
-
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- cc++;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- branchlength += GET2(cc,1);
- cc += 5;
- break;
-
- default:
- branchlength++;
- break;
- }
- break;
-
- /* Backreferences and subroutine calls are treated in the same way: we find
- the minimum length for the subpattern. A recursion, however, causes an
- a flag to be set that causes the length of this branch to be ignored. The
- logic is that a recursion can only make sense if there is another
- alternation that stops the recursing. That will provide the minimum length
- (when no recursion happens). A backreference within the group that it is
- referencing behaves in the same way.
-
- If PCRE_JAVASCRIPT_COMPAT is set, a backreference to an unset bracket
- matches an empty string (by default it causes a matching failure), so in
- that case we must set the minimum length to zero. */
-
- case OP_REF:
- if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)
- {
- ce = cs = (uschar *)_pcre_find_bracket(startcode, utf8, GET2(cc, 1));
- if (cs == NULL) return -2;
- do ce += GET(ce, 1); while (*ce == OP_ALT);
- if (cc > cs && cc < ce)
- {
- d = 0;
- had_recurse = TRUE;
- }
- else d = find_minlength(cs, startcode, options);
- }
- else d = 0;
- cc += 3;
-
- /* Handle repeated back references */
-
- switch (*cc)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- min = 0;
- cc++;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- min = GET2(cc, 1);
- cc += 5;
- break;
-
- default:
- min = 1;
- break;
- }
-
- branchlength += min * d;
- break;
-
- case OP_RECURSE:
- cs = ce = (uschar *)startcode + GET(cc, 1);
- if (cs == NULL) return -2;
- do ce += GET(ce, 1); while (*ce == OP_ALT);
- if (cc > cs && cc < ce)
- had_recurse = TRUE;
- else
- branchlength += find_minlength(cs, startcode, options);
- cc += 1 + LINK_SIZE;
- break;
-
- /* Anything else does not or need not match a character. We can get the
- item's length from the table, but for those that can match zero occurrences
- of a character, we must take special action for UTF-8 characters. */
-
- case OP_UPTO:
- case OP_NOTUPTO:
- case OP_MINUPTO:
- case OP_NOTMINUPTO:
- case OP_POSUPTO:
- case OP_STAR:
- case OP_MINSTAR:
- case OP_NOTMINSTAR:
- case OP_POSSTAR:
- case OP_NOTPOSSTAR:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_NOTMINQUERY:
- case OP_POSQUERY:
- case OP_NOTPOSQUERY:
- cc += _pcre_OP_lengths[op];
-#ifdef SUPPORT_UTF8
- if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];
-#endif
- break;
-
- /* For the record, these are the opcodes that are matched by "default":
- OP_ACCEPT, OP_CLOSE, OP_COMMIT, OP_FAIL, OP_PRUNE, OP_SET_SOM, OP_SKIP,
- OP_THEN. */
-
- default:
- cc += _pcre_OP_lengths[op];
- break;
- }
- }
-/* Control never gets here */
-}
-
-
-
-/*************************************************
-* Set a bit and maybe its alternate case *
-*************************************************/
-
-/* Given a character, set its bit in the table, and also the bit for the other
-version of a letter if we are caseless.
-
-Arguments:
- start_bits points to the bit map
- c is the character
- caseless the caseless flag
- cd the block with char table pointers
-
-Returns: nothing
-*/
-
-static void
-set_table_bit(uschar *start_bits, unsigned int c, BOOL caseless,
- compile_data *cd)
-{
-start_bits[c/8] |= (1 << (c&7));
-if (caseless && (cd->ctypes[c] & ctype_letter) != 0)
- start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7));
-}
-
-
-
-/*************************************************
-* Create bitmap of starting bytes *
-*************************************************/
-
-/* This function scans a compiled unanchored expression recursively and
-attempts to build a bitmap of the set of possible starting bytes. As time goes
-by, we may be able to get more clever at doing this. The SSB_CONTINUE return is
-useful for parenthesized groups in patterns such as (a*)b where the group
-provides some optional starting bytes but scanning must continue at the outer
-level to find at least one mandatory byte. At the outermost level, this
-function fails unless the result is SSB_DONE.
-
-Arguments:
- code points to an expression
- start_bits points to a 32-byte table, initialized to 0
- caseless the current state of the caseless flag
- utf8 TRUE if in UTF-8 mode
- cd the block with char table pointers
-
-Returns: SSB_FAIL => Failed to find any starting bytes
- SSB_DONE => Found mandatory starting bytes
- SSB_CONTINUE => Found optional starting bytes
-*/
-
-static int
-set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless,
- BOOL utf8, compile_data *cd)
-{
-register int c;
-int yield = SSB_DONE;
-
-#if 0
-/* ========================================================================= */
-/* The following comment and code was inserted in January 1999. In May 2006,
-when it was observed to cause compiler warnings about unused values, I took it
-out again. If anybody is still using OS/2, they will have to put it back
-manually. */
-
-/* This next statement and the later reference to dummy are here in order to
-trick the optimizer of the IBM C compiler for OS/2 into generating correct
-code. Apparently IBM isn't going to fix the problem, and we would rather not
-disable optimization (in this module it actually makes a big difference, and
-the pcre module can use all the optimization it can get). */
-
-volatile int dummy;
-/* ========================================================================= */
-#endif
-
-do
- {
- const uschar *tcode = code + (((int)*code == OP_CBRA)? 3:1) + LINK_SIZE;
- BOOL try_next = TRUE;
-
- while (try_next) /* Loop for items in this branch */
- {
- int rc;
- switch(*tcode)
- {
- /* Fail if we reach something we don't understand */
-
- default:
- return SSB_FAIL;
-
- /* If we hit a bracket or a positive lookahead assertion, recurse to set
- bits from within the subpattern. If it can't find anything, we have to
- give up. If it finds some mandatory character(s), we are done for this
- branch. Otherwise, carry on scanning after the subpattern. */
-
- case OP_BRA:
- case OP_SBRA:
- case OP_CBRA:
- case OP_SCBRA:
- case OP_ONCE:
- case OP_ASSERT:
- rc = set_start_bits(tcode, start_bits, caseless, utf8, cd);
- if (rc == SSB_FAIL) return SSB_FAIL;
- if (rc == SSB_DONE) try_next = FALSE; else
- {
- do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
- tcode += 1 + LINK_SIZE;
- }
- break;
-
- /* If we hit ALT or KET, it means we haven't found anything mandatory in
- this branch, though we might have found something optional. For ALT, we
- continue with the next alternative, but we have to arrange that the final
- result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET,
- return SSB_CONTINUE: if this is the top level, that indicates failure,
- but after a nested subpattern, it causes scanning to continue. */
-
- case OP_ALT:
- yield = SSB_CONTINUE;
- try_next = FALSE;
- break;
-
- case OP_KET:
- case OP_KETRMAX:
- case OP_KETRMIN:
- return SSB_CONTINUE;
-
- /* Skip over callout */
-
- case OP_CALLOUT:
- tcode += 2 + 2*LINK_SIZE;
- break;
-
- /* Skip over lookbehind and negative lookahead assertions */
-
- case OP_ASSERT_NOT:
- case OP_ASSERTBACK:
- case OP_ASSERTBACK_NOT:
- do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
- tcode += 1 + LINK_SIZE;
- break;
-
- /* Skip over an option setting, changing the caseless flag */
-
- case OP_OPT:
- caseless = (tcode[1] & PCRE_CASELESS) != 0;
- tcode += 2;
- break;
-
- /* BRAZERO does the bracket, but carries on. */
-
- case OP_BRAZERO:
- case OP_BRAMINZERO:
- if (set_start_bits(++tcode, start_bits, caseless, utf8, cd) == SSB_FAIL)
- return SSB_FAIL;
-/* =========================================================================
- See the comment at the head of this function concerning the next line,
- which was an old fudge for the benefit of OS/2.
- dummy = 1;
- ========================================================================= */
- do tcode += GET(tcode,1); while (*tcode == OP_ALT);
- tcode += 1 + LINK_SIZE;
- break;
-
- /* SKIPZERO skips the bracket. */
-
- case OP_SKIPZERO:
- tcode++;
- do tcode += GET(tcode,1); while (*tcode == OP_ALT);
- tcode += 1 + LINK_SIZE;
- break;
-
- /* Single-char * or ? sets the bit and tries the next item */
-
- case OP_STAR:
- case OP_MINSTAR:
- case OP_POSSTAR:
- case OP_QUERY:
- case OP_MINQUERY:
- case OP_POSQUERY:
- set_table_bit(start_bits, tcode[1], caseless, cd);
- tcode += 2;
-#ifdef SUPPORT_UTF8
- if (utf8 && tcode[-1] >= 0xc0)
- tcode += _pcre_utf8_table4[tcode[-1] & 0x3f];
-#endif
- break;
-
- /* Single-char upto sets the bit and tries the next */
-
- case OP_UPTO:
- case OP_MINUPTO:
- case OP_POSUPTO:
- set_table_bit(start_bits, tcode[3], caseless, cd);
- tcode += 4;
-#ifdef SUPPORT_UTF8
- if (utf8 && tcode[-1] >= 0xc0)
- tcode += _pcre_utf8_table4[tcode[-1] & 0x3f];
-#endif
- break;
-
- /* At least one single char sets the bit and stops */
-
- case OP_EXACT: /* Fall through */
- tcode += 2;
-
- case OP_CHAR:
- case OP_CHARNC:
- case OP_PLUS:
- case OP_MINPLUS:
- case OP_POSPLUS:
- set_table_bit(start_bits, tcode[1], caseless, cd);
- try_next = FALSE;
- break;
-
- /* Single character type sets the bits and stops */
-
- case OP_NOT_DIGIT:
- for (c = 0; c < 32; c++)
- start_bits[c] |= ~cd->cbits[c+cbit_digit];
- try_next = FALSE;
- break;
-
- case OP_DIGIT:
- for (c = 0; c < 32; c++)
- start_bits[c] |= cd->cbits[c+cbit_digit];
- try_next = FALSE;
- break;
-
- /* The cbit_space table has vertical tab as whitespace; we have to
- discard it. */
-
- case OP_NOT_WHITESPACE:
- for (c = 0; c < 32; c++)
- {
- int d = cd->cbits[c+cbit_space];
- if (c == 1) d &= ~0x08;
- start_bits[c] |= ~d;
- }
- try_next = FALSE;
- break;
-
- /* The cbit_space table has vertical tab as whitespace; we have to
- discard it. */
-
- case OP_WHITESPACE:
- for (c = 0; c < 32; c++)
- {
- int d = cd->cbits[c+cbit_space];
- if (c == 1) d &= ~0x08;
- start_bits[c] |= d;
- }
- try_next = FALSE;
- break;
-
- case OP_NOT_WORDCHAR:
- for (c = 0; c < 32; c++)
- start_bits[c] |= ~cd->cbits[c+cbit_word];
- try_next = FALSE;
- break;
-
- case OP_WORDCHAR:
- for (c = 0; c < 32; c++)
- start_bits[c] |= cd->cbits[c+cbit_word];
- try_next = FALSE;
- break;
-
- /* One or more character type fudges the pointer and restarts, knowing
- it will hit a single character type and stop there. */
-
- case OP_TYPEPLUS:
- case OP_TYPEMINPLUS:
- tcode++;
- break;
-
- case OP_TYPEEXACT:
- tcode += 3;
- break;
-
- /* Zero or more repeats of character types set the bits and then
- try again. */
-
- case OP_TYPEUPTO:
- case OP_TYPEMINUPTO:
- case OP_TYPEPOSUPTO:
- tcode += 2; /* Fall through */
-
- case OP_TYPESTAR:
- case OP_TYPEMINSTAR:
- case OP_TYPEPOSSTAR:
- case OP_TYPEQUERY:
- case OP_TYPEMINQUERY:
- case OP_TYPEPOSQUERY:
- switch(tcode[1])
- {
- case OP_ANY:
- case OP_ALLANY:
- return SSB_FAIL;
-
- case OP_NOT_DIGIT:
- for (c = 0; c < 32; c++)
- start_bits[c] |= ~cd->cbits[c+cbit_digit];
- break;
-
- case OP_DIGIT:
- for (c = 0; c < 32; c++)
- start_bits[c] |= cd->cbits[c+cbit_digit];
- break;
-
- /* The cbit_space table has vertical tab as whitespace; we have to
- discard it. */
-
- case OP_NOT_WHITESPACE:
- for (c = 0; c < 32; c++)
- {
- int d = cd->cbits[c+cbit_space];
- if (c == 1) d &= ~0x08;
- start_bits[c] |= ~d;
- }
- break;
-
- /* The cbit_space table has vertical tab as whitespace; we have to
- discard it. */
-
- case OP_WHITESPACE:
- for (c = 0; c < 32; c++)
- {
- int d = cd->cbits[c+cbit_space];
- if (c == 1) d &= ~0x08;
- start_bits[c] |= d;
- }
- break;
-
- case OP_NOT_WORDCHAR:
- for (c = 0; c < 32; c++)
- start_bits[c] |= ~cd->cbits[c+cbit_word];
- break;
-
- case OP_WORDCHAR:
- for (c = 0; c < 32; c++)
- start_bits[c] |= cd->cbits[c+cbit_word];
- break;
- }
-
- tcode += 2;
- break;
-
- /* Character class where all the information is in a bit map: set the
- bits and either carry on or not, according to the repeat count. If it was
- a negative class, and we are operating with UTF-8 characters, any byte
- with a value >= 0xc4 is a potentially valid starter because it starts a
- character with a value > 255. */
-
- case OP_NCLASS:
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */
- memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */
- }
-#endif
- /* Fall through */
-
- case OP_CLASS:
- {
- tcode++;
-
- /* In UTF-8 mode, the bits in a bit map correspond to character
- values, not to byte values. However, the bit map we are constructing is
- for byte values. So we have to do a conversion for characters whose
- value is > 127. In fact, there are only two possible starting bytes for
- characters in the range 128 - 255. */
-
-#ifdef SUPPORT_UTF8
- if (utf8)
- {
- for (c = 0; c < 16; c++) start_bits[c] |= tcode[c];
- for (c = 128; c < 256; c++)
- {
- if ((tcode[c/8] && (1 << (c&7))) != 0)
- {
- int d = (c >> 6) | 0xc0; /* Set bit for this starter */
- start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */
- c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */
- }
- }
- }
-
- /* In non-UTF-8 mode, the two bit maps are completely compatible. */
-
- else
-#endif
- {
- for (c = 0; c < 32; c++) start_bits[c] |= tcode[c];
- }
-
- /* Advance past the bit map, and act on what follows */
-
- tcode += 32;
- switch (*tcode)
- {
- case OP_CRSTAR:
- case OP_CRMINSTAR:
- case OP_CRQUERY:
- case OP_CRMINQUERY:
- tcode++;
- break;
-
- case OP_CRRANGE:
- case OP_CRMINRANGE:
- if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5;
- else try_next = FALSE;
- break;
-
- default:
- try_next = FALSE;
- break;
- }
- }
- break; /* End of bitmap class handling */
-
- } /* End of switch */
- } /* End of try_next loop */
-
- code += GET(code, 1); /* Advance to next branch */
- }
-while (*code == OP_ALT);
-return yield;
-}
-
-
-
-/*************************************************
-* Study a compiled expression *
-*************************************************/
-
-/* This function is handed a compiled expression that it must study to produce
-information that will speed up the matching. It returns a pcre_extra block
-which then gets handed back to pcre_exec().
-
-Arguments:
- re points to the compiled expression
- options contains option bits
- errorptr points to where to place error messages;
- set NULL unless error
-
-Returns: pointer to a pcre_extra block, with study_data filled in and the
- appropriate flags set;
- NULL on error or if no optimization possible
-*/
-
-PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION
-pcre_study(const pcre *external_re, int options, const char **errorptr)
-{
-int min;
-BOOL bits_set = FALSE;
-uschar start_bits[32];
-pcre_extra *extra;
-pcre_study_data *study;
-const uschar *tables;
-uschar *code;
-compile_data compile_block;
-const real_pcre *re = (const real_pcre *)external_re;
-
-*errorptr = NULL;
-
-if (re == NULL || re->magic_number != MAGIC_NUMBER)
- {
- *errorptr = "argument is not a compiled regular expression";
- return NULL;
- }
-
-if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)
- {
- *errorptr = "unknown or incorrect option bit(s) set";
- return NULL;
- }
-
-code = (uschar *)re + re->name_table_offset +
- (re->name_count * re->name_entry_size);
-
-/* For an anchored pattern, or an unanchored pattern that has a first char, or
-a multiline pattern that matches only at "line starts", there is no point in
-seeking a list of starting bytes. */
-
-if ((re->options & PCRE_ANCHORED) == 0 &&
- (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0)
- {
- /* Set the character tables in the block that is passed around */
-
- tables = re->tables;
- if (tables == NULL)
- (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
- (void *)(&tables));
-
- compile_block.lcc = tables + lcc_offset;
- compile_block.fcc = tables + fcc_offset;
- compile_block.cbits = tables + cbits_offset;
- compile_block.ctypes = tables + ctypes_offset;
-
- /* See if we can find a fixed set of initial characters for the pattern. */
-
- memset(start_bits, 0, 32 * sizeof(uschar));
- bits_set = set_start_bits(code, start_bits,
- (re->options & PCRE_CASELESS) != 0, (re->options & PCRE_UTF8) != 0,
- &compile_block) == SSB_DONE;
- }
-
-/* Find the minimum length of subject string. */
-
-min = find_minlength(code, code, re->options);
-
-/* Return NULL if no optimization is possible. */
-
-if (!bits_set && min < 0) return NULL;
-
-/* Get a pcre_extra block and a pcre_study_data block. The study data is put in
-the latter, which is pointed to by the former, which may also get additional
-data set later by the calling program. At the moment, the size of
-pcre_study_data is fixed. We nevertheless save it in a field for returning via
-the pcre_fullinfo() function so that if it becomes variable in the future, we
-don't have to change that code. */
-
-extra = (pcre_extra *)(pcre_malloc)
- (sizeof(pcre_extra) + sizeof(pcre_study_data));
-
-if (extra == NULL)
- {
- *errorptr = "failed to get memory";
- return NULL;
- }
-
-study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra));
-extra->flags = PCRE_EXTRA_STUDY_DATA;
-extra->study_data = study;
-
-study->size = sizeof(pcre_study_data);
-study->flags = 0;
-
-if (bits_set)
- {
- study->flags |= PCRE_STUDY_MAPPED;
- memcpy(study->start_bits, start_bits, sizeof(start_bits));
- }
-
-if (min >= 0)
- {
- study->flags |= PCRE_STUDY_MINLEN;
- study->minlength = min;
- }
-
-return extra;
-}
-
-/* End of pcre_study.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains some fixed tables that are used by more than one of the
-PCRE code modules. The tables are also #included by the pcretest program, which
-uses macros to change their names from _pcre_xxx to xxxx, thereby avoiding name
-clashes with the library. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
-the definition is next to the definition of the opcodes in pcre_internal.h. */
-
-const uschar _pcre_OP_lengths[] = { OP_LENGTHS };
-
-
-
-/*************************************************
-* Tables for UTF-8 support *
-*************************************************/
-
-/* These are the breakpoints for different numbers of bytes in a UTF-8
-character. */
-
-#ifdef SUPPORT_UTF8
-
-const int _pcre_utf8_table1[] =
- { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
-
-const int _pcre_utf8_table1_size = sizeof(_pcre_utf8_table1)/sizeof(int);
-
-/* These are the indicator bits and the mask for the data bits to set in the
-first byte of a character, indexed by the number of additional bytes. */
-
-const int _pcre_utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
-const int _pcre_utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
-
-/* Table of the number of extra bytes, indexed by the first byte masked with
-0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */
-
-const uschar _pcre_utf8_table4[] = {
- 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,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
-
-/* Table to translate from particular type value to the general value. */
-
-const int _pcre_ucp_gentype[] = {
- ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */
- ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */
- ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */
- ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */
- ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */
- ucp_P, ucp_P, /* Ps, Po */
- ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */
- ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */
-};
-
-/* The pcre_utt[] table below translates Unicode property names into type and
-code values. It is searched by binary chop, so must be in collating sequence of
-name. Originally, the table contained pointers to the name strings in the first
-field of each entry. However, that leads to a large number of relocations when
-a shared library is dynamically loaded. A significant reduction is made by
-putting all the names into a single, large string and then using offsets in the
-table itself. Maintenance is more error-prone, but frequent changes to this
-data are unlikely.
-
-July 2008: There is now a script called maint/GenerateUtt.py that can be used
-to generate this data instead of maintaining it entirely by hand.
-
-The script was updated in March 2009 to generate a new EBCDIC-compliant
-version. Like all other character and string literals that are compared against
-the regular expression pattern, we must use STR_ macros instead of literal
-strings to make sure that UTF-8 support works on EBCDIC platforms. */
-
-#define STRING_Any0 STR_A STR_n STR_y "\0"
-#define STRING_Arabic0 STR_A STR_r STR_a STR_b STR_i STR_c "\0"
-#define STRING_Armenian0 STR_A STR_r STR_m STR_e STR_n STR_i STR_a STR_n "\0"
-#define STRING_Avestan0 STR_A STR_v STR_e STR_s STR_t STR_a STR_n "\0"
-#define STRING_Balinese0 STR_B STR_a STR_l STR_i STR_n STR_e STR_s STR_e "\0"
-#define STRING_Bamum0 STR_B STR_a STR_m STR_u STR_m "\0"
-#define STRING_Bengali0 STR_B STR_e STR_n STR_g STR_a STR_l STR_i "\0"
-#define STRING_Bopomofo0 STR_B STR_o STR_p STR_o STR_m STR_o STR_f STR_o "\0"
-#define STRING_Braille0 STR_B STR_r STR_a STR_i STR_l STR_l STR_e "\0"
-#define STRING_Buginese0 STR_B STR_u STR_g STR_i STR_n STR_e STR_s STR_e "\0"
-#define STRING_Buhid0 STR_B STR_u STR_h STR_i STR_d "\0"
-#define STRING_C0 STR_C "\0"
-#define STRING_Canadian_Aboriginal0 STR_C STR_a STR_n STR_a STR_d STR_i STR_a STR_n STR_UNDERSCORE STR_A STR_b STR_o STR_r STR_i STR_g STR_i STR_n STR_a STR_l "\0"
-#define STRING_Carian0 STR_C STR_a STR_r STR_i STR_a STR_n "\0"
-#define STRING_Cc0 STR_C STR_c "\0"
-#define STRING_Cf0 STR_C STR_f "\0"
-#define STRING_Cham0 STR_C STR_h STR_a STR_m "\0"
-#define STRING_Cherokee0 STR_C STR_h STR_e STR_r STR_o STR_k STR_e STR_e "\0"
-#define STRING_Cn0 STR_C STR_n "\0"
-#define STRING_Co0 STR_C STR_o "\0"
-#define STRING_Common0 STR_C STR_o STR_m STR_m STR_o STR_n "\0"
-#define STRING_Coptic0 STR_C STR_o STR_p STR_t STR_i STR_c "\0"
-#define STRING_Cs0 STR_C STR_s "\0"
-#define STRING_Cuneiform0 STR_C STR_u STR_n STR_e STR_i STR_f STR_o STR_r STR_m "\0"
-#define STRING_Cypriot0 STR_C STR_y STR_p STR_r STR_i STR_o STR_t "\0"
-#define STRING_Cyrillic0 STR_C STR_y STR_r STR_i STR_l STR_l STR_i STR_c "\0"
-#define STRING_Deseret0 STR_D STR_e STR_s STR_e STR_r STR_e STR_t "\0"
-#define STRING_Devanagari0 STR_D STR_e STR_v STR_a STR_n STR_a STR_g STR_a STR_r STR_i "\0"
-#define STRING_Egyptian_Hieroglyphs0 STR_E STR_g STR_y STR_p STR_t STR_i STR_a STR_n STR_UNDERSCORE STR_H STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
-#define STRING_Ethiopic0 STR_E STR_t STR_h STR_i STR_o STR_p STR_i STR_c "\0"
-#define STRING_Georgian0 STR_G STR_e STR_o STR_r STR_g STR_i STR_a STR_n "\0"
-#define STRING_Glagolitic0 STR_G STR_l STR_a STR_g STR_o STR_l STR_i STR_t STR_i STR_c "\0"
-#define STRING_Gothic0 STR_G STR_o STR_t STR_h STR_i STR_c "\0"
-#define STRING_Greek0 STR_G STR_r STR_e STR_e STR_k "\0"
-#define STRING_Gujarati0 STR_G STR_u STR_j STR_a STR_r STR_a STR_t STR_i "\0"
-#define STRING_Gurmukhi0 STR_G STR_u STR_r STR_m STR_u STR_k STR_h STR_i "\0"
-#define STRING_Han0 STR_H STR_a STR_n "\0"
-#define STRING_Hangul0 STR_H STR_a STR_n STR_g STR_u STR_l "\0"
-#define STRING_Hanunoo0 STR_H STR_a STR_n STR_u STR_n STR_o STR_o "\0"
-#define STRING_Hebrew0 STR_H STR_e STR_b STR_r STR_e STR_w "\0"
-#define STRING_Hiragana0 STR_H STR_i STR_r STR_a STR_g STR_a STR_n STR_a "\0"
-#define STRING_Imperial_Aramaic0 STR_I STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_UNDERSCORE STR_A STR_r STR_a STR_m STR_a STR_i STR_c "\0"
-#define STRING_Inherited0 STR_I STR_n STR_h STR_e STR_r STR_i STR_t STR_e STR_d "\0"
-#define STRING_Inscriptional_Pahlavi0 STR_I STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_UNDERSCORE STR_P STR_a STR_h STR_l STR_a STR_v STR_i "\0"
-#define STRING_Inscriptional_Parthian0 STR_I STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_UNDERSCORE STR_P STR_a STR_r STR_t STR_h STR_i STR_a STR_n "\0"
-#define STRING_Javanese0 STR_J STR_a STR_v STR_a STR_n STR_e STR_s STR_e "\0"
-#define STRING_Kaithi0 STR_K STR_a STR_i STR_t STR_h STR_i "\0"
-#define STRING_Kannada0 STR_K STR_a STR_n STR_n STR_a STR_d STR_a "\0"
-#define STRING_Katakana0 STR_K STR_a STR_t STR_a STR_k STR_a STR_n STR_a "\0"
-#define STRING_Kayah_Li0 STR_K STR_a STR_y STR_a STR_h STR_UNDERSCORE STR_L STR_i "\0"
-#define STRING_Kharoshthi0 STR_K STR_h STR_a STR_r STR_o STR_s STR_h STR_t STR_h STR_i "\0"
-#define STRING_Khmer0 STR_K STR_h STR_m STR_e STR_r "\0"
-#define STRING_L0 STR_L "\0"
-#define STRING_L_AMPERSAND0 STR_L STR_AMPERSAND "\0"
-#define STRING_Lao0 STR_L STR_a STR_o "\0"
-#define STRING_Latin0 STR_L STR_a STR_t STR_i STR_n "\0"
-#define STRING_Lepcha0 STR_L STR_e STR_p STR_c STR_h STR_a "\0"
-#define STRING_Limbu0 STR_L STR_i STR_m STR_b STR_u "\0"
-#define STRING_Linear_B0 STR_L STR_i STR_n STR_e STR_a STR_r STR_UNDERSCORE STR_B "\0"
-#define STRING_Lisu0 STR_L STR_i STR_s STR_u "\0"
-#define STRING_Ll0 STR_L STR_l "\0"
-#define STRING_Lm0 STR_L STR_m "\0"
-#define STRING_Lo0 STR_L STR_o "\0"
-#define STRING_Lt0 STR_L STR_t "\0"
-#define STRING_Lu0 STR_L STR_u "\0"
-#define STRING_Lycian0 STR_L STR_y STR_c STR_i STR_a STR_n "\0"
-#define STRING_Lydian0 STR_L STR_y STR_d STR_i STR_a STR_n "\0"
-#define STRING_M0 STR_M "\0"
-#define STRING_Malayalam0 STR_M STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m "\0"
-#define STRING_Mc0 STR_M STR_c "\0"
-#define STRING_Me0 STR_M STR_e "\0"
-#define STRING_Meetei_Mayek0 STR_M STR_e STR_e STR_t STR_e STR_i STR_UNDERSCORE STR_M STR_a STR_y STR_e STR_k "\0"
-#define STRING_Mn0 STR_M STR_n "\0"
-#define STRING_Mongolian0 STR_M STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n "\0"
-#define STRING_Myanmar0 STR_M STR_y STR_a STR_n STR_m STR_a STR_r "\0"
-#define STRING_N0 STR_N "\0"
-#define STRING_Nd0 STR_N STR_d "\0"
-#define STRING_New_Tai_Lue0 STR_N STR_e STR_w STR_UNDERSCORE STR_T STR_a STR_i STR_UNDERSCORE STR_L STR_u STR_e "\0"
-#define STRING_Nko0 STR_N STR_k STR_o "\0"
-#define STRING_Nl0 STR_N STR_l "\0"
-#define STRING_No0 STR_N STR_o "\0"
-#define STRING_Ogham0 STR_O STR_g STR_h STR_a STR_m "\0"
-#define STRING_Ol_Chiki0 STR_O STR_l STR_UNDERSCORE STR_C STR_h STR_i STR_k STR_i "\0"
-#define STRING_Old_Italic0 STR_O STR_l STR_d STR_UNDERSCORE STR_I STR_t STR_a STR_l STR_i STR_c "\0"
-#define STRING_Old_Persian0 STR_O STR_l STR_d STR_UNDERSCORE STR_P STR_e STR_r STR_s STR_i STR_a STR_n "\0"
-#define STRING_Old_South_Arabian0 STR_O STR_l STR_d STR_UNDERSCORE STR_S STR_o STR_u STR_t STR_h STR_UNDERSCORE STR_A STR_r STR_a STR_b STR_i STR_a STR_n "\0"
-#define STRING_Old_Turkic0 STR_O STR_l STR_d STR_UNDERSCORE STR_T STR_u STR_r STR_k STR_i STR_c "\0"
-#define STRING_Oriya0 STR_O STR_r STR_i STR_y STR_a "\0"
-#define STRING_Osmanya0 STR_O STR_s STR_m STR_a STR_n STR_y STR_a "\0"
-#define STRING_P0 STR_P "\0"
-#define STRING_Pc0 STR_P STR_c "\0"
-#define STRING_Pd0 STR_P STR_d "\0"
-#define STRING_Pe0 STR_P STR_e "\0"
-#define STRING_Pf0 STR_P STR_f "\0"
-#define STRING_Phags_Pa0 STR_P STR_h STR_a STR_g STR_s STR_UNDERSCORE STR_P STR_a "\0"
-#define STRING_Phoenician0 STR_P STR_h STR_o STR_e STR_n STR_i STR_c STR_i STR_a STR_n "\0"
-#define STRING_Pi0 STR_P STR_i "\0"
-#define STRING_Po0 STR_P STR_o "\0"
-#define STRING_Ps0 STR_P STR_s "\0"
-#define STRING_Rejang0 STR_R STR_e STR_j STR_a STR_n STR_g "\0"
-#define STRING_Runic0 STR_R STR_u STR_n STR_i STR_c "\0"
-#define STRING_S0 STR_S "\0"
-#define STRING_Samaritan0 STR_S STR_a STR_m STR_a STR_r STR_i STR_t STR_a STR_n "\0"
-#define STRING_Saurashtra0 STR_S STR_a STR_u STR_r STR_a STR_s STR_h STR_t STR_r STR_a "\0"
-#define STRING_Sc0 STR_S STR_c "\0"
-#define STRING_Shavian0 STR_S STR_h STR_a STR_v STR_i STR_a STR_n "\0"
-#define STRING_Sinhala0 STR_S STR_i STR_n STR_h STR_a STR_l STR_a "\0"
-#define STRING_Sk0 STR_S STR_k "\0"
-#define STRING_Sm0 STR_S STR_m "\0"
-#define STRING_So0 STR_S STR_o "\0"
-#define STRING_Sundanese0 STR_S STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e "\0"
-#define STRING_Syloti_Nagri0 STR_S STR_y STR_l STR_o STR_t STR_i STR_UNDERSCORE STR_N STR_a STR_g STR_r STR_i "\0"
-#define STRING_Syriac0 STR_S STR_y STR_r STR_i STR_a STR_c "\0"
-#define STRING_Tagalog0 STR_T STR_a STR_g STR_a STR_l STR_o STR_g "\0"
-#define STRING_Tagbanwa0 STR_T STR_a STR_g STR_b STR_a STR_n STR_w STR_a "\0"
-#define STRING_Tai_Le0 STR_T STR_a STR_i STR_UNDERSCORE STR_L STR_e "\0"
-#define STRING_Tai_Tham0 STR_T STR_a STR_i STR_UNDERSCORE STR_T STR_h STR_a STR_m "\0"
-#define STRING_Tai_Viet0 STR_T STR_a STR_i STR_UNDERSCORE STR_V STR_i STR_e STR_t "\0"
-#define STRING_Tamil0 STR_T STR_a STR_m STR_i STR_l "\0"
-#define STRING_Telugu0 STR_T STR_e STR_l STR_u STR_g STR_u "\0"
-#define STRING_Thaana0 STR_T STR_h STR_a STR_a STR_n STR_a "\0"
-#define STRING_Thai0 STR_T STR_h STR_a STR_i "\0"
-#define STRING_Tibetan0 STR_T STR_i STR_b STR_e STR_t STR_a STR_n "\0"
-#define STRING_Tifinagh0 STR_T STR_i STR_f STR_i STR_n STR_a STR_g STR_h "\0"
-#define STRING_Ugaritic0 STR_U STR_g STR_a STR_r STR_i STR_t STR_i STR_c "\0"
-#define STRING_Vai0 STR_V STR_a STR_i "\0"
-#define STRING_Yi0 STR_Y STR_i "\0"
-#define STRING_Z0 STR_Z "\0"
-#define STRING_Zl0 STR_Z STR_l "\0"
-#define STRING_Zp0 STR_Z STR_p "\0"
-#define STRING_Zs0 STR_Z STR_s "\0"
-
-const char _pcre_utt_names[] =
- STRING_Any0
- STRING_Arabic0
- STRING_Armenian0
- STRING_Avestan0
- STRING_Balinese0
- STRING_Bamum0
- STRING_Bengali0
- STRING_Bopomofo0
- STRING_Braille0
- STRING_Buginese0
- STRING_Buhid0
- STRING_C0
- STRING_Canadian_Aboriginal0
- STRING_Carian0
- STRING_Cc0
- STRING_Cf0
- STRING_Cham0
- STRING_Cherokee0
- STRING_Cn0
- STRING_Co0
- STRING_Common0
- STRING_Coptic0
- STRING_Cs0
- STRING_Cuneiform0
- STRING_Cypriot0
- STRING_Cyrillic0
- STRING_Deseret0
- STRING_Devanagari0
- STRING_Egyptian_Hieroglyphs0
- STRING_Ethiopic0
- STRING_Georgian0
- STRING_Glagolitic0
- STRING_Gothic0
- STRING_Greek0
- STRING_Gujarati0
- STRING_Gurmukhi0
- STRING_Han0
- STRING_Hangul0
- STRING_Hanunoo0
- STRING_Hebrew0
- STRING_Hiragana0
- STRING_Imperial_Aramaic0
- STRING_Inherited0
- STRING_Inscriptional_Pahlavi0
- STRING_Inscriptional_Parthian0
- STRING_Javanese0
- STRING_Kaithi0
- STRING_Kannada0
- STRING_Katakana0
- STRING_Kayah_Li0
- STRING_Kharoshthi0
- STRING_Khmer0
- STRING_L0
- STRING_L_AMPERSAND0
- STRING_Lao0
- STRING_Latin0
- STRING_Lepcha0
- STRING_Limbu0
- STRING_Linear_B0
- STRING_Lisu0
- STRING_Ll0
- STRING_Lm0
- STRING_Lo0
- STRING_Lt0
- STRING_Lu0
- STRING_Lycian0
- STRING_Lydian0
- STRING_M0
- STRING_Malayalam0
- STRING_Mc0
- STRING_Me0
- STRING_Meetei_Mayek0
- STRING_Mn0
- STRING_Mongolian0
- STRING_Myanmar0
- STRING_N0
- STRING_Nd0
- STRING_New_Tai_Lue0
- STRING_Nko0
- STRING_Nl0
- STRING_No0
- STRING_Ogham0
- STRING_Ol_Chiki0
- STRING_Old_Italic0
- STRING_Old_Persian0
- STRING_Old_South_Arabian0
- STRING_Old_Turkic0
- STRING_Oriya0
- STRING_Osmanya0
- STRING_P0
- STRING_Pc0
- STRING_Pd0
- STRING_Pe0
- STRING_Pf0
- STRING_Phags_Pa0
- STRING_Phoenician0
- STRING_Pi0
- STRING_Po0
- STRING_Ps0
- STRING_Rejang0
- STRING_Runic0
- STRING_S0
- STRING_Samaritan0
- STRING_Saurashtra0
- STRING_Sc0
- STRING_Shavian0
- STRING_Sinhala0
- STRING_Sk0
- STRING_Sm0
- STRING_So0
- STRING_Sundanese0
- STRING_Syloti_Nagri0
- STRING_Syriac0
- STRING_Tagalog0
- STRING_Tagbanwa0
- STRING_Tai_Le0
- STRING_Tai_Tham0
- STRING_Tai_Viet0
- STRING_Tamil0
- STRING_Telugu0
- STRING_Thaana0
- STRING_Thai0
- STRING_Tibetan0
- STRING_Tifinagh0
- STRING_Ugaritic0
- STRING_Vai0
- STRING_Yi0
- STRING_Z0
- STRING_Zl0
- STRING_Zp0
- STRING_Zs0;
-
-const ucp_type_table _pcre_utt[] = {
- { 0, PT_ANY, 0 },
- { 4, PT_SC, ucp_Arabic },
- { 11, PT_SC, ucp_Armenian },
- { 20, PT_SC, ucp_Avestan },
- { 28, PT_SC, ucp_Balinese },
- { 37, PT_SC, ucp_Bamum },
- { 43, PT_SC, ucp_Bengali },
- { 51, PT_SC, ucp_Bopomofo },
- { 60, PT_SC, ucp_Braille },
- { 68, PT_SC, ucp_Buginese },
- { 77, PT_SC, ucp_Buhid },
- { 83, PT_GC, ucp_C },
- { 85, PT_SC, ucp_Canadian_Aboriginal },
- { 105, PT_SC, ucp_Carian },
- { 112, PT_PC, ucp_Cc },
- { 115, PT_PC, ucp_Cf },
- { 118, PT_SC, ucp_Cham },
- { 123, PT_SC, ucp_Cherokee },
- { 132, PT_PC, ucp_Cn },
- { 135, PT_PC, ucp_Co },
- { 138, PT_SC, ucp_Common },
- { 145, PT_SC, ucp_Coptic },
- { 152, PT_PC, ucp_Cs },
- { 155, PT_SC, ucp_Cuneiform },
- { 165, PT_SC, ucp_Cypriot },
- { 173, PT_SC, ucp_Cyrillic },
- { 182, PT_SC, ucp_Deseret },
- { 190, PT_SC, ucp_Devanagari },
- { 201, PT_SC, ucp_Egyptian_Hieroglyphs },
- { 222, PT_SC, ucp_Ethiopic },
- { 231, PT_SC, ucp_Georgian },
- { 240, PT_SC, ucp_Glagolitic },
- { 251, PT_SC, ucp_Gothic },
- { 258, PT_SC, ucp_Greek },
- { 264, PT_SC, ucp_Gujarati },
- { 273, PT_SC, ucp_Gurmukhi },
- { 282, PT_SC, ucp_Han },
- { 286, PT_SC, ucp_Hangul },
- { 293, PT_SC, ucp_Hanunoo },
- { 301, PT_SC, ucp_Hebrew },
- { 308, PT_SC, ucp_Hiragana },
- { 317, PT_SC, ucp_Imperial_Aramaic },
- { 334, PT_SC, ucp_Inherited },
- { 344, PT_SC, ucp_Inscriptional_Pahlavi },
- { 366, PT_SC, ucp_Inscriptional_Parthian },
- { 389, PT_SC, ucp_Javanese },
- { 398, PT_SC, ucp_Kaithi },
- { 405, PT_SC, ucp_Kannada },
- { 413, PT_SC, ucp_Katakana },
- { 422, PT_SC, ucp_Kayah_Li },
- { 431, PT_SC, ucp_Kharoshthi },
- { 442, PT_SC, ucp_Khmer },
- { 448, PT_GC, ucp_L },
- { 450, PT_LAMP, 0 },
- { 453, PT_SC, ucp_Lao },
- { 457, PT_SC, ucp_Latin },
- { 463, PT_SC, ucp_Lepcha },
- { 470, PT_SC, ucp_Limbu },
- { 476, PT_SC, ucp_Linear_B },
- { 485, PT_SC, ucp_Lisu },
- { 490, PT_PC, ucp_Ll },
- { 493, PT_PC, ucp_Lm },
- { 496, PT_PC, ucp_Lo },
- { 499, PT_PC, ucp_Lt },
- { 502, PT_PC, ucp_Lu },
- { 505, PT_SC, ucp_Lycian },
- { 512, PT_SC, ucp_Lydian },
- { 519, PT_GC, ucp_M },
- { 521, PT_SC, ucp_Malayalam },
- { 531, PT_PC, ucp_Mc },
- { 534, PT_PC, ucp_Me },
- { 537, PT_SC, ucp_Meetei_Mayek },
- { 550, PT_PC, ucp_Mn },
- { 553, PT_SC, ucp_Mongolian },
- { 563, PT_SC, ucp_Myanmar },
- { 571, PT_GC, ucp_N },
- { 573, PT_PC, ucp_Nd },
- { 576, PT_SC, ucp_New_Tai_Lue },
- { 588, PT_SC, ucp_Nko },
- { 592, PT_PC, ucp_Nl },
- { 595, PT_PC, ucp_No },
- { 598, PT_SC, ucp_Ogham },
- { 604, PT_SC, ucp_Ol_Chiki },
- { 613, PT_SC, ucp_Old_Italic },
- { 624, PT_SC, ucp_Old_Persian },
- { 636, PT_SC, ucp_Old_South_Arabian },
- { 654, PT_SC, ucp_Old_Turkic },
- { 665, PT_SC, ucp_Oriya },
- { 671, PT_SC, ucp_Osmanya },
- { 679, PT_GC, ucp_P },
- { 681, PT_PC, ucp_Pc },
- { 684, PT_PC, ucp_Pd },
- { 687, PT_PC, ucp_Pe },
- { 690, PT_PC, ucp_Pf },
- { 693, PT_SC, ucp_Phags_Pa },
- { 702, PT_SC, ucp_Phoenician },
- { 713, PT_PC, ucp_Pi },
- { 716, PT_PC, ucp_Po },
- { 719, PT_PC, ucp_Ps },
- { 722, PT_SC, ucp_Rejang },
- { 729, PT_SC, ucp_Runic },
- { 735, PT_GC, ucp_S },
- { 737, PT_SC, ucp_Samaritan },
- { 747, PT_SC, ucp_Saurashtra },
- { 758, PT_PC, ucp_Sc },
- { 761, PT_SC, ucp_Shavian },
- { 769, PT_SC, ucp_Sinhala },
- { 777, PT_PC, ucp_Sk },
- { 780, PT_PC, ucp_Sm },
- { 783, PT_PC, ucp_So },
- { 786, PT_SC, ucp_Sundanese },
- { 796, PT_SC, ucp_Syloti_Nagri },
- { 809, PT_SC, ucp_Syriac },
- { 816, PT_SC, ucp_Tagalog },
- { 824, PT_SC, ucp_Tagbanwa },
- { 833, PT_SC, ucp_Tai_Le },
- { 840, PT_SC, ucp_Tai_Tham },
- { 849, PT_SC, ucp_Tai_Viet },
- { 858, PT_SC, ucp_Tamil },
- { 864, PT_SC, ucp_Telugu },
- { 871, PT_SC, ucp_Thaana },
- { 878, PT_SC, ucp_Thai },
- { 883, PT_SC, ucp_Tibetan },
- { 891, PT_SC, ucp_Tifinagh },
- { 900, PT_SC, ucp_Ugaritic },
- { 909, PT_SC, ucp_Vai },
- { 913, PT_SC, ucp_Yi },
- { 916, PT_GC, ucp_Z },
- { 918, PT_PC, ucp_Zl },
- { 921, PT_PC, ucp_Zp },
- { 924, PT_PC, ucp_Zs }
-};
-
-const int _pcre_utt_size = sizeof(_pcre_utt)/sizeof(ucp_type_table);
-
-#endif /* SUPPORT_UTF8 */
-
-/* End of pcre_tables.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains an internal function that tests a compiled pattern to
-see if it was compiled with the opposite endianness. If so, it uses an
-auxiliary local function to flip the appropriate bytes. */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Flip bytes in an integer *
-*************************************************/
-
-/* This function is called when the magic number in a regex doesn't match, in
-order to flip its bytes to see if we are dealing with a pattern that was
-compiled on a host of different endianness. If so, this function is used to
-flip other byte values.
-
-Arguments:
- value the number to flip
- n the number of bytes to flip (assumed to be 2 or 4)
-
-Returns: the flipped value
-*/
-
-static unsigned long int
-byteflip(unsigned long int value, int n)
-{
-if (n == 2) return ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8);
-return ((value & 0x000000ff) << 24) |
- ((value & 0x0000ff00) << 8) |
- ((value & 0x00ff0000) >> 8) |
- ((value & 0xff000000) >> 24);
-}
-
-
-
-/*************************************************
-* Test for a byte-flipped compiled regex *
-*************************************************/
-
-/* This function is called from pcre_exec(), pcre_dfa_exec(), and also from
-pcre_fullinfo(). Its job is to test whether the regex is byte-flipped - that
-is, it was compiled on a system of opposite endianness. The function is called
-only when the native MAGIC_NUMBER test fails. If the regex is indeed flipped,
-we flip all the relevant values into a different data block, and return it.
-
-Arguments:
- re points to the regex
- study points to study data, or NULL
- internal_re points to a new regex block
- internal_study points to a new study block
-
-Returns: the new block if is is indeed a byte-flipped regex
- NULL if it is not
-*/
-
-real_pcre *
-_pcre_try_flipped(const real_pcre *re, real_pcre *internal_re,
- const pcre_study_data *study, pcre_study_data *internal_study)
-{
-if (byteflip(re->magic_number, sizeof(re->magic_number)) != MAGIC_NUMBER)
- return NULL;
-
-*internal_re = *re; /* To copy other fields */
-internal_re->size = byteflip(re->size, sizeof(re->size));
-internal_re->options = byteflip(re->options, sizeof(re->options));
-internal_re->flags = (pcre_uint16)byteflip(re->flags, sizeof(re->flags));
-internal_re->top_bracket =
- (pcre_uint16)byteflip(re->top_bracket, sizeof(re->top_bracket));
-internal_re->top_backref =
- (pcre_uint16)byteflip(re->top_backref, sizeof(re->top_backref));
-internal_re->first_byte =
- (pcre_uint16)byteflip(re->first_byte, sizeof(re->first_byte));
-internal_re->req_byte =
- (pcre_uint16)byteflip(re->req_byte, sizeof(re->req_byte));
-internal_re->name_table_offset =
- (pcre_uint16)byteflip(re->name_table_offset, sizeof(re->name_table_offset));
-internal_re->name_entry_size =
- (pcre_uint16)byteflip(re->name_entry_size, sizeof(re->name_entry_size));
-internal_re->name_count =
- (pcre_uint16)byteflip(re->name_count, sizeof(re->name_count));
-
-if (study != NULL)
- {
- *internal_study = *study; /* To copy other fields */
- internal_study->size = byteflip(study->size, sizeof(study->size));
- internal_study->flags = byteflip(study->flags, sizeof(study->flags));
- internal_study->minlength = byteflip(study->minlength,
- sizeof(study->minlength));
- }
-
-return internal_re;
-}
-
-/* End of pcre_tryflipped.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2006 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-/* This file has been modified to use glib instead of the internal table
- * in ucptable.c -- Marco Barisione */
-
-/* This module contains code for searching the table of Unicode character
-properties. */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-#include "ucp.h" /* Category definitions */
-
-/* Table to translate from particular type value to the general value. */
-
-#ifdef NOT_USED_IN_GLIB
-
-static int ucp_gentype[] = {
- ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */
- ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */
- ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */
- ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */
- ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */
- ucp_P, ucp_P, /* Ps, Po */
- ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */
- ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */
-};
-
-
-
-/*************************************************
-* Search table and return type *
-*************************************************/
-
-/* Three values are returned: the category is ucp_C, ucp_L, etc. The detailed
-character type is ucp_Lu, ucp_Nd, etc. The script is ucp_Latin, etc.
-
-Arguments:
- c the character value
- type_ptr the detailed character type is returned here
- script_ptr the script is returned here
-
-Returns: the character type category
-*/
-
-int
-_pcre_ucp_findprop(const unsigned int c, int *type_ptr, int *script_ptr)
-{
-/* Note that the Unicode types have the same values in glib and in
- * PCRE, so ucp_Ll == G_UNICODE_LOWERCASE_LETTER,
- * ucp_Zs == G_UNICODE_SPACE_SEPARATOR, and so on. */
-*type_ptr = g_unichar_type(c);
-*script_ptr = g_unichar_get_script(c);
-return ucp_gentype[*type_ptr];
-}
-
-#endif
-
-
-
-/*************************************************
-* Search table and return other case *
-*************************************************/
-
-/* If the given character is a letter, and there is another case for the
-letter, return the other case. Otherwise, return -1.
-
-Arguments:
- c the character value
-
-Returns: the other case or NOTACHAR if none
-*/
-
-unsigned int
-_pcre_ucp_othercase(const unsigned int c)
-{
-int other_case = NOTACHAR;
-
-if (g_unichar_islower(c))
- other_case = g_unichar_toupper(c);
-else if (g_unichar_isupper(c))
- other_case = g_unichar_tolower(c);
-
-if (other_case == c)
- other_case = NOTACHAR;
-
-return other_case;
-}
-
-
-/* End of pcre_ucp_searchfuncs.c */
+++ /dev/null
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* PCRE is a library of functions to support regular expressions whose syntax
-and semantics are as close as possible to those of the Perl 5 language.
-
- Written by Philip Hazel
- Copyright (c) 1997-2009 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-
-/* This module contains an internal function that is used to match an extended
-class. It is used by both pcre_exec() and pcre_def_exec(). */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "pcre_internal.h"
-
-
-/*************************************************
-* Match character against an XCLASS *
-*************************************************/
-
-/* This function is called to match a character against an extended class that
-might contain values > 255 and/or Unicode properties.
-
-Arguments:
- c the character
- data points to the flag byte of the XCLASS data
-
-Returns: TRUE if character matches, else FALSE
-*/
-
-BOOL
-_pcre_xclass(int c, const uschar *data)
-{
-int t;
-BOOL negated = (*data & XCL_NOT) != 0;
-
-/* Character values < 256 are matched against a bitmap, if one is present. If
-not, we still carry on, because there may be ranges that start below 256 in the
-additional data. */
-
-if (c < 256)
- {
- if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0)
- return !negated; /* char found */
- }
-
-/* First skip the bit map if present. Then match against the list of Unicode
-properties or large chars or ranges that end with a large char. We won't ever
-encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */
-
-if ((*data++ & XCL_MAP) != 0) data += 32;
-
-while ((t = *data++) != XCL_END)
- {
- int x, y;
- if (t == XCL_SINGLE)
- {
- GETCHARINC(x, data);
- if (c == x) return !negated;
- }
- else if (t == XCL_RANGE)
- {
- GETCHARINC(x, data);
- GETCHARINC(y, data);
- if (c >= x && c <= y) return !negated;
- }
-
-#ifdef SUPPORT_UCP
- else /* XCL_PROP & XCL_NOTPROP */
- {
- int chartype = UCD_CHARTYPE(c);
- switch(*data)
- {
- case PT_ANY:
- if (t == XCL_PROP) return !negated;
- break;
-
- case PT_LAMP:
- if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) ==
- (t == XCL_PROP)) return !negated;
- break;
-
- case PT_GC:
- if ((data[1] == _pcre_ucp_gentype[chartype]) == (t == XCL_PROP)) return !negated;
- break;
-
- case PT_PC:
- if ((data[1] == chartype) == (t == XCL_PROP)) return !negated;
- break;
-
- case PT_SC:
- if ((data[1] == UCD_SCRIPT(c)) == (t == XCL_PROP)) return !negated;
- break;
-
- /* This should never occur, but compilers may mutter if there is no
- default. */
-
- default:
- return FALSE;
- }
-
- data += 2;
- }
-#endif /* SUPPORT_UCP */
- }
-
-return negated; /* char did not match */
-}
-
-/* End of pcre_xclass.c */
+++ /dev/null
-/*************************************************
-* Unicode Property Table handler *
-*************************************************/
-
-#ifndef _UCP_H
-#define _UCP_H
-
-/* This file contains definitions of the property values that are returned by
-the UCD access macros. New values that are added for new releases of Unicode
-should always be at the end of each enum, for backwards compatibility. */
-
-/* These are the general character categories. */
-
-enum {
- ucp_C, /* Other */
- ucp_L, /* Letter */
- ucp_M, /* Mark */
- ucp_N, /* Number */
- ucp_P, /* Punctuation */
- ucp_S, /* Symbol */
- ucp_Z /* Separator */
-};
-
-/* These are the particular character types. */
-
-enum {
- ucp_Cc, /* Control */
- ucp_Cf, /* Format */
- ucp_Cn, /* Unassigned */
- ucp_Co, /* Private use */
- ucp_Cs, /* Surrogate */
- ucp_Ll, /* Lower case letter */
- ucp_Lm, /* Modifier letter */
- ucp_Lo, /* Other letter */
- ucp_Lt, /* Title case letter */
- ucp_Lu, /* Upper case letter */
- ucp_Mc, /* Spacing mark */
- ucp_Me, /* Enclosing mark */
- ucp_Mn, /* Non-spacing mark */
- ucp_Nd, /* Decimal number */
- ucp_Nl, /* Letter number */
- ucp_No, /* Other number */
- ucp_Pc, /* Connector punctuation */
- ucp_Pd, /* Dash punctuation */
- ucp_Pe, /* Close punctuation */
- ucp_Pf, /* Final punctuation */
- ucp_Pi, /* Initial punctuation */
- ucp_Po, /* Other punctuation */
- ucp_Ps, /* Open punctuation */
- ucp_Sc, /* Currency symbol */
- ucp_Sk, /* Modifier symbol */
- ucp_Sm, /* Mathematical symbol */
- ucp_So, /* Other symbol */
- ucp_Zl, /* Line separator */
- ucp_Zp, /* Paragraph separator */
- ucp_Zs /* Space separator */
-};
-
-/* These are the script identifications. */
-
-enum {
- ucp_Arabic = G_UNICODE_SCRIPT_ARABIC,
- ucp_Armenian = G_UNICODE_SCRIPT_ARMENIAN,
- ucp_Bengali = G_UNICODE_SCRIPT_BENGALI,
- ucp_Bopomofo = G_UNICODE_SCRIPT_BOPOMOFO,
- ucp_Braille = G_UNICODE_SCRIPT_BRAILLE,
- ucp_Buginese = G_UNICODE_SCRIPT_BUGINESE,
- ucp_Buhid = G_UNICODE_SCRIPT_BUHID,
- ucp_Canadian_Aboriginal = G_UNICODE_SCRIPT_CANADIAN_ABORIGINAL,
- ucp_Cherokee = G_UNICODE_SCRIPT_CHEROKEE,
- ucp_Common = G_UNICODE_SCRIPT_COMMON,
- ucp_Coptic = G_UNICODE_SCRIPT_COPTIC,
- ucp_Cypriot = G_UNICODE_SCRIPT_CYPRIOT,
- ucp_Cyrillic = G_UNICODE_SCRIPT_CYRILLIC,
- ucp_Deseret = G_UNICODE_SCRIPT_DESERET,
- ucp_Devanagari = G_UNICODE_SCRIPT_DEVANAGARI,
- ucp_Ethiopic = G_UNICODE_SCRIPT_ETHIOPIC,
- ucp_Georgian = G_UNICODE_SCRIPT_GEORGIAN,
- ucp_Glagolitic = G_UNICODE_SCRIPT_GLAGOLITIC,
- ucp_Gothic = G_UNICODE_SCRIPT_GOTHIC,
- ucp_Greek = G_UNICODE_SCRIPT_GREEK,
- ucp_Gujarati = G_UNICODE_SCRIPT_GUJARATI,
- ucp_Gurmukhi = G_UNICODE_SCRIPT_GURMUKHI,
- ucp_Han = G_UNICODE_SCRIPT_HAN,
- ucp_Hangul = G_UNICODE_SCRIPT_HANGUL,
- ucp_Hanunoo = G_UNICODE_SCRIPT_HANUNOO,
- ucp_Hebrew = G_UNICODE_SCRIPT_HEBREW,
- ucp_Hiragana = G_UNICODE_SCRIPT_HIRAGANA,
- ucp_Inherited = G_UNICODE_SCRIPT_INHERITED,
- ucp_Kannada = G_UNICODE_SCRIPT_KANNADA,
- ucp_Katakana = G_UNICODE_SCRIPT_KATAKANA,
- ucp_Kharoshthi = G_UNICODE_SCRIPT_KHAROSHTHI,
- ucp_Khmer = G_UNICODE_SCRIPT_KHMER,
- ucp_Lao = G_UNICODE_SCRIPT_LAO,
- ucp_Latin = G_UNICODE_SCRIPT_LATIN,
- ucp_Limbu = G_UNICODE_SCRIPT_LIMBU,
- ucp_Linear_B = G_UNICODE_SCRIPT_LINEAR_B,
- ucp_Malayalam = G_UNICODE_SCRIPT_MALAYALAM,
- ucp_Mongolian = G_UNICODE_SCRIPT_MONGOLIAN,
- ucp_Myanmar = G_UNICODE_SCRIPT_MYANMAR,
- ucp_New_Tai_Lue = G_UNICODE_SCRIPT_NEW_TAI_LUE,
- ucp_Ogham = G_UNICODE_SCRIPT_OGHAM,
- ucp_Old_Italic = G_UNICODE_SCRIPT_OLD_ITALIC,
- ucp_Old_Persian = G_UNICODE_SCRIPT_OLD_PERSIAN,
- ucp_Oriya = G_UNICODE_SCRIPT_ORIYA,
- ucp_Osmanya = G_UNICODE_SCRIPT_OSMANYA,
- ucp_Runic = G_UNICODE_SCRIPT_RUNIC,
- ucp_Shavian = G_UNICODE_SCRIPT_SHAVIAN,
- ucp_Sinhala = G_UNICODE_SCRIPT_SINHALA,
- ucp_Syloti_Nagri = G_UNICODE_SCRIPT_SYLOTI_NAGRI,
- ucp_Syriac = G_UNICODE_SCRIPT_SYRIAC,
- ucp_Tagalog = G_UNICODE_SCRIPT_TAGALOG,
- ucp_Tagbanwa = G_UNICODE_SCRIPT_TAGBANWA,
- ucp_Tai_Le = G_UNICODE_SCRIPT_TAI_LE,
- ucp_Tamil = G_UNICODE_SCRIPT_TAMIL,
- ucp_Telugu = G_UNICODE_SCRIPT_TELUGU,
- ucp_Thaana = G_UNICODE_SCRIPT_THAANA,
- ucp_Thai = G_UNICODE_SCRIPT_THAI,
- ucp_Tibetan = G_UNICODE_SCRIPT_TIBETAN,
- ucp_Tifinagh = G_UNICODE_SCRIPT_TIFINAGH,
- ucp_Ugaritic = G_UNICODE_SCRIPT_UGARITIC,
- ucp_Yi = G_UNICODE_SCRIPT_YI,
- ucp_Balinese = G_UNICODE_SCRIPT_BALINESE,
- ucp_Cuneiform = G_UNICODE_SCRIPT_CUNEIFORM,
- ucp_Nko = G_UNICODE_SCRIPT_NKO,
- ucp_Phags_Pa = G_UNICODE_SCRIPT_PHAGS_PA,
- ucp_Phoenician = G_UNICODE_SCRIPT_PHOENICIAN,
- ucp_Carian = G_UNICODE_SCRIPT_CARIAN,
- ucp_Cham = G_UNICODE_SCRIPT_CHAM,
- ucp_Kayah_Li = G_UNICODE_SCRIPT_KAYAH_LI,
- ucp_Lepcha = G_UNICODE_SCRIPT_LEPCHA,
- ucp_Lycian = G_UNICODE_SCRIPT_LYCIAN,
- ucp_Lydian = G_UNICODE_SCRIPT_LYDIAN,
- ucp_Ol_Chiki = G_UNICODE_SCRIPT_OL_CHIKI,
- ucp_Rejang = G_UNICODE_SCRIPT_REJANG,
- ucp_Saurashtra = G_UNICODE_SCRIPT_SAURASHTRA,
- ucp_Sundanese = G_UNICODE_SCRIPT_SUNDANESE,
- ucp_Vai = G_UNICODE_SCRIPT_VAI,
- ucp_Avestan = G_UNICODE_SCRIPT_AVESTAN,
- ucp_Bamum = G_UNICODE_SCRIPT_BAMUM,
- ucp_Egyptian_Hieroglyphs = G_UNICODE_SCRIPT_EGYPTIAN_HIEROGLYPHS,
- ucp_Imperial_Aramaic = G_UNICODE_SCRIPT_IMPERIAL_ARAMAIC,
- ucp_Inscriptional_Pahlavi = G_UNICODE_SCRIPT_INSCRIPTIONAL_PAHLAVI,
- ucp_Inscriptional_Parthian = G_UNICODE_SCRIPT_INSCRIPTIONAL_PARTHIAN,
- ucp_Javanese = G_UNICODE_SCRIPT_JAVANESE,
- ucp_Kaithi = G_UNICODE_SCRIPT_KAITHI,
- ucp_Lisu = G_UNICODE_SCRIPT_LISU,
- ucp_Meetei_Mayek = G_UNICODE_SCRIPT_MEETEI_MAYEK,
- ucp_Old_South_Arabian = G_UNICODE_SCRIPT_OLD_SOUTH_ARABIAN,
- ucp_Old_Turkic = G_UNICODE_SCRIPT_OLD_TURKISH,
- ucp_Samaritan = G_UNICODE_SCRIPT_SAMARITAN,
- ucp_Tai_Tham = G_UNICODE_SCRIPT_TAI_THAM,
- ucp_Tai_Viet = G_UNICODE_SCRIPT_TAI_VIET
-};
-
-#endif
-
-/* End of ucp.h */
-
+++ /dev/null
-/*
- * iconv library implemented with Win32 API.
- *
- * This file is placed in the public domain.
- *
- * Maintainer: Yukihiro Nakadaira <yukihiro.nakadaira@gmail.com>
- *
- * If $WINICONV_LIBICONV_DLL environment variable was defined, win_iconv
- * loads the specified DLL dynamically and uses it. If loading the DLL
- * or iconv_open() failed, falls back to internal conversion.
- * $WINICONV_LIBICONV_DLL is a comma separated list. The first loadable
- * DLL is used. The specified DLL should have iconv_open(),
- * iconv_close() and iconv() functions. Or these functions can be
- * libiconv_open(), libiconv_close() and libiconv().
- *
- * Win32 API does not support strict encoding conversion for some
- * codepage. And MLang function drop or replace invalid bytes and does
- * not return useful error status as iconv. This implementation cannot
- * be used for encoding validation purpose.
- */
-
-/* for WC_NO_BEST_FIT_CHARS */
-#ifndef WINVER
-# define WINVER 0x0500
-#endif
-
-#define STRICT
-#include <windows.h>
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#if 0
-# define MAKE_EXE
-# define MAKE_DLL
-# define USE_LIBICONV_DLL
-#endif
-
-#if !defined(DEFAULT_LIBICONV_DLL)
-# define DEFAULT_LIBICONV_DLL ""
-#endif
-
-#define MB_CHAR_MAX 16
-
-#define UNICODE_MODE_BOM_DONE 1
-#define UNICODE_MODE_SWAPPED 2
-
-#define FLAG_USE_BOM_ENDIAN 1
-#define FLAG_TRANSLIT 2 /* //TRANSLIT */
-#define FLAG_IGNORE 4 /* //IGNORE (not implemented) */
-
-#define return_error(code) \
- do { \
- errno = code; \
- return -1; \
- } while (0)
-
-#define xstrlcpy(dst, src, size) \
- do { \
- strncpy(dst, src, size); \
- dst[size - 1] = 0; \
- } while (0)
-
-#define xstrlcpyn(dst, src, srclen, size) \
- xstrlcpy(dst, src, xmin((srclen) + 1, size))
-
-#define xmin(a, b) ((a) < (b) ? (a) : (b))
-#define xmax(a, b) ((a) > (b) ? (a) : (b))
-
-#define STATIC_STRLEN(arr) (sizeof(arr) - 1)
-
-typedef unsigned char uchar;
-typedef unsigned short ushort;
-typedef unsigned int uint;
-
-typedef void* iconv_t;
-
-iconv_t iconv_open(const char *tocode, const char *fromcode);
-int iconv_close(iconv_t cd);
-size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-
-/* libiconv interface for vim */
-#if defined(MAKE_DLL)
-int
-iconvctl (iconv_t cd, int request, void* argument)
-{
- /* not supported */
- return 0;
-}
-#endif
-
-typedef struct compat_t compat_t;
-typedef struct csconv_t csconv_t;
-typedef struct rec_iconv_t rec_iconv_t;
-
-typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);
-typedef int (*f_iconv_close)(iconv_t cd);
-typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-typedef int* (*f_errno)(void);
-typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);
-typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);
-
-#define COMPAT_IN 1
-#define COMPAT_OUT 2
-
-/* unicode mapping for compatibility with other conversion table. */
-struct compat_t {
- uint in;
- uint out;
- uint flag;
-};
-
-struct csconv_t {
- int codepage;
- int flags;
- f_mbtowc mbtowc;
- f_wctomb wctomb;
- f_mblen mblen;
- f_flush flush;
- DWORD mode;
- compat_t *compat;
-};
-
-struct rec_iconv_t {
- iconv_t cd;
- f_iconv_close iconv_close;
- f_iconv iconv;
- f_errno _errno;
- csconv_t from;
- csconv_t to;
-#if defined(USE_LIBICONV_DLL)
- HMODULE hlibiconv;
-#endif
-};
-
-static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);
-static int win_iconv_close(iconv_t cd);
-static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-
-static int load_mlang();
-static csconv_t make_csconv(const char *name);
-static int name_to_codepage(const char *name);
-static uint utf16_to_ucs4(const ushort *wbuf);
-static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);
-static int is_unicode(int codepage);
-static int mbtowc_flags(int codepage);
-static int must_use_null_useddefaultchar(int codepage);
-static void check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize);
-static char *strrstr(const char *str, const char *token);
-
-#if defined(USE_LIBICONV_DLL)
-static int libiconv_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);
-static PVOID MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size);
-static HMODULE find_imported_module_by_funcname(HMODULE hModule, const char *funcname);
-
-static HMODULE hwiniconv;
-static HMODULE hlastdll; /* keep dll loaded for efficiency (unnecessary?) */
-#endif
-
-static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
-static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
-static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
-static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);
-static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);
-
-static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
-static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
-static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);
-
-static struct {
- int codepage;
- const char *name;
-} codepage_alias[] = {
- {65001, "CP65001"},
- {65001, "UTF8"},
- {65001, "UTF-8"},
-
- {1200, "CP1200"},
- {1200, "UTF16LE"},
- {1200, "UTF-16LE"},
- {1200, "UCS2LE"},
- {1200, "UCS-2LE"},
-
- {1201, "CP1201"},
- {1201, "UTF16BE"},
- {1201, "UTF-16BE"},
- {1201, "UCS2BE"},
- {1201, "UCS-2BE"},
- {1201, "unicodeFFFE"},
-
- {12000, "CP12000"},
- {12000, "UTF32LE"},
- {12000, "UTF-32LE"},
- {12000, "UCS4LE"},
- {12000, "UCS-4LE"},
-
- {12001, "CP12001"},
- {12001, "UTF32BE"},
- {12001, "UTF-32BE"},
- {12001, "UCS4BE"},
- {12001, "UCS-4BE"},
-
-#ifndef GLIB_COMPILATION
- /*
- * Default is big endian.
- * See rfc2781 4.3 Interpreting text labelled as UTF-16.
- */
- {1201, "UTF16"},
- {1201, "UTF-16"},
- {12001, "UTF32"},
- {12001, "UTF-32"},
- {12001, "UCS-4"},
- {12001, "UCS4"},
-#else
- /* Default is little endian, because the platform is */
- {1200, "UTF16"},
- {1200, "UTF-16"},
- {1200, "UCS2"},
- {1200, "UCS-2"},
- {12000, "UTF32"},
- {12000, "UTF-32"},
- {12000, "UCS4"},
- {12000, "UCS-4"},
-#endif
-
- /* copy from libiconv `iconv -l` */
- /* !IsValidCodePage(367) */
- {20127, "ANSI_X3.4-1968"},
- {20127, "ANSI_X3.4-1986"},
- {20127, "ASCII"},
- {20127, "CP367"},
- {20127, "IBM367"},
- {20127, "ISO-IR-6"},
- {20127, "ISO646-US"},
- {20127, "ISO_646.IRV:1991"},
- {20127, "US"},
- {20127, "US-ASCII"},
- {20127, "CSASCII"},
-
- /* !IsValidCodePage(819) */
- {1252, "CP819"},
- {1252, "IBM819"},
- {28591, "ISO-8859-1"},
- {28591, "ISO-IR-100"},
- {28591, "ISO8859-1"},
- {28591, "ISO_8859-1"},
- {28591, "ISO_8859-1:1987"},
- {28591, "L1"},
- {28591, "LATIN1"},
- {28591, "CSISOLATIN1"},
-
- {1250, "CP1250"},
- {1250, "MS-EE"},
- {1250, "WINDOWS-1250"},
-
- {1251, "CP1251"},
- {1251, "MS-CYRL"},
- {1251, "WINDOWS-1251"},
-
- {1252, "CP1252"},
- {1252, "MS-ANSI"},
- {1252, "WINDOWS-1252"},
-
- {1253, "CP1253"},
- {1253, "MS-GREEK"},
- {1253, "WINDOWS-1253"},
-
- {1254, "CP1254"},
- {1254, "MS-TURK"},
- {1254, "WINDOWS-1254"},
-
- {1255, "CP1255"},
- {1255, "MS-HEBR"},
- {1255, "WINDOWS-1255"},
-
- {1256, "CP1256"},
- {1256, "MS-ARAB"},
- {1256, "WINDOWS-1256"},
-
- {1257, "CP1257"},
- {1257, "WINBALTRIM"},
- {1257, "WINDOWS-1257"},
-
- {1258, "CP1258"},
- {1258, "WINDOWS-1258"},
-
- {850, "850"},
- {850, "CP850"},
- {850, "IBM850"},
- {850, "CSPC850MULTILINGUAL"},
-
- /* !IsValidCodePage(862) */
- {862, "862"},
- {862, "CP862"},
- {862, "IBM862"},
- {862, "CSPC862LATINHEBREW"},
-
- {866, "866"},
- {866, "CP866"},
- {866, "IBM866"},
- {866, "CSIBM866"},
-
- /* !IsValidCodePage(154) */
- {154, "CP154"},
- {154, "CYRILLIC-ASIAN"},
- {154, "PT154"},
- {154, "PTCP154"},
- {154, "CSPTCP154"},
-
- /* !IsValidCodePage(1133) */
- {1133, "CP1133"},
- {1133, "IBM-CP1133"},
-
- {874, "CP874"},
- {874, "WINDOWS-874"},
-
- /* !IsValidCodePage(51932) */
- {51932, "CP51932"},
- {51932, "MS51932"},
- {51932, "WINDOWS-51932"},
- {51932, "EUC-JP"},
-
- {932, "CP932"},
- {932, "MS932"},
- {932, "SHIFFT_JIS"},
- {932, "SHIFFT_JIS-MS"},
- {932, "SJIS"},
- {932, "SJIS-MS"},
- {932, "SJIS-OPEN"},
- {932, "SJIS-WIN"},
- {932, "WINDOWS-31J"},
- {932, "WINDOWS-932"},
- {932, "CSWINDOWS31J"},
-
- {50221, "CP50221"},
- {50221, "ISO-2022-JP"},
- {50221, "ISO-2022-JP-MS"},
- {50221, "ISO2022-JP"},
- {50221, "ISO2022-JP-MS"},
- {50221, "MS50221"},
- {50221, "WINDOWS-50221"},
-
- {936, "CP936"},
- {936, "GBK"},
- {936, "MS936"},
- {936, "WINDOWS-936"},
-
- {950, "CP950"},
- {950, "BIG5"},
-
- {949, "CP949"},
- {949, "UHC"},
- {949, "EUC-KR"},
-
- {1361, "CP1361"},
- {1361, "JOHAB"},
-
- {437, "437"},
- {437, "CP437"},
- {437, "IBM437"},
- {437, "CSPC8CODEPAGE437"},
-
- {737, "CP737"},
-
- {775, "CP775"},
- {775, "IBM775"},
- {775, "CSPC775BALTIC"},
-
- {852, "852"},
- {852, "CP852"},
- {852, "IBM852"},
- {852, "CSPCP852"},
-
- /* !IsValidCodePage(853) */
- {853, "CP853"},
-
- {855, "855"},
- {855, "CP855"},
- {855, "IBM855"},
- {855, "CSIBM855"},
-
- {857, "857"},
- {857, "CP857"},
- {857, "IBM857"},
- {857, "CSIBM857"},
-
- /* !IsValidCodePage(858) */
- {858, "CP858"},
-
- {860, "860"},
- {860, "CP860"},
- {860, "IBM860"},
- {860, "CSIBM860"},
-
- {861, "861"},
- {861, "CP-IS"},
- {861, "CP861"},
- {861, "IBM861"},
- {861, "CSIBM861"},
-
- {863, "863"},
- {863, "CP863"},
- {863, "IBM863"},
- {863, "CSIBM863"},
-
- {864, "CP864"},
- {864, "IBM864"},
- {864, "CSIBM864"},
-
- {865, "865"},
- {865, "CP865"},
- {865, "IBM865"},
- {865, "CSIBM865"},
-
- {869, "869"},
- {869, "CP-GR"},
- {869, "CP869"},
- {869, "IBM869"},
- {869, "CSIBM869"},
-
- /* !IsValidCodePage(1152) */
- {1125, "CP1125"},
-
- /*
- * Code Page Identifiers
- * http://msdn2.microsoft.com/en-us/library/ms776446.aspx
- */
- {37, "IBM037"}, /* IBM EBCDIC US-Canada */
- {437, "IBM437"}, /* OEM United States */
- {500, "IBM500"}, /* IBM EBCDIC International */
- {708, "ASMO-708"}, /* Arabic (ASMO 708) */
- /* 709 Arabic (ASMO-449+, BCON V4) */
- /* 710 Arabic - Transparent Arabic */
- {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */
- {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */
- {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */
- {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */
- {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */
- {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */
- {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */
- {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */
- {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */
- {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */
- {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */
- {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */
- {864, "IBM864"}, /* OEM Arabic; Arabic (864) */
- {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */
- {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */
- {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */
- {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */
- {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */
- {875, "cp875"}, /* IBM EBCDIC Greek Modern */
- {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */
- {932, "shift-jis"}, /* alternative name for it */
- {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */
- {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */
- {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */
- {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */
- {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */
- {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */
- {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */
- {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */
- {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */
- {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */
- {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */
- {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */
- {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */
- {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */
- {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */
- {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */
- {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */
- {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */
- {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */
- {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */
- {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */
- {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */
- {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */
- {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */
- {1361, "Johab"}, /* Korean (Johab) */
- {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */
- {10001, "x-mac-japanese"}, /* Japanese (Mac) */
- {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */
- {10003, "x-mac-korean"}, /* Korean (Mac) */
- {10004, "x-mac-arabic"}, /* Arabic (Mac) */
- {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */
- {10006, "x-mac-greek"}, /* Greek (Mac) */
- {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */
- {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */
- {10010, "x-mac-romanian"}, /* Romanian (Mac) */
- {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */
- {10021, "x-mac-thai"}, /* Thai (Mac) */
- {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */
- {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */
- {10081, "x-mac-turkish"}, /* Turkish (Mac) */
- {10082, "x-mac-croatian"}, /* Croatian (Mac) */
- {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */
- {20001, "x-cp20001"}, /* TCA Taiwan */
- {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */
- {20003, "x-cp20003"}, /* IBM5550 Taiwan */
- {20004, "x-cp20004"}, /* TeleText Taiwan */
- {20005, "x-cp20005"}, /* Wang Taiwan */
- {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */
- {20106, "x-IA5-German"}, /* IA5 German (7-bit) */
- {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */
- {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */
- {20127, "us-ascii"}, /* US-ASCII (7-bit) */
- {20261, "x-cp20261"}, /* T.61 */
- {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */
- {20273, "IBM273"}, /* IBM EBCDIC Germany */
- {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */
- {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */
- {20280, "IBM280"}, /* IBM EBCDIC Italy */
- {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */
- {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */
- {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */
- {20297, "IBM297"}, /* IBM EBCDIC France */
- {20420, "IBM420"}, /* IBM EBCDIC Arabic */
- {20423, "IBM423"}, /* IBM EBCDIC Greek */
- {20424, "IBM424"}, /* IBM EBCDIC Hebrew */
- {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */
- {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */
- {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */
- {20871, "IBM871"}, /* IBM EBCDIC Icelandic */
- {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */
- {20905, "IBM905"}, /* IBM EBCDIC Turkish */
- {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */
- {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */
- {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */
- {20949, "x-cp20949"}, /* Korean Wansung */
- {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */
- /* 21027 (deprecated) */
- {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */
- {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
- {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
- {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
- {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
- {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */
- {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */
- {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */
- {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */
- {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */
- {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */
- {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */
- {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */
- {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */
- {28597, "iso8859-7"}, /* ISO 8859-7 Greek */
- {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
- {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
- {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */
- {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */
- {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */
- {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */
- {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */
- {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */
- {29001, "x-Europa"}, /* Europa 3 */
- {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
- {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
- {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */
- {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */
- {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */
- {50225, "iso-2022-kr"}, /* ISO 2022 Korean */
- {50225, "iso2022-kr"}, /* ISO 2022 Korean */
- {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */
- /* 50229 ISO 2022 Traditional Chinese */
- /* 50930 EBCDIC Japanese (Katakana) Extended */
- /* 50931 EBCDIC US-Canada and Japanese */
- /* 50933 EBCDIC Korean Extended and Korean */
- /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */
- /* 50936 EBCDIC Simplified Chinese */
- /* 50937 EBCDIC US-Canada and Traditional Chinese */
- /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */
- {51932, "euc-jp"}, /* EUC Japanese */
- {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */
- {51949, "euc-kr"}, /* EUC Korean */
- /* 51950 EUC Traditional Chinese */
- {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */
- {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */
- {57002, "x-iscii-de"}, /* ISCII Devanagari */
- {57003, "x-iscii-be"}, /* ISCII Bengali */
- {57004, "x-iscii-ta"}, /* ISCII Tamil */
- {57005, "x-iscii-te"}, /* ISCII Telugu */
- {57006, "x-iscii-as"}, /* ISCII Assamese */
- {57007, "x-iscii-or"}, /* ISCII Oriya */
- {57008, "x-iscii-ka"}, /* ISCII Kannada */
- {57009, "x-iscii-ma"}, /* ISCII Malayalam */
- {57010, "x-iscii-gu"}, /* ISCII Gujarati */
- {57011, "x-iscii-pa"}, /* ISCII Punjabi */
-
- {0, NULL}
-};
-
-/*
- * SJIS SHIFTJIS table CP932 table
- * ---- --------------------------- --------------------------------
- * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS
- * 7E U+203E OVERLINE U+007E TILDE
- * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR
- * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS
- * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE
- * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO
- * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS
- * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN
- * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN
- * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN
- *
- * EUC-JP and ISO-2022-JP should be compatible with CP932.
- *
- * Kernel and MLang have different Unicode mapping table. Make sure
- * which API is used.
- */
-static compat_t cp932_compat[] = {
- {0x00A5, 0x005C, COMPAT_OUT},
- {0x203E, 0x007E, COMPAT_OUT},
- {0x2014, 0x2015, COMPAT_OUT},
- {0x301C, 0xFF5E, COMPAT_OUT},
- {0x2016, 0x2225, COMPAT_OUT},
- {0x2212, 0xFF0D, COMPAT_OUT},
- {0x00A2, 0xFFE0, COMPAT_OUT},
- {0x00A3, 0xFFE1, COMPAT_OUT},
- {0x00AC, 0xFFE2, COMPAT_OUT},
- {0, 0, 0}
-};
-
-static compat_t cp20932_compat[] = {
- {0x00A5, 0x005C, COMPAT_OUT},
- {0x203E, 0x007E, COMPAT_OUT},
- {0x2014, 0x2015, COMPAT_OUT},
- {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},
- {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},
- {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},
- {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},
- {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},
- {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},
- {0, 0, 0}
-};
-
-static compat_t *cp51932_compat = cp932_compat;
-
-/* cp20932_compat for kernel. cp932_compat for mlang. */
-static compat_t *cp5022x_compat = cp932_compat;
-
-typedef HRESULT (WINAPI *CONVERTINETSTRING)(
- LPDWORD lpdwMode,
- DWORD dwSrcEncoding,
- DWORD dwDstEncoding,
- LPCSTR lpSrcStr,
- LPINT lpnSrcSize,
- LPBYTE lpDstStr,
- LPINT lpnDstSize
-);
-typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(
- LPDWORD lpdwMode,
- DWORD dwSrcEncoding,
- LPCSTR lpSrcStr,
- LPINT lpnMultiCharCount,
- LPWSTR lpDstStr,
- LPINT lpnWideCharCount
-);
-typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(
- LPDWORD lpdwMode,
- DWORD dwEncoding,
- LPCWSTR lpSrcStr,
- LPINT lpnWideCharCount,
- LPSTR lpDstStr,
- LPINT lpnMultiCharCount
-);
-typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(
- DWORD dwSrcEncoding,
- DWORD dwDstEncoding
-);
-typedef HRESULT (WINAPI *LCIDTORFC1766A)(
- LCID Locale,
- LPSTR pszRfc1766,
- int nChar
-);
-typedef HRESULT (WINAPI *LCIDTORFC1766W)(
- LCID Locale,
- LPWSTR pszRfc1766,
- int nChar
-);
-typedef HRESULT (WINAPI *RFC1766TOLCIDA)(
- LCID *pLocale,
- LPSTR pszRfc1766
-);
-typedef HRESULT (WINAPI *RFC1766TOLCIDW)(
- LCID *pLocale,
- LPWSTR pszRfc1766
-);
-static CONVERTINETSTRING ConvertINetString;
-static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;
-static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;
-static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;
-static LCIDTORFC1766A LcidToRfc1766A;
-static RFC1766TOLCIDA Rfc1766ToLcidA;
-
-static int
-load_mlang()
-{
- HMODULE h = NULL;
- char mlang_dll[MAX_PATH + 100];
- int n;
- if (ConvertINetString != NULL)
- return TRUE;
- n = GetSystemDirectory(mlang_dll, MAX_PATH);
- if (n > 0 && n < MAX_PATH)
- {
- if (mlang_dll[n-1] != '\\' &&
- mlang_dll[n-1] != '/')
- strcat(mlang_dll, "\\");
- strcat(mlang_dll, "mlang.dll");
- h = LoadLibrary(mlang_dll);
- }
- if (!h)
- return FALSE;
- ConvertINetString = (CONVERTINETSTRING)GetProcAddress(h, "ConvertINetString");
- ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddress(h, "ConvertINetMultiByteToUnicode");
- ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddress(h, "ConvertINetUnicodeToMultiByte");
- IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddress(h, "IsConvertINetStringAvailable");
- LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddress(h, "LcidToRfc1766A");
- Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddress(h, "Rfc1766ToLcidA");
- return TRUE;
-}
-
-iconv_t
-iconv_open(const char *tocode, const char *fromcode)
-{
- rec_iconv_t *cd;
-
- cd = (rec_iconv_t *)calloc(1, sizeof(rec_iconv_t));
- if (cd == NULL)
- {
- errno = ENOMEM;
- return (iconv_t)(-1);
- }
-
-#if defined(USE_LIBICONV_DLL)
- if (libiconv_iconv_open(cd, tocode, fromcode))
- return (iconv_t)cd;
-#endif
-
- if (win_iconv_open(cd, tocode, fromcode))
- return (iconv_t)cd;
-
- free(cd);
- errno = EINVAL;
- return (iconv_t)(-1);
-}
-
-int
-iconv_close(iconv_t _cd)
-{
- rec_iconv_t *cd = (rec_iconv_t *)_cd;
- int r = cd->iconv_close(cd->cd);
- int e = *(cd->_errno());
-#if defined(USE_LIBICONV_DLL)
- if (cd->hlibiconv != NULL)
- FreeLibrary(cd->hlibiconv);
-#endif
- free(cd);
- errno = e;
- return r;
-}
-
-size_t
-iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
-{
- rec_iconv_t *cd = (rec_iconv_t *)_cd;
- size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);
- errno = *(cd->_errno());
- return r;
-}
-
-static int
-win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)
-{
- cd->from = make_csconv(fromcode);
- cd->to = make_csconv(tocode);
- if (cd->from.codepage == -1 || cd->to.codepage == -1)
- return FALSE;
- cd->iconv_close = win_iconv_close;
- cd->iconv = win_iconv;
- cd->_errno = _errno;
- cd->cd = (iconv_t)cd;
- return TRUE;
-}
-
-static int
-win_iconv_close(iconv_t cd)
-{
- return 0;
-}
-
-static size_t
-win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
-{
- rec_iconv_t *cd = (rec_iconv_t *)_cd;
- ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */
- int insize;
- int outsize;
- int wsize;
- DWORD mode;
- uint wc;
- compat_t *cp;
- int i;
-
- if (inbuf == NULL || *inbuf == NULL)
- {
- if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)
- {
- outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);
- if (outsize == -1)
- return (size_t)(-1);
- *outbuf += outsize;
- *outbytesleft -= outsize;
- }
- if (is_unicode(cd->from.codepage) && (cd->from.mode & UNICODE_MODE_SWAPPED))
- cd->from.codepage ^= 1;
- cd->from.mode = 0;
- cd->to.mode = 0;
- return 0;
- }
-
- while (*inbytesleft != 0)
- {
- mode = cd->from.mode;
- wsize = MB_CHAR_MAX;
-
- insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);
- if (insize == -1)
- return (size_t)(-1);
-
- if (is_unicode(cd->from.codepage) && !(cd->from.mode & UNICODE_MODE_BOM_DONE))
- {
- check_utf_bom(cd, wbuf, &wsize);
- cd->from.mode |= UNICODE_MODE_BOM_DONE;
- }
-
- if (wsize == 0)
- {
- *inbuf += insize;
- *inbytesleft -= insize;
- continue;
- }
-
- if (cd->from.compat != NULL)
- {
- wc = utf16_to_ucs4(wbuf);
- cp = cd->from.compat;
- for (i = 0; cp[i].in != 0; ++i)
- {
- if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)
- {
- ucs4_to_utf16(cp[i].in, wbuf, &wsize);
- break;
- }
- }
- }
-
- if (cd->to.compat != NULL)
- {
- wc = utf16_to_ucs4(wbuf);
- cp = cd->to.compat;
- for (i = 0; cp[i].in != 0; ++i)
- {
- if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)
- {
- ucs4_to_utf16(cp[i].out, wbuf, &wsize);
- break;
- }
- }
- }
-
- outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);
- if (outsize == -1)
- {
- cd->from.mode = mode;
- return (size_t)(-1);
- }
-
- *inbuf += insize;
- *outbuf += outsize;
- *inbytesleft -= insize;
- *outbytesleft -= outsize;
- }
-
- return 0;
-}
-
-static csconv_t
-make_csconv(const char *_name)
-{
- CPINFOEX cpinfoex;
- csconv_t cv;
- int use_compat = TRUE;
- int flag = 0;
- char name[128];
- char *p;
-
- xstrlcpy(name, _name, sizeof(name));
-
- /* check for option "enc_name//opt1//opt2" */
- while ((p = strrstr(name, "//")) != NULL)
- {
- if (_stricmp(p + 2, "nocompat") == 0)
- use_compat = FALSE;
- else if (_stricmp(p + 2, "translit") == 0)
- flag |= FLAG_TRANSLIT;
- else if (_stricmp(p + 2, "ignore") == 0)
- flag |= FLAG_IGNORE;
- *p = 0;
- }
-
- cv.mode = 0;
- cv.flags = flag;
- cv.mblen = NULL;
- cv.flush = NULL;
- cv.compat = NULL;
- cv.codepage = name_to_codepage(name);
- if (cv.codepage == 1200 || cv.codepage == 1201)
- {
- cv.mbtowc = utf16_mbtowc;
- cv.wctomb = utf16_wctomb;
- if (_stricmp(name, "UTF-16") == 0 ||
- _stricmp(name, "UTF16") == 0 ||
- _stricmp(name, "UCS-2") == 0 ||
- _stricmp(name, "UCS2") == 0)
- cv.flags |= FLAG_USE_BOM_ENDIAN;
- }
- else if (cv.codepage == 12000 || cv.codepage == 12001)
- {
- cv.mbtowc = utf32_mbtowc;
- cv.wctomb = utf32_wctomb;
- if (_stricmp(name, "UTF-32") == 0 ||
- _stricmp(name, "UTF32") == 0 ||
- _stricmp(name, "UCS-4") == 0 ||
- _stricmp(name, "UCS4") == 0)
- cv.flags |= FLAG_USE_BOM_ENDIAN;
- }
- else if (cv.codepage == 65001)
- {
- cv.mbtowc = kernel_mbtowc;
- cv.wctomb = kernel_wctomb;
- cv.mblen = utf8_mblen;
- }
- else if ((cv.codepage == 50220 || cv.codepage == 50221 || cv.codepage == 50222) && load_mlang())
- {
- cv.mbtowc = iso2022jp_mbtowc;
- cv.wctomb = iso2022jp_wctomb;
- cv.flush = iso2022jp_flush;
- }
- else if (cv.codepage == 51932 && load_mlang())
- {
- cv.mbtowc = mlang_mbtowc;
- cv.wctomb = mlang_wctomb;
- cv.mblen = eucjp_mblen;
- }
- else if (IsValidCodePage(cv.codepage)
- && GetCPInfoEx(cv.codepage, 0, &cpinfoex) != 0)
- {
- cv.mbtowc = kernel_mbtowc;
- cv.wctomb = kernel_wctomb;
- if (cpinfoex.MaxCharSize == 1)
- cv.mblen = sbcs_mblen;
- else if (cpinfoex.MaxCharSize == 2)
- cv.mblen = dbcs_mblen;
- else
- cv.mblen = mbcs_mblen;
- }
- else
- {
- /* not supported */
- cv.codepage = -1;
- }
- if (use_compat)
- {
- switch (cv.codepage)
- {
- case 932: cv.compat = cp932_compat; break;
- case 20932: cv.compat = cp20932_compat; break;
- case 51932: cv.compat = cp51932_compat; break;
- case 50220: case 50221: case 50222: cv.compat = cp5022x_compat; break;
- }
- }
- return cv;
-}
-
-static int
-name_to_codepage(const char *name)
-{
- int i;
-
- if (*name == '\0' ||
- strcmp(name, "char") == 0)
- return GetACP();
- else if (strcmp(name, "wchar_t") == 0)
- return 1200;
- else if (_strnicmp(name, "cp", 2) == 0)
- return atoi(name + 2); /* CP123 */
- else if ('0' <= name[0] && name[0] <= '9')
- return atoi(name); /* 123 */
- else if (_strnicmp(name, "xx", 2) == 0)
- return atoi(name + 2); /* XX123 for debug */
-
- for (i = 0; codepage_alias[i].name != NULL; ++i)
- if (_stricmp(name, codepage_alias[i].name) == 0)
- return codepage_alias[i].codepage;
- return -1;
-}
-
-/*
- * http://www.faqs.org/rfcs/rfc2781.html
- */
-static uint
-utf16_to_ucs4(const ushort *wbuf)
-{
- uint wc = wbuf[0];
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
- wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;
- return wc;
-}
-
-static void
-ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)
-{
- if (wc < 0x10000)
- {
- wbuf[0] = wc;
- *wbufsize = 1;
- }
- else
- {
- wc -= 0x10000;
- wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);
- wbuf[1] = 0xDC00 | (wc & 0x3FF);
- *wbufsize = 2;
- }
-}
-
-static int
-is_unicode(int codepage)
-{
- return (codepage == 1200 || codepage == 1201 ||
- codepage == 12000 || codepage == 12001 ||
- codepage == 65000 || codepage == 65001);
-}
-
-/*
- * Check if codepage is one of those for which the dwFlags parameter
- * to MultiByteToWideChar() must be zero. Return zero or
- * MB_ERR_INVALID_CHARS. The docs in Platform SDK for for Windows
- * Server 2003 R2 claims that also codepage 65001 is one of these, but
- * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave
- * out 65001 (UTF-8), and that indeed seems to be the case on XP, it
- * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting
- * from UTF-8.
- */
-static int
-mbtowc_flags(int codepage)
-{
- return (codepage == 50220 || codepage == 50221 ||
- codepage == 50222 || codepage == 50225 ||
- codepage == 50227 || codepage == 50229 ||
- codepage == 52936 || codepage == 54936 ||
- (codepage >= 57002 && codepage <= 57011) ||
- codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;
-}
-
-/*
- * Check if codepage is one those for which the lpUsedDefaultChar
- * parameter to WideCharToMultiByte() must be NULL. The docs in
- * Platform SDK for for Windows Server 2003 R2 claims that this is the
- * list below, while the MSDN docs for MSVS2008 claim that it is only
- * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform
- * SDK seems to be correct, at least for XP.
- */
-static int
-must_use_null_useddefaultchar(int codepage)
-{
- return (codepage == 65000 || codepage == 65001 ||
- codepage == 50220 || codepage == 50221 ||
- codepage == 50222 || codepage == 50225 ||
- codepage == 50227 || codepage == 50229 ||
- codepage == 52936 || codepage == 54936 ||
- (codepage >= 57002 && codepage <= 57011) ||
- codepage == 42);
-}
-
-static void
-check_utf_bom(rec_iconv_t *cd, ushort *wbuf, int *wbufsize)
-{
- /* If we have a BOM, trust it, despite what the caller said */
- if (wbuf[0] == 0xFFFE && (cd->from.flags & FLAG_USE_BOM_ENDIAN))
- {
- /* swap endian: 1200 <-> 1201 or 12000 <-> 12001 */
- cd->from.codepage ^= 1;
- cd->from.mode |= UNICODE_MODE_SWAPPED;
- wbuf[0] = 0xFEFF;
- }
-
- /*
- * Remove BOM.
- * Don't do this if "to" is Unicode,
- * except if "to" is UTF-8.
- */
- if (wbuf[0] == 0xFEFF && (!is_unicode(cd->to.codepage) || cd->to.codepage == 65001))
- *wbufsize = 0;
-}
-
-static char *
-strrstr(const char *str, const char *token)
-{
- int len = strlen(token);
- const char *p = str + strlen(str);
-
- while (str <= --p)
- if (p[0] == token[0] && strncmp(p, token, len) == 0)
- return (char *)p;
- return NULL;
-}
-
-#if defined(USE_LIBICONV_DLL)
-static int
-libiconv_iconv_open(rec_iconv_t *cd, const char *fromcode, const char *tocode)
-{
- HMODULE hlibiconv = NULL;
- HMODULE hmsvcrt = NULL;
- char dllname[_MAX_PATH];
- const char *p;
- const char *e;
- f_iconv_open _iconv_open;
-
- /*
- * always try to load dll, so that we can switch dll in runtime.
- */
-
- /* XXX: getenv() can't get variable set by SetEnvironmentVariable() */
- p = getenv("WINICONV_LIBICONV_DLL");
- if (p == NULL)
- p = DEFAULT_LIBICONV_DLL;
- /* parse comma separated value */
- for ( ; *p != 0; p = (*e == ',') ? e + 1 : e)
- {
- e = strchr(p, ',');
- if (p == e)
- continue;
- else if (e == NULL)
- e = p + strlen(p);
- xstrlcpyn(dllname, p, e - p, sizeof(dllname));
- hlibiconv = LoadLibrary(dllname);
- if (hlibiconv != NULL)
- {
- if (hlibiconv == hwiniconv)
- {
- FreeLibrary(hlibiconv);
- hlibiconv = NULL;
- continue;
- }
- break;
- }
- }
-
- if (hlastdll != NULL)
- {
- /* decrement reference count */
- FreeLibrary(hlastdll);
- hlastdll = NULL;
- }
-
- if (hlibiconv == NULL)
- goto failed;
-
- hmsvcrt = find_imported_module_by_funcname(hlibiconv, "_errno");
- if (hmsvcrt == NULL)
- goto failed;
-
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "libiconv_open");
- if (_iconv_open == NULL)
- _iconv_open = (f_iconv_open)GetProcAddress(hlibiconv, "iconv_open");
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "libiconv_close");
- if (cd->iconv_close == NULL)
- cd->iconv_close = (f_iconv_close)GetProcAddress(hlibiconv, "iconv_close");
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "libiconv");
- if (cd->iconv == NULL)
- cd->iconv = (f_iconv)GetProcAddress(hlibiconv, "iconv");
- cd->_errno = (f_errno)GetProcAddress(hmsvcrt, "_errno");
- if (_iconv_open == NULL || cd->iconv_close == NULL
- || cd->iconv == NULL || cd->_errno == NULL)
- goto failed;
-
- /* increment reference count */
- hlastdll = LoadLibrary(dllname);
-
- cd->cd = _iconv_open(tocode, fromcode);
- if (cd->cd == (iconv_t)(-1))
- goto failed;
-
- cd->hlibiconv = hlibiconv;
- return TRUE;
-
-failed:
- if (hlibiconv != NULL)
- FreeLibrary(hlibiconv);
- /* do not free hmsvcrt which is obtained by GetModuleHandle() */
- return FALSE;
-}
-
-/*
- * Reference:
- * http://forums.belution.com/ja/vc/000/234/78s.shtml
- * http://nienie.com/~masapico/api_ImageDirectoryEntryToData.html
- *
- * The formal way is
- * imagehlp.h or dbghelp.h
- * imagehlp.lib or dbghelp.lib
- * ImageDirectoryEntryToData()
- */
-#define TO_DOS_HEADER(base) ((PIMAGE_DOS_HEADER)(base))
-#define TO_NT_HEADERS(base) ((PIMAGE_NT_HEADERS)((LPBYTE)(base) + TO_DOS_HEADER(base)->e_lfanew))
-static PVOID
-MyImageDirectoryEntryToData(LPVOID Base, BOOLEAN MappedAsImage, USHORT DirectoryEntry, PULONG Size)
-{
- /* TODO: MappedAsImage? */
- PIMAGE_DATA_DIRECTORY p;
- p = TO_NT_HEADERS(Base)->OptionalHeader.DataDirectory + DirectoryEntry;
- if (p->VirtualAddress == 0) {
- *Size = 0;
- return NULL;
- }
- *Size = p->Size;
- return (PVOID)((LPBYTE)Base + p->VirtualAddress);
-}
-
-static HMODULE
-find_imported_module_by_funcname(HMODULE hModule, const char *funcname)
-{
- DWORD Base;
- ULONG Size;
- PIMAGE_IMPORT_DESCRIPTOR Imp;
- PIMAGE_THUNK_DATA Name; /* Import Name Table */
- PIMAGE_IMPORT_BY_NAME ImpName;
-
- Base = (DWORD)hModule;
- Imp = MyImageDirectoryEntryToData(
- (LPVOID)Base,
- TRUE,
- IMAGE_DIRECTORY_ENTRY_IMPORT,
- &Size);
- if (Imp == NULL)
- return NULL;
- for ( ; Imp->OriginalFirstThunk != 0; ++Imp)
- {
- Name = (PIMAGE_THUNK_DATA)(Base + Imp->OriginalFirstThunk);
- for ( ; Name->u1.Ordinal != 0; ++Name)
- {
- if (!IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal))
- {
- ImpName = (PIMAGE_IMPORT_BY_NAME)
- (Base + (DWORD)Name->u1.AddressOfData);
- if (strcmp((char *)ImpName->Name, funcname) == 0)
- return GetModuleHandle((char *)(Base + Imp->Name));
- }
- }
- }
- return NULL;
-}
-#endif
-
-static int
-sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
-{
- return 1;
-}
-
-static int
-dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
-{
- int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;
- if (bufsize < len)
- return_error(EINVAL);
- return len;
-}
-
-static int
-mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
-{
- int len = 0;
-
- if (cv->codepage == 54936) {
- if (buf[0] <= 0x7F) len = 1;
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
- bufsize >= 2 &&
- ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||
- (buf[1] >= 0x80 && buf[1] <= 0xFE))) len = 2;
- else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
- bufsize >= 4 &&
- buf[1] >= 0x30 && buf[1] <= 0x39) len = 4;
- else
- return_error(EINVAL);
- return len;
- }
- else
- return_error(EINVAL);
-}
-
-static int
-utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize)
-{
- int len = 0;
-
- if (buf[0] < 0x80) len = 1;
- else if ((buf[0] & 0xE0) == 0xC0) len = 2;
- else if ((buf[0] & 0xF0) == 0xE0) len = 3;
- else if ((buf[0] & 0xF8) == 0xF0) len = 4;
- else if ((buf[0] & 0xFC) == 0xF8) len = 5;
- else if ((buf[0] & 0xFE) == 0xFC) len = 6;
-
- if (len == 0)
- return_error(EILSEQ);
- else if (bufsize < len)
- return_error(EINVAL);
- return len;
-}
-
-static int
-eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize)
-{
- if (buf[0] < 0x80) /* ASCII */
- return 1;
- else if (buf[0] == 0x8E) /* JIS X 0201 */
- {
- if (bufsize < 2)
- return_error(EINVAL);
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))
- return_error(EILSEQ);
- return 2;
- }
- else if (buf[0] == 0x8F) /* JIS X 0212 */
- {
- if (bufsize < 3)
- return_error(EINVAL);
- else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)
- || !(0xA1 <= buf[2] && buf[2] <= 0xFE))
- return_error(EILSEQ);
- return 3;
- }
- else /* JIS X 0208 */
- {
- if (bufsize < 2)
- return_error(EINVAL);
- else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)
- || !(0xA1 <= buf[1] && buf[1] <= 0xFE))
- return_error(EILSEQ);
- return 2;
- }
-}
-
-static int
-kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
-{
- int len;
-
- len = cv->mblen(cv, buf, bufsize);
- if (len == -1)
- return -1;
- /* If converting from ASCII, reject 8bit
- * chars. MultiByteToWideChar() doesn't. Note that for ASCII we
- * know that the mblen function is sbcs_mblen() so len is 1.
- */
- if (cv->codepage == 20127 && buf[0] >= 0x80)
- return_error(EILSEQ);
- *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),
- (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);
- if (*wbufsize == 0)
- return_error(EILSEQ);
- return len;
-}
-
-static int
-kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
-{
- BOOL usedDefaultChar = 0;
- BOOL *p = NULL;
- int flags = 0;
- int len;
-
- if (bufsize == 0)
- return_error(E2BIG);
- if (!must_use_null_useddefaultchar(cv->codepage))
- {
- p = &usedDefaultChar;
-#ifdef WC_NO_BEST_FIT_CHARS
- if (!(cv->flags & FLAG_TRANSLIT))
- flags |= WC_NO_BEST_FIT_CHARS;
-#endif
- }
- len = WideCharToMultiByte(cv->codepage, flags,
- (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);
- if (len == 0)
- {
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
- return_error(E2BIG);
- return_error(EILSEQ);
- }
- else if (usedDefaultChar)
- return_error(EILSEQ);
- else if (cv->mblen(cv, buf, len) != len) /* validate result */
- return_error(EILSEQ);
- return len;
-}
-
-/*
- * It seems that the mode (cv->mode) is fixnum.
- * For example, when converting iso-2022-jp(cp50221) to unicode:
- * in ascii sequence: mode=0xC42C0000
- * in jisx0208 sequence: mode=0xC42C0001
- * "C42C" is same for each convert session.
- * It should be: ((codepage-1)<<16)|state
- */
-static int
-mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
-{
- int len;
- int insize;
- HRESULT hr;
-
- len = cv->mblen(cv, buf, bufsize);
- if (len == -1)
- return -1;
- insize = len;
- hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,
- (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);
- if (hr != S_OK || insize != len)
- return_error(EILSEQ);
- return len;
-}
-
-static int
-mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
-{
- char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */
- int tmpsize = MB_CHAR_MAX;
- int insize = wbufsize;
- HRESULT hr;
-
- hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,
- (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);
- if (hr != S_OK || insize != wbufsize)
- return_error(EILSEQ);
- else if (bufsize < tmpsize)
- return_error(E2BIG);
- else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)
- return_error(EILSEQ);
- memcpy(buf, tmpbuf, tmpsize);
- return tmpsize;
-}
-
-static int
-utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
-{
- if (bufsize < 2)
- return_error(EINVAL);
- if (cv->codepage == 1200) /* little endian */
- wbuf[0] = (buf[1] << 8) | buf[0];
- else if (cv->codepage == 1201) /* big endian */
- wbuf[0] = (buf[0] << 8) | buf[1];
- if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)
- return_error(EILSEQ);
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
- {
- if (bufsize < 4)
- return_error(EINVAL);
- if (cv->codepage == 1200) /* little endian */
- wbuf[1] = (buf[3] << 8) | buf[2];
- else if (cv->codepage == 1201) /* big endian */
- wbuf[1] = (buf[2] << 8) | buf[3];
- if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))
- return_error(EILSEQ);
- *wbufsize = 2;
- return 4;
- }
- *wbufsize = 1;
- return 2;
-}
-
-static int
-utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
-{
- if (bufsize < 2)
- return_error(E2BIG);
- if (cv->codepage == 1200) /* little endian */
- {
- buf[0] = (wbuf[0] & 0x00FF);
- buf[1] = (wbuf[0] & 0xFF00) >> 8;
- }
- else if (cv->codepage == 1201) /* big endian */
- {
- buf[0] = (wbuf[0] & 0xFF00) >> 8;
- buf[1] = (wbuf[0] & 0x00FF);
- }
- if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
- {
- if (bufsize < 4)
- return_error(E2BIG);
- if (cv->codepage == 1200) /* little endian */
- {
- buf[2] = (wbuf[1] & 0x00FF);
- buf[3] = (wbuf[1] & 0xFF00) >> 8;
- }
- else if (cv->codepage == 1201) /* big endian */
- {
- buf[2] = (wbuf[1] & 0xFF00) >> 8;
- buf[3] = (wbuf[1] & 0x00FF);
- }
- return 4;
- }
- return 2;
-}
-
-static int
-utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
-{
- uint wc;
-
- if (bufsize < 4)
- return_error(EINVAL);
- if (cv->codepage == 12000) /* little endian */
- wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
- else if (cv->codepage == 12001) /* big endian */
- wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
- if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)
- return_error(EILSEQ);
- ucs4_to_utf16(wc, wbuf, wbufsize);
- return 4;
-}
-
-static int
-utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
-{
- uint wc;
-
- if (bufsize < 4)
- return_error(E2BIG);
- wc = utf16_to_ucs4(wbuf);
- if (cv->codepage == 12000) /* little endian */
- {
- buf[0] = wc & 0x000000FF;
- buf[1] = (wc & 0x0000FF00) >> 8;
- buf[2] = (wc & 0x00FF0000) >> 16;
- buf[3] = (wc & 0xFF000000) >> 24;
- }
- else if (cv->codepage == 12001) /* big endian */
- {
- buf[0] = (wc & 0xFF000000) >> 24;
- buf[1] = (wc & 0x00FF0000) >> 16;
- buf[2] = (wc & 0x0000FF00) >> 8;
- buf[3] = wc & 0x000000FF;
- }
- return 4;
-}
-
-/*
- * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
- * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow
- * 1 byte Kana)
- * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte
- * Kana - SO/SI)
- *
- * MultiByteToWideChar() and WideCharToMultiByte() behave differently
- * depending on Windows version. On XP, WideCharToMultiByte() doesn't
- * terminate result sequence with ascii escape. But Vista does.
- * Use MLang instead.
- */
-
-#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))
-#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)
-#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)
-
-#define ISO2022_SI 0
-#define ISO2022_SO 1
-
-/* shift in */
-static const char iso2022_SI_seq[] = "\x0F";
-/* shift out */
-static const char iso2022_SO_seq[] = "\x0E";
-
-typedef struct iso2022_esc_t iso2022_esc_t;
-struct iso2022_esc_t {
- const char *esc;
- int esc_len;
- int len;
- int cs;
-};
-
-#define ISO2022JP_CS_ASCII 0
-#define ISO2022JP_CS_JISX0201_ROMAN 1
-#define ISO2022JP_CS_JISX0201_KANA 2
-#define ISO2022JP_CS_JISX0208_1978 3
-#define ISO2022JP_CS_JISX0208_1983 4
-#define ISO2022JP_CS_JISX0212 5
-
-static iso2022_esc_t iso2022jp_esc[] = {
- {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},
- {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},
- {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},
- {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */
- {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},
- {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},
- {NULL, 0, 0, 0}
-};
-
-static int
-iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
-{
- iso2022_esc_t *iesc = iso2022jp_esc;
- char tmp[MB_CHAR_MAX];
- int insize;
- HRESULT hr;
- DWORD dummy = 0;
- int len;
- int esc_len;
- int cs;
- int shift;
- int i;
-
- if (buf[0] == 0x1B)
- {
- for (i = 0; iesc[i].esc != NULL; ++i)
- {
- esc_len = iesc[i].esc_len;
- if (bufsize < esc_len)
- {
- if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)
- return_error(EINVAL);
- }
- else
- {
- if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)
- {
- cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);
- *wbufsize = 0;
- return esc_len;
- }
- }
- }
- /* not supported escape sequence */
- return_error(EILSEQ);
- }
- else if (buf[0] == iso2022_SO_seq[0])
- {
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);
- *wbufsize = 0;
- return 1;
- }
- else if (buf[0] == iso2022_SI_seq[0])
- {
- cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);
- *wbufsize = 0;
- return 1;
- }
-
- cs = ISO2022_MODE_CS(cv->mode);
- shift = ISO2022_MODE_SHIFT(cv->mode);
-
- /* reset the mode for informal sequence */
- if (buf[0] < 0x20)
- {
- cs = ISO2022JP_CS_ASCII;
- shift = ISO2022_SI;
- }
-
- len = iesc[cs].len;
- if (bufsize < len)
- return_error(EINVAL);
- for (i = 0; i < len; ++i)
- if (!(buf[i] < 0x80))
- return_error(EILSEQ);
- esc_len = iesc[cs].esc_len;
- memcpy(tmp, iesc[cs].esc, esc_len);
- if (shift == ISO2022_SO)
- {
- memcpy(tmp + esc_len, iso2022_SO_seq, 1);
- esc_len += 1;
- }
- memcpy(tmp + esc_len, buf, len);
-
- if ((cv->codepage == 50220 || cv->codepage == 50221
- || cv->codepage == 50222) && shift == ISO2022_SO)
- {
- /* XXX: shift-out cannot be used for mbtowc (both kernel and
- * mlang) */
- esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;
- memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);
- memcpy(tmp + esc_len, buf, len);
- }
-
- insize = len + esc_len;
- hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,
- (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);
- if (hr != S_OK || insize != len + esc_len)
- return_error(EILSEQ);
-
- /* Check for conversion error. Assuming defaultChar is 0x3F. */
- /* ascii should be converted from ascii */
- if (wbuf[0] == buf[0]
- && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
- return_error(EILSEQ);
-
- /* reset the mode for informal sequence */
- if (cv->mode != ISO2022_MODE(cs, shift))
- cv->mode = ISO2022_MODE(cs, shift);
-
- return len;
-}
-
-static int
-iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
-{
- iso2022_esc_t *iesc = iso2022jp_esc;
- char tmp[MB_CHAR_MAX];
- int tmpsize = MB_CHAR_MAX;
- int insize = wbufsize;
- HRESULT hr;
- DWORD dummy = 0;
- int len;
- int esc_len;
- int cs;
- int shift;
- int i;
-
- /*
- * MultiByte = [escape sequence] + character + [escape sequence]
- *
- * Whether trailing escape sequence is added depends on which API is
- * used (kernel or MLang, and its version).
- */
- hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,
- (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);
- if (hr != S_OK || insize != wbufsize)
- return_error(EILSEQ);
- else if (bufsize < tmpsize)
- return_error(E2BIG);
-
- if (tmpsize == 1)
- {
- cs = ISO2022JP_CS_ASCII;
- esc_len = 0;
- }
- else
- {
- for (i = 1; iesc[i].esc != NULL; ++i)
- {
- esc_len = iesc[i].esc_len;
- if (strncmp(tmp, iesc[i].esc, esc_len) == 0)
- {
- cs = iesc[i].cs;
- break;
- }
- }
- if (iesc[i].esc == NULL)
- /* not supported escape sequence */
- return_error(EILSEQ);
- }
-
- shift = ISO2022_SI;
- if (tmp[esc_len] == iso2022_SO_seq[0])
- {
- shift = ISO2022_SO;
- esc_len += 1;
- }
-
- len = iesc[cs].len;
-
- /* Check for converting error. Assuming defaultChar is 0x3F. */
- /* ascii should be converted from ascii */
- if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))
- return_error(EILSEQ);
- else if (tmpsize < esc_len + len)
- return_error(EILSEQ);
-
- if (cv->mode == ISO2022_MODE(cs, shift))
- {
- /* remove escape sequence */
- if (esc_len != 0)
- memmove(tmp, tmp + esc_len, len);
- esc_len = 0;
- }
- else
- {
- if (cs == ISO2022JP_CS_ASCII)
- {
- esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;
- memmove(tmp + esc_len, tmp, len);
- memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);
- }
- if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)
- {
- /* shift-in before changing to other mode */
- memmove(tmp + 1, tmp, len + esc_len);
- memcpy(tmp, iso2022_SI_seq, 1);
- esc_len += 1;
- }
- }
-
- if (bufsize < len + esc_len)
- return_error(E2BIG);
- memcpy(buf, tmp, len + esc_len);
- cv->mode = ISO2022_MODE(cs, shift);
- return len + esc_len;
-}
-
-static int
-iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)
-{
- iso2022_esc_t *iesc = iso2022jp_esc;
- int esc_len;
-
- if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
- {
- esc_len = 0;
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
- esc_len += 1;
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
- if (bufsize < esc_len)
- return_error(E2BIG);
-
- esc_len = 0;
- if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
- {
- memcpy(buf, iso2022_SI_seq, 1);
- esc_len += 1;
- }
- if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
- {
- memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,
- iesc[ISO2022JP_CS_ASCII].esc_len);
- esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
- }
- return esc_len;
- }
- return 0;
-}
-
-#if defined(MAKE_DLL) && defined(USE_LIBICONV_DLL)
-BOOL WINAPI
-DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
-{
- switch( fdwReason )
- {
- case DLL_PROCESS_ATTACH:
- hwiniconv = (HMODULE)hinstDLL;
- break;
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
-}
-#endif
-
-#if defined(MAKE_EXE)
-#include <stdio.h>
-#include <fcntl.h>
-#include <io.h>
-int
-main(int argc, char **argv)
-{
- char *fromcode = NULL;
- char *tocode = NULL;
- int i;
- char inbuf[BUFSIZ];
- char outbuf[BUFSIZ];
- const char *pin;
- char *pout;
- size_t inbytesleft;
- size_t outbytesleft;
- size_t rest = 0;
- iconv_t cd;
- size_t r;
- FILE *in = stdin;
-
- _setmode(_fileno(stdin), _O_BINARY);
- _setmode(_fileno(stdout), _O_BINARY);
-
- for (i = 1; i < argc; ++i)
- {
- if (strcmp(argv[i], "-l") == 0)
- {
- for (i = 0; codepage_alias[i].name != NULL; ++i)
- printf("%s\n", codepage_alias[i].name);
- return 0;
- }
-
- if (strcmp(argv[i], "-f") == 0)
- fromcode = argv[++i];
- else if (strcmp(argv[i], "-t") == 0)
- tocode = argv[++i];
- else
- {
- in = fopen(argv[i], "rb");
- if (in == NULL)
- {
- fprintf(stderr, "cannot open %s\n", argv[i]);
- return 1;
- }
- break;
- }
- }
-
- if (fromcode == NULL || tocode == NULL)
- {
- printf("usage: %s -f from-enc -t to-enc [file]\n", argv[0]);
- return 0;
- }
-
- cd = iconv_open(tocode, fromcode);
- if (cd == (iconv_t)(-1))
- {
- perror("iconv_open error");
- return 1;
- }
-
- while ((inbytesleft = fread(inbuf + rest, 1, sizeof(inbuf) - rest, in)) != 0
- || rest != 0)
- {
- inbytesleft += rest;
- pin = inbuf;
- pout = outbuf;
- outbytesleft = sizeof(outbuf);
- r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);
- if (r == (size_t)(-1) && errno != EINVAL && errno != E2BIG)
- {
- perror("conversion error");
- return 1;
- }
- memmove(inbuf, pin, inbytesleft);
- rest = inbytesleft;
- }
- pout = outbuf;
- outbytesleft = sizeof(outbuf);
- r = iconv(cd, NULL, NULL, &pout, &outbytesleft);
- fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, stdout);
- if (r == (size_t)(-1))
- {
- perror("conversion error");
- return 1;
- }
-
- iconv_close(cd);
-
- return 0;
-}
-#endif
+++ /dev/null
-makefile.msc
+++ /dev/null
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- gthread-impl.c
-
-LOCAL_MODULE := libgthread-2.0
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH) \
- $(GLIB_TOP)/android-internal \
- $(GLIB_C_INCLUDES)
-
-LOCAL_CFLAGS := \
- -DHAVE_CONFIG_H \
- -DG_LOG_DOMAIN=\"GThread\" \
- -D_POSIX4_DRAFT_SOURCE \
- -D_POSIX4A_DRAFT10_SOURCE \
- -U_OSF_SOURCE \
- -DG_DISABLE_DEPRECATED
-
-ifeq ($(GLIB_BUILD_STATIC),true)
-include $(BUILD_STATIC_LIBRARY)
-else
-LOCAL_SHARED_LIBRARIES := libglib-2.0
-
-include $(BUILD_SHARED_LIBRARY)
-endif
+++ /dev/null
-=== ChangeLog discontinued ===
-
- With the move to git, GLib is switching from a ChangeLog file
- to relying on commit messages to provide change history. Please
- see README.commits for guidance on the expected message format.
-
-2009-03-13 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.20.0 ===
-
-2009-03-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.10 ===
-
-2009-03-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.9 ===
-
-2009-02-17 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.8 ===
-
-2009-02-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.7 ===
-
-2009-02-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.6 ===
-
-2009-01-19 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.5 ===
-
-2009-01-05 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.4 ===
-
-2008-12-15 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.3 ===
-
-2008-12-01 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.2 ===
-
-2008-12-01 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.1 ===
-
-2008-10-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.19.0 ===
-
-2008-09-17 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.18.1 ===
-
-2008-09-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.18.0 ===
-
-2008-08-18 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.7 ===
-
-2008-08-04 Matthias Clasen <mclasen@redhat.com>
-
- Bug 460920 – build fix for --disable-threads
-
- * gthread-impl.c: Implement g_thread_init_with_errorcheck_mutexes
- in the !G_THREAD_ENABLED case. Pointed out by Jan Nieuwenhuizen
-
-2008-08-04 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.6 ===
-
-2008-08-04 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.5 ===
-
-2008-07-27 Tor Lillqvist <tml@novell.com>
-
- * Makefile.am (gthread-2.0.lib): Pass appropriate -machine flag to lib.exe.
-
-2008-07-21 Matthias Clasen <mclasen2redhat.com>
-
- * === Released 2.17.4 ===
-
-2008-07-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.3 ===
-
-2008-06-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.2 ===
-
-2008-06-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.1 ===
-
-2008-05-28 Michael Natterer <mitch@imendio.com>
-
- * Makefile.am: don't define G_DISABLE_SINGLE_INCLUDES, it's in
- the global CPPFLAGS now.
-
-2008-05-27 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.17.0 ===
-
-2008-05-05 Michael Natterer <mitch@imendio.com>
-
- * Makefile.am. build with G_DISABLE_SINGLE_INCLUDES to prevent
- code from being checked in that breaks the build of applications
- which use G_DISABLE_SINGLE_INCLUDES.
-
-2008-03-16 Tor Lillqvist <tml@novell.com>
-
- * Makefile.am: Define gthread_def locally here instead of using an
- Autoconf variable.
-
-2008-03-10 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.16.1 ===
-
-2008-03-10 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.16.0 ===
-
-2008-02-25 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.6 ===
-
-2008-02-11 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.5 ===
-
-2008-01-28 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.4 ===
-
-2008-01-27 Matthias Clasen <mclasen@redhat.com>
-
- * gthread-posix.c:
- * gthread-win32.c: Replace uses of G_GNUC_PRETTY_FUNCTION
- by __FUNCTION__.
-
-2008-01-21 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.3 ===
-
-2008-01-14 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.2 ===
-
-008-01-07 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.1 ===
-
-2007-12-20 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.15.0 ===
-
-2007-11-07 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.14.3 ===
-
-2007-10-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.14.2 ===
-
-2007-09-19 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.14.1 ===
-
-2007-08-03 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.14.0 ===
-
-2007-07-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.7 ===
-
-Fri Jun 29 2007 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.6 ===
-
-2007-06-18 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.5 ===
-
-2007-06-05 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.4 ===
-
-2007-06-04 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.3 ===
-
-2007-05-22 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.2 ===
-
-2007-05-03 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.1 ===
-
-2007-03-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.13.0 ===
-
-2007-01-19 Tor Lillqvist <tml@novell.com>
-
- * Makefile.am (gthread-2.0.lib): Use $(srcdir) for builds outside
- srcdir.
-
-2007-01-16 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c (g_gettime_win32_impl):
- GetSystemTimeAsFileTime() returns 100s of nanoseconds since 1601,
- so offset to Unix epoch (1970) and multiply by 100 to get
- nanoseconds which is what we want.
-
-2006-12-28 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c (g_thread_impl_init): Correct link to discussion
- about CRITICAL_SECTIONs vs. mutexes. Thanks to Felix Kater for
- pointing this out.
-
-2006-08-15 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.12.2 ===
-
-2006-07-22 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.12.1 ===
-
-2006-07-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.12.0 ===
-
-2006-06-20 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.11.4 ===
-
-2006-06-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.11.3 ===
-
-2006-06-05 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.11.2 ===
-
-2006-05-15 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.11.1 ===
-
-2006-05-03 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c (g_thread_exit_win32_impl): Free with free() and
- not g_free() what has been allocated with calloc(). (#340530, Jake
- Goulding)
-
-2006-05-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.11.0 ===
-
-2006-03-11 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c: #define _WIN32_WINDOWS as 0x0401 to get
- declaration for IsDebuggerPresent() when using MSVC6. (#333879,
- Kazuki Iwamoto)
-
-2006-03-07 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.10.1 ===
-
-2006-03-02 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c (G_PRIVATE_MAX): Increase to 100. 16 was rather
- low.
- (g_private_new_win32_impl): Can't use g_error() here as
- g_private_new() is called a few times by GLib internally before
- the messaging system that g_error() requires is ready. Thanks to
- Tim Janik for noticing. Just display a MessageBox() and abort()
- instead.
-
-2006-02-24 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.10.0 ===
-
-2006-02-20 Tor Lillqvist <tml@novell.com>
-
- * gthread-win32.c (g_thread_exit_win32_impl): Make the
- implementation of GPrivate behave more closely as in POSIX
- threads: The value associacted with a GPrivate must be set to NULL
- before calling the destructor. (The destructor gets the original
- value as argument.) A destructor might re-associate a non-NULL
- value with some GPrivate. To deal with this, if after all
- destructors have been called, there still are some non-NULL
- values, the process is repeated. (#331367)
-
-2006-02-10 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.6 ===
-
-2006-01-27 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.5 ===
-
-2006-01-18 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.4 ===
-
-2006-01-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.3 ===
-
-2006-01-05 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.2 ===
-
-2005-12-09 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.1 ===
-
-2005-12-02 Matthias Clasen <mclasen@redhat.com>
-
- * Makefile.am: Remove gthread-solaris.c
-
-2005-11-17 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.9.0 ===
-
-2005-08-23 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.8.1 ===
-
-2005-08-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.8.0 ===
-
-2005-08-05 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.7 ===
-
-2005-08-03 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.6 ===
-
-2005-08-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.5 ===
-
-2005-07-21 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.4 ===
-
-2005-07-15 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.3 ===
-
-2005-07-09 Tor Lillqvist <tml@novell.com>
-
- * Makefile.am: Don't use the scripts in build/win32 to compile
- gthread.rc into a resource object file. (This means we lose the
- build number increment magic, but I doubt it was that useful
- anyway.) Instead use windres directly. To pass the normal .o file
- produced by windres through libtool, which wants .lo files, pass
- it directly to the linker using a -Wl option.
-
- * gthread.rc.in: Thus replace BUILDNUMBER with 0.
-
-2005-07-08 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.2 ===
-
-2005-06-30 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.1 ===
-
-2005-06-26 Tor Lillqvist <tml@novell.com>
-
- * Makefile.am: libtool installs/uninstalls the import library, no
- need to do it ourselves. Do still install/uninstall the .def file,
- though.
-
-2005-06-10 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.7.0 ===
-
-2005-06-09 Matthias Clasen <mclasen@redhat.com>
-
- * gthread-posix.c (g_thread_create_posix_impl): Allow
- setstacksize to fail. (#304790, Michael Banck)
-
-2005-01-07 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.6.1 ===
-
-2004-12-16 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.6.0 ===
-
-2004-12-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.7 ===
-
-2004-11-12 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.6 ===
-
-2004-11-02 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.5 ===
-
-2004-10-27 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.4 ===
-
-2004-09-18 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.3 ===
-
-2004-08-25 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.2 ===
-
-2004-08-01 Matthias Clasen <mclasen@redhat.com>
-
- * === Released 2.5.1 ===
-
-Sun Jul 18 18:03:08 2004 Soeren Sandmann <sandmann@daimi.au.dk>
-
- * === Released 2.5.0 ===
-
-2002-11-23 Tor Lillqvist <tml@iki.fi>
-
- * gthread-win32.c (g_cond_timed_wait_win32_impl): Fix two bugs: 1)
- If abs_time is NULL, should use infinite time. 2) Check for
- current time already being past abs_time. (#99294, Christopher
- R. Palmer, fix by Sebastian Wilhelmi)
-
-Mon Nov 4 14:45:24 2002 Owen Taylor <otaylor@redhat.com>
-
- * gthread-posix.c gthread-solaris.c: Include <config.h>
-
-2002-03-10 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c (g_thread_create_posix_impl): Do a comparison,
- not an assignment, stupid! Spotted by Daniel Elstner
- <daniel.elstner@gmx.net>.
-
-2002-02-09 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c: Only compile most of this file, if
- G_THREADS_ENABLED is set.
-
- * Fixed typo in G_THREADS_ENABLED.
-
-2002-01-16 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-solaris.c: Use g_free instead of free. Pointed out by
- Sam O'Connor <sam@panviva.com>.
-
-2001-10-23 Tor Lillqvist <tml@iki.fi>
-
- * Makefile.am: (Win32): If we have built the MSVC import library,
- install it. Install the gcc import library. Also support
- uninstall.
-
-2001-09-28 Tor Lillqvist <tml@iki.fi>
-
- * gthread-win32.c: Use an extra level of indirection for GMutex.
- It is now a pointer either to a pointer to a CRITICAL_SECTION
- struct, or to a mutex HANDLE. This is needed in case the user
- defines G_ERRORCHECK_MUTEXES. G_MUTEX_SIZE must correctly reflect
- the size of *GMutex, but this used to vary depending on whether we
- at run-time chose to use CRITICAL_SECTIONs or mutexes.
- (g_mutex_free_win32_cs_impl, g_cond_free_win32_impl): Call
- DeleteCriticalSection() when done with it.
-
- * gthread-impl.c (g_thread_init_with_errorcheck_mutexes): Call
- g_thread_impl_init() before accessing
- g_thread_functions_for_glib_use_default, as the
- g_thread_impl_init() function might modify it.
-
-2001-09-26 Tor Lillqvist <tml@iki.fi>
-
- * makefile.mingw.in: Fix couple of typos.
-
- * gthread.def: Add g_thread_init_with_errorcheck_mutexes.
-
-2001-09-25 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c: Corrected the array size (cough, cough). Pointed
- out by gpablo@intersystems.com.ar. Fixes #61065.
-
-2001-09-25 Tor Lillqvist <tml@iki.fi>
-
- * Makefile.am: Use new macros for .def file, and check for
- MS_LIB_AVAILABLE, new rule to build MS import library.
-
- * makefile.msc.in: Use same DLL and import library names as
- libtool.
-
-2001-09-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Add g_thread_equal_posix_impl and add to the
- function vector g_thread_functions_for_glib_use_default.
-
- * gthread-solaris.c, gthread-win32.c: Add NULL as equal function,
- as on those two platforms you don't need an equal function.
-
-2001-09-19 Tor Lillqvist <tml@iki.fi>
-
- * gthread.rc.in: Correct InternalName and OriginalFilename to
- match what we actually produce.
-
-2001-07-20 Hans Breuer <hans@breuer.org>
-
- * makefile.msc.in : reflect glib move
-
-2001-06-07 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-win32.c: Use g_win32_error_message to beautify error
- messages.
-
-2001-05-24 Hans Breuer <hans@breuer.org>
-
- * makefile.msc.in : pthread isn't required anymore
-
-2001-05-22 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-win32.c (g_cond_wait_internal): Also return TRUE for
- late arrived signals. Thanks to Steven Brooks
- <umbrook0@cs.umanitoba.ca> for pointing out.
-
- * gthread-impl.c (g_thread_init): Move the thread implementation
- initialization to before assigning GThreadFuncs, which now is just
- struct assigned and not memcpy'ed. Completed check for zero
- members of GThreadFuncs.
-
- * makefile.mingw: Don't link to pthread anymore.
-
- * gthread-win32.c: New file for native thread support for
- win32. Thanks to Hans Breuer <hans@breuer.org> to got me
- kickstarted.
-
- * Makefile.am: Also distribute gthread-win32.c.
-
-Fri May 4 04:14:45 2001 Tim Janik <timj@gtk.org>
-
- * gthread-posix.c (g_cond_timed_wait_posix_impl): don't g_assert()
- the user specified time, but g_return_val_if_fail() here.
-
-2001-04-03 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Added special case for priorities on
- FreeBSD. Thanks to David Reid <dreid@jetnet.co.uk> for the info.
-
- * gthread-impl.c: Made two macros safe with ().
-
-2001-03-10 Tor Lillqvist <tml@iki.fi>
-
- * Makefile.am: Use the _LIBADD dependency on libglib only on
- Win32.
-
-2001-02-21 Tor Lillqvist <tml@iki.fi>
-
- * Makefile.am: Use libglib-1.3.la from top_builddir. Invoke
- libtool with -no-undefined for Win32 and Cygwin.
-
- * gthread-impl.c (g_thread_init): Win32 code snippet used also on
- Cygwin.
-
-2001-02-15 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Removed the G_THREAD_USE_PID_SURROGATE
- implementation, which is now in gthread.c.
-
-2001-01-30 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c (g_thread_init_with_errorcheck_mutexes): Call
- g_thread_impl_init(), as g_thread_init won't call it.
-
- * gthread-impl.c (g_mutex_free_errorcheck_impl): Fixed it for
- real. Sorry for this mess. It looked like a real obvious fix, so I
- didn't check. Bad boy. Added some casts to quiet the compiler.
-
-2001-01-29 Havoc Pennington <hp@redhat.com>
-
- * gthread-impl.c (g_mutex_free_errorcheck_impl): hack this so it
- compiles, needs fixing for real.
-
-2001-01-29 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c (g_mutex_free_errorcheck_impl): Add new check to
- errorcheck mutexes to abort, if a locked mutex is freed.
-
-2001-01-03 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-solaris.c, gthread-posix.c: Made g_thread_min_stack_size
- static.
-
-2000-11-28 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c: Revamped errorcheck mutexes and added errorcheck
- cond_wait() and cond_timed_wait() funtions. This makes he whole
- thing work. Now we only show the location of the locking/unlocking
- for -DG_ERRORCHECK_MUTEXES and not the name of the mutex.
-
-2000-11-21 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c, gthread-posix.c, gthread-solaris.c: Removed
- g_thread_map_priority function in favour of the
- g_thread_priority_map array. Initialize the array with
- PRIORITY_{...}_VALUE, if available and interpolate beetween the
- bounds if .._NORMAL_.. and .._HIGH_.. are not available.
-
- * gthread-posix.c: If we should use the PID niceness as a
- surrogate for thread priorities (G_THREAD_USE_PID_SURROGATE is
- defined), then disable normal priority handling and use PIDs and
- setpriority() instead. Depends on the thread to write its PID into
- the place after the thread id right after thread creation.
-
-2000-11-15 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Include <sched.h> if available.
-
-2000-11-02 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-none.c: Add G_MUTEX_SIZE as needed for gthread-impl.c
-
-2000-10-25 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * Makefile.am : Add @GLIB_DEBUG_FLAGS@ to INCLUDES for accessing
- -DG_ENABLE_DEBUG as needed in gthread-posix.c.
-
- * gthread-posix.c: Revamped error handling for native thread
- function calls. Now EPERM errors are ignored for some commands and
- only a warning message is output once (at first occurrence).
-
-2000-10-15 Raja R Harinath <harinath@cs.umn.edu>
-
- * Makefile.am (BUILT_EXTRA_DIST): New variable.
- (dist-hook): Handle $(BUILT_EXTRA_DIST).
-
-2000-09-29 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-impl.c: Added errorcheck mutexes. New exported function
- g_thread_init_with_errorcheck_mutexes, which is called instead of
- g_thread_init, when compiled with -DG_ERRORCHECK_MUTEXES. New
- static functions
- g_mutex_(new|lock|trylock|unlock|free)_errorcheck_impl to
- implement errorcheck mutexes.
-
- * gthread-posix.impl.c, gthread-solaris-impl.c: Define the size of
- a mutex.
-
-2000-09-21 Tor Lillqvist <tml@iki.fi>
-
- * makefile.mingw.in: Use pthreads macros from ../build.
-
-2000-09-06 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c, gthread-solaris.c:
- s/G_MICROSEC/G_USEC_PER_SEC/ and s/G_NANOSEC/G_NSEC_PER_SEC/
-
-2000-09-01 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c (g_thread_create_posix_impl): Use GError to
- report errors.
-
- * gthread-solaris.c (g_thread_create_solaris_impl): Use GError to
- report errors as well.
-
-2000-05-13 Tor Lillqvist <tml@iki.fi>
-
- * makefile.mingw.in: New file, with gthread stuff moved from
- ../makefile.mingw.in.
-
- * Makefile.am: Add to EXTRA_DIST, add rule to build makefile.mingw.
-
-2000-04-25 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-solaris.c (g_mutex_new_solaris_impl): Changed the scope
- of the initialized mutex to USYNC_THREAD. Thanks to Soeren
- Sandmann <sandmann@daimi.au.dk> for pointing that out.
-
-2000-03-20 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c (posix_check_for_error): Forgot a '}' in a macro
- for DCE-threads. Thanks to Karl Nelson <kenelson@ece.ucdavis.edu>
- for pointing that out.
-
-2000-03-17 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Don't use priorities for threads, when the
- minimal/maximal priorities couldn't be determined at configure
- time.
-
- * gthread-posix.c: Don't check for errors, when setting the scope
- of a tread to system, as some posix implementations can't do that
- and we don't want the thing to fail because of that.
-
-2000-02-22 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c, gthread-solaris.c: check for sysconf
- (_SC_THREAD_STACK_MIN), which returns the minimal stack size for
- new threads. Patch from Soeren Sandmann <sandmann@daimi.au.dk>.
-
-1999-11-16 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c, gthread-solaris.c: Changed the prototype of
- thread_create and thread_self to return the system thread into
- provided memory instead of a return value. This is necessary, as
- HPUX has a pthread_t, that is bigger than the biggest integral
- type there. Made some more functions static.
-
- * gthread-posix.c: Small fixes for DCE threads: Detaching has to
- be done after thread creation for DCE.
-
-1999-06-21 Tor Lillqvist <tml@iki.fi>
-
- * gthread-posix.c: Guard pthread_attr_setscope call with test
- for _POSIX_THREAD_PRIORITY_SCHEDULING, which should be defined
- in a <pthread.h> that supports that feature.
-
-1999-06-17 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c, gthread-solaris.c: Added the native
- implementations for the GLib's extended thread support.
-
- * gthread-nspr.c: Removed for good. NSPR is nothing we would want
- to build upon.
-
- * gthread.c: Renamed to gthread-impl.c to avoid confusion with
- ../gthread.c (Formerly known as the file called gmutex.c)
-
- * testgthread.c: Removed. The new and much extended tests are in
- ../tests/thread-test.c.
-
- * Makefile.am: Changed to reflect the changes above.
-
-1999-03-31 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Use the right default arguments for the
- construction of mutexes and conds for dce threads, these are
- &pthread_(cond|mutex)attr_default instead of NULL. Hint from
- D. Emilio Grimaldo Tunon <emilio_tunon@nl.compuware.com>.
-
-1999-03-18 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * Makefile.am (INCLUDES): Added @GTHREAD_COMPILE_IMPL_DEFINES@.
-
-1999-03-12 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c (g_private_get_posix_impl): Fixed typo for DCE
- implementation.
-
-1999-03-11 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Now handle both dce and posix threads. They are
- sufficently equal. Please do not commit my change to
- glib-1-2/gthread/gthread-posix.c from 1999-03-03, as the current
- change will take care of that too.
-
-1999-03-03 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c: Fixed broken mutex_trylock and slightly broken
- cond_timed_wait functions.
-
-1999-02-15 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * testgthread.c (test_mutexes): Use new signature of
- g_static_mutex* functions.
-
-1999-02-08 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * gthread-posix.c (g_private_get_posix_impl): Use the
- HAVE_PTHREAD_GETSPECIFIC_POSIX macro to determine, which signature
- to use for pthread_getspecific.
-
-Tue Jan 19 20:56:02 1999 Tor Lillqvist <tml@iki.fi>
-
- * Makefile.am (EXTRA_DIST): Added gthread.def.
-
-Sun Jan 17 10:58:19 1999 Tor Lillqvist <tml@iki.fi>
-
- * gthread.def: New file.
-
-1999-01-16 1999 Tor Lillqvist <tml@iki.fi>
-
- * gthread-posix.c: Conditionalize <sys/time.h> inclusion.
-
-1999-01-07 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * testgthread.c: conditionally compile according to the
- G_THREADS_IMPL_??? macros.
- (test_private_func): use rand_r instead of rand to make it
- thread safe.
-
-1998-12-18 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * testgthread.c (new_thread): As a joinable thread seems to be the
- default on posix, leave the explicit setting out, as it causes
- problems on some older platforms.
-
-Wed Dec 16 22:21:33 CST 1998 Shawn T. Amundson <amundson@gtk.org>
-
- * gthread-posix.c: use g_free in mutex_free (from Tim Janik)
-
-Thu Dec 17 03:38:57 1998 Tim Janik <timj@gtk.org>
-
- * Makefile.am: -DG_LOG_DOMAIN="GThread", we don't need an extern
- variable for that (noticed by Joel Becker <jlbec@ocala.cs.miami.edu>)
-
-Wed Dec 16 03:16:16 1998 Tim Janik <timj@gtk.org>
-
- * testgthread.c: s/g_thread_supported/g_thread_supported ()/
- * gthread.c: s/g_thread_supported/g_threads_got_initialized/
- (g_thread_init): bail out if G_THREADS_ENABLED is not defined.
-
-1998-12-15 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
-
- * Makefile.am (EXTRA_DIST): updated.
-
- * testgthread.c, gthread-*.c: Changed private to private_key to
- avoid problems when compiling with under C++.
-
- * gthread-none.c:
- s/g_mutex_functions_for_glib_use/g_thread_functions_for_glib_use/
-
- * ChangeLog: from now on there is an extra ChangeLog for gthread
-
-
+++ /dev/null
-## Process this file with automake to produce Makefile.in
-include $(top_srcdir)/Makefile.decl
-
-SUBDIRS = . tests
-DIST_SUBDIRS = tests
-
-AM_CPPFLAGS = \
- $(glib_INCLUDES) \
- -DG_LOG_DOMAIN=\"GThread\" \
- @GTHREAD_COMPILE_IMPL_DEFINES@ \
- @GLIB_DEBUG_FLAGS@ \
- -DG_DISABLE_DEPRECATED
-
-EXTRA_DIST += \
- makefile.msc.in \
- gthread-posix.c \
- gthread-win32.c \
- gthread-none.c \
- gthread.def \
- gthread.rc.in
-
-BUILT_EXTRA_DIST = \
- makefile.msc \
- gthread.rc
-
-libglib = $(top_builddir)/glib/libglib-2.0.la
-
-top_builddir_full=`cd \$(top_builddir); pwd`
-
-lib_LTLIBRARIES = libgthread-2.0.la
-
-if OS_WIN32_AND_DLL_COMPILATION
-if MS_LIB_AVAILABLE
-noinst_DATA = gthread-2.0.lib
-
-install_ms_lib_cmd = $(INSTALL) gthread-2.0.lib $(DESTDIR)$(libdir)
-uninstall_ms_lib_cmd = -rm $(DESTDIR)$(libdir)/gthread-2.0.lib
-endif
-endif
-
-install-ms-lib:
- $(install_ms_lib_cmd)
-
-uninstall-ms-lib:
- $(uninstall_ms_lib_cmd)
-
-if PLATFORM_WIN32
-no_undefined = -no-undefined
-endif
-
-if OS_WIN32_AND_DLL_COMPILATION
-export_symbols = -export-symbols $(srcdir)/gthread.def
-gthread_def = gthread.def
-
-install-def-file:
- $(INSTALL) $(srcdir)/gthread.def $(DESTDIR)$(libdir)/gthread-2.0.def
-
-uninstall-def-file:
- -rm $(DESTDIR)$(libdir)/gthread-2.0.def
-else
-install-def-file:
-uninstall-def-file:
-endif
-
-if OS_WIN32_AND_DLL_COMPILATION
-gthread_win32_res = gthread-win32-res.o
-gthread_win32_res_ldflag = -Wl,$(gthread_win32_res)
-endif
-
-libgthread_2_0_la_SOURCES = gthread-impl.c
-libgthread_2_0_la_LDFLAGS = $(GLIB_LINK_FLAGS) \
- $(gthread_win32_res_ldflag) \
- -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
- -export-dynamic $(no_undefined) $(export_symbols)
-
-libgthread_2_0_la_LIBADD = $(G_THREAD_LIBS_EXTRA) $(G_THREAD_LIBS_FOR_GTHREAD) $(libglib)
-
-libgthread_2_0_la_DEPENDENCIES = $(gthread_win32_res) $(gthread_def)
-
-gthread-win32-res.o: gthread.rc
- $(AM_V_GEN) $(WINDRES) gthread.rc $@
-
-gthread-2.0.lib: libgthread-2.0.la gthread.def
- lib -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgthread-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:$(srcdir)/gthread.def -out:$@
-
-dist-hook: $(BUILT_EXTRA_DIST)
- files='$(BUILT_EXTRA_DIST)'; \
- for f in $$files; do \
- if test -f $$f; then d=.; else d=$(srcdir); fi; \
- cp $$d/$$f $(distdir) || exit 1; done
-
-install-data-local: install-ms-lib install-def-file
-
-uninstall-local: uninstall-ms-lib uninstall-def-file
-
-if HAVE_GLIB_RUNTIME_LIBDIR
-install-data-hook:
- mkdir -p $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
- mv $(DESTDIR)$(libdir)/libgthread-2.0.so.0 $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
- mv $(DESTDIR)$(libdir)/libgthread-2.0.so.0.$(LT_CURRENT).$(LT_REVISION) $(DESTDIR)$(libdir)/$(GLIB_RUNTIME_LIBDIR)
- rm -f $(DESTDIR)$(libdir)/libgthread-2.0.so
- ln -s $(GLIB_RUNTIME_LIBDIR)/libgthread-2.0.so.0.$(LT_CURRENT).$(LT_REVISION) $(DESTDIR)$(libdir)/libgthread-2.0.so
-endif
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gthread.c: thread related functions
- * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "glib.h"
-#include "gthreadprivate.h"
-
-#ifdef G_THREADS_ENABLED
-
-static GSystemThread zero_thread; /* This is initialized to all zero */
-static gboolean thread_system_already_initialized = FALSE;
-static gint g_thread_priority_map [G_THREAD_PRIORITY_URGENT + 1];
-
-#include G_THREAD_SOURCE
-
-#ifndef PRIORITY_LOW_VALUE
-# define PRIORITY_LOW_VALUE 0
-#endif
-
-#ifndef PRIORITY_URGENT_VALUE
-# define PRIORITY_URGENT_VALUE 0
-#endif
-
-#ifndef PRIORITY_NORMAL_VALUE
-# define PRIORITY_NORMAL_VALUE \
- ((PRIORITY_LOW_VALUE * 6 + PRIORITY_URGENT_VALUE * 4) / 10)
-#endif /* PRIORITY_NORMAL_VALUE */
-
-#ifndef PRIORITY_HIGH_VALUE
-# define PRIORITY_HIGH_VALUE \
- ((PRIORITY_NORMAL_VALUE + PRIORITY_URGENT_VALUE * 2) / 3)
-#endif /* PRIORITY_HIGH_VALUE */
-
-void g_mutex_init (void);
-void g_mem_init (void);
-void g_messages_init (void);
-void g_convert_init (void);
-void g_rand_init (void);
-void g_main_thread_init (void);
-
-typedef struct _GMutexDebugInfo GMutexDebugInfo;
-struct _GMutexDebugInfo
-{
- gchar *location;
- GSystemThread owner;
-};
-
-#define G_MUTEX_DEBUG_INFO(mutex) \
- (((GMutexDebugInfo*)(((char*)mutex)+G_MUTEX_SIZE)))
-
-static GMutex *
-g_mutex_new_errorcheck_impl (void)
-{
- GMutex *retval = g_thread_functions_for_glib_use_default.mutex_new ();
- GMutexDebugInfo *info;
- retval = g_realloc (retval, G_MUTEX_SIZE + sizeof (GMutexDebugInfo));
-
- info = G_MUTEX_DEBUG_INFO (retval);
- g_system_thread_assign (info->owner, zero_thread);
- info->location = "invalid";
-
- return retval;
-}
-
-static void
-g_mutex_lock_errorcheck_impl (GMutex *mutex,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
-
- GSystemThread self;
- g_thread_functions_for_glib_use.thread_self (&self);
-
- if (g_system_thread_equal (info->owner, self))
- g_error ("Trying to recursively lock a mutex at '%s', "
- "previously locked at '%s'",
- loc, info->location);
-
- g_thread_functions_for_glib_use_default.mutex_lock (mutex);
-
- g_system_thread_assign (info->owner, self);
- info->location = loc;
-}
-
-static gboolean
-g_mutex_trylock_errorcheck_impl (GMutex *mutex,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
-
- GSystemThread self;
- g_thread_functions_for_glib_use.thread_self (&self);
-
- if (g_system_thread_equal (info->owner, self))
- g_error ("Trying to recursivly lock a mutex at '%s', "
- "previously locked at '%s'",
- loc, info->location);
-
- if (!g_thread_functions_for_glib_use_default.mutex_trylock (mutex))
- return FALSE;
-
- g_system_thread_assign (info->owner, self);
- info->location = loc;
-
- return TRUE;
-}
-
-static void
-g_mutex_unlock_errorcheck_impl (GMutex *mutex,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
-
- GSystemThread self;
- g_thread_functions_for_glib_use.thread_self (&self);
-
- if (g_system_thread_equal (info->owner, zero_thread))
- g_error ("Trying to unlock an unlocked mutex at '%s'", loc);
-
- if (!g_system_thread_equal (info->owner, self))
- g_warning ("Trying to unlock a mutex at '%s', "
- "previously locked by a different thread at '%s'",
- loc, info->location);
-
- g_system_thread_assign (info->owner, zero_thread);
- info->location = NULL;
-
- g_thread_functions_for_glib_use_default.mutex_unlock (mutex);
-}
-
-static void
-g_mutex_free_errorcheck_impl (GMutex *mutex,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
-
- if (info && !g_system_thread_equal (info->owner, zero_thread))
- g_error ("Trying to free a locked mutex at '%s', "
- "which was previously locked at '%s'",
- loc, info->location);
-
- g_thread_functions_for_glib_use_default.mutex_free (mutex);
-}
-
-static void
-g_cond_wait_errorcheck_impl (GCond *cond,
- GMutex *mutex,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
-
- GSystemThread self;
- g_thread_functions_for_glib_use.thread_self (&self);
-
- if (g_system_thread_equal (info->owner, zero_thread))
- g_error ("Trying to use an unlocked mutex in g_cond_wait() at '%s'", loc);
-
- if (!g_system_thread_equal (info->owner, self))
- g_error ("Trying to use a mutex locked by another thread in "
- "g_cond_wait() at '%s'", loc);
-
- g_system_thread_assign (info->owner, zero_thread);
- loc = info->location;
-
- g_thread_functions_for_glib_use_default.cond_wait (cond, mutex);
-
- g_system_thread_assign (info->owner, self);
- info->location = loc;
-}
-
-
-static gboolean
-g_cond_timed_wait_errorcheck_impl (GCond *cond,
- GMutex *mutex,
- GTimeVal *end_time,
- const gulong magic,
- gchar * const location)
-{
- GMutexDebugInfo *info = G_MUTEX_DEBUG_INFO (mutex);
- gchar *loc = (magic == G_MUTEX_DEBUG_MAGIC) ? location : "unknown";
- gboolean retval;
-
- GSystemThread self;
- g_thread_functions_for_glib_use.thread_self (&self);
-
- if (g_system_thread_equal (info->owner, zero_thread))
- g_error ("Trying to use an unlocked mutex in g_cond_timed_wait() at '%s'",
- loc);
-
- if (!g_system_thread_equal (info->owner, self))
- g_error ("Trying to use a mutex locked by another thread in "
- "g_cond_timed_wait() at '%s'", loc);
-
- g_system_thread_assign (info->owner, zero_thread);
- loc = info->location;
-
- retval = g_thread_functions_for_glib_use_default.cond_timed_wait (cond,
- mutex,
- end_time);
-
- g_system_thread_assign (info->owner, self);
- info->location = loc;
-
- return retval;
-}
-
-
-/* unshadow function declaration. See gthread.h */
-#undef g_thread_init
-
-void
-g_thread_init_with_errorcheck_mutexes (GThreadFunctions* init)
-{
- GThreadFunctions errorcheck_functions;
- if (init)
- g_error ("Errorcheck mutexes can only be used for native "
- "thread implementations. Sorry." );
-
-#ifdef HAVE_G_THREAD_IMPL_INIT
- /* This isn't called in g_thread_init, as it doesn't think to get
- * the default implementation, so we have to call it on our own.
- *
- * We must call this before copying
- * g_thread_functions_for_glib_use_default as the
- * implementation-specific init function might modify the contents
- * of g_thread_functions_for_glib_use_default based on operating
- * system version, C library version, or whatever. */
- g_thread_impl_init();
-#endif /* HAVE_G_THREAD_IMPL_INIT */
-
- errorcheck_functions = g_thread_functions_for_glib_use_default;
- errorcheck_functions.mutex_new = g_mutex_new_errorcheck_impl;
- errorcheck_functions.mutex_lock =
- (void (*)(GMutex *)) g_mutex_lock_errorcheck_impl;
- errorcheck_functions.mutex_trylock =
- (gboolean (*)(GMutex *)) g_mutex_trylock_errorcheck_impl;
- errorcheck_functions.mutex_unlock =
- (void (*)(GMutex *)) g_mutex_unlock_errorcheck_impl;
- errorcheck_functions.mutex_free =
- (void (*)(GMutex *)) g_mutex_free_errorcheck_impl;
- errorcheck_functions.cond_wait =
- (void (*)(GCond *, GMutex *)) g_cond_wait_errorcheck_impl;
- errorcheck_functions.cond_timed_wait =
- (gboolean (*)(GCond *, GMutex *, GTimeVal *))
- g_cond_timed_wait_errorcheck_impl;
-
- g_thread_init (&errorcheck_functions);
-}
-
-void
-g_thread_init (GThreadFunctions* init)
-{
- gboolean supported;
-
- if (thread_system_already_initialized)
- {
- if (init != NULL)
- g_warning ("GThread system already initialized, ignoring custom thread implementation.");
-
- return;
- }
-
- thread_system_already_initialized = TRUE;
-
- if (init == NULL)
- {
-#ifdef HAVE_G_THREAD_IMPL_INIT
- /* now do any initialization stuff required by the
- * implementation, but only if called with a NULL argument, of
- * course. Otherwise it's up to the user to do so. */
- g_thread_impl_init();
-#endif /* HAVE_G_THREAD_IMPL_INIT */
- init = &g_thread_functions_for_glib_use_default;
- }
- else
- g_thread_use_default_impl = FALSE;
-
- g_thread_functions_for_glib_use = *init;
- if (g_thread_gettime_impl)
- g_thread_gettime = g_thread_gettime_impl;
-
- supported = (init->mutex_new &&
- init->mutex_lock &&
- init->mutex_trylock &&
- init->mutex_unlock &&
- init->mutex_free &&
- init->cond_new &&
- init->cond_signal &&
- init->cond_broadcast &&
- init->cond_wait &&
- init->cond_timed_wait &&
- init->cond_free &&
- init->private_new &&
- init->private_get &&
- init->private_set &&
- init->thread_create &&
- init->thread_yield &&
- init->thread_join &&
- init->thread_exit &&
- init->thread_set_priority &&
- init->thread_self);
-
- /* if somebody is calling g_thread_init (), it means that he wants to
- * have thread support, so check this
- */
- if (!supported)
- {
- if (g_thread_use_default_impl)
- g_error ("Threads are not supported on this platform.");
- else
- g_error ("The supplied thread function vector is invalid.");
- }
-
- g_thread_priority_map [G_THREAD_PRIORITY_LOW] = PRIORITY_LOW_VALUE;
- g_thread_priority_map [G_THREAD_PRIORITY_NORMAL] = PRIORITY_NORMAL_VALUE;
- g_thread_priority_map [G_THREAD_PRIORITY_HIGH] = PRIORITY_HIGH_VALUE;
- g_thread_priority_map [G_THREAD_PRIORITY_URGENT] = PRIORITY_URGENT_VALUE;
-
- g_thread_init_glib ();
-}
-
-#else /* !G_THREADS_ENABLED */
-
-void
-g_thread_init (GThreadFunctions* init)
-{
- g_error ("GLib thread support is disabled.");
-}
-
-void
-g_thread_init_with_errorcheck_mutexes (GThreadFunctions* init)
-{
- g_error ("GLib thread support is disabled.");
-}
-
-#endif /* !G_THREADS_ENABLED */
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gthread.c: fallback thread system implementation
- * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-static GThreadFunctions
-g_thread_functions_for_glib_use_default; /* is NULLified */
-
-static guint64 (*g_thread_gettime_impl) (void) = NULL;
-
-#define G_MUTEX_SIZE 0
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gthread.c: posix thread system implementation
- * Copyright 1998 Sebastian Wilhelmi; University of Karlsruhe
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include <pthread.h>
-#include <errno.h>
-#include <stdlib.h>
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#ifdef HAVE_SCHED_H
-#include <sched.h>
-#endif
-
-#define posix_check_err(err, name) G_STMT_START{ \
- int error = (err); \
- if (error) \
- g_error ("file %s: line %d (%s): error '%s' during '%s'", \
- __FILE__, __LINE__, G_STRFUNC, \
- g_strerror (error), name); \
- }G_STMT_END
-
-#define posix_check_cmd(cmd) posix_check_err (posix_error (cmd), #cmd)
-
-#ifdef G_ENABLE_DEBUG
-static gboolean posix_check_cmd_prio_warned = FALSE;
-# define posix_check_cmd_prio(cmd) G_STMT_START{ \
- int err = posix_error (cmd); \
- if (err == EPERM) \
- { \
- if (!posix_check_cmd_prio_warned) \
- { \
- posix_check_cmd_prio_warned = TRUE; \
- g_warning ("Priorities can only be changed " \
- "(resp. increased) by root."); \
- } \
- } \
- else \
- posix_check_err (err, #cmd); \
- }G_STMT_END
-#else /* G_ENABLE_DEBUG */
-# define posix_check_cmd_prio(cmd) G_STMT_START{ \
- int err = posix_error (cmd); \
- if (err != EPERM) \
- posix_check_err (err, #cmd); \
- }G_STMT_END
-#endif /* G_ENABLE_DEBUG */
-
-#if defined(G_THREADS_IMPL_POSIX)
-# define posix_error(what) (what)
-# define mutexattr_default NULL
-# define condattr_default NULL
-#elif defined(G_THREADS_IMPL_DCE)
-# define posix_error(what) ((what) == -1 ? errno : 0)
-# define pthread_key_create(a, b) pthread_keycreate (a, b)
-# define pthread_attr_init(a) pthread_attr_create (a)
-# define pthread_attr_destroy(a) pthread_attr_delete (a)
-# define pthread_create(a, b, c, d) pthread_create (a, *b, c, d)
-# define mutexattr_default (pthread_mutexattr_default)
-# define condattr_default (pthread_condattr_default)
-#else /* neither G_THREADS_IMPL_POSIX nor G_THREADS_IMPL_DCE are defined */
-# error This should not happen. Contact the GLib team.
-#endif
-
-#if defined (POSIX_MIN_PRIORITY) && defined (POSIX_MAX_PRIORITY)
-# define HAVE_PRIORITIES 1
-static gint priority_normal_value;
-# ifdef __FreeBSD__
- /* FreeBSD threads use different priority values from the POSIX_
- * defines so we just set them here. The corresponding macros
- * PTHREAD_MIN_PRIORITY and PTHREAD_MAX_PRIORITY are implied to be
- * exported by the docs, but they aren't.
- */
-# define PRIORITY_LOW_VALUE 0
-# define PRIORITY_URGENT_VALUE 31
-# else /* !__FreeBSD__ */
-# define PRIORITY_LOW_VALUE POSIX_MIN_PRIORITY
-# define PRIORITY_URGENT_VALUE POSIX_MAX_PRIORITY
-# endif /* !__FreeBSD__ */
-# define PRIORITY_NORMAL_VALUE priority_normal_value
-#endif /* POSIX_MIN_PRIORITY && POSIX_MAX_PRIORITY */
-
-static gulong g_thread_min_stack_size = 0;
-
-#define G_MUTEX_SIZE (sizeof (pthread_mutex_t))
-
-#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_MONOTONIC_CLOCK)
-#define USE_CLOCK_GETTIME 1
-static gint posix_clock = 0;
-#endif
-
-#if defined(_SC_THREAD_STACK_MIN) || defined (HAVE_PRIORITIES) || defined (USE_CLOCK_GETTIME)
-#define HAVE_G_THREAD_IMPL_INIT
-static void
-g_thread_impl_init(void)
-{
-#ifdef _SC_THREAD_STACK_MIN
- g_thread_min_stack_size = MAX (sysconf (_SC_THREAD_STACK_MIN), 0);
-#endif /* _SC_THREAD_STACK_MIN */
-#ifdef HAVE_PRIORITIES
-# ifdef G_THREADS_IMPL_POSIX
- {
- struct sched_param sched;
- int policy;
- posix_check_cmd (pthread_getschedparam (pthread_self(), &policy, &sched));
- priority_normal_value = sched.sched_priority;
- }
-# else /* G_THREADS_IMPL_DCE */
- posix_check_cmd (priority_normal_value =
- pthread_getprio (*(pthread_t*)thread,
- g_thread_priority_map [priority]));
-# endif
-#endif /* HAVE_PRIORITIES */
-
-#ifdef USE_CLOCK_GETTIME
- if (sysconf (_SC_MONOTONIC_CLOCK) >= 0)
- posix_clock = CLOCK_MONOTONIC;
- else
- posix_clock = CLOCK_REALTIME;
-#endif
-}
-#endif /* _SC_THREAD_STACK_MIN || HAVE_PRIORITIES */
-
-static GMutex *
-g_mutex_new_posix_impl (void)
-{
- GMutex *result = (GMutex *) g_new (pthread_mutex_t, 1);
- posix_check_cmd (pthread_mutex_init ((pthread_mutex_t *) result,
- mutexattr_default));
- return result;
-}
-
-static void
-g_mutex_free_posix_impl (GMutex * mutex)
-{
- posix_check_cmd (pthread_mutex_destroy ((pthread_mutex_t *) mutex));
- g_free (mutex);
-}
-
-/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use
- functions from gmem.c and gmessages.c; */
-
-/* pthread_mutex_lock, pthread_mutex_unlock can be taken directly, as
- signature and semantic are right, but without error check then!!!!,
- we might want to change this therefore. */
-
-static gboolean
-g_mutex_trylock_posix_impl (GMutex * mutex)
-{
- int result;
-
- result = pthread_mutex_trylock ((pthread_mutex_t *) mutex);
-
-#ifdef G_THREADS_IMPL_POSIX
- if (result == EBUSY)
- return FALSE;
-#else /* G_THREADS_IMPL_DCE */
- if (result == 0)
- return FALSE;
-#endif
-
- posix_check_err (posix_error (result), "pthread_mutex_trylock");
- return TRUE;
-}
-
-static GCond *
-g_cond_new_posix_impl (void)
-{
- GCond *result = (GCond *) g_new (pthread_cond_t, 1);
- posix_check_cmd (pthread_cond_init ((pthread_cond_t *) result,
- condattr_default));
- return result;
-}
-
-/* pthread_cond_signal, pthread_cond_broadcast and pthread_cond_wait
- can be taken directly, as signature and semantic are right, but
- without error check then!!!!, we might want to change this
- therefore. */
-
-#define G_NSEC_PER_SEC 1000000000
-
-static gboolean
-g_cond_timed_wait_posix_impl (GCond * cond,
- GMutex * entered_mutex,
- GTimeVal * abs_time)
-{
- int result;
- struct timespec end_time;
- gboolean timed_out;
-
- g_return_val_if_fail (cond != NULL, FALSE);
- g_return_val_if_fail (entered_mutex != NULL, FALSE);
-
- if (!abs_time)
- {
- result = pthread_cond_wait ((pthread_cond_t *)cond,
- (pthread_mutex_t *) entered_mutex);
- timed_out = FALSE;
- }
- else
- {
- end_time.tv_sec = abs_time->tv_sec;
- end_time.tv_nsec = abs_time->tv_usec * (G_NSEC_PER_SEC / G_USEC_PER_SEC);
-
- g_return_val_if_fail (end_time.tv_nsec < G_NSEC_PER_SEC, TRUE);
-
- result = pthread_cond_timedwait ((pthread_cond_t *) cond,
- (pthread_mutex_t *) entered_mutex,
- &end_time);
-#ifdef G_THREADS_IMPL_POSIX
- timed_out = (result == ETIMEDOUT);
-#else /* G_THREADS_IMPL_DCE */
- timed_out = (result == -1) && (errno == EAGAIN);
-#endif
- }
-
- if (!timed_out)
- posix_check_err (posix_error (result), "pthread_cond_timedwait");
-
- return !timed_out;
-}
-
-static void
-g_cond_free_posix_impl (GCond * cond)
-{
- posix_check_cmd (pthread_cond_destroy ((pthread_cond_t *) cond));
- g_free (cond);
-}
-
-static GPrivate *
-g_private_new_posix_impl (GDestroyNotify destructor)
-{
- GPrivate *result = (GPrivate *) g_new (pthread_key_t, 1);
- posix_check_cmd (pthread_key_create ((pthread_key_t *) result, destructor));
- return result;
-}
-
-/* NOTE: the functions g_private_get and g_private_set may not use
- functions from gmem.c and gmessages.c */
-
-static void
-g_private_set_posix_impl (GPrivate * private_key, gpointer value)
-{
- if (!private_key)
- return;
- pthread_setspecific (*(pthread_key_t *) private_key, value);
-}
-
-static gpointer
-g_private_get_posix_impl (GPrivate * private_key)
-{
- if (!private_key)
- return NULL;
-#ifdef G_THREADS_IMPL_POSIX
- return pthread_getspecific (*(pthread_key_t *) private_key);
-#else /* G_THREADS_IMPL_DCE */
- {
- void* data;
- posix_check_cmd (pthread_getspecific (*(pthread_key_t *) private_key,
- &data));
- return data;
- }
-#endif
-}
-
-static void
-g_thread_create_posix_impl (GThreadFunc thread_func,
- gpointer arg,
- gulong stack_size,
- gboolean joinable,
- gboolean bound,
- GThreadPriority priority,
- gpointer thread,
- GError **error)
-{
- pthread_attr_t attr;
- gint ret;
-
- g_return_if_fail (thread_func);
- g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
- g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
-
- posix_check_cmd (pthread_attr_init (&attr));
-
-#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE
- if (stack_size)
- {
- stack_size = MAX (g_thread_min_stack_size, stack_size);
- /* No error check here, because some systems can't do it and
- * we simply don't want threads to fail because of that. */
- pthread_attr_setstacksize (&attr, stack_size);
- }
-#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */
-
-#ifdef PTHREAD_SCOPE_SYSTEM
- if (bound)
- /* No error check here, because some systems can't do it and we
- * simply don't want threads to fail because of that. */
- pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
-#endif /* PTHREAD_SCOPE_SYSTEM */
-
-#ifdef G_THREADS_IMPL_POSIX
- posix_check_cmd (pthread_attr_setdetachstate (&attr,
- joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED));
-#endif /* G_THREADS_IMPL_POSIX */
-
-#ifdef HAVE_PRIORITIES
-# ifdef G_THREADS_IMPL_POSIX
- {
- struct sched_param sched;
- posix_check_cmd (pthread_attr_getschedparam (&attr, &sched));
- sched.sched_priority = g_thread_priority_map [priority];
- posix_check_cmd_prio (pthread_attr_setschedparam (&attr, &sched));
- }
-# else /* G_THREADS_IMPL_DCE */
- posix_check_cmd_prio
- (pthread_attr_setprio (&attr, g_thread_priority_map [priority]));
-# endif /* G_THREADS_IMPL_DCE */
-#endif /* HAVE_PRIORITIES */
- ret = posix_error (pthread_create (thread, &attr,
- (void* (*)(void*))thread_func, arg));
-
- posix_check_cmd (pthread_attr_destroy (&attr));
-
- if (ret == EAGAIN)
- {
- g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
- "Error creating thread: %s", g_strerror (ret));
- return;
- }
-
- posix_check_err (ret, "pthread_create");
-
-#ifdef G_THREADS_IMPL_DCE
- if (!joinable)
- posix_check_cmd (pthread_detach (thread));
-#endif /* G_THREADS_IMPL_DCE */
-}
-
-static void
-g_thread_yield_posix_impl (void)
-{
- POSIX_YIELD_FUNC;
-}
-
-static void
-g_thread_join_posix_impl (gpointer thread)
-{
- gpointer ignore;
- posix_check_cmd (pthread_join (*(pthread_t*)thread, &ignore));
-}
-
-static void
-g_thread_exit_posix_impl (void)
-{
- pthread_exit (NULL);
-}
-
-static void
-g_thread_set_priority_posix_impl (gpointer thread, GThreadPriority priority)
-{
- g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
- g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
-#ifdef HAVE_PRIORITIES
-# ifdef G_THREADS_IMPL_POSIX
- {
- struct sched_param sched;
- int policy;
- posix_check_cmd (pthread_getschedparam (*(pthread_t*)thread, &policy,
- &sched));
- sched.sched_priority = g_thread_priority_map [priority];
- posix_check_cmd_prio (pthread_setschedparam (*(pthread_t*)thread, policy,
- &sched));
- }
-# else /* G_THREADS_IMPL_DCE */
- posix_check_cmd_prio (pthread_setprio (*(pthread_t*)thread,
- g_thread_priority_map [priority]));
-# endif
-#endif /* HAVE_PRIORITIES */
-}
-
-static void
-g_thread_self_posix_impl (gpointer thread)
-{
- *(pthread_t*)thread = pthread_self();
-}
-
-static gboolean
-g_thread_equal_posix_impl (gpointer thread1, gpointer thread2)
-{
- return (pthread_equal (*(pthread_t*)thread1, *(pthread_t*)thread2) != 0);
-}
-
-#ifdef USE_CLOCK_GETTIME
-static guint64
-gettime (void)
-{
- struct timespec tv;
-
- clock_gettime (posix_clock, &tv);
-
- return (guint64) tv.tv_sec * G_NSEC_PER_SEC + tv.tv_nsec;
-}
-static guint64 (*g_thread_gettime_impl)(void) = gettime;
-#else
-static guint64 (*g_thread_gettime_impl)(void) = 0;
-#endif
-
-static GThreadFunctions g_thread_functions_for_glib_use_default =
-{
- g_mutex_new_posix_impl,
- (void (*)(GMutex *)) pthread_mutex_lock,
- g_mutex_trylock_posix_impl,
- (void (*)(GMutex *)) pthread_mutex_unlock,
- g_mutex_free_posix_impl,
- g_cond_new_posix_impl,
- (void (*)(GCond *)) pthread_cond_signal,
- (void (*)(GCond *)) pthread_cond_broadcast,
- (void (*)(GCond *, GMutex *)) pthread_cond_wait,
- g_cond_timed_wait_posix_impl,
- g_cond_free_posix_impl,
- g_private_new_posix_impl,
- g_private_get_posix_impl,
- g_private_set_posix_impl,
- g_thread_create_posix_impl,
- g_thread_yield_posix_impl,
- g_thread_join_posix_impl,
- g_thread_exit_posix_impl,
- g_thread_set_priority_posix_impl,
- g_thread_self_posix_impl,
- g_thread_equal_posix_impl
-};
+++ /dev/null
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * gthread.c: solaris thread system implementation
- * Copyright 1998-2001 Sebastian Wilhelmi; University of Karlsruhe
- * Copyright 2001 Hans Breuer
- *
- * 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 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.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000. See the AUTHORS
- * file for a list of people on the GLib Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/*
- * MT safe
- */
-
-#include "config.h"
-
-#include "glib.h"
-
-#define STRICT
-#define _WIN32_WINDOWS 0x0401 /* to get IsDebuggerPresent */
-#include <windows.h>
-#undef STRICT
-
-#include <process.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#define win32_check_for_error(what) G_STMT_START{ \
- if (!(what)) \
- g_error ("file %s: line %d (%s): error %s during %s", \
- __FILE__, __LINE__, G_STRFUNC, \
- g_win32_error_message (GetLastError ()), #what); \
- }G_STMT_END
-
-#define G_MUTEX_SIZE (sizeof (gpointer))
-
-#define PRIORITY_LOW_VALUE THREAD_PRIORITY_BELOW_NORMAL
-#define PRIORITY_NORMAL_VALUE THREAD_PRIORITY_NORMAL
-#define PRIORITY_HIGH_VALUE THREAD_PRIORITY_ABOVE_NORMAL
-#define PRIORITY_URGENT_VALUE THREAD_PRIORITY_HIGHEST
-
-static DWORD g_thread_self_tls;
-static DWORD g_private_tls;
-static DWORD g_cond_event_tls;
-static CRITICAL_SECTION g_thread_global_spinlock;
-
-typedef BOOL (__stdcall *GTryEnterCriticalSectionFunc) (CRITICAL_SECTION *);
-
-static GTryEnterCriticalSectionFunc try_enter_critical_section = NULL;
-
-/* As noted in the docs, GPrivate is a limited resource, here we take
- * a rather low maximum to save memory, use GStaticPrivate instead. */
-#define G_PRIVATE_MAX 100
-
-static GDestroyNotify g_private_destructors[G_PRIVATE_MAX];
-
-static guint g_private_next = 0;
-
-/* A "forward" declaration of this structure */
-static GThreadFunctions g_thread_functions_for_glib_use_default;
-
-typedef struct _GThreadData GThreadData;
-struct _GThreadData
-{
- GThreadFunc func;
- gpointer data;
- HANDLE thread;
- gboolean joinable;
-};
-
-struct _GCond
-{
- GPtrArray *array;
- CRITICAL_SECTION lock;
-};
-
-static GMutex *
-g_mutex_new_win32_cs_impl (void)
-{
- CRITICAL_SECTION *cs = g_new (CRITICAL_SECTION, 1);
- gpointer *retval = g_new (gpointer, 1);
-
- InitializeCriticalSection (cs);
- *retval = cs;
- return (GMutex *) retval;
-}
-
-static void
-g_mutex_free_win32_cs_impl (GMutex *mutex)
-{
- gpointer *ptr = (gpointer *) mutex;
- CRITICAL_SECTION *cs = (CRITICAL_SECTION *) *ptr;
-
- DeleteCriticalSection (cs);
- g_free (cs);
- g_free (mutex);
-}
-
-/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use
- functions from gmem.c and gmessages.c; */
-
-static void
-g_mutex_lock_win32_cs_impl (GMutex *mutex)
-{
- EnterCriticalSection (*(CRITICAL_SECTION **)mutex);
-}
-
-static gboolean
-g_mutex_trylock_win32_cs_impl (GMutex * mutex)
-{
- return try_enter_critical_section (*(CRITICAL_SECTION **)mutex);
-}
-
-static void
-g_mutex_unlock_win32_cs_impl (GMutex *mutex)
-{
- LeaveCriticalSection (*(CRITICAL_SECTION **)mutex);
-}
-
-static GMutex *
-g_mutex_new_win32_impl (void)
-{
- HANDLE handle;
- HANDLE *retval;
- win32_check_for_error (handle = CreateMutex (NULL, FALSE, NULL));
- retval = g_new (HANDLE, 1);
- *retval = handle;
- return (GMutex *) retval;
-}
-
-static void
-g_mutex_free_win32_impl (GMutex *mutex)
-{
- win32_check_for_error (CloseHandle (*(HANDLE *) mutex));
- g_free (mutex);
-}
-
-/* NOTE: the functions g_mutex_lock and g_mutex_unlock may not use
- functions from gmem.c and gmessages.c; */
-
-static void
-g_mutex_lock_win32_impl (GMutex *mutex)
-{
- WaitForSingleObject (*(HANDLE *) mutex, INFINITE);
-}
-
-static gboolean
-g_mutex_trylock_win32_impl (GMutex * mutex)
-{
- DWORD result;
- win32_check_for_error (WAIT_FAILED !=
- (result = WaitForSingleObject (*(HANDLE *)mutex, 0)));
- return result != WAIT_TIMEOUT;
-}
-
-static void
-g_mutex_unlock_win32_impl (GMutex *mutex)
-{
- ReleaseMutex (*(HANDLE *) mutex);
-}
-
-static GCond *
-g_cond_new_win32_impl (void)
-{
- GCond *retval = g_new (GCond, 1);
-
- retval->array = g_ptr_array_new ();
- InitializeCriticalSection (&retval->lock);
-
- return retval;
-}
-
-static void
-g_cond_signal_win32_impl (GCond * cond)
-{
- EnterCriticalSection (&cond->lock);
-
- if (cond->array->len > 0)
- {
- SetEvent (g_ptr_array_index (cond->array, 0));
- g_ptr_array_remove_index (cond->array, 0);
- }
-
- LeaveCriticalSection (&cond->lock);
-}
-
-static void
-g_cond_broadcast_win32_impl (GCond * cond)
-{
- guint i;
- EnterCriticalSection (&cond->lock);
-
- for (i = 0; i < cond->array->len; i++)
- SetEvent (g_ptr_array_index (cond->array, i));
-
- g_ptr_array_set_size (cond->array, 0);
- LeaveCriticalSection (&cond->lock);
-}
-
-static gboolean
-g_cond_wait_internal (GCond *cond,
- GMutex *entered_mutex,
- gulong milliseconds)
-{
- gulong retval;
- HANDLE event = TlsGetValue (g_cond_event_tls);
-
- if (!event)
- {
- win32_check_for_error (event = CreateEvent (0, FALSE, FALSE, NULL));
- TlsSetValue (g_cond_event_tls, event);
- }
-
- EnterCriticalSection (&cond->lock);
-
- /* The event must not be signaled. Check this */
- g_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT);
-
- g_ptr_array_add (cond->array, event);
- LeaveCriticalSection (&cond->lock);
-
- g_thread_functions_for_glib_use_default.mutex_unlock (entered_mutex);
-
- win32_check_for_error (WAIT_FAILED !=
- (retval = WaitForSingleObject (event, milliseconds)));
-
- g_thread_functions_for_glib_use_default.mutex_lock (entered_mutex);
-
- if (retval == WAIT_TIMEOUT)
- {
- EnterCriticalSection (&cond->lock);
- g_ptr_array_remove (cond->array, event);
-
- /* In the meantime we could have been signaled, so we must again
- * wait for the signal, this time with no timeout, to reset
- * it. retval is set again to honour the late arrival of the
- * signal */
- win32_check_for_error (WAIT_FAILED !=
- (retval = WaitForSingleObject (event, 0)));
-
- LeaveCriticalSection (&cond->lock);
- }
-
-#ifndef G_DISABLE_ASSERT
- EnterCriticalSection (&cond->lock);
-
- /* Now event must not be inside the array, check this */
- g_assert (g_ptr_array_remove (cond->array, event) == FALSE);
-
- LeaveCriticalSection (&cond->lock);
-#endif /* !G_DISABLE_ASSERT */
-
- return retval != WAIT_TIMEOUT;
-}
-
-static void
-g_cond_wait_win32_impl (GCond *cond,
- GMutex *entered_mutex)
-{
- g_return_if_fail (cond != NULL);
- g_return_if_fail (entered_mutex != NULL);
-
- g_cond_wait_internal (cond, entered_mutex, INFINITE);
-}
-
-static gboolean
-g_cond_timed_wait_win32_impl (GCond *cond,
- GMutex *entered_mutex,
- GTimeVal *abs_time)
-{
- GTimeVal current_time;
- gulong to_wait;
-
- g_return_val_if_fail (cond != NULL, FALSE);
- g_return_val_if_fail (entered_mutex != NULL, FALSE);
-
- if (!abs_time)
- to_wait = INFINITE;
- else
- {
- g_get_current_time (¤t_time);
- if (abs_time->tv_sec < current_time.tv_sec ||
- (abs_time->tv_sec == current_time.tv_sec &&
- abs_time->tv_usec <= current_time.tv_usec))
- to_wait = 0;
- else
- to_wait = (abs_time->tv_sec - current_time.tv_sec) * 1000 +
- (abs_time->tv_usec - current_time.tv_usec) / 1000;
- }
-
- return g_cond_wait_internal (cond, entered_mutex, to_wait);
-}
-
-static void
-g_cond_free_win32_impl (GCond * cond)
-{
- DeleteCriticalSection (&cond->lock);
- g_ptr_array_free (cond->array, TRUE);
- g_free (cond);
-}
-
-static GPrivate *
-g_private_new_win32_impl (GDestroyNotify destructor)
-{
- GPrivate *result;
- EnterCriticalSection (&g_thread_global_spinlock);
- if (g_private_next >= G_PRIVATE_MAX)
- {
- char buf[100];
- sprintf (buf,
- "Too many GPrivate allocated. Their number is limited to %d.",
- G_PRIVATE_MAX);
- MessageBox (NULL, buf, NULL, MB_ICONERROR|MB_SETFOREGROUND);
- if (IsDebuggerPresent ())
- G_BREAKPOINT ();
- abort ();
- }
- g_private_destructors[g_private_next] = destructor;
- result = GUINT_TO_POINTER (g_private_next);
- g_private_next++;
- LeaveCriticalSection (&g_thread_global_spinlock);
-
- return result;
-}
-
-/* NOTE: the functions g_private_get and g_private_set may not use
- functions from gmem.c and gmessages.c */
-
-static void
-g_private_set_win32_impl (GPrivate * private_key, gpointer value)
-{
- gpointer* array = TlsGetValue (g_private_tls);
- guint index = GPOINTER_TO_UINT (private_key);
-
- if (index >= G_PRIVATE_MAX)
- return;
-
- if (!array)
- {
- array = (gpointer*) calloc (G_PRIVATE_MAX, sizeof (gpointer));
- TlsSetValue (g_private_tls, array);
- }
-
- array[index] = value;
-}
-
-static gpointer
-g_private_get_win32_impl (GPrivate * private_key)
-{
- gpointer* array = TlsGetValue (g_private_tls);
- guint index = GPOINTER_TO_UINT (private_key);
-
- if (index >= G_PRIVATE_MAX || !array)
- return NULL;
-
- return array[index];
-}
-
-static void
-g_thread_set_priority_win32_impl (gpointer thread, GThreadPriority priority)
-{
- GThreadData *target = *(GThreadData **)thread;
-
- g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
- g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
-
- win32_check_for_error (SetThreadPriority (target->thread,
- g_thread_priority_map [priority]));
-}
-
-static void
-g_thread_self_win32_impl (gpointer thread)
-{
- GThreadData *self = TlsGetValue (g_thread_self_tls);
-
- if (!self)
- {
- /* This should only happen for the main thread! */
- HANDLE handle = GetCurrentThread ();
- HANDLE process = GetCurrentProcess ();
- self = g_new (GThreadData, 1);
- win32_check_for_error (DuplicateHandle (process, handle, process,
- &self->thread, 0, FALSE,
- DUPLICATE_SAME_ACCESS));
- win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
- self->func = NULL;
- self->data = NULL;
- self->joinable = FALSE;
- }
-
- *(GThreadData **)thread = self;
-}
-
-static void
-g_thread_exit_win32_impl (void)
-{
- GThreadData *self = TlsGetValue (g_thread_self_tls);
- guint i, private_max;
- gpointer *array = TlsGetValue (g_private_tls);
- HANDLE event = TlsGetValue (g_cond_event_tls);
-
- EnterCriticalSection (&g_thread_global_spinlock);
- private_max = g_private_next;
- LeaveCriticalSection (&g_thread_global_spinlock);
-
- if (array)
- {
- gboolean some_data_non_null;
-
- do {
- some_data_non_null = FALSE;
- for (i = 0; i < private_max; i++)
- {
- GDestroyNotify destructor = g_private_destructors[i];
- GDestroyNotify data = array[i];
-
- if (data)
- some_data_non_null = TRUE;
-
- array[i] = NULL;
-
- if (destructor && data)
- destructor (data);
- }
- } while (some_data_non_null);
-
- free (array);
-
- win32_check_for_error (TlsSetValue (g_private_tls, NULL));
- }
-
- if (self)
- {
- if (!self->joinable)
- {
- win32_check_for_error (CloseHandle (self->thread));
- g_free (self);
- }
- win32_check_for_error (TlsSetValue (g_thread_self_tls, NULL));
- }
-
- if (event)
- {
- CloseHandle (event);
- win32_check_for_error (TlsSetValue (g_cond_event_tls, NULL));
- }
-
- _endthreadex (0);
-}
-
-static guint __stdcall
-g_thread_proxy (gpointer data)
-{
- GThreadData *self = (GThreadData*) data;
-
- win32_check_for_error (TlsSetValue (g_thread_self_tls, self));
-
- self->func (self->data);
-
- g_thread_exit_win32_impl ();
-
- g_assert_not_reached ();
-
- return 0;
-}
-
-static void
-g_thread_create_win32_impl (GThreadFunc func,
- gpointer data,
- gulong stack_size,
- gboolean joinable,
- gboolean bound,
- GThreadPriority priority,
- gpointer thread,
- GError **error)
-{
- guint ignore;
- GThreadData *retval;
-
- g_return_if_fail (func);
- g_return_if_fail (priority >= G_THREAD_PRIORITY_LOW);
- g_return_if_fail (priority <= G_THREAD_PRIORITY_URGENT);
-
- retval = g_new(GThreadData, 1);
- retval->func = func;
- retval->data = data;
-
- retval->joinable = joinable;
-
- retval->thread = (HANDLE) _beginthreadex (NULL, stack_size, g_thread_proxy,
- retval, 0, &ignore);
-
- if (retval->thread == NULL)
- {
- gchar *win_error = g_win32_error_message (GetLastError ());
- g_set_error (error, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN,
- "Error creating thread: %s", win_error);
- g_free (retval);
- g_free (win_error);
- return;
- }
-
- *(GThreadData **)thread = retval;
-
- g_thread_set_priority_win32_impl (thread, priority);
-}
-
-static void
-g_thread_yield_win32_impl (void)
-{
- Sleep(0);
-}
-
-static void
-g_thread_join_win32_impl (gpointer thread)
-{
- GThreadData *target = *(GThreadData **)thread;
-
- g_return_if_fail (target->joinable);
-
- win32_check_for_error (WAIT_FAILED !=
- WaitForSingleObject (target->thread, INFINITE));
-
- win32_check_for_error (CloseHandle (target->thread));
- g_free (target);
-}
-
-static guint64
-g_thread_gettime_impl (void)
-{
- guint64 v;
-
- /* Returns 100s of nanoseconds since start of 1601 */
- GetSystemTimeAsFileTime ((FILETIME *)&v);
-
- /* Offset to Unix epoch */
- v -= G_GINT64_CONSTANT (116444736000000000);
- /* Convert to nanoseconds */
- v *= 100;
-
- return v;
-}
-
-static GThreadFunctions g_thread_functions_for_glib_use_default =
-{
- g_mutex_new_win32_impl, /* mutex */
- g_mutex_lock_win32_impl,
- g_mutex_trylock_win32_impl,
- g_mutex_unlock_win32_impl,
- g_mutex_free_win32_impl,
- g_cond_new_win32_impl, /* condition */
- g_cond_signal_win32_impl,
- g_cond_broadcast_win32_impl,
- g_cond_wait_win32_impl,
- g_cond_timed_wait_win32_impl,
- g_cond_free_win32_impl,
- g_private_new_win32_impl, /* private thread data */
- g_private_get_win32_impl,
- g_private_set_win32_impl,
- g_thread_create_win32_impl, /* thread */
- g_thread_yield_win32_impl,
- g_thread_join_win32_impl,
- g_thread_exit_win32_impl,
- g_thread_set_priority_win32_impl,
- g_thread_self_win32_impl,
- NULL /* no equal function necessary */
-};
-
-#define HAVE_G_THREAD_IMPL_INIT
-static void
-g_thread_impl_init ()
-{
- static gboolean beenhere = FALSE;
- HMODULE kernel32;
-
- if (beenhere)
- return;
-
- beenhere = TRUE;
-
- win32_check_for_error (TLS_OUT_OF_INDEXES !=
- (g_thread_self_tls = TlsAlloc ()));
- win32_check_for_error (TLS_OUT_OF_INDEXES !=
- (g_private_tls = TlsAlloc ()));
- win32_check_for_error (TLS_OUT_OF_INDEXES !=
- (g_cond_event_tls = TlsAlloc ()));
- InitializeCriticalSection (&g_thread_global_spinlock);
-
- /* Here we are looking for TryEnterCriticalSection in KERNEL32.DLL,
- * if it is found, we can use the in general faster critical
- * sections instead of mutexes. See
- * http://world.std.com/~jmhart/csmutx.htm for some discussion.
- */
- kernel32 = GetModuleHandle ("KERNEL32.DLL");
- if (kernel32)
- {
- try_enter_critical_section = (GTryEnterCriticalSectionFunc)
- GetProcAddress(kernel32, "TryEnterCriticalSection");
-
- /* Even if TryEnterCriticalSection is found, it is not
- * necessarily working..., we have to check it */
- if (try_enter_critical_section &&
- try_enter_critical_section (&g_thread_global_spinlock))
- {
- LeaveCriticalSection (&g_thread_global_spinlock);
-
- g_thread_functions_for_glib_use_default.mutex_new =
- g_mutex_new_win32_cs_impl;
- g_thread_functions_for_glib_use_default.mutex_lock =
- g_mutex_lock_win32_cs_impl;
- g_thread_functions_for_glib_use_default.mutex_trylock =
- g_mutex_trylock_win32_cs_impl;
- g_thread_functions_for_glib_use_default.mutex_unlock =
- g_mutex_unlock_win32_cs_impl;
- g_thread_functions_for_glib_use_default.mutex_free =
- g_mutex_free_win32_cs_impl;
- }
- }
-}
+++ /dev/null
-EXPORTS
- g_thread_init
- g_thread_init_with_errorcheck_mutexes
+++ /dev/null
-#include <winver.h>
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
- PRODUCTVERSION @GLIB_MAJOR_VERSION@,@GLIB_MINOR_VERSION@,@GLIB_MICRO_VERSION@,0
- FILEFLAGSMASK 0
- FILEFLAGS 0
- FILEOS VOS__WINDOWS32
- FILETYPE VFT_DLL
- FILESUBTYPE VFT2_UNKNOWN
- BEGIN
- BLOCK "StringFileInfo"
- BEGIN
- BLOCK "040904B0"
- BEGIN
- VALUE "CompanyName", "The GLib developer community"
- VALUE "FileDescription", "GThread"
- VALUE "FileVersion", "@GLIB_VERSION@.0"
- VALUE "InternalName", "libgthread-2.0-@LT_CURRENT_MINUS_AGE@"
- VALUE "LegalCopyright", "Copyright © 1995-2010 Peter Mattis, Spencer Kimball, Josh MacDonald, Sebastian Wilhelmi and others."
- VALUE "OriginalFilename", "libgthread-2.0-@LT_CURRENT_MINUS_AGE@.dll"
- VALUE "ProductName", "GLib"
- VALUE "ProductVersion", "@GLIB_VERSION@"
- END
- END
- BLOCK "VarFileInfo"
- BEGIN
- VALUE "Translation", 0x409, 1200
- END
- END
+++ /dev/null
-## Makefile for building the gthread dll with Microsoft C
-## Use: nmake -f makefile.msc install
-
-TOP = ..\..
-
-!INCLUDE ..\build\win32\make.msc
-
-################################################################
-
-INCLUDES = -FImsvc_recommended_pragmas.h -I .. -I . -I ..\glib
-DEFINES = -DHAVE_CONFIG_H -DG_LOG_DOMAIN=\"GThread\"
-all : \
- libgthread-2.0-@LT_CURRENT_MINUS_AGE@.dll
-
-gthread_OBJECTS = \
- gthread-impl.obj
-
-gthread-impl.obj : gthread-impl.c gthread-win32.c
- $(CC) -c $(CFLAGS) gthread-impl.c
-
-gthread.res : gthread.rc
- rc -DBUILDNUMBER=0 -r -fo gthread.res gthread.rc
-
-libgthread-2.0-@LT_CURRENT_MINUS_AGE@.dll : $(gthread_OBJECTS) gthread.def gthread.res
- $(CC) $(CFLAGS) -LD -Fe$@ $(gthread_OBJECTS) gthread.res \
- ..\glib\glib-2.0.lib $(DEPCLIBS) user32.lib $(LDFLAGS) /implib:gthread-2.0.lib /def:gthread.def
+++ /dev/null
-1bit-mutex
-1bit-emufutex
+++ /dev/null
-/*
- * Copyright © 2008 Ryan Lortie
- * Copyright © 2010 Codethink Limited
- *
- * This program 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 of the
- * License, or (at your option) any later version.
- *
- * See the included COPYING file for more information.
- */
-
-/* LOCKS should be more than the number of contention
- * counters in gthread.c in order to ensure we exercise
- * the case where they overlap.
- */
-#define LOCKS 48
-#define ITERATIONS 10000
-#define THREADS 100
-
-
-#if TEST_EMULATED_FUTEX
- /* this is defined for the 1bit-mutex-emufutex test.
- *
- * we want to test the emulated futex even if futex(2) is available.
- */
-
- /* side-step some glib build stuff */
- #ifndef DISABLE_VISIBILITY
- #define DISABLE_VISIBILITY
- #endif
- #define GLIB_COMPILATION
-
- /* rebuild gbitlock.c without futex support,
- defining our own version of the g_bit_*lock symbols
- */
- #define g_bit_lock _emufutex_g_bit_lock
- #define g_bit_trylock _emufutex_g_bit_trylock
- #define g_bit_unlock _emufutex_g_bit_unlock
- #define _g_futex_thread_init _emufutex_g_futex_thread_init
-
- #define G_BIT_LOCK_FORCE_FUTEX_EMULATION
-
- #include <glib/gbitlock.c>
-#endif
-
-#include <glib.h>
-
-volatile GThread *owners[LOCKS];
-volatile gint locks[LOCKS];
-volatile gint bits[LOCKS];
-
-static void
-acquire (int nr)
-{
- GThread *self;
-
- self = g_thread_self ();
-
- if (!g_bit_trylock (&locks[nr], bits[nr]))
- {
- if (g_test_verbose ())
- g_print ("thread %p going to block on lock %d\n", self, nr);
- g_bit_lock (&locks[nr], bits[nr]);
- }
-
- g_assert (owners[nr] == NULL); /* hopefully nobody else is here */
- owners[nr] = self;
-
- /* let some other threads try to ruin our day */
- g_thread_yield ();
- g_thread_yield ();
- g_thread_yield ();
-
- g_assert (owners[nr] == self); /* hopefully this is still us... */
- owners[nr] = NULL; /* make way for the next guy */
- g_bit_unlock (&locks[nr], bits[nr]);
-}
-
-static gpointer
-thread_func (gpointer data)
-{
- gint i;
-
- for (i = 0; i < ITERATIONS; i++)
- acquire (g_random_int () % LOCKS);
-
- return NULL;
-}
-
-static void
-testcase (void)
-{
- GThread *threads[THREADS];
- int i;
-
- g_thread_init (NULL);
-
-#ifdef TEST_EMULATED_FUTEX
- _g_futex_thread_init ();
- #define SUFFIX "-emufutex"
-
- /* ensure that we are using the emulated futex by checking
- * (at compile-time) for the existance of 'g_futex_mutex'
- */
- g_assert (g_futex_mutex != NULL);
-#else
- #define SUFFIX ""
-#endif
-
- for (i = 0; i < LOCKS; i++)
- bits[i] = g_random_int () % 32;
-
- for (i = 0; i < THREADS; i++)
- threads[i] = g_thread_create (thread_func, NULL, TRUE, NULL);
-
- for (i = 0; i < THREADS; i++)
- g_thread_join (threads[i]);
-
- for (i = 0; i < LOCKS; i++)
- {
- g_assert (owners[i] == NULL);
- g_assert (locks[i] == 0);
- }
-}
-
-int
-main (int argc, char **argv)
-{
- g_test_init (&argc, &argv, NULL);
-
- g_test_add_func ("/glib/1bit-mutex" SUFFIX, testcase);
-
- return g_test_run ();
-}
+++ /dev/null
-include $(top_srcdir)/Makefile.decl
-
-INCLUDES = -g $(gthread_INCLUDES) $(GLIB_DEBUG_FLAGS)
-
-noinst_PROGRAMS = $(TEST_PROGS)
-progs_ldadd = $(top_builddir)/glib/libglib-2.0.la \
- $(top_builddir)/gthread/libgthread-2.0.la
-
-TEST_PROGS += 1bit-mutex
-1bit_mutex_LDADD = $(progs_ldadd) $(top_builddir)/gthread/libgthread-2.0.la
-
-TEST_PROGS += 1bit-emufutex
-1bit_emufutex_SOURCES = 1bit-mutex.c
-1bit_emufutex_CFLAGS = -DTEST_EMULATED_FUTEX
-1bit_emufutex_LDADD = $(progs_ldadd) $(top_builddir)/gthread/libgthread-2.0.la
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+/**
+ * @def RS_IDENTITY
+ * @brief
+ */
+#define IDENTITY ("1111111111111111")
+/* @def RS_CLIENT_PSK
+ * @brief
+ */
+#define RS_CLIENT_PSK ("AAAAAAAAAAAAAAAA")
+
+int gReceived;
+CABool_t gLocalUnicastPort;
+CABool_t gLocalSecurePort;
+
void request_handler(const CARemoteEndpoint_t* object, const CARequestInfo_t* requestInfo);
void response_handler(const CARemoteEndpoint_t* object, const CAResponseInfo_t* responseInfo);
+void get_resource_uri(char *URI, char *resourceURI, int length);
+int get_secure_information(CAPayload_t payLoad);
+void send_response(CARemoteEndpoint_t* endpoint, CAInfo_t* info);
+CAConnectivityType_t get_network_type(int selectedNetwork);
+CAConnectivityType_t gSelectedNwType = CA_LE;
static CAToken_t gLastRequestToken = NULL;
+static const char *gSecureInfoData = "{\"oc\":[{\"href\":\"%s\",\"prop\":{\"rt\":[\"core.led\"],"
+ "\"if\":[\"oc.mi.def\"],\"obs\":1,\"sec\":1,\"port\":%d}}]}";
+static const char *gNormalInfoData = "{\"oc\":[{\"href\":\"%s\",\"prop\":{\"rt\":[\"core.led\"],"
+ "\"if\":[\"oc.mi.def\"],\"obs\":1}}]}";
+
+static CADtlsPskCredsBlob_t *pskCredsBlob = NULL;
+
+
+void clearDtlsCredentialInfo()
+{
+ printf("clearDtlsCredentialInfo IN\n");
+ if (pskCredsBlob)
+ {
+ // Initialize sensitive data to zeroes before freeing.
+ memset(pskCredsBlob->creds, 0, sizeof(CADtlsPskCreds_t)*(pskCredsBlob->num));
+ free(pskCredsBlob->creds);
+
+ memset(pskCredsBlob, 0, sizeof(CADtlsPskCredsBlob_t));
+ free(pskCredsBlob);
+ pskCredsBlob = NULL;
+ }
+ printf("clearDtlsCredentialInfo OUT\n");
+}
+
+// Internal API. Invoked by OC stack to retrieve credentials from this module
+void CAGetDtlsPskCredentials(CADtlsPskCredsBlob_t **credInfo)
+{
+ printf("CAGetDtlsPskCredentials IN\n");
+
+ *credInfo = pskCredsBlob;
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMInitialize
+ printf("CAGetDtlsPskCredentials OUT\n");
+}
+
+int32_t SetCredentials()
+{
+ printf("SetCredentials IN\n");
+ pskCredsBlob = (CADtlsPskCredsBlob_t *)malloc(sizeof(CADtlsPskCredsBlob_t));
+
+ memset(pskCredsBlob, 0x0, sizeof(CADtlsPskCredsBlob_t));
+ memcpy(pskCredsBlob->rsIdentity, IDENTITY, DTLS_PSK_ID_LEN);
+
+ pskCredsBlob->num = 1;
+
+ pskCredsBlob->creds = (CADtlsPskCreds_t *)malloc(sizeof(CADtlsPskCreds_t) *(pskCredsBlob->num));
+
+ memcpy(pskCredsBlob->creds[0].clientIdentity, IDENTITY, DTLS_PSK_ID_LEN);
+ memcpy(pskCredsBlob->creds[0].rsClientPsk, RS_CLIENT_PSK, DTLS_PSK_PSK_LEN);
+
+ printf("SetCredentials OUT\n");
+ return 1;
+}
+
+
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMInitialize
(JNIEnv *env, jobject obj, jobject context)
{
LOGI("RMInitialize");
//Currently set context for WiFiCore
CAJniSetContext(context);
+ CALEServerJNISetContext(env, context);
+ CALEClientJNISetContext(env, context);
+
+ if (SetCredentials() == 0)
+ {
+ printf("SetCredentials failed\n");
+ }
if(CA_STATUS_OK != CAInitialize())
{
CATerminate();
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMStartListeningServer(JNIEnv *env,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMStartListeningServer(JNIEnv *env,
jobject obj)
{
LOGI("RMStartListeningServer");
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMStartDiscoveryServer(JNIEnv *env,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMStartDiscoveryServer(JNIEnv *env,
jobject obj)
{
LOGI("RMStartDiscoveryServer");
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMRegisterHandler(JNIEnv *env,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMRegisterHandler(JNIEnv *env,
jobject obj)
{
LOGI("RMRegisterHandler");
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMFindResource(JNIEnv *env, jobject obj,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMFindResource(JNIEnv *env, jobject obj,
jstring uri)
{
const char* strUri = (*env)->GetStringUTFChars(env, uri, NULL);
token = NULL;
}
+ LOGI("generated token %s\n", (token != NULL) ? token : "");
+
if(CA_STATUS_OK != CAFindResource(strUri, token))
{
LOGI("Could not find resource");
else
{
LOGI("find resource to %s URI", strUri);
- gLastRequestToken = "";
+ gLastRequestToken = token;
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendRequest(JNIEnv *env, jobject obj,
- jstring data)
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSendRequest(JNIEnv *env, jobject obj,
+ jstring uri, jint selectedNetwork, jint isSecured, jint msgType)
{
- const char* strData = (*env)->GetStringUTFChars(env, data, NULL);
- LOGI("RMSendRequest - %s", strData);
+ const char* strUri = (*env)->GetStringUTFChars(env, uri, NULL);
+ LOGI("RMSendRequest - %s", strUri);
+
+ CAConnectivityType_t connectivityType;
+ connectivityType = get_network_type(selectedNetwork);
+
+ //create remote endpoint
+ CARemoteEndpoint_t* endpoint = NULL;
+
+ if(CA_STATUS_OK != CACreateRemoteEndpoint(strUri, connectivityType, &endpoint))
+ {
+ LOGI("Could not create remote end point");
+ CADestroyRemoteEndpoint(endpoint);
+ }
+
+ CAMessageType_t messageType = msgType;
// create token
CAToken_t token = NULL;
token = NULL;
}
+ char resourceURI[15] = {0};
+
+ get_resource_uri(strUri, resourceURI, 14);
+
CAInfo_t requestData;
memset(&requestData, 0, sizeof(CAInfo_t));
requestData.token = token;
- requestData.payload = "Temp Json Payload";
+
+
+ if (isSecured == 1)
+ {
+ int length = strlen(gSecureInfoData) + strlen(resourceURI) + 1;
+ requestData.payload = (CAPayload_t) malloc(length);
+ sprintf(requestData.payload, gSecureInfoData, resourceURI, gLocalSecurePort);
+ }
+ else
+ {
+ int length = strlen(gNormalInfoData) + strlen(resourceURI) + 1;
+ requestData.payload = (CAPayload_t) malloc(length);
+ sprintf(requestData.payload, gNormalInfoData, resourceURI);
+ }
+
+ requestData.type = messageType;
CARequestInfo_t requestInfo;
memset(&requestInfo, 0, sizeof(CARequestInfo_t));
requestInfo.method = CA_GET;
requestInfo.info = requestData;
- //create remote endpoint
- CARemoteEndpoint_t* endpoint = NULL;
-
- if(CA_STATUS_OK != CACreateRemoteEndpoint(strData, &endpoint))
- {
- LOGI("Could not create remote end point");
- }
-
// send request
if(CA_STATUS_OK != CASendRequest(endpoint, &requestInfo))
{
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendResponse(JNIEnv *env, jobject obj,
- jstring data)
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSendNotification(JNIEnv *env,
+ jobject obj, jstring uri, jint selectedNetwork)
{
- const char* strData = (*env)->GetStringUTFChars(env, data, NULL);
- LOGI("RMSendResponse - %s", strData);
-
- const CAURI_t endpoint_uri = "/a/temp_uri";
-
- // create token
- CAToken_t token = NULL;
- CAResult_t res = CAGenerateToken(&token);
- if (res != CA_STATUS_OK)
- {
- printf("token generate error!!\n");
- token = NULL;
- }
-
- CAInfo_t responseData;
- memset(&responseData, 0, sizeof(CAInfo_t));
- responseData.token = token;
- responseData.payload = (char*) strData;
+ const char* strUri = (*env)->GetStringUTFChars(env, uri, NULL);
+ LOGI("RMSendNotification - %s", strUri);
- CAResponseInfo_t responseInfo;
- memset(&responseInfo, 0, sizeof(CAResponseInfo_t));
- responseInfo.result = 203;
- responseInfo.info = responseData;
+ CAConnectivityType_t connectivityType;
+ connectivityType = get_network_type(selectedNetwork);
+ // create remote endpoint
CARemoteEndpoint_t* endpoint = NULL;
- if(CA_STATUS_OK != CACreateRemoteEndpoint(endpoint_uri, &endpoint))
+ if(CA_STATUS_OK != CACreateRemoteEndpoint(strUri, connectivityType, &endpoint))
{
LOGI("Could not create remote end point");
- }
-
- if(CA_STATUS_OK != CASendResponse(endpoint, &responseInfo))
- {
- LOGI("Could not send response");
- }
-}
-
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendNotification(JNIEnv *env,
- jobject obj, jstring data)
-{
- const char* strData = (*env)->GetStringUTFChars(env, data, NULL);
- LOGI("RMSendNotification - %s", strData);
-
- const CAURI_t endpoint_uri = "/a/temp_uri";
-
- // create token
- CAToken_t token = NULL;
- CAResult_t res = CAGenerateToken(&token);
- if (res != CA_STATUS_OK)
- {
- printf("token generate error!!\n");
- token = NULL;
+ CADestroyRemoteEndpoint(endpoint);
+ return;
}
CAInfo_t respondeData;
memset(&respondeData, 0, sizeof(CAInfo_t));
- respondeData.token = token;
- respondeData.payload = (char*)strData;
+ respondeData.token = "client token";
+ respondeData.payload = "Temp Notification Data";
CAResponseInfo_t responseInfo;
memset(&responseInfo, 0, sizeof(CAResponseInfo_t));
responseInfo.result = CA_SUCCESS;
responseInfo.info = respondeData;
- CARemoteEndpoint_t* endpoint = NULL;
-
- if(CA_STATUS_OK != CACreateRemoteEndpoint(endpoint_uri, &endpoint))
+ // send request
+ if(CA_STATUS_OK != CASendNotification(endpoint, &responseInfo))
{
- LOGI("Could not create remote end point");
+ LOGI("Could not send notification");
}
- if(CA_STATUS_OK != CASendNotification(endpoint, &responseInfo))
+ // destroy remote endpoint
+ if (endpoint != NULL)
{
- LOGI("Could not send notification");
+ CADestroyRemoteEndpoint(endpoint);
}
+
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSelectNetwork(JNIEnv *env, jobject obj,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSelectNetwork(JNIEnv *env, jobject obj,
jint networkType)
{
LOGI("RMSelectNetwork Type : %d", networkType);
}
}
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMHandleRequestResponse(JNIEnv *env,
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMHandleRequestResponse(JNIEnv *env,
jobject obj)
{
LOGI("RMHandleRequestResponse");
void request_handler(const CARemoteEndpoint_t* object, const CARequestInfo_t* requestInfo)
{
- /*
- printf("[CALLBACK] request_handler, uri : %s, data : %s\n",
- (object != NULL) ? object->resourceUri : "",
- (requestInfo != NULL) ? requestInfo->info.payload : "");
- */
- LOGI("[CALLBACK] request_handler, uri: %s, data: %s, token: %s \n",
- (object != NULL) ? object->resourceUri : "",
- (requestInfo != NULL) ? requestInfo->info.payload : "",
- (requestInfo->info.token != NULL) ? requestInfo->info.token : "");
-
- if (gLastRequestToken != NULL && requestInfo->info.token != NULL
- && (strcmp((char*)gLastRequestToken, requestInfo->info.token) == 0))
+ if (!object)
{
- printf("token is same. received request of it's own. skip.. \n");
+ LOGI("Remote endpoint is NULL!");
+ return;
+ }
+ if (!requestInfo)
+ {
+ LOGI("Request info is NULL!");
return;
}
- LOGI("[CALLBACK] request_handler, address : %s\n",
- (object != NULL) ? object->addressInfo.IP.ipAddress : "");
+ LOGI("##########received request from remote device #############\n");
+ LOGI("Uri: %s\n", object->resourceUri);
+ LOGI("Remote Address: %s Port: %d secured:%d\n", object->addressInfo.IP.ipAddress,
+ object->addressInfo.IP.port, object->isSecured);
+
+ LOGI("Data: %s\n", requestInfo->info.payload);
+
+ if (gLastRequestToken != NULL && requestInfo->info.token != NULL
+ && (strcmp((char *)gLastRequestToken, requestInfo->info.token) == 0))
+ {
+ LOGI("token is same. received request of it's own. skip.. \n");
+ return;
+ }
if (requestInfo->info.options)
{
uint32_t i;
for (i = 0; i < len; i++)
{
- LOGI("[CALLBACK] request_handler, option ID : %d\n",
- requestInfo->info.options[i].optionID);
- LOGI("[CALLBACK] request_handler, options data length : %d\n",
- requestInfo->info.options[i].optionLength);
- LOGI("[CALLBACK] request_handler, options data : %s\n",
- requestInfo->info.options[i].optionData);
+ LOGI("Option %d\n", i + 1);
+ LOGI("ID : %d\n", requestInfo->info.options[i].optionID);
+ LOGI("Data[%d]: %s\n", requestInfo->info.options[i].optionLength,
+ requestInfo->info.options[i].optionData);
}
}
+ printf("############################################################\n");
- printf("send response with URI\n");
+ //Check if this has secure communication information
+ if (requestInfo->info.payload)
+ {
+ int securePort = get_secure_information(requestInfo->info.payload);
+ if (0 < securePort) //Set the remote endpoint secure details and send response
+ {
+ LOGI("This is secure resource...\n");
+ char *uri = NULL;
+ int length = 0;
+
+ length = 8; //length of "coaps://"
+ length += strlen(object->addressInfo.IP.ipAddress) + 5; // length of "ipaddress:port"
+ length += strlen(object->resourceUri) + 1;
+
+ uri = calloc(1,sizeof(char)*length);
+ if (!uri)
+ {
+ printf("Failed to create new uri\n");
+ return;
+ }
+ sprintf(uri,"coaps://%s:%d/%s",object->addressInfo.IP.ipAddress,
+ securePort, object->resourceUri);
+
+ CARemoteEndpoint_t *endpoint = NULL;
+ if (CA_STATUS_OK != CACreateRemoteEndpoint(uri, object->connectivityType, &endpoint))
+ {
+ LOGI("Failed to create duplicate of remote endpoint!\n");
+ return;
+ }
+ endpoint->isSecured = CA_TRUE;
+ object = endpoint;
+ }
+ }
- CAInfo_t responseData;
- //responseData = (CAInfo*) malloc(sizeof(CAInfo));
- memset(&responseData, 0, sizeof(CAInfo_t));
- if (requestInfo != NULL)
+ LOGI("send response with URI\n");
+ send_response(object, (requestInfo != NULL) ? &requestInfo->info : NULL);
+
+ gReceived = 1;
+
+}
+
+void response_handler(const CARemoteEndpoint_t* object, const CAResponseInfo_t* responseInfo)
+{
+
+ LOGI("##########Received response from remote device #############\n");
+ LOGI("Uri: %s\n", object->resourceUri);
+ LOGI("Remote Address: %s Port: %d secured:%d\n", object->addressInfo.IP.ipAddress,
+ object->addressInfo.IP.port, object->isSecured);
+ LOGI("response result : %d\n", responseInfo->result);
+ LOGI("Data: %s\n", responseInfo->info.payload);
+
+ if (responseInfo->info.options)
{
- responseData.token = requestInfo->info.token;
+ uint32_t len = responseInfo->info.numOptions;
+ uint32_t i;
+ for (i = 0; i < len; i++)
+ {
+ LOGI("Option %d\n", i + 1);
+ LOGI("ID : %d\n", responseInfo->info.options[i].optionID);
+ LOGI("Data[%d]: %s\n", responseInfo->info.options[i].optionLength,
+ responseInfo->info.options[i].optionData);
+ }
}
- else
+ LOGI("############################################################\n");
+ gReceived = 1;
+
+ //Check if this has secure communication information
+ if (responseInfo->info.payload)
{
- responseData.token = "";
+ int securePort = get_secure_information(responseInfo->info.payload);
+ if (0 < securePort) //Set the remote endpoint secure details and send response
+ {
+ LOGI("This is secure resource...\n");
+ }
}
+}
+
+void get_resource_uri(char *URI, char *resourceURI, int length)
+{
+ char *startPos = URI;
+ char *temp = NULL;
+ if (NULL != (temp = strstr(URI, "://")))
+ {
+ startPos = strchr(temp + 3, '/');
+ if (!startPos)
+ {
+ printf("Resource URI is missing\n");
+ return;
+ }
+ }
+
+ char *endPos = strchr(startPos, '?');
+ if (!endPos)
+ {
+ endPos = URI + strlen(URI);
+ }
+ endPos -= 1;
+
+ if (endPos - startPos <= length)
+ memcpy(resourceURI, startPos + 1, endPos - startPos);
+
+ printf("URI: %s, ResourceURI:%s\n", URI, resourceURI);
+}
+
+int get_secure_information(CAPayload_t payLoad)
+{
+ printf("entering get_secure_information\n");
+
+ if (!payLoad)
+ {
+ printf("Payload is NULL\n");
+ return -1;
+ }
+
+ char *subString = NULL;
+ if (NULL == (subString = strstr(payLoad, "\"sec\":1")))
+ {
+ printf("This is not secure resource\n");
+ return -1;
+ }
+
+ if (NULL == (subString = strstr(payLoad, "\"port\":")))
+ {
+ printf("This secure resource does not have port information\n");
+ return -1;
+ }
+
+ char *startPos = strstr(subString, ":");
+ if (!startPos)
+ {
+ printf("Parsing failed !\n");
+ return -1;
+ }
+
+ char *endPos = strstr(startPos, "}");
+ if (!endPos)
+ {
+ printf("Parsing failed !\n");
+ return -1;
+ }
+
+ char portStr[4] = {0};
+ memcpy(portStr, startPos + 1, (endPos-1) - startPos);
+
+ printf("secured port is: %s\n", portStr);
+ return atoi(portStr);
+}
+
+void send_response(CARemoteEndpoint_t* endpoint, CAInfo_t* info)
+{
+ LOGI("entering send_response\n");
+
+ CAInfo_t responseData;
+ memset(&responseData, 0, sizeof(CAInfo_t));
+ responseData.type =
+ (info != NULL) ?
+ ((info->type == CA_MSG_CONFIRM) ? CA_MSG_ACKNOWLEDGE : CA_MSG_NONCONFIRM) :
+ CA_MSG_NONCONFIRM;
+ responseData.messageId = (info != NULL) ? info->messageId : 0;
+ responseData.token = (info != NULL) ? info->token : "";
responseData.payload = "response payload";
CAResponseInfo_t responseInfo;
- //responseInfo = (CAResponseInfo*) malloc(sizeof(CAResponseInfo));
memset(&responseInfo, 0, sizeof(CAResponseInfo_t));
responseInfo.result = 203;
responseInfo.info = responseData;
+ if (CA_TRUE == endpoint->isSecured)
+ LOGI("Sending response on secure communication\n");
+ else
+ LOGI("Sending response on non-secure communication\n");
+
// send request (connectivityType from remoteEndpoint of request Info)
- CAResult_t res = CASendResponse(object, &responseInfo);
+ CAResult_t res = CASendResponse(endpoint, &responseInfo);
if (res != CA_STATUS_OK)
{
LOGI("send response error\n");
LOGI("send response success\n");
}
+ LOGI("=============================================\n");
}
-void response_handler(const CARemoteEndpoint_t* object, const CAResponseInfo_t* responseInfo)
+CAConnectivityType_t get_network_type(int selectedNetwork)
{
- LOGI("[CALLBACK] response_handler, uri : %s, data : %s\n",
- (object != NULL) ? object->resourceUri : "",
- (responseInfo != NULL) ? responseInfo->info.payload : "");
+ int number = selectedNetwork;
- LOGI("[CALLBACK] response_handler, address : %s\n",
- (object != NULL) ? object->addressInfo.IP.ipAddress : "");
+ number = (number < 0 || number > 3) ? 0 : 1 << number;
- if (responseInfo->info.options)
+ if (number & CA_ETHERNET)
{
- uint32_t len = responseInfo->info.numOptions;
- uint32_t i;
- for (i = 0; i < len; i++)
- {
- LOGI("[CALLBACK] response_handler, option ID : %d\n",
- responseInfo->info.options[i].optionID);
- LOGI("[CALLBACK] response_handler, options data length : %d\n",
- responseInfo->info.options[i].optionLength);
- LOGI("[CALLBACK] response_handler, options data : %s\n",
- responseInfo->info.options[i].optionData);
- }
+ return CA_ETHERNET;
+ }
+ if (number & CA_WIFI)
+ {
+ return CA_WIFI;
+ }
+ if (number & CA_EDR)
+ {
+ return CA_EDR;
+ }
+ if (number & CA_LE)
+ {
+ return CA_LE;
}
- //printf("send request with URI\n");
- //send_request_tmp(object, (responseInfo != NULL) ? responseInfo->info.token : "");
}
* Method: RMInitialize
* Signature: (Landroid/content/Context;)I
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMInitialize
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMInitialize
(JNIEnv *, jobject, jobject);
/*
/*
* Class: com_iotivity_service_RMInterface
* Method: RMStartListeningServer
- * Signature: ()I
+ * Signature: ()V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMStartListeningServer
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMStartListeningServer
(JNIEnv *, jobject);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMStartDiscoveryServer
- * Signature: ()I
+ * Signature: ()V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMStartDiscoveryServer
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMStartDiscoveryServer
(JNIEnv *, jobject);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMRegisterHandler
- * Signature: ()I
+ * Signature: ()V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMRegisterHandler
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMRegisterHandler
(JNIEnv *, jobject);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMFindResource
- * Signature: (Ljava/lang/String;)I
+ * Signature: (Ljava/lang/String;)V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMFindResource
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMFindResource
(JNIEnv *, jobject, jstring);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMSendRequest
- * Signature: (Ljava/lang/String;)I
+ * Signature: (Ljava/lang/String;)V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendRequest
- (JNIEnv *, jobject, jstring);
-
-/*
- * Class: com_iotivity_service_RMInterface
- * Method: RMSendResponse
- * Signature: (Ljava/lang/String;)I
- */
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendResponse
- (JNIEnv *, jobject, jstring);
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSendRequest
+ (JNIEnv *, jobject, jstring, jint, jint, jint);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMSendNotification
- * Signature: (Ljava/lang/String;)I
+ * Signature: (Ljava/lang/String;)V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSendNotification
- (JNIEnv *, jobject, jstring);
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSendNotification
+ (JNIEnv *, jobject, jstring, jint);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMSelectNetwork
- * Signature: (I)I
+ * Signature: (I)V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMSelectNetwork
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMSelectNetwork
(JNIEnv *, jobject, jint);
/*
* Class: com_iotivity_service_RMInterface
* Method: RMHandleRequestResponse
- * Signature: ()I
+ * Signature: ()V
*/
-JNIEXPORT jint JNICALL Java_com_iotivity_service_RMInterface_RMHandleRequestResponse
+JNIEXPORT void JNICALL Java_com_iotivity_service_RMInterface_RMHandleRequestResponse
(JNIEnv *, jobject);
#ifdef __cplusplus
android:text="@string/notification" />
</RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/layout_request_setting"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/layout_find" >
+
+ <Button
+ android:id="@+id/btn_Request_setting"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:text="@string/request_setting" />
+ </RelativeLayout>
+
<RelativeLayout
android:id="@+id/layout_request"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_below="@id/layout_find" >
+ android:layout_below="@id/layout_request_setting" >
<Button
android:id="@+id/btn_Request"
<string name="app_name">sample_service</string>
<string name="find">Find</string>
<string name="notify">Notify</string>
+ <string name="request_setting">Request Setting</string>
<string name="request">Request</string>
<string name="response">Response</string>
<string name="uri">127.0.0.0:5000/a/light</string>
- <string name="notification">notification</string>
- <string name="req_data">request data</string>
- <string name="resp_data">response data</string>
+ <string name="notification">resourceUri/notification</string>
+ <string name="req_data">coaps://10.11.12.13:4545/resource_uri</string>
+ <string name="resp_data">resourceUri/response</string>
<string name="receive">Receive</string>
<string name="received">Received Message</string>
<string name="action_settings">Settings</string>
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseSettings;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
public class CALeInterface {
public CALeInterface() {
CARegisterLeScanCallback(mLeScanCallback);
CARegisterLeGattCallback(mGattCallback);
-// CARegisterLeGattServerCallback(mGattServerCallback);
-// CARegisterBluetoothLeAdvertiseCallback(mAdvertiseCallback);
-// CARegisterBluetoothLeScanCallback(mScanCallback);
+ CARegisterLeGattServerCallback(mGattServerCallback);
+ CARegisterBluetoothLeAdvertiseCallback(mAdvertiseCallback);
}
public static void getLeScanCallback() {
CARegisterLeGattCallback(mGattCallback);
}
-// public static void getLeGattServerCallback() {
-// CARegisterLeGattServerCallback(mGattServerCallback);
-// }
-
-// public static void getBluetoothLeAdvertiseCallback() {
-// CARegisterBluetoothLeAdvertiseCallback(mAdvertiseCallback);
-// }
-//
-// public static void getBluetoothLeScanCallback() {
-// CARegisterBluetoothLeScanCallback(mScanCallback);
-// }
-
+ public static void getLeGattServerCallback() {
+ CARegisterLeGattServerCallback(mGattServerCallback);
+ }
+
+ public static void getBluetoothLeAdvertiseCallback() {
+ CARegisterBluetoothLeAdvertiseCallback(mAdvertiseCallback);
+ }
+
+ public static IntentFilter getActionStateIntentFilter() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ return filter;
+ }
+
public native static void CARegisterLeScanCallback(BluetoothAdapter.LeScanCallback callback);
+
public native static void CARegisterLeGattCallback(BluetoothGattCallback callback);
+
public native static void CARegisterLeGattServerCallback(BluetoothGattServerCallback callback);
-// public native static void CARegisterBluetoothLeAdvertiseCallback(AdvertiseCallback callback);
-// public native static void CARegisterBluetoothLeScanCallback(ScanCallback callback);
+
+ public native static void CARegisterBluetoothLeAdvertiseCallback(AdvertiseCallback callback);
// BluetoothAdapter.LeScanCallback
public native static void CALeScanCallback(BluetoothDevice device, int rssi, byte[] scanRecord);
public native static void CALeGattServicesDiscoveredCallback(BluetoothGatt gatt, int status);
- public native static void CALeGattCharacteristicReadCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, String data, int status);
+ public native static void CALeGattCharacteristicReadCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] data, int status);
- public native static void CALeGattCharacteristicWriteCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, String data, int status);
+ public native static void CALeGattCharacteristicWriteCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] data, int status);
- public native static void CALeGattCharacteristicChangedCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
+ public native static void CALeGattCharacteristicChangedCallback(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] data);
public native static void CALeGattDescriptorReadCallback(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status);
public native static void CALeGattServerServiceAddedCallback(int status, BluetoothGattService service);
public native static void CALeGattServerCharacteristicReadRequestCallback(BluetoothDevice device,
- int requestId, int offset, BluetoothGattCharacteristic characteristic);
+ int requestId, int offset, BluetoothGattCharacteristic characteristic, byte[] data);
public native static void CALeGattServerCharacteristicWriteRequestCallback(BluetoothDevice device, int requestId,
- BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value);
+ BluetoothGattCharacteristic characteristic, byte[] data, boolean preparedWrite,
+ boolean responseNeeded, int offset, byte[] value);
public native static void CALeGattServerDescriptorReadRequestCallback(BluetoothDevice device,
int requestId, int offset, BluetoothGattDescriptor descriptor);
public native static void CALeGattServerNotificationSentCallback(BluetoothDevice device, int status);
// AdvertiseCallback
-// public native static void CALeAdvertiseStartSuccessCallback(AdvertiseSettings settingsInEffect);
-//
-// public native static void CALeAdvertiseStartFailureCallback(int errorCode);
-
- // ScanCallback
-// public native static void CABluetoothLeScanResultCallback(int callbackType, ScanResult result);
-//
-// public native static void CABluetoothLeBatchScanResultsCallback(List<ScanResult> results);
-//
-// public native static void CABluetoothLeScanFailedCallback(int errorCode);
-
-
+ public native static void CALeAdvertiseStartSuccessCallback(AdvertiseSettings settingsInEffect);
+
+ public native static void CALeAdvertiseStartFailureCallback(int errorCode);
+
+ // Network Monitor
+ public native static void CALeStateChangedCallback(int state);
+
+ // Callback
private static BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
- String data = new String(characteristic.getValue());
- CALeGattCharacteristicReadCallback(gatt, characteristic, data, status);
+ CALeGattCharacteristicReadCallback(gatt, characteristic,
+ characteristic.getValue(), status);
}
@Override
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
- String data = new String(characteristic.getValue());
- CALeGattCharacteristicWriteCallback(gatt, characteristic, data, status);
+ CALeGattCharacteristicWriteCallback(gatt, characteristic,
+ characteristic.getValue(), status);
}
@Override
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
- String data = new String(characteristic.getValue());
- CALeGattCharacteristicChangedCallback(gatt, characteristic, data);
+ CALeGattCharacteristicChangedCallback(gatt, characteristic,
+ characteristic.getValue());
}
@Override
}
};
- /*
+
private static final BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
@Override
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
- CALeGattServerCharacteristicReadRequestCallback(device, requestId, offset, characteristic);
+ CALeGattServerCharacteristicReadRequestCallback(device, requestId, offset, characteristic, characteristic.getValue());
}
@Override
preparedWrite, responseNeeded, offset, value);
CALeGattServerCharacteristicWriteRequestCallback(device, requestId, characteristic,
- preparedWrite, responseNeeded, offset, value);
+ value, preparedWrite, responseNeeded, offset, value);
}
@Override
}
};
- private static final ScanCallback mScanCallback = new ScanCallback() {
-
- @Override
- public void onScanResult(int callbackType, ScanResult result) {
- super.onScanResult(callbackType, result);
-
- CABluetoothLeScanResultCallback(callbackType, result);
- }
-
+ private static final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+
@Override
- public void onBatchScanResults(List<ScanResult> results) {
- super.onBatchScanResults(results);
+ public void onReceive(Context context, Intent intent) {
- CABluetoothLeBatchScanResultsCallback(results);
- }
-
- @Override
- public void onScanFailed(int errorCode) {
- super.onScanFailed(errorCode);
+ String action = intent.getAction();
- CABluetoothLeScanFailedCallback(errorCode);
+ if (action != null && action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+
+ if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_OFF) // STATE_ON:12, STATE_OFF:10
+ {
+ CALeStateChangedCallback(state);
+ }
+ }
}
};
- */
}
-
package com.iotivity.service;
import java.util.ArrayList;
public class MainActivity extends Activity {
- static RMInterface RM = new RMInterface();
+ static RMInterface RM = new RMInterface();
+
+ private final CharSequence[] mCheckBoxItems = { Network.WIFI.name(),
+ Network.LE.name() };
+
+ private final CharSequence[] mDTLSCheckBoxItems = { DTLS.SECURED.name(),
+ DTLS.UNSECURED.name() };
+
+ private final CharSequence[] mMsgTyleCheckBoxItems = { MsgType.CON.name(),
+ MsgType.NON.name() };
+
+ private enum Mode {
+ SERVER, CLIENT, BOTH, UNKNOWN
+ };
+
+ private enum Network {
+ WIFI, LE
+ };
+
+ private enum DTLS {
+ SECURED, UNSECURED
+ };
+
+ private enum MsgType {
+ CON, NON
+ };
+
+ private Mode mCurrentMode = Mode.UNKNOWN;
+
+ private ArrayList<Integer> mSelectedItems = new ArrayList<Integer>();
+
+ private ArrayList<Integer> mSRSelectedItems = new ArrayList<Integer>();
+
+ private boolean mCheckedItems[] = { false, false };
+
+ private RelativeLayout mFindResourceLayout = null;
+
+ private RelativeLayout mSendNotificationLayout = null;
+
+ private RelativeLayout mSendRequestLayout = null;
+
+ private RelativeLayout mSendRequestSettingLayout = null;
+
+ private RelativeLayout mSendResponseLayout = null;
+
+ private RelativeLayout mReceiveLayout = null;
+
+ private TextView mMode_tv = null;
+
+ private TextView mNetwork_tv = null;
+
+ private EditText mUri_ed = null;
+
+ private EditText mNotification_ed = null;
+
+ private EditText mReqData_ed = null;
+
+ private EditText mRespData_ed = null;
+
+ private Button mFind_btn = null;
+
+ private Button mNotify_btn = null;
+
+ private Button mReqeust_btn = null;
+
+ private Button mReqeust_setting_btn = null;
+
+ private Button mResponse_btn = null;
+
+ private Button mRecv_btn = null;
+
+ /**
+ * Defined ConnectivityType in cacommon.c
+ *
+ * CA_ETHERNET = (1 << 0) CA_WIFI = (1 << 1) CA_EDR = (1 << 2) CA_LE = (1 <<
+ * 3)
+ */
+ private int CA_WIFI = (1 << 1);
+ private int CA_LE = (1 << 3);
+ private int isSecured = 0;
+ private int msgType = 0;
+ int selectedNetwork = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // Initialize UI
+ mFindResourceLayout = (RelativeLayout) findViewById(R.id.layout_find);
+ mSendNotificationLayout = (RelativeLayout) findViewById(R.id.layout_notify);
+ mSendRequestLayout = (RelativeLayout) findViewById(R.id.layout_request);
+ mSendRequestSettingLayout = (RelativeLayout) findViewById(R.id.layout_request_setting);
+ mSendResponseLayout = (RelativeLayout) findViewById(R.id.layout_response);
+ mReceiveLayout = (RelativeLayout) findViewById(R.id.layout_receive);
+
+ mMode_tv = (TextView) findViewById(R.id.tv_mode);
+ mNetwork_tv = (TextView) findViewById(R.id.tv_network);
- private final CharSequence[] mCheckBoxItems = {
- Network.WIFI.name(), Network.LE.name()
- };
+ mUri_ed = (EditText) findViewById(R.id.et_uri);
+ mNotification_ed = (EditText) findViewById(R.id.et_notification);
+ mReqData_ed = (EditText) findViewById(R.id.et_req_data);
+ mRespData_ed = (EditText) findViewById(R.id.et_resp_data);
- private enum Mode {
- SERVER, CLIENT, BOTH, UNKNOWN
- };
+ mFind_btn = (Button) findViewById(R.id.btn_find_resource);
+ mNotify_btn = (Button) findViewById(R.id.btn_notify);
+ mReqeust_btn = (Button) findViewById(R.id.btn_Request);
+ mReqeust_setting_btn = (Button) findViewById(R.id.btn_Request_setting);
+ mResponse_btn = (Button) findViewById(R.id.btn_Response);
+ mRecv_btn = (Button) findViewById(R.id.btn_receive);
- private enum Network {
- WIFI, LE
- };
+ mFind_btn.setOnClickListener(mFindResourceHandler);
+ mNotify_btn.setOnClickListener(mNotifyHandler);
+ mReqeust_btn.setOnClickListener(mSendRequestHandler);
+ mReqeust_setting_btn.setOnClickListener(mSendRequestSettingHandler);
+ mResponse_btn.setOnClickListener(mSendResponseHandler);
+ mRecv_btn.setOnClickListener(mResponseHandler);
- private Mode mCurrentMode = Mode.UNKNOWN;
+ showSelectModeView();
- private ArrayList<Integer> mSelectedItems = new ArrayList<Integer>();
+ // Initialize Connectivity Abstraction
+ RM.RMInitialize(getApplicationContext());
+ // Select default network(WIFI)
+ RM.RMSelectNetwork(CA_WIFI);
+ // set handler
+ RM.RMRegisterHandler();
+ }
- private boolean mCheckedItems[] = {
- false, false
- };
+ private void showSelectModeView() {
- private RelativeLayout mFindResourceLayout = null;
+ mFindResourceLayout.setVisibility(View.INVISIBLE);
+ mSendNotificationLayout.setVisibility(View.INVISIBLE);
+ mSendRequestLayout.setVisibility(View.INVISIBLE);
+ mSendRequestSettingLayout.setVisibility(View.INVISIBLE);
+ mSendResponseLayout.setVisibility(View.INVISIBLE);
+ mReceiveLayout.setVisibility(View.INVISIBLE);
- private RelativeLayout mSendNotificationLayout = null;
+ mMode_tv.setText("Select Mode (Server or Client)");
+ }
- private RelativeLayout mSendRequestLayout = null;
+ private void showNetworkView() {
- private RelativeLayout mSendResponseLayout = null;
+ mNetwork_tv.setText("Select Network Type");
+ }
- private RelativeLayout mReceiveLayout = null;
+ private void showModeView() {
- private TextView mMode_tv = null;
+ if (mCurrentMode == Mode.SERVER) {
- private TextView mNetwork_tv = null;
+ mFindResourceLayout.setVisibility(View.INVISIBLE);
+ mSendNotificationLayout.setVisibility(View.VISIBLE);
+ mSendRequestLayout.setVisibility(View.INVISIBLE);
+ mSendRequestSettingLayout.setVisibility(View.INVISIBLE);
+ mSendResponseLayout.setVisibility(View.VISIBLE);
+ mReceiveLayout.setVisibility(View.VISIBLE);
- private EditText mUri_ed = null;
+ mNetwork_tv.setText("");
- private EditText mNotification_ed = null;
+ } else if (mCurrentMode == Mode.CLIENT) {
- private EditText mReqData_ed = null;
+ mFindResourceLayout.setVisibility(View.VISIBLE);
+ mSendNotificationLayout.setVisibility(View.INVISIBLE);
+ mSendRequestLayout.setVisibility(View.VISIBLE);
+ mSendRequestSettingLayout.setVisibility(View.VISIBLE);
+ mSendResponseLayout.setVisibility(View.INVISIBLE);
+ mReceiveLayout.setVisibility(View.VISIBLE);
- private EditText mRespData_ed = null;
+ mNetwork_tv.setText("");
+ }
+ }
- private Button mFind_btn = null;
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
- private Button mNotify_btn = null;
+ // Terminate Connectivity Abstraction
+ RM.RMTerminate();
+ }
- private Button mReqeust_btn = null;
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
- private Button mResponse_btn = null;
+ menu.add(0, 1, Menu.NONE, "Start Server");
+ menu.add(0, 2, Menu.NONE, "Start Client");
+ menu.add(0, 3, Menu.NONE, "Select Network");
- private Button mRecv_btn = null;
+ return true;
+ }
- /**
- * Defined ConnectivityType in cacommon.c
- *
- * CA_ETHERNET = (1 << 0)
- * CA_WIFI = (1 << 1)
- * CA_EDR = (1 << 2)
- * CA_LE = (1 << 3)
- */
- private int CA_WIFI = (1 << 1);
- private int CA_LE = (1 << 3);
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
+ switch (item.getItemId()) {
- // Initialize UI
- mFindResourceLayout = (RelativeLayout)findViewById(R.id.layout_find);
- mSendNotificationLayout = (RelativeLayout)findViewById(R.id.layout_notify);
- mSendRequestLayout = (RelativeLayout)findViewById(R.id.layout_request);
- mSendResponseLayout = (RelativeLayout)findViewById(R.id.layout_response);
- mReceiveLayout = (RelativeLayout)findViewById(R.id.layout_receive);
+ case 1:
- mMode_tv = (TextView)findViewById(R.id.tv_mode);
- mNetwork_tv = (TextView)findViewById(R.id.tv_network);
+ RM.RMStartListeningServer();
- mUri_ed = (EditText)findViewById(R.id.et_uri);
- mNotification_ed = (EditText)findViewById(R.id.et_notification);
- mReqData_ed = (EditText)findViewById(R.id.et_req_data);
- mRespData_ed = (EditText)findViewById(R.id.et_resp_data);
+ if (mCurrentMode == Mode.UNKNOWN || mSelectedItems.size() == 0) {
+ mCurrentMode = Mode.SERVER;
+ mMode_tv.setText("MODE: " + mCurrentMode.toString());
+ showNetworkView();
- mFind_btn = (Button)findViewById(R.id.btn_find_resource);
- mNotify_btn = (Button)findViewById(R.id.btn_notify);
- mReqeust_btn = (Button)findViewById(R.id.btn_Request);
- mResponse_btn = (Button)findViewById(R.id.btn_Response);
- mRecv_btn = (Button)findViewById(R.id.btn_receive);
+ } else {
+ mCurrentMode = Mode.SERVER;
+ mMode_tv.setText("MODE: " + mCurrentMode.toString());
+ showModeView();
+ }
- mFind_btn.setOnClickListener(mFindResourceHandler);
- mNotify_btn.setOnClickListener(mNotifyHandler);
- mReqeust_btn.setOnClickListener(mSendRequestHandler);
- mResponse_btn.setOnClickListener(mSendResponseHandler);
- mRecv_btn.setOnClickListener(mResponseHandler);
+ break;
- showSelectModeView();
+ case 2:
- // Initialize Connectivity Abstraction
- RM.RMInitialize(getApplicationContext());
- // Select default network(WIFI)
- RM.RMSelectNetwork(CA_WIFI);
- // set handler
- RM.RMRegisterHandler();
- }
+ RM.RMStartDiscoveryServer();
- private void showSelectModeView() {
+ if (mCurrentMode == Mode.UNKNOWN || mSelectedItems.size() == 0) {
+ mCurrentMode = Mode.CLIENT;
+ mMode_tv.setText("MODE: " + mCurrentMode.toString());
+ showNetworkView();
- mFindResourceLayout.setVisibility(View.INVISIBLE);
- mSendNotificationLayout.setVisibility(View.INVISIBLE);
- mSendRequestLayout.setVisibility(View.INVISIBLE);
- mSendResponseLayout.setVisibility(View.INVISIBLE);
- mReceiveLayout.setVisibility(View.INVISIBLE);
+ } else {
+ mCurrentMode = Mode.CLIENT;
+ mMode_tv.setText("MODE: " + mCurrentMode.toString());
+ showModeView();
+ }
- mMode_tv.setText("Select Mode (Server or Client)");
- }
+ break;
- private void showNetworkView() {
+ case 3:
- mNetwork_tv.setText("Select Network Type");
- }
+ showAlertDialog("Select Network");
- private void showModeView() {
+ break;
+ }
- if (mCurrentMode == Mode.SERVER) {
+ return super.onOptionsItemSelected(item);
+ }
- mFindResourceLayout.setVisibility(View.INVISIBLE);
- mSendNotificationLayout.setVisibility(View.VISIBLE);
- mSendRequestLayout.setVisibility(View.INVISIBLE);
- mSendResponseLayout.setVisibility(View.VISIBLE);
- mReceiveLayout.setVisibility(View.VISIBLE);
+ private OnClickListener mFindResourceHandler = new OnClickListener() {
- mNetwork_tv.setText("");
+ @Override
+ public void onClick(View v) {
- } else if (mCurrentMode == Mode.CLIENT) {
+ RM.RMFindResource(mUri_ed.getText().toString());
- mFindResourceLayout.setVisibility(View.VISIBLE);
- mSendNotificationLayout.setVisibility(View.INVISIBLE);
- mSendRequestLayout.setVisibility(View.VISIBLE);
- mSendResponseLayout.setVisibility(View.INVISIBLE);
- mReceiveLayout.setVisibility(View.VISIBLE);
+ }
+ };
- mNetwork_tv.setText("");
- }
- }
+ private OnClickListener mNotifyHandler = new OnClickListener() {
- @Override
- protected void onDestroy() {
- super.onDestroy();
+ @Override
+ public void onClick(View v) {
- // Terminate Connectivity Abstraction
- RM.RMTerminate();
- }
+ RM.RMSendNotification(mNotification_ed.getText().toString(), selectedNetwork);
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ }
+ };
- menu.add(0, 1, Menu.NONE, "Start Server");
- menu.add(0, 2, Menu.NONE, "Start Client");
- menu.add(0, 3, Menu.NONE, "Select Network");
+ private OnClickListener mSendRequestHandler = new OnClickListener() {
- return true;
- }
+ @Override
+ public void onClick(View v) {
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ RM.RMSendRequest(mReqData_ed.getText().toString(), selectedNetwork, isSecured, msgType);
- switch (item.getItemId()) {
+ }
+ };
+
+ private OnClickListener mSendRequestSettingHandler = new OnClickListener() {
- case 1:
+ @Override
+ public void onClick(View v) {
- RM.RMStartListeningServer();
+ checkMsgSecured("Select DTLS Type");
+ checkMsgType("Select Msg Type");
+ }
+ };
- if (mCurrentMode == Mode.UNKNOWN || mSelectedItems.size() == 0) {
- mCurrentMode = Mode.SERVER;
- mMode_tv.setText("MODE: " + mCurrentMode.toString());
- showNetworkView();
+ private OnClickListener mSendResponseHandler = new OnClickListener() {
- } else {
- mCurrentMode = Mode.SERVER;
- mMode_tv.setText("MODE: " + mCurrentMode.toString());
- showModeView();
- }
+ @Override
+ public void onClick(View v) {
- break;
+ RM.RMSendResponse(mRespData_ed.getText().toString(), selectedNetwork);
- case 2:
+ }
+ };
- RM.RMStartDiscoveryServer();
+ private OnClickListener mResponseHandler = new OnClickListener() {
- if (mCurrentMode == Mode.UNKNOWN || mSelectedItems.size() == 0) {
- mCurrentMode = Mode.CLIENT;
- mMode_tv.setText("MODE: " + mCurrentMode.toString());
- showNetworkView();
+ @Override
+ public void onClick(View v) {
- } else {
- mCurrentMode = Mode.CLIENT;
- mMode_tv.setText("MODE: " + mCurrentMode.toString());
- showModeView();
- }
+ RM.RMHandleRequestResponse();
+ }
+ };
- break;
+ private void showAlertDialog(String title) {
- case 3:
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle(title)
+ .setMultiChoiceItems(mCheckBoxItems, mCheckedItems,
+ new DialogInterface.OnMultiChoiceClickListener() {
- showAlertDialog("Select Network");
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which, boolean isChecked) {
- break;
- }
+ if (isChecked) {
- return super.onOptionsItemSelected(item);
- }
+ mSelectedItems.add(which);
- private OnClickListener mFindResourceHandler = new OnClickListener() {
+ } else if (mSelectedItems.contains(which)) {
- @Override
- public void onClick(View v) {
+ mSelectedItems.remove(Integer
+ .valueOf(which));
+ }
+ }
+ })
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- RM.RMFindResource(mUri_ed.getText().toString());
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
- }
- };
+ int interestedNetwork = 0;
- private OnClickListener mNotifyHandler = new OnClickListener() {
+ for (int i = 0; i < mSelectedItems.size(); i++) {
- @Override
- public void onClick(View v) {
+ if (mSelectedItems.get(i) == Network.WIFI.ordinal()) {
+ interestedNetwork |= CA_WIFI;
- RM.RMSendNotification(mNotification_ed.getText().toString());
+ } else if (mSelectedItems.get(i) == Network.LE
+ .ordinal()) {
+ interestedNetwork |= CA_LE;
+ }
+ }
- }
- };
+ RM.RMSelectNetwork(interestedNetwork);
+ selectedNetwork = interestedNetwork;
- private OnClickListener mSendRequestHandler = new OnClickListener() {
+ if (interestedNetwork != 0
+ && mCurrentMode != Mode.UNKNOWN) {
+ showModeView();
+ }
+ }
+ }).show();
+ }
- @Override
- public void onClick(View v) {
+ private void checkMsgSecured(String title) {
- RM.RMSendRequest(mReqData_ed.getText().toString());
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle(title)
+ .setMultiChoiceItems(mDTLSCheckBoxItems, mCheckedItems,
+ new DialogInterface.OnMultiChoiceClickListener() {
- }
- };
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which, boolean isChecked) {
- private OnClickListener mSendResponseHandler = new OnClickListener() {
+ if (isChecked) {
- @Override
- public void onClick(View v) {
+ mSRSelectedItems.add(which);
- RM.RMSendResponse(mRespData_ed.getText().toString());
+ } else if (mSRSelectedItems.contains(which)) {
- }
- };
+ mSRSelectedItems.remove(Integer
+ .valueOf(which));
+ }
+ }
+ })
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- private OnClickListener mResponseHandler = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
- @Override
- public void onClick(View v) {
+ for (int i = 0; i < mSRSelectedItems.size(); i++) {
- RM.RMHandleRequestResponse();
- }
- };
+ if (mSRSelectedItems.get(i) == DTLS.SECURED
+ .ordinal()) {
+ isSecured = 1;
- private void showAlertDialog(String title) {
+ } else if (mSRSelectedItems.get(i) == DTLS.UNSECURED
+ .ordinal()) {
+ isSecured = 0;
+ }
+ }
+ }
+ }).show();
- AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
- builder.setTitle(title)
- .setMultiChoiceItems(mCheckBoxItems, mCheckedItems,
- new DialogInterface.OnMultiChoiceClickListener() {
+ mSRSelectedItems.clear();
+ }
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ private void checkMsgType(String title) {
- if (isChecked) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle(title)
+ .setMultiChoiceItems(mMsgTyleCheckBoxItems, mCheckedItems,
+ new DialogInterface.OnMultiChoiceClickListener() {
- mSelectedItems.add(which);
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which, boolean isChecked) {
- } else if (mSelectedItems.contains(which)) {
+ if (isChecked) {
- mSelectedItems.remove(Integer.valueOf(which));
- }
- }
- }).setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ mSRSelectedItems.add(which);
- @Override
- public void onClick(DialogInterface dialog, int which) {
+ } else if (mSRSelectedItems.contains(which)) {
- int interestedNetwork = 0;
+ mSRSelectedItems.remove(Integer
+ .valueOf(which));
+ }
+ }
+ })
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
- for (int i = 0; i < mSelectedItems.size(); i++) {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
- if (mSelectedItems.get(i) == Network.WIFI.ordinal()) {
- interestedNetwork |= CA_WIFI;
+ for (int i = 0; i < mSRSelectedItems.size(); i++) {
- } else if (mSelectedItems.get(i) == Network.LE.ordinal()) {
- interestedNetwork |= CA_LE;
- }
- }
+ if (mSRSelectedItems.get(i) == MsgType.CON
+ .ordinal()) {
+ msgType = 0;
- RM.RMSelectNetwork(interestedNetwork);
+ } else if (mSRSelectedItems.get(i) == MsgType.NON
+ .ordinal()) {
+ msgType = 1;
+ }
+ }
+
+ }
+ }).show();
- if (interestedNetwork != 0 && mCurrentMode != Mode.UNKNOWN) {
- showModeView();
- }
- }
- }).show();
- }
+ mSRSelectedItems.clear();
+ }
}
System.loadLibrary("CAInterface");
}
- public native int RMInitialize(Context context);
+ public native void RMInitialize(Context context);
public native void RMTerminate();
- public native int RMStartListeningServer();
+ public native void RMStartListeningServer();
- public native int RMStartDiscoveryServer();
+ public native void RMStartDiscoveryServer();
- public native int RMRegisterHandler();
+ public native void RMRegisterHandler();
- public native int RMFindResource(String uri);
+ public native void RMFindResource(String uri);
- public native int RMSendRequest(String requestData);
+ public native void RMSendRequest(String requestUri, int selectedNetwork, int isSecured, int msgType);
- public native int RMSendResponse(String responseData);
+ public native void RMSendNotification(String notification, int selectedNetwork);
- public native int RMSendNotification(String notification);
+ public native void RMSelectNetwork(int interestedNetwork);
- public native int RMSelectNetwork(int interestedNetwork);
-
- public native int RMHandleRequestResponse();
+ public native void RMHandleRequestResponse();
}
void start_discovery_server();
void find_resource();
void send_request();
-void send_response(CARemoteEndpoint_t *endpoint, CAToken_t request_token);
+void send_response(CARemoteEndpoint_t *endpoint, const CAInfo_t* info);
void advertise_resource();
void send_notification();
void select_network();
void response_handler(const CARemoteEndpoint_t *object, const CAResponseInfo_t *responseInfo);
void send_request_tmp(CARemoteEndpoint_t *endpoint, CAToken_t token);
void terminate();
+CAConnectivityType_t get_network_type();
+
void getData(char *readInput, int bufferLength, int *dataLength)
{
char buf[MAX_BUF_LEN];
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ CAConnectivityType_t selectedNetwork;
+ selectedNetwork = get_network_type();
+
printf("============");
printf("10.11.12.13:4545/res_uri (for IP)");
printf("10:11:12:13:45:45/res_uri (for BT)");
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- CAResult_t res = CACreateRemoteEndpoint(buf, &endpoint);
+ CAResult_t res = CACreateRemoteEndpoint(buf,selectedNetwork,&endpoint);
if (res != CA_STATUS_OK)
{
printf("Out of memory");
{
char buf[MAX_BUF_LEN];
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ CAConnectivityType_t selectedNetwork;
+ selectedNetwork = get_network_type();
printf("============");
printf("10.11.12.13:4545/res_uri (for IP)");
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- CAResult_t res = CACreateRemoteEndpoint(buf, &endpoint);
+ CAResult_t res = CACreateRemoteEndpoint(buf,selectedNetwork,&endpoint);
if (CA_STATUS_OK != res)
{
printf("Out of memory");
void request_handler(const CARemoteEndpoint_t *object, const CARequestInfo_t *requestInfo)
{
+ if (!object)
+ {
+ printf("Remote endpoint is NULL!");
+ return;
+ }
+
+ if (!requestInfo)
+ {
+ printf("Request info is NULL!");
+ return;
+ }
+
printf("uri: ");
- printf((object != NULL) ? object->resourceUri : "");
+ printf(object->resourceUri);
+ printf("RAddr: ");
+ printf(object->addressInfo.IP.ipAddress);
+ printf("Port: ");
+ printf(object->addressInfo.IP.port);
printf("data: ");
- printf((requestInfo != NULL) ? requestInfo->info.payload : "");
+ printf(requestInfo->info.payload);
+#if 1
+ if (requestInfo->info.options)
+ {
+ uint32_t len = requestInfo->info.numOptions;
+ uint32_t i;
+ for (i = 0; i < len; i++)
+ {
+ printf("Option:");
+ printf(i+1);
+ printf("ID:");
+ printf(requestInfo->info.options[i].optionID);
+ printf("Data:");
+ printf((char*)requestInfo->info.options[i].optionData);
+ }
+ }
+#endif
printf("send response");
- send_response((CARemoteEndpoint_t *)object, requestInfo->info.token);
+ send_response((CARemoteEndpoint_t *)object, (requestInfo != NULL) ? &requestInfo->info : NULL);
}
void response_handler(const CARemoteEndpoint_t *object, const CAResponseInfo_t *responseInfo)
printf((object != NULL) ? object->resourceUri : "");
printf("data: ");
printf((responseInfo != NULL) ? responseInfo->info.payload : "");
+ printf("res result=");
+ printf(responseInfo->result);
}
-void send_response(CARemoteEndpoint_t *endpoint, CAToken_t request_token)
+void send_response(CARemoteEndpoint_t *endpoint, const CAInfo_t* info)
{
printf("============");
CAInfo_t responseData;
memset(&responseData, 0, sizeof(CAInfo_t));
- responseData.token = request_token;
+ responseData.type =
+ (info != NULL) ?
+ ((info->type == CA_MSG_CONFIRM) ? CA_MSG_ACKNOWLEDGE : CA_MSG_NONCONFIRM) :
+ CA_MSG_NONCONFIRM;
+ responseData.messageId = (info != NULL) ? info->messageId : 0;
+ responseData.token = (info != NULL) ? (CAToken_t)info->token : (CAToken_t)"";
responseData.payload = (CAPayload_t)"response payload";
CAResponseInfo_t responseInfo;
responseInfo.info = responseData;
// send request (connectivityType from remoteEndpoint of request Info)
- CASendResponse(endpoint, &responseInfo);
+ CAResult_t res = CASendResponse(endpoint, &responseInfo);
+ if(res != CA_STATUS_OK)
+ {
+ printf("Snd Resp error");
+ }
+ else
+ {
+ printf("Snd Resp success");
+ }
printf("============");
{
unselect_network();
}
+
+CAConnectivityType_t get_network_type()
+{
+ char buf[MAX_BUF_LEN];
+
+ printf("\n=============================================\n");
+ printf("\tselect network type\n");
+ printf("ETHERNET : 0\n");
+ printf("WIFI : 1\n");
+ printf("EDR : 2\n");
+ printf("LE : 3\n");
+ printf("select : ");
+
+ memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ gets(buf);
+
+ int number = buf[0] - '0';
+
+ number = (number < 0 || number > 3) ? 0 : 1 << number;
+
+ if (number & CA_ETHERNET)
+ {
+ return CA_ETHERNET;
+ }
+ if (number & CA_WIFI)
+ {
+ return CA_WIFI;
+ }
+ if (number & CA_EDR)
+ {
+ return CA_EDR;
+ }
+ if (number & CA_LE)
+ {
+ return CA_LE;
+ }
+
+ printf("\n=============================================\n");
+}
+
char get_menu();
void process();
+CAConnectivityType_t get_network_type();
void start_listening_server();
void start_discovery_server();
static const char *gNormalInfoData = "{\"oc\":[{\"href\":\"%s\",\"prop\":{\"rt\":[\"core.led\"],"
"\"if\":[\"oc.mi.def\"],\"obs\":1}}]}";
-
static CADtlsPskCredsBlob_t *pskCredsBlob = NULL;
-
void clearDtlsCredentialInfo()
{
printf("clearDtlsCredentialInfo IN\n");
if (pskCredsBlob)
{
// Initialize sensitive data to zeroes before freeing.
- memset(pskCredsBlob->creds, 0, sizeof(CADtlsPskCreds_t)*(pskCredsBlob->num));
+ memset(pskCredsBlob->creds, 0, sizeof(CADtlsPskCreds_t) * (pskCredsBlob->num));
free(pskCredsBlob->creds);
memset(pskCredsBlob, 0, sizeof(CADtlsPskCredsBlob_t));
pskCredsBlob->num = 1;
- pskCredsBlob->creds = (CADtlsPskCreds_t *)malloc(sizeof(CADtlsPskCreds_t) *(pskCredsBlob->num));
+ pskCredsBlob->creds = (CADtlsPskCreds_t *)malloc(sizeof(CADtlsPskCreds_t) * (pskCredsBlob->num));
memcpy(pskCredsBlob->creds[0].clientIdentity, IDENTITY, DTLS_PSK_ID_LEN);
memcpy(pskCredsBlob->creds[0].rsClientPsk, RS_CLIENT_PSK, DTLS_PSK_PSK_LEN);
void send_request()
{
char secureRequest[2] = {0};
+ CAConnectivityType_t selectedNetwork;
+
+ selectedNetwork = get_network_type();
+
printf("Do you want to send secure request ?.... enter (0/1): ");
gets(secureRequest);
if ('1' == secureRequest[0])
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- if (CA_STATUS_OK != CACreateRemoteEndpoint(uri, &endpoint)
+ if (CA_STATUS_OK != CACreateRemoteEndpoint(uri, selectedNetwork, &endpoint)
|| !endpoint)
{
printf("Failed to create remote endpoint!\n");
return;
}
- endpoint->connectivityType = gSelectedNwType;
+ //endpoint->connectivityType = gSelectedNwType;
char buf[MAX_BUF_LEN];
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
void send_request_all()
{
char buf[MAX_BUF_LEN];
+ CAConnectivityType_t selectedNetwork;
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ selectedNetwork = get_network_type();
+
printf("\n=============================================\n");
printf("10.11.12.13:4545/resource_uri ( for IP )\n");
printf("10:11:12:13:45:45/resource_uri ( for BT )\n");
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- CAResult_t res = CACreateRemoteEndpoint(buf, &endpoint);
+ CAResult_t res = CACreateRemoteEndpoint(buf, selectedNetwork, &endpoint);
if (res != CA_STATUS_OK)
{
void send_notification()
{
char buf[MAX_BUF_LEN];
+ CAConnectivityType_t selectedNetwork;
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ selectedNetwork = get_network_type();
+
printf("\n=============================================\n");
printf("10.11.12.13:4545/resource_uri ( for IP )\n");
printf("10:11:12:13:45:45/resource_uri ( for BT )\n");
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- CAResult_t res = CACreateRemoteEndpoint(buf, &endpoint);
+ CAResult_t res = CACreateRemoteEndpoint(buf, selectedNetwork, &endpoint);
if (res != CA_STATUS_OK)
{
length += strlen(object->addressInfo.IP.ipAddress) + 5; // length of "ipaddress:port"
length += strlen(object->resourceUri) + 1;
- uri = calloc(1,sizeof(char)*length);
+ uri = calloc(1, sizeof(char) * length);
if (!uri)
{
printf("Failed to create new uri\n");
return;
}
- sprintf(uri,"coaps://%s:%d/%s",object->addressInfo.IP.ipAddress,
- securePort, object->resourceUri);
+ sprintf(uri, "coaps://%s:%d/%s", object->addressInfo.IP.ipAddress,
+ securePort, object->resourceUri);
CARemoteEndpoint_t *endpoint = NULL;
- if (CA_STATUS_OK != CACreateRemoteEndpoint(uri,&endpoint))
+ if (CA_STATUS_OK != CACreateRemoteEndpoint(uri, object->connectivityType, &endpoint))
{
printf("Failed to create duplicate of remote endpoint!\n");
return;
}
- endpoint->connectivityType = object->connectivityType;
+ //endpoint->connectivityType = object->connectivityType;
endpoint->isSecured = CA_TRUE;
object = endpoint;
}
printf("Uri: %s\n", object->resourceUri);
printf("Remote Address: %s Port: %d secured:%d\n", object->addressInfo.IP.ipAddress,
object->addressInfo.IP.port, object->isSecured);
+ printf("response result : %d\n", responseInfo->result);
printf("Data: %s\n", responseInfo->info.payload);
if (responseInfo->info.options)
}
}
-void send_response(CARemoteEndpoint_t* endpoint, CAInfo_t* info)
+void send_response(CARemoteEndpoint_t *endpoint, CAInfo_t *info)
{
printf("entering send_response\n");
CAInfo_t responseData;
memset(&responseData, 0, sizeof(CAInfo_t));
responseData.type =
- (info != NULL) ?
- ((info->type == CA_MSG_CONFIRM) ? CA_MSG_ACKNOWLEDGE : CA_MSG_NONCONFIRM) :
- CA_MSG_NONCONFIRM;
+ (info != NULL) ?
+ ((info->type == CA_MSG_CONFIRM) ? CA_MSG_ACKNOWLEDGE : CA_MSG_NONCONFIRM) :
+ CA_MSG_NONCONFIRM;
responseData.messageId = (info != NULL) ? info->messageId : 0;
responseData.token = (info != NULL) ? info->token : "";
responseData.payload = "response payload";
// send request (connectivityType from remoteEndpoint of request Info)
CAResult_t res = CASendResponse(endpoint, &responseInfo);
if (res != CA_STATUS_OK)
- {
+{
printf("send response error\n");
}
else
}
char portStr[4] = {0};
- memcpy(portStr, startPos + 1, (endPos-1) - startPos);
+ memcpy(portStr, startPos + 1, (endPos - 1) - startPos);
printf("secured port is: %s\n", portStr);
return atoi(portStr);
printf("URI: %s, ResourceURI:%s\n", URI, resourceURI);
}
+
+CAConnectivityType_t get_network_type()
+{
+ char buf[MAX_BUF_LEN];
+
+ printf("\n=============================================\n");
+ printf("\tselect network type\n");
+ printf("ETHERNET : 0\n");
+ printf("WIFI : 1\n");
+ printf("EDR : 2\n");
+ printf("LE : 3\n");
+ printf("select : ");
+
+ memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ gets(buf);
+
+ int number = buf[0] - '0';
+
+ number = (number < 0 || number > 3) ? 0 : 1 << number;
+
+ if (number & CA_ETHERNET)
+ {
+ return CA_ETHERNET;
+ }
+ if (number & CA_WIFI)
+ {
+ return CA_WIFI;
+ }
+ if (number & CA_EDR)
+ {
+ return CA_EDR;
+ }
+ if (number & CA_LE)
+ {
+ return CA_LE;
+ }
+
+ printf("\n=============================================\n");
+}
void response_handler(const CARemoteEndpoint_t *object, const CAResponseInfo_t *responseInfo);
void send_request_tmp(CARemoteEndpoint_t *endpoint, CAToken_t token);
void terminate();
+CAConnectivityType_t get_network_type();
+
void pthread_func()
{
char buf[MAX_BUF_LEN];
memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ CAConnectivityType_t selectedNetwork;
+
+ selectedNetwork = get_network_type();
printf("\n=============================================\n");
printf("10.11.12.13:4545/resource_uri ( for IP )\n");
// create remote endpoint
CARemoteEndpoint_t *endpoint = NULL;
- CAResult_t res = CACreateRemoteEndpoint(buf, &endpoint);
+ CAResult_t res = CACreateRemoteEndpoint(buf,selectedNetwork, &endpoint);
if (res != CA_STATUS_OK)
{
{
unselect_network();
}
+
+CAConnectivityType_t get_network_type()
+{
+ char buf[MAX_BUF_LEN];
+
+ printf("\n=============================================\n");
+ printf("\tselect network type\n");
+ printf("ETHERNET : 0\n");
+ printf("WIFI : 1\n");
+ printf("EDR : 2\n");
+ printf("LE : 3\n");
+ printf("select : ");
+
+ memset(buf, 0, sizeof(char) * MAX_BUF_LEN);
+ gets(buf);
+
+ int number = buf[0] - '0';
+
+ number = (number < 0 || number > 3) ? 0 : 1 << number;
+
+ if (number & CA_ETHERNET)
+ {
+ return CA_ETHERNET;
+ }
+ if (number & CA_WIFI)
+ {
+ return CA_WIFI;
+ }
+ if (number & CA_EDR)
+ {
+ return CA_EDR;
+ }
+ if (number & CA_LE)
+ {
+ return CA_LE;
+ }
+
+ printf("\n=============================================\n");
+}
+
return DTLS_FAIL;
}
- int32_t retLen = dtls_write(gCaDtlsContext->dtlsContext, (session_t *)dstSession, data, dataLen);
+ int32_t retLen = dtls_write(gCaDtlsContext->dtlsContext, (session_t *)dstSession, data,
+ dataLen);
OIC_LOG_V(DEBUG, NET_DTLS_TAG, "dtls_write retun len [%d]", retLen);
if (0 == retLen)
{
list_length = u_arraylist_length(gCaDtlsContext->cacheList);
for (list_index = 0; list_index < list_length; list_index++)
{
- stCACacheMessage_t *msg = (stCACacheMessage_t *)u_arraylist_get(gCaDtlsContext->cacheList, list_index);
- CAFreeCacheMsg(msg);
+ stCACacheMessage_t *msg = (stCACacheMessage_t *)u_arraylist_get(gCaDtlsContext->cacheList,
+ list_index);
+ CAFreeCacheMsg(msg);
}
u_arraylist_free(gCaDtlsContext->cacheList);
gCaDtlsContext->cacheList = NULL;
static CABool_t CAIsAddressMatching(const stCADtlsAddrInfo_t *a, const stCADtlsAddrInfo_t *b)
{
if (a->size != b->size || a->addr.sa.sa_family != b->addr.sa.sa_family)
- return CA_FALSE;
+ return CA_FALSE;
if ((a->addr.sin.sin_port == b->addr.sin.sin_port) &&
- (memcmp(&a->addr.sin.sin_addr, &b->addr.sin.sin_addr,
- sizeof(struct in_addr)) == 0))
+ (memcmp(&a->addr.sin.sin_addr, &b->addr.sin.sin_addr,
+ sizeof(struct in_addr)) == 0))
{
- return CA_TRUE;
+ return CA_TRUE;
}
return CA_FALSE;
}
{
OIC_LOG(DEBUG, NET_DTLS_TAG, "IN");
- ///TODO: if dstSession is coming as NULL, what we will do with cached msg.(will be cleared in termination)
+ ///TODO: if dstSession is coming as NULL, what we will do with cached msg.
+ //(will be cleared in termination)
VERIFY_NON_NULL_VOID(dstSession, NET_DTLS_TAG, "Param dstSession is NULL");
uint32_t list_index = 0;
list_length = u_arraylist_length(gCaDtlsContext->cacheList);
for (list_index = 0; list_index < list_length; list_index++)
{
- stCACacheMessage_t *msg = (stCACacheMessage_t *)u_arraylist_get(gCaDtlsContext->cacheList, list_index);
- if((NULL != msg) && (CA_TRUE == CAIsAddressMatching(msg->destSession, dstSession)))
- {
- eDtlsRet_t ret = CAAdapterNetDtlsEncryptInternal(msg->destSession,
+ stCACacheMessage_t *msg = (stCACacheMessage_t *)u_arraylist_get(gCaDtlsContext->cacheList,
+ list_index);
+ if ((NULL != msg) && (CA_TRUE == CAIsAddressMatching(msg->destSession, dstSession)))
+ {
+ eDtlsRet_t ret = CAAdapterNetDtlsEncryptInternal(msg->destSession,
msg->data, msg->dataLen);
- if (ret == DTLS_OK)
- {
- OIC_LOG(DEBUG, NET_DTLS_TAG, "CAAdapterNetDtlsEncryptInternal success");
- }
- else
- {
- OIC_LOG(ERROR, NET_DTLS_TAG, "CAAdapterNetDtlsEncryptInternal failed.");
- }
- u_arraylist_remove(gCaDtlsContext->cacheList, list_index);
- CAFreeCacheMsg(msg);
- break;
- }
+ if (ret == DTLS_OK)
+ {
+ OIC_LOG(DEBUG, NET_DTLS_TAG, "CAAdapterNetDtlsEncryptInternal success");
+ }
+ else
+ {
+ OIC_LOG(ERROR, NET_DTLS_TAG, "CAAdapterNetDtlsEncryptInternal failed.");
+ }
+ u_arraylist_remove(gCaDtlsContext->cacheList, list_index);
+ CAFreeCacheMsg(msg);
+ break;
+ }
}
u_mutex_unlock(gDtlsListMutex);
return 0;
}
- if((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type) &&
- (NULL != gCaDtlsContext->adapterCallbacks[type].recvCallback))
+ if ((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type) &&
+ (NULL != gCaDtlsContext->adapterCallbacks[type].recvCallback))
{
- gCaDtlsContext->adapterCallbacks[type].recvCallback(remoteAddress, port,
- buf, bufLen, CA_TRUE);
+ gCaDtlsContext->adapterCallbacks[type].recvCallback(remoteAddress, port,
+ buf, bufLen, CA_TRUE);
}
else
{
- OIC_LOG_V(DEBUG, NET_DTLS_TAG, "recvCallback Callback or adapter type is wrong [%d]", type );
+ OIC_LOG_V(DEBUG, NET_DTLS_TAG, "recvCallback Callback or adapter type is wrong [%d]", type );
}
u_mutex_unlock(gDtlsContextMutex);
//Mutex is not required for gCaDtlsContext. It will be called in same thread.
int32_t sentLen = 0;
- if((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type) &&
- (NULL != gCaDtlsContext->adapterCallbacks[type].sendCallback))
+ if ((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type) &&
+ (NULL != gCaDtlsContext->adapterCallbacks[type].sendCallback))
{
- sentLen = gCaDtlsContext->adapterCallbacks[type].sendCallback(remoteAddress, port,
- buf, bufLen);
+ sentLen = gCaDtlsContext->adapterCallbacks[type].sendCallback(remoteAddress, port,
+ buf, bufLen);
}
else
{
- OIC_LOG_V(DEBUG, NET_DTLS_TAG, "send Callback or adapter type is wrong [%d]", type );
+ OIC_LOG_V(DEBUG, NET_DTLS_TAG, "send Callback or adapter type is wrong [%d]", type );
}
OIC_LOG_V(DEBUG, NET_DTLS_TAG, "sent buffer length [%d]", sentLen);
int32_t ret = -1;
- CADtlsPskCredsBlob_t * credInfo = NULL;
+ CADtlsPskCredsBlob_t *credInfo = NULL;
CAGetDtlsPskCredentials(&credInfo);
VERIFY_NON_NULL_RET(credInfo, NET_DTLS_TAG, "CAGetDtlsPskCredentials credInfo is NULL", 0);
- if ((type == DTLS_PSK_HINT) || (type == DTLS_PSK_IDENTITY)) {
- if (DTLS_PSK_ID_LEN <= resultLen){
+ if ((type == DTLS_PSK_HINT) || (type == DTLS_PSK_IDENTITY))
+ {
+ if (DTLS_PSK_ID_LEN <= resultLen)
+ {
memcpy(result, credInfo->rsIdentity, DTLS_PSK_ID_LEN);
ret = DTLS_PSK_ID_LEN;
}
}
- if ((type == DTLS_PSK_KEY) && (desc) && (descLen == DTLS_PSK_PSK_LEN)) {
+ if ((type == DTLS_PSK_KEY) && (desc) && (descLen == DTLS_PSK_PSK_LEN))
+ {
int index = 0;
- for (index =0; index < credInfo->num; index++) {
+ for (index = 0; index < credInfo->num; index++)
+ {
if (memcmp(desc, credInfo->creds[index].clientIdentity, DTLS_PSK_ID_LEN) == 0)
{
memcpy(result, credInfo->creds[index].rsClientPsk, DTLS_PSK_PSK_LEN);
}
void CADTLSSetAdapterCallbacks(CAPacketReceivedCallback recvCallback,
- CAPacketSendCallback sendCallback, eDtlsAdapterType_t type)
+ CAPacketSendCallback sendCallback, eDtlsAdapterType_t type)
{
OIC_LOG(DEBUG, NET_DTLS_TAG, "IN");
u_mutex_lock(gDtlsContextMutex);
return;
}
- if((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type))
+ if ((0 <= type) && (MAX_SUPPORTED_ADAPTERS > type))
{
gCaDtlsContext->adapterCallbacks[type].recvCallback = recvCallback;
gCaDtlsContext->adapterCallbacks[type].sendCallback = sendCallback;
}
else
{
- OIC_LOG(ERROR, NET_DTLS_TAG, "CAAdapterNetDtlsInit done already!");
- return CA_STATUS_OK;
+ OIC_LOG(ERROR, NET_DTLS_TAG, "CAAdapterNetDtlsInit done already!");
+ return CA_STATUS_OK;
}
if (NULL == gDtlsListMutex)
OIC_LOG(ERROR, NET_DTLS_TAG, "Context malloc failed");
u_mutex_unlock(gDtlsContextMutex);
u_mutex_free(gDtlsContextMutex);
- u_mutex_free(gDtlsListMutex);
+ u_mutex_free(gDtlsListMutex);
return CA_MEMORY_ALLOC_FAILED;
}
if (NULL == gCaDtlsContext->cacheList)
{
OIC_LOG(ERROR, NET_DTLS_TAG, "cacheList initialization failed!");
- u_mutex_unlock(gDtlsListMutex);
+ u_mutex_unlock(gDtlsListMutex);
u_mutex_unlock(gDtlsContextMutex);
u_mutex_free(gDtlsContextMutex);
- u_mutex_free(gDtlsListMutex);
+ u_mutex_free(gDtlsListMutex);
OICFree(gCaDtlsContext);
gCaDtlsContext = NULL;
return CA_STATUS_FAILED;
OIC_LOG(DEBUG, NET_DTLS_TAG, "IN");
- VERIFY_NON_NULL_RET(remoteAddress, NET_DTLS_TAG, "Param remoteAddress is NULL" , CA_STATUS_FAILED);
+ VERIFY_NON_NULL_RET(remoteAddress, NET_DTLS_TAG,"Param remoteAddress is NULL",CA_STATUS_FAILED);
VERIFY_NON_NULL_RET(data, NET_DTLS_TAG, "Param data is NULL" , CA_STATUS_FAILED);
message->data = buf;
message->dataLen = dataLen;
- message->destSession = addrInfo;
+ message->destSession = addrInfo;
CAResult_t result = CADtlsCacheMsg(message);
if (CA_STATUS_OK == result)
#define CA_ADAPTER_UTILS_TAG "CA_ADAPTER_UTILS"
CALocalConnectivity_t *CAAdapterCreateLocalEndpoint(CAConnectivityType_t type,
- const char *address)
+ const char *address)
{
CALocalConnectivity_t *info = (CALocalConnectivity_t *)
OICMalloc(sizeof(CALocalConnectivity_t));
}
CARemoteEndpoint_t *CAAdapterCreateRemoteEndpoint(CAConnectivityType_t type,
- const char *address,
- const char *resourceUri)
+ const char *address,
+ const char *resourceUri)
{
CARemoteEndpoint_t *info = (CARemoteEndpoint_t *)
OICMalloc(sizeof(CARemoteEndpoint_t));
VERIFY_NON_NULL_RET(ipAddress1, CA_ADAPTER_UTILS_TAG, "First address", false);
VERIFY_NON_NULL_RET(ipAddress2, CA_ADAPTER_UTILS_TAG, "Second address", false);
VERIFY_NON_NULL_RET(netMask, CA_ADAPTER_UTILS_TAG, "netMask", false);
+
int32_t ipList1[8] = {0};
int32_t ipList2[8] = {0};
int32_t maskList[8] = {0};
- /* IP address check */
- if(!ipAddress1 || !ipAddress2)
- {
- return false;
- }
-
/* Local Loopback Address */
- if(0 == strncmp(ipAddress1, "127.", 4)
- || 0 == strncmp(ipAddress2, "127.", 4))
+ if (0 == strncmp(ipAddress1, "127.", 4)
+ || 0 == strncmp(ipAddress2, "127.", 4))
{
return true;
}
int16_t index = 0;
int16_t lastDotIndex = 0;
int16_t ip1TokenCount = 0;
- while('\0' != ipAdrs1[index])
+ while ('\0' != ipAdrs1[index])
{
- if('.' == ipAdrs1[index])
+ if ('.' == ipAdrs1[index])
{
ipAdrs1[index] = '\0';
ipList1[ip1TokenCount++] = atoi(ipAdrs1 + lastDotIndex);
index = 0;
lastDotIndex = 0;
int16_t ip2TokenCount = 0;
- while('\0' != ipAdrs2[index])
+ while ('\0' != ipAdrs2[index])
{
- if('.' == ipAdrs2[index])
+ if ('.' == ipAdrs2[index])
{
ipAdrs2[index] = '\0';
ipList2[ip2TokenCount++] = atoi(ipAdrs2 + lastDotIndex);
index = 0;
lastDotIndex = 0;
int16_t maskTokenCount = 0;
- while('\0' != nMask[index])
+ while ('\0' != nMask[index])
{
- if('.' == nMask[index])
+ if ('.' == nMask[index])
{
nMask[index] = '\0';
maskList[maskTokenCount++] = atoi(nMask + lastDotIndex);
// Add last touple
maskList[maskTokenCount] = atoi(nMask + lastDotIndex);
- if(ip1TokenCount < 3 || ip2TokenCount < 3 || maskTokenCount < 3)
- {
+ if (ip1TokenCount < 3 || ip2TokenCount < 3 || maskTokenCount < 3)
+ {
OIC_LOG_V(ERROR, CA_ADAPTER_UTILS_TAG, "Address or mask is invalid!");
- return false;
- }
+ OICFree(ipAdrs1);
+ OICFree(ipAdrs2);
+ OICFree(nMask);
+ return false;
+ }
OICFree(ipAdrs1);
OICFree(ipAdrs2);
OICFree(nMask);
- if(((ipList1[0]& maskList[0]) == (ipList2[0]& maskList[0]))
- &&((ipList1[1]& maskList[1]) == (ipList2[1]& maskList[1]))
- &&((ipList1[2]& maskList[2]) == (ipList2[2]& maskList[2]))
- &&((ipList1[3]& maskList[3]) == (ipList2[3]& maskList[3])))
+ if (((ipList1[0]& maskList[0]) == (ipList2[0]& maskList[0]))
+ && ((ipList1[1]& maskList[1]) == (ipList2[1]& maskList[1]))
+ && ((ipList1[2]& maskList[2]) == (ipList2[2]& maskList[2]))
+ && ((ipList1[3]& maskList[3]) == (ipList2[3]& maskList[3])))
{
return true;
}
return false;
-}
\ No newline at end of file
+}
******************************************************************/
#include "camsgparser.h"
-#include "cacommon.h"
-#include "caadapterutils.h"
#include <string.h>
+#include <math.h>
+
+#include "cacommon.h"
+#include "caadapterutils.h"
#define CA_MSG_PARSER_TAG "CA_MSG_PARSER"
{
if (i > 7)
{
- *header |= 1 << i - 8;
+ *header |= (1 << (i - 8));
}
else
{
- *(header + 1) |= 1 << i;
+ *(header + 1) |= (1 << i);
}
}
else
{
if (i > 7)
{
- *header |= 0 << i - 8 ;
+ *header |= (0 << (i - 8));
}
else
{
- *(header + 1) |= 0 << i;
+ *(header + 1) |= (0 << i);
}
}
}
{
OIC_LOG(DEBUG, CA_MSG_PARSER_TAG, "IN");
- VERIFY_NON_NULL(*data, NULL, "Param data is NULL");
+ VERIFY_NON_NULL(data, NULL, "Param data is NULL");
uint32_t length = 0;
OIC_LOG_V(DEBUG, CA_MSG_PARSER_TAG, "generatingf the Header info");
char *header = (char *) OICMalloc(sizeof(char) * CA_HEADER_LENGTH);
- VERIFY_NON_NULL_RET(*header, CA_MSG_PARSER_TAG, "Malloc failed", -1);
+ VERIFY_NON_NULL_RET(header, CA_MSG_PARSER_TAG, "Malloc failed", -1);
+
*dataSegment = (char *) OICMalloc(sizeof(char) * length);
+ ///TODO: memory leak is here.
VERIFY_NON_NULL_RET(*dataSegment, CA_MSG_PARSER_TAG, "Malloc failed", -1);
memset(header, 0x0, sizeof(char) * CA_HEADER_LENGTH );
* @brief This function sends data to specified remote bluetooth device.
*/
static CAResult_t CABTManagerSendUnicastData(const char *remoteAddress, const char *serviceUUID,
- void *data, uint32_t dataLength, uint32_t *sentLength);
+ void *data, uint32_t dataLength, uint32_t *sentLength);
/**
* @fn CABTManagerSendMulticastData
* @brief This function sends data to all bluetooth devices running OIC service.
*/
static CAResult_t CABTManagerSendMulticastData(const char *serviceUUID, void *data,
- uint32_t dataLength, uint32_t *sentLength);
+ uint32_t dataLength, uint32_t *sentLength);
/**
* @fn CABTGetAdapterEnableState
}
u_mutex_unlock(gBTDeviceListMutex);
- /* //Create RemoteEndPoint
- CARemoteEndpoint_t *remoteEndpoint = NULL;
- remoteEndpoint = CAAdapterCreateRemoteEndpoint(CA_EDR, device->remoteAddress,
- OIC_BT_SERVICE_ID);
- if (NULL == remoteEndpoint)
- {
- OIC_LOG_V(ERROR, BLUETOOTH_ADAPTER_TAG, "Failed to crate remote endpoint!");
- return;
- }
-
- void *copyData = OICMalloc(data->data_size);
- if (NULL == copyData)
- {
- OIC_LOG_V(ERROR, BLUETOOTH_ADAPTER_TAG, "Failed allocate memory!");
- CAAdapterFreeRemoteEndpoint(remoteEndpoint);
- return;
- }
- memcpy(copyData, data->data, data->data_size);
- */
uint32_t sentLength = 0;
CAResult_t res = CABTManagerPushDataToReceiverQueue(device->remoteAddress, OIC_BT_SERVICE_ID,
data->data, (uint32_t)data->data_size, &sentLength);
-// gNetworkPacketReceivedCallback(remoteEndpoint, copyData, (uint32_t)data->data_size);
+
+ OIC_LOG_V(ERROR, BLUETOOTH_ADAPTER_TAG, "CABTManagerPushDataToReceiverQueue done [%d]", res);
OIC_LOG_V(DEBUG, BLUETOOTH_ADAPTER_TAG, "OUT");
}
*/
CAResult_t CABTManagerGetInterface(CALocalConnectivity_t **info);
-#if 0
/**
* @fn CABTManagerReadData
* @brief All received data will be notified to upper layer.
*
*/
CAResult_t CABTManagerReadData(void);
-#endif
#ifdef __cplusplus
} /* extern "C" */
#include "jni.h"
#include "caleadapter.h"
#include "calecore.h"
+#include "caleserver.h"
#include "logger.h"
-#define TAG PCF("CA")
+#define TAG PCF("CA_LE_ADAPTER")
+
+// GATT server type
+static jboolean gIsBluetoothGattServer;
// received packet callback
static CANetworkPacketReceivedCallback gLEReceivedCallback = NULL;
registerCallback(handler, CA_LE);
CALEInitialize(handle);
+ CALEServerInitialize(handle);
+
CALESetCallback(CALEPacketReceiveCallback);
+ CALEServerSetCallback(CALEPacketReceiveCallback);
return CA_STATUS_OK;
}
CAResult_t CAStartLE()
{
OIC_LOG_V(DEBUG, TAG, "CAStartLE");
- CANativeLEStartScan();
+ //CANativeLEStartScan();
return CA_STATUS_OK;
}
CAResult_t CAStartLEListeningServer()
{
OIC_LOG_V(DEBUG, TAG, "CAStartLEListeningServer");
- CALEStartMulticastServer();
-// CALESendMulticastMessage("1111", 4);
+
+ gIsBluetoothGattServer = JNI_TRUE;
+
+ // start gatt service
+ CALEServerStartMulticastServer();
return CA_STATUS_OK;
}
CAResult_t CAStartLEDiscoveryServer()
{
OIC_LOG_V(DEBUG, TAG, "CAStartLEDiscoveryServer");
+
+ gIsBluetoothGattServer = JNI_FALSE;
+
+ // start scan through gatt client
CALEStartMulticastServer();
return CA_STATUS_OK;
uint32_t CASendLEUnicastData(const CARemoteEndpoint_t* endpoint, void* data, uint32_t dataLen)
{
- OIC_LOG_V(DEBUG, TAG, "CASendLEUnicastData");
-// "E1:70:56:CF:E3:67"
- CALESendUnicastMessage(endpoint->addressInfo.BT.btMacAddress, data, dataLen);
+
+ if(gIsBluetoothGattServer == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CALESendUnicastData");
+ CALESendUnicastMessage(endpoint->addressInfo.BT.btMacAddress, data, dataLen);
+ }
+ else if(gIsBluetoothGattServer == JNI_TRUE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendUnicastData");
+ CALEServerSendUnicastMessage(endpoint->addressInfo.BT.btMacAddress, data, dataLen);
+ }
return 0;
}
uint32_t CASendLEMulticastData(void* data, uint32_t dataLen)
{
- OIC_LOG_V(DEBUG, TAG, "CASendLEMulticastData");
- CALESendMulticastMessage(data, dataLen);
+ if(gIsBluetoothGattServer == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CASendLEMulticastData");
+ CALESendMulticastMessage(data, dataLen);
+ }
+ else if(gIsBluetoothGattServer == JNI_TRUE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendMulticastMessage");
+ CALEServerSendMulticastMessage(data, dataLen);
+ }
return 0;
}
uint32_t CASendLENotification(const CARemoteEndpoint_t* endpoint, void* data, uint32_t dataLen)
{
- OIC_LOG_V(DEBUG, TAG, "CASendLENotification");
- CALESendUnicastMessage(endpoint->addressInfo.BT.btMacAddress, data, dataLen);
+ if(gIsBluetoothGattServer == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "not server mode");
+ return -1;
+ }
+ else if(gIsBluetoothGattServer == JNI_TRUE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendLEUnicastData");
+ CALEServerSendUnicastMessage(endpoint->addressInfo.BT.btMacAddress, data, dataLen);
+ }
return 0;
}
CAResult_t CAStopLE()
{
- OIC_LOG_V(DEBUG, TAG, "CAStopLE");
- CANativeLEStopScan();
+
+ if(gIsBluetoothGattServer == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CA Stop LE Scan");
+ CANativeLEStopScan();
+ }
+ else if(gIsBluetoothGattServer == JNI_TRUE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "CA Stop Gatt Server");
+ }
return CA_STATUS_OK;
}
void CATerminateLE()
{
- OIC_LOG_V(DEBUG, TAG, "TerminatLE");
- CALETerminate();
+ if(gIsBluetoothGattServer == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Terminat Gatt Client");
+ CALETerminate();
+ }
+ else if(gIsBluetoothGattServer == JNI_TRUE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Terminat Gatt Server");
+ CALEServerTerminate();
+ }
}
#include <stdio.h>
#include <string.h>
#include <jni.h>
+
#include "calecore.h"
+#include "caleserver.h"
#include "logger.h"
#include "oic_malloc.h"
#include "uthreadpool.h" /* for thread pool */
+#include "umutex.h"
#include "uarraylist.h"
#include "com_iotivity_jar_CALeInterface.h"
+//#define DEBUG_MODE
#define TAG PCF("CA")
-#define METHODID_OBJECTNONPARAM "()Landroid/bluetooth/BluetoothAdapter;"
-#define METHODID_INTNONPARAM "()I"
-#define METHODID_STRINGNONPARAM "()Ljava/lang/String;"
-#define METHODID_OBJECT_STRINGUUIDPARAM "(Ljava/lang/String;Ljava/util/UUID;)Ljava/lang/Object;"
-#define METHODID_ONRESPONSE_PARAM "(Ljava/lang/String;)V"
-#define CLASSPATH_BT_ADPATER "android/bluetooth/BluetoothAdapter"
-#define CLASSPATH_BT_UUID "java/util/UUID"
+static const char *METHODID_OBJECTNONPARAM = "()Landroid/bluetooth/BluetoothAdapter;";
+static const char *METHODID_STRINGNONPARAM = "()Ljava/lang/String;";
+static const char *CLASSPATH_BT_ADPATER = "android/bluetooth/BluetoothAdapter";
+static const char *CLASSPATH_BT_UUID = "java/util/UUID";
+static const char *CLASSPATH_BT_GATT = "android/bluetooth/BluetoothGatt";
+
+static const char *IOTIVITY_GATT_SERVIE_UUID = "713d0000-503e-4c75-ba94-3148f18d941e";
+static const char *IOTIVITY_GATT_TX_UUID = "713d0003-503e-4c75-ba94-3148f18d941e";
+static const char *IOTIVITY_GATT_RX_UUID = "713d0002-503e-4c75-ba94-3148f18d941e";
static const uint32_t STATE_CONNECTED = 2;
static const uint32_t STATE_DISCONNECTED = 0;
static jobject gLeScanCallback;
static jobject gLeGattCallback;
static jobject gContext;
+static jobjectArray gUUIDList;
static jboolean gIsStartServer;
+static jboolean gIsFinishSendData;
+
+static jbyteArray gSendBuffer;
+static uint32_t gTargetCnt = 0;
+static uint32_t gCurrentSentCnt = 0;
+
+/** mutex for synchrnoization **/
+static u_mutex gThreadMutex;
+/** conditional mutex for synchrnoization **/
+static u_cond gThreadCond;
////////////////////////////////////////////////////////////////////////////////////////////////////
+//FIXME getting context
+void CALEClientJNISetContext(JNIEnv *env, jobject context)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEClientJNISetContext");
+
+ if(context == NULL)
+ {
+ OIC_LOG_V(DEBUG, TAG, "context is null");
+ return;
+ }
+
+ gContext = (*env)->NewGlobalRef(env, context);
+}
+
void CALeCreateJniInterfaceObject()
{
OIC_LOG_V(DEBUG, TAG, "CALeCreateJniInterfaceObject");
return;
}
- jobject jni_instance = (*env)->NewObject(env, LeJniInterface, LeInterfaceConstructorMethod);
- gContext = (*env)->NewGlobalRef(env, jni_instance);
+ (*env)->NewObject(env, LeJniInterface, LeInterfaceConstructorMethod);
OIC_LOG_V(DEBUG, TAG, "Create CALeInterface instance");
if(isAttached)
}
g_jvm = jvm; /* cache the JavaVM pointer */
+ //FIXME
//JVM required for WifiCore to work with JNI interface
CAWiFiJniInit(jvm);
+ CALeServerJniInit(env, jvm);
return JNI_VERSION_1_6;
}
{
OIC_LOG_V(DEBUG, TAG, "CALeGattConnectionStateChangeCallback - status %d, newstate %d", status, newstate);
- if(GATT_SUCCESS == status && STATE_CONNECTED == newstate)
+ if(GATT_SUCCESS == status && STATE_CONNECTED == newstate) // le connected
{
if(gatt) {
CANativeAddGattobjToList(env, gatt);
CANativeLEDiscoverServices(env, gatt);
}
}
- else if (GATT_SUCCESS == status && STATE_DISCONNECTED == newstate)
+ else if (GATT_SUCCESS == status && STATE_DISCONNECTED == newstate) // le disconnected
{
if(gatt) {
+#ifdef DEBUG_MODE
+ CANativeGattClose(env, gatt);
CANativeRemoveGattObj(env, gatt);
+#endif
}
}
+ else // other error
+ {
+ CANativeSendFinsih(env, gatt);
+ }
}
/*
{
OIC_LOG_V(DEBUG, TAG, "CALeGattServicesDiscoveredCallback - status %d: ", status);
- if(0 == status)
+ if(0 != status) // discovery error
{
- jboolean ret = CANativeSetCharacteristicNoti(env, gatt);
- if(1 == ret)
- {
- jstring data = (*env)->NewStringUTF(env, "HelloWorld");
- jobject jni_obj_character = CANativeCreateGattCharacteristic(env, gatt, data);
- if(jni_obj_character)
- {
- CANativeLESendData(env, gatt, jni_obj_character);
- }
- }
+ CANativeSendFinsih(env, gatt);
+ return;
+ }
+
+ // read Characteristic
+// CANativeReadCharacteristic(env, gatt);
+
+ jboolean ret_rx = CANativeSetCharacteristicNoti(env, gatt, IOTIVITY_GATT_RX_UUID);
+ if(!ret_rx) // SetCharacteristicNoti is failed
+ {
+ CANativeSendFinsih(env, gatt);
+ return;
+ }
+
+// jstring data = (*env)->NewStringUTF(env, "HelloWorld");
+ jobject jni_obj_character = CANativeCreateGattCharacteristic(env, gatt, gSendBuffer);
+ if(!jni_obj_character)
+ {
+ CANativeSendFinsih(env, gatt);
+ return;
+ }
+
+ sleep(1);
+ jboolean ret = CANativeLESendData(env, gatt, jni_obj_character);
+ if(!ret)
+ {
+ CANativeSendFinsih(env, gatt);
+ return;
}
}
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;I)V
*/
JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicReadCallback
- (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jstring data, jint status)
+ (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jbyteArray data, jint status)
{
OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicReadCallback - status : %d", status);
- const char* readData = (*env)->GetStringUTFChars(env, data, NULL);
+ jboolean isCopy;
+ char* readData = (char*)(*env)->GetByteArrayElements(env, data, &isCopy);
+
+ jstring jni_address = CANativeGetAddressFromGattObj(env, gatt);
+ const char* address = (*env)->GetStringUTFChars(env, jni_address, NULL);
+
OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicReadCallback - read data : %s", readData);
+
+ gPacketReceiveCallback(address, readData);
}
/*
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;I)V
*/
JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicWriteCallback
- (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jstring data, jint status)
+ (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jbyteArray data, jint status)
{
OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicWriteCallback - status : %d", status);
- const char* writeData = (*env)->GetStringUTFChars(env, data, NULL);
+ jboolean isCopy;
+ char* writeData = (char*)(*env)->GetByteArrayElements(env, data, &isCopy);
+
+ jstring jni_address = CANativeGetAddressFromGattObj(env, gatt);
+ const char* address = (*env)->GetStringUTFChars(env, jni_address, NULL);
+
+ gPacketReceiveCallback(address, writeData);
+
OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicWriteCallback - write data : %s", writeData);
-// jobjectArray jni_obj_data_array = CANativeGetValueFromCharacteristic(env, characteristic);
-// if(!jni_obj_data_array)
-// {
-// OIC_LOG_V(DEBUG, TAG, "jni_obj_data_array is null");
-// return;
-// }
+#ifdef DEBUG_MODE
+ CANativeSendFinsih(env, gatt);
+#endif
+
+ if(0 != status)
+ {
+ CANativeSendFinsih(env, gatt);
+ }
}
/*
* Signature: (Landroid/bluetooth/BluetoothGatt;Landroid/bluetooth/BluetoothGattCharacteristic;)V
*/
JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattCharacteristicChangedCallback
- (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jstring data)
+ (JNIEnv *env, jobject obj, jobject gatt, jobject characteristic, jbyteArray data)
{
OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicChangedCallback");
- const char* changedData = (*env)->GetStringUTFChars(env, data, NULL);
- OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicChangedCallback - data : %s", changedData);
+ jboolean isCopy;
+ char* NotificationData = (char*)(*env)->GetByteArrayElements(env, data, &isCopy);
+
+ jstring jni_address = CANativeGetAddressFromGattObj(env, gatt);
+ const char* address = (*env)->GetStringUTFChars(env, jni_address, NULL);
- CANativeLEDisconnect(env, gatt);
+ OIC_LOG_V(DEBUG, TAG, "CALeGattCharacteristicChangedCallback - data : %s", NotificationData);
+
+ gPacketReceiveCallback(address, NotificationData);
+
+ CANativeSendFinsih(env, gatt);
}
/*
gThreadPoolHandle = handle;
+ // init mutex for send logic
+ gThreadMutex = u_mutex_new();
+ gThreadCond = u_cond_new();
+
+ CANativeCreateUUIDList();
+
CALeCreateJniInterfaceObject(); /* create java CALeInterface instance*/
}
if(gLeScanCallback)
{
CANativeLEStopScanImpl(env, gLeScanCallback);
+ sleep(1);
}
if(gLeScanCallback)
(*env)->DeleteGlobalRef(env, gContext);
}
+ if(gSendBuffer)
+ {
+ (*env)->DeleteGlobalRef(env, gSendBuffer);
+ }
+
+ if(gUUIDList)
+ {
+ (*env)->DeleteGlobalRef(env, gUUIDList);
+ }
+
CANativeRemoveAllDevices(env);
CANativeRemoveAllGattObjsList(env);
gIsStartServer = FALSE;
+ gIsFinishSendData = FALSE;
if(isAttached)
(*g_jvm)->DetachCurrentThread(g_jvm);
+ // delete mutex object
+ u_mutex_free(gThreadMutex);
+ gThreadMutex = NULL;
+ u_cond_free(gThreadCond);
+}
+
+void CANativeSendFinsih(JNIEnv *env, jobject gatt)
+{
+ OIC_LOG_V(DEBUG, TAG, "CANativeSendFinsih");
+
+ if(gatt)
+ {
+ CANativeLEDisconnect(env, gatt);
+ }
+ CANativeupdateSendCnt(env);
}
int32_t CALESendUnicastMessage(const char* address, const char* data, uint32_t dataLen)
if(res != JNI_OK)
{
OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
- return;
+ return 0;
}
isAttached = TRUE;
}
if(isAttached)
(*g_jvm)->DetachCurrentThread(g_jvm);
- return 0;
+ return 1;
}
int32_t CALEStartUnicastServer(const char* address)
if(res != JNI_OK)
{
OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
- return;
+ return 0;
}
isAttached = TRUE;
}
gIsStartServer = TRUE;
+
CANativeLEStartScan();
if(isAttached)
if(res != JNI_OK)
{
OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
- return;
+ return 0;
}
isAttached = TRUE;
}
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] set byteArray for data");
+ if(gSendBuffer)
+ {
+ (*env)->DeleteGlobalRef(env, gSendBuffer);
+ }
+ jbyteArray jni_arr = (*env)->NewByteArray(env, dataLen);
+ (*env)->SetByteArrayRegion(env, jni_arr, 0, dataLen, (jbyte*)data);
+ gSendBuffer = (jbyteArray)(*env)->NewGlobalRef(env, jni_arr);
+
// connect to gatt server
CANativeLEStopScanImpl(env, gLeScanCallback);
+ sleep(1);
jobject jni_obj_bluetoothDevice = NULL;
if(gContext && gdeviceList)
if(!jarrayObj)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jarrayObj is null");
- return;
+ return 0;
}
jstring jni_setAddress = CANativeGetAddressFromBTDevice(env, jarrayObj);
if(!jni_setAddress)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_setAddress is null");
- return;
+ return 0;
}
const char* setAddress = (*env)->GetStringUTFChars(env, jni_setAddress, NULL);
if(isAttached)
(*g_jvm)->DetachCurrentThread(g_jvm);
- return 0;
+ return 1;
}
int32_t CALESendMulticastMessageImpl(JNIEnv *env, const char* data, uint32_t dataLen)
{
- OIC_LOG_V(DEBUG, TAG, "CASendMulticastMessageImpl, send to, data: %s", data);
+ OIC_LOG_V(DEBUG, TAG, "CASendMulticastMessageImpl, send to, data: %s, %d", data, dataLen);
if(!gdeviceList)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] gdeviceList is null");
return 0;
}
+ OIC_LOG(DEBUG, TAG, "set wait");
+
+
+ gIsFinishSendData = FALSE;
+
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] set byteArray for data");
+ if(gSendBuffer)
+ {
+ (*env)->DeleteGlobalRef(env, gSendBuffer);
+ }
+ jbyteArray jni_arr = (*env)->NewByteArray(env, dataLen);
+ (*env)->SetByteArrayRegion(env, jni_arr, 0, dataLen, (jbyte*)data);
+ gSendBuffer = (jbyteArray)(*env)->NewGlobalRef(env, jni_arr);
// connect to gatt server
CANativeLEStopScanImpl(env, gLeScanCallback);
+ sleep(1);
// reset gatt list
CANativeRemoveAllGattObjsList(env);
CANativeCreateGattObjList(env);
- jint index;
- for (index = 0; index < u_arraylist_length(gdeviceList); index++)
- {
+ gTargetCnt = u_arraylist_length(gdeviceList);
+
+ jint index = 0;
+
+ while (index < u_arraylist_length(gdeviceList)) {
jobject jarrayObj = (jobject) u_arraylist_get(gdeviceList, index);
if(!jarrayObj)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jarrayObj is null");
- return;
+ continue;
}
- CANativeLEConnect(env, jarrayObj, gContext, FALSE, gLeGattCallback);
+ if(0 <= CANativeLEConnect(env, jarrayObj, gContext, FALSE, gLeGattCallback))
+ {
+ // connection failure
+ index++;
+ }
sleep(1);
}
-// CANativeLEStartScan();
- return 0;
+ // wait for finish to send data through "CALeGattServicesDiscoveredCallback"
+ if(!gIsFinishSendData)
+ {
+ u_mutex_lock(gThreadMutex);
+ u_cond_wait(gThreadCond, gThreadMutex);
+ OIC_LOG(DEBUG, TAG, "unset wait");
+ u_mutex_unlock(gThreadMutex);
+ }
+
+ // start LE Scan again
+#ifdef DEBUG_MODE
+ CANativeLEStartScan();
+#endif
+
+ return 1;
}
/**
if (jni_fid_stateon == 0)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] get_field_state is 0");
- return;
+ return -1;
}
jint jni_int_val = (*env)->GetStaticIntField(env, jni_cid_BTAdapter, jni_fid_stateon);
if(!jni_cid_device_list)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_device_list is null");
- return 0;
+ return NULL;
}
jmethodID jni_mid_getAddress = (*env)->GetMethodID(env, jni_cid_device_list, "getAddress",
if(!jni_mid_getAddress)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_getAddress is null");
- return 0;
+ return NULL;
}
jstring jni_address = (jstring)(*env)->CallObjectMethod(env, bluetoothDevice, jni_mid_getAddress);
if(!jni_address)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_address is null");
- return 0;
+ return NULL;
}
return jni_address;
}
if(!gatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] gatt is null");
- return 0;
+ return NULL;
}
- jclass jni_cid_gattdevice_list = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_gattdevice_list = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_gattdevice_list)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_gattdevice_list is null");
- return 0;
+ return NULL;
}
jmethodID jni_mid_getDevice = (*env)->GetMethodID(env, jni_cid_gattdevice_list, "getDevice",
if(!jni_mid_getDevice)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_getDevice is null");
- return 0;
+ return NULL;
}
jobject jni_obj_device = (*env)->CallObjectMethod(env, gatt, jni_mid_getDevice);
if(!jni_obj_device)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_device is null");
- return 0;
+ return NULL;
}
jstring jni_address = CANativeGetAddressFromBTDevice(env, jni_obj_device);
if(!jni_address)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_address is null");
- return 0;
+ return NULL;
}
return jni_address;
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] get BluetoothGatt class");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
return;
}
- OIC_LOG(DEBUG, TAG, "[BLE][Native] get gatt disconnect method");
jmethodID jni_mid_closeGatt = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "close","()V");
if(!jni_mid_closeGatt)
{
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] CANativeLEStartScan");
- // create new object array
- jclass jni_cid_uuid_list = (*env)->FindClass(env, CLASSPATH_BT_UUID);
- if(!jni_cid_uuid_list)
- {
- OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_uuid_list is null");
- return;
- }
-
- jobjectArray jni_obj_uuid_list = (jobjectArray)(*env)->NewObjectArray(env, 1, jni_cid_uuid_list, NULL);
- if(!jni_obj_uuid_list)
- {
- OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_uuid_list is null");
- return;
- }
-
- // remove previous list and create list again
- CANativeRemoveAllDevices(env);
- CANativeCreateScanDeviceList(env);
-
- // make uuid list
- jobject jni_obj_uuid = CANativeGetUUIDObject(env, "713d0000-503e-4c75-ba94-3148f18d941e");
- (*env)->SetObjectArrayElement(env, jni_obj_uuid_list, 0, jni_obj_uuid);
// scan gatt server with UUID
- if(gLeScanCallback && jni_obj_uuid_list)
+ if(gLeScanCallback && gUUIDList)
{
-// CANativeLEStartScanWithUUIDImpl(env, jni_obj_uuid_list, gLeScanCallback);
- CANativeLEStartScanImpl(env, gLeScanCallback);
+ CANativeLEStartScanWithUUIDImpl(env, gUUIDList, gLeScanCallback);
}
if(isAttached)
jboolean jni_obj_startLeScan = (*env)->CallBooleanMethod(env, jni_obj_BTAdapter, jni_mid_startLeScan, callback);
if(!jni_obj_startLeScan)
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: jni_obj_startLeScan is null");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan is failed");
return;
}
else
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan..");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan is started");
}
}
jboolean jni_obj_startLeScan = (*env)->CallBooleanMethod(env, jni_obj_BTAdapter, jni_mid_startLeScan, uuids, callback);
if(!jni_obj_startLeScan)
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: jni_obj_startLeScan is null");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan With UUID is failed");
return;
}
else
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan..");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] startLeScan With UUID is started");
}
}
jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter, jni_mid_getDefaultAdapter);
if(!jni_obj_BTAdapter)
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] getState From BTAdapter: jni_obj_BTAdapter is null");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_BTAdapter is null");
return;
}
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - request to stop LE Scan");
// call start le scan method
(*env)->CallVoidMethod(env, jni_obj_BTAdapter, jni_mid_stopLeScan, callback);
}
-void CANativeLEConnect(JNIEnv *env, jobject bluetoothDevice, jobject context, jboolean autoconnect, jobject callback)
+int32_t CANativeLEConnect(JNIEnv *env, jobject bluetoothDevice, jobject context,
+ jboolean autoconnect, jobject callback)
{
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return 0;
}
jstring jni_address = CANativeGetAddressFromBTDevice(env, bluetoothDevice);
if(!jni_cid_BluetoothDevice)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: jni_cid_BluetoothDevice is null");
- return;
+ return 0;
}
// get connectGatt method
OIC_LOG(DEBUG, TAG, "[BLE][Native] get connectGatt method");
- jmethodID jni_mid_connectGatt = (*env)->GetMethodID(env, jni_cid_BluetoothDevice, "connectGatt",
+ jmethodID jni_mid_connectGatt = (*env)->GetMethodID(env, jni_cid_BluetoothDevice,
+ "connectGatt",
"(Landroid/content/Context;ZLandroid/bluetooth/BluetoothGattCallback;)Landroid/bluetooth/BluetoothGatt;");
if(!jni_mid_connectGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: jni_mid_connectGatt is null");
- return;
+ return 0;
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] Call object method - connectGatt");
- jobject jni_obj_connectGatt = (*env)->CallObjectMethod(env, bluetoothDevice, jni_mid_connectGatt, context, autoconnect, callback);
+ jobject jni_obj_connectGatt = (*env)->CallObjectMethod(env, bluetoothDevice,
+ jni_mid_connectGatt, NULL, autoconnect, callback);
if(!jni_obj_connectGatt)
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: connectGatt was failed..obj will be removed");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - connectGatt was failed.obj will be removed");
CANativeRemoveDevice(env, jni_address);
- return;
+ CANativeupdateSendCnt(env);
+ return -1;
}
else
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] bleConnect: connecting..");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - connecting..");
}
+ return 1;
}
void CANativeLEDisconnect(JNIEnv *env, jobject bluetoothGatt)
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] get BluetoothGatt classjobject bluetoothGatt");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] get gatt disconnect method");
- jmethodID jni_mid_disconnectGatt = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "disconnect","()V");
+ jmethodID jni_mid_disconnectGatt = (*env)->GetMethodID(env, jni_cid_BluetoothGatt,
+ "disconnect","()V");
if(!jni_mid_disconnectGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_disconnectGatt is null");
}
// call disconnect gatt method
- OIC_LOG(DEBUG, TAG, "[BLE][Native] request disconnectGatt");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - request disconnectGatt");
(*env)->CallVoidMethod(env, bluetoothGatt, jni_mid_disconnectGatt);
}
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] get BluetoothGatt class");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] discovery gatt services method");
- jmethodID jni_mid_discoverServices = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "discoverServices","()Z");
+ jmethodID jni_mid_discoverServices = (*env)->GetMethodID(env, jni_cid_BluetoothGatt,
+ "discoverServices","()Z");
if(!jni_mid_discoverServices)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_discoverServices is null");
return;
}
// call disconnect gatt method
- OIC_LOG(DEBUG, TAG, "[BLE][Native] request discovery gatt services");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - request discovery gatt services");
(*env)->CallBooleanMethod(env, bluetoothGatt, jni_mid_discoverServices);
}
-void CANativeLESendData(JNIEnv *env, jobject bluetoothGatt, jobject gattCharacteristic)
+jboolean CANativeLESendData(JNIEnv *env, jobject bluetoothGatt, jobject gattCharacteristic)
{
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return FALSE;
}
// WRITE GATT CHARACTERISTIC
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] get BluetoothGatt class");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
- return;
+ return FALSE;
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] write characteristic method");
- jmethodID jni_mid_writeCharacteristic = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "writeCharacteristic", "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z");
+ jmethodID jni_mid_writeCharacteristic = (*env)->GetMethodID(env, jni_cid_BluetoothGatt,
+ "writeCharacteristic", "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z");
if(!jni_mid_writeCharacteristic)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_writeCharacteristic is null");
- return;
+ return FALSE;
}
// call disconnect gatt method
- OIC_LOG(DEBUG, TAG, "[BLE][Native] request write gatt characteristic");
- (*env)->CallBooleanMethod(env, bluetoothGatt, jni_mid_writeCharacteristic, gattCharacteristic);
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - request to write gatt characteristic");
+ jboolean ret = (jboolean)(*env)->CallBooleanMethod(env, bluetoothGatt, jni_mid_writeCharacteristic, gattCharacteristic);
+ if(ret)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] writeCharacteristic is success");
+ }
+ else
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] writeCharacteristic is failed");
+ return FALSE;
+ }
+ return TRUE;
+}
+void CANativeReadCharacteristic(JNIEnv *env, jobject bluetoothGatt)
+{
+
+ if(!CANativeIsEnableBTAdapter(env))
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
+ return;
+ }
+
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
+ if(!jni_cid_BluetoothGatt)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
+ return;
+ }
+
+ jstring jni_uuid = (*env)->NewStringUTF(env, IOTIVITY_GATT_RX_UUID);
+ jobject jni_obj_GattCharacteristic = CANativeGetGattService(env, bluetoothGatt, jni_uuid);
+ if(!jni_obj_GattCharacteristic)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_GattCharacteristic is null");
+ return;
+ }
+
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] read characteristic method");
+ jmethodID jni_mid_readCharacteristic = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "readCharacteristic", "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z");
+ if(!jni_mid_readCharacteristic)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_readCharacteristic is null");
+ return;
+ }
+
+ // call disconnect gatt method
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - request to read gatt characteristic");
+ jboolean ret = (*env)->CallBooleanMethod(env, bluetoothGatt, jni_mid_readCharacteristic, jni_obj_GattCharacteristic);
+ if(ret)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] readCharacteristic is success");
+ }
+ else
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] readCharacteristic is failed");
+ }
}
-jboolean CANativeSetCharacteristicNoti(JNIEnv *env, jobject bluetoothGatt)
+jboolean CANativeSetCharacteristicNoti(JNIEnv *env, jobject bluetoothGatt, const char* uuid)
{
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return FALSE;
}
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] CANativeSetCharacteristicNoti");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
return FALSE;
}
- jobject jni_obj_GattCharacteristic = CANativeGetGattService(env, bluetoothGatt, "713d0002-503e-4c75-ba94-3148f18d941e");
+ jstring jni_uuid = (*env)->NewStringUTF(env, uuid);
+ jobject jni_obj_GattCharacteristic = CANativeGetGattService(env, bluetoothGatt, jni_uuid);
if(!jni_obj_GattCharacteristic)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_GattCharacteristic is null");
}
// set Characteristic Notification
- OIC_LOG(DEBUG, TAG, "[BLE][Native] get gatt disconnect method");
jmethodID jni_mid_setNotification = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "setCharacteristicNotification",
"(Landroid/bluetooth/BluetoothGattCharacteristic;Z)Z");
if(!jni_mid_setNotification)
jboolean ret = (*env)->CallBooleanMethod(env, bluetoothGatt, jni_mid_setNotification, jni_obj_GattCharacteristic, enable);
if(1 == ret)
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] setCharacteristicNotification is success");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - setCharacteristicNotification is success");
return TRUE;
}
else
{
- OIC_LOG(DEBUG, TAG, "[BLE][Native] setCharacteristicNotification is failed");
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] CALL API - setCharacteristicNotification is failed");
return FALSE;
}
}
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return NULL;
}
// get BluetoothGatt class
OIC_LOG(DEBUG, TAG, "[BLE][Native] CANativeGetGattService");
- jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, "android/bluetooth/BluetoothGatt");
+ jclass jni_cid_BluetoothGatt = (*env)->FindClass(env, CLASSPATH_BT_GATT);
if(!jni_cid_BluetoothGatt)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_BluetoothGatt is null");
return NULL;
}
- OIC_LOG(DEBUG, TAG, "[BLE][Native] get gatt disconnect method");
jmethodID jni_mid_getService = (*env)->GetMethodID(env, jni_cid_BluetoothGatt, "getService",
"(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattService;");
if(!jni_mid_getService)
return NULL;
}
- jobject jni_obj_service_uuid = CANativeGetUUIDObject(env, "713d0000-503e-4c75-ba94-3148f18d941e");
+ jobject jni_obj_service_uuid = CANativeGetUUIDObject(env, IOTIVITY_GATT_SERVIE_UUID);
if(!jni_obj_service_uuid)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_service_uuid is null");
return NULL;
}
- // get
OIC_LOG(DEBUG, TAG, "[BLE][Native] request to get Characteristic");
jobject jni_obj_GattCharacteristic = (*env)->CallObjectMethod(env, jni_obj_gattService, jni_mid_getCharacteristic, jni_obj_tx_uuid);
return jni_obj_GattCharacteristic;
}
-jobject CANativeCreateGattCharacteristic(JNIEnv *env, jobject bluetoothGatt, jstring data)
+jobject CANativeCreateGattCharacteristic(JNIEnv *env, jobject bluetoothGatt, jbyteArray data)
{
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return NULL;
}
OIC_LOG(DEBUG, TAG, "[BLE][Native] CANativeCreateGattCharacteristic");
- jobject jni_obj_GattCharacteristic = CANativeGetGattService(env, bluetoothGatt, "713d0003-503e-4c75-ba94-3148f18d941e");
+ jstring jni_uuid = (*env)->NewStringUTF(env, IOTIVITY_GATT_TX_UUID);
+ jobject jni_obj_GattCharacteristic = CANativeGetGattService(env, bluetoothGatt, jni_uuid);
if(!jni_obj_GattCharacteristic)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_GattCharacteristic is null");
OIC_LOG(DEBUG, TAG, "[BLE][Native] set value in Characteristic");
jmethodID jni_mid_setValue = (*env)->GetMethodID(env, jni_cid_BTGattCharacteristic, "setValue",
- "(Ljava/lang/String;)Z");
+ "([B)Z");
if(!jni_mid_setValue)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_mid_setValue is null");
if(!CANativeIsEnableBTAdapter(env))
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] BT adpater is not enable");
- return;
+ return NULL;
}
jclass jni_cid_BTGattCharacteristic = (*env)->FindClass(env, "android/bluetooth/BluetoothGattCharacteristic");
return jni_obj_data_array;
}
+
+void CANativeCreateUUIDList()
+{
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
+ }
+
+ // create new object array
+ jclass jni_cid_uuid_list = (*env)->FindClass(env, CLASSPATH_BT_UUID);
+ if(!jni_cid_uuid_list)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_cid_uuid_list is null");
+ return;
+ }
+
+ jobjectArray jni_obj_uuid_list = (jobjectArray)(*env)->NewObjectArray(env, 1, jni_cid_uuid_list, NULL);
+ if(!jni_obj_uuid_list)
+ {
+ OIC_LOG(DEBUG, TAG, "[BLE][Native] jni_obj_uuid_list is null");
+ return;
+ }
+
+ // remove previous list and create list again
+ CANativeRemoveAllDevices(env);
+ CANativeCreateScanDeviceList(env);
+
+ // make uuid list
+ jobject jni_obj_uuid = CANativeGetUUIDObject(env, IOTIVITY_GATT_SERVIE_UUID);
+ (*env)->SetObjectArrayElement(env, jni_obj_uuid_list, 0, jni_obj_uuid);
+
+ gUUIDList = (jobjectArray)(*env)->NewGlobalRef(env, jni_obj_uuid_list);
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+}
+
void CANativeCreateScanDeviceList(JNIEnv *env)
{
OIC_LOG(DEBUG, TAG, "[BLE][Native] CANativeCreateScanDeviceList");
gGattObjectList->size--;
gGattObjectList->length--;
}
+
+void CANativeupdateSendCnt(JNIEnv *env)
+{
+ // mutex lock
+ u_mutex_lock(gThreadMutex);
+
+ gCurrentSentCnt++;
+
+ if(gTargetCnt <= gCurrentSentCnt)
+ {
+ gTargetCnt = 0;
+ gCurrentSentCnt = 0;
+
+ if(gSendBuffer)
+ {
+ (*env)->DeleteGlobalRef(env, gSendBuffer);
+ gSendBuffer = NULL;
+ }
+ // notity the thread
+ u_cond_signal(gThreadCond);
+ gIsFinishSendData = TRUE;
+ OIC_LOG(DEBUG, TAG, "set signal for send data");
+ }
+ // mutex unlock
+ u_mutex_unlock(gThreadMutex);
+}
--- /dev/null
+#include <jni.h>
+#include <stdio.h>
+#include <android/log.h>
+#include "logger.h"
+#include "com_iotivity_jar_CALeInterface.h"
+
+#define TAG PCF("CA_LE_SERVER")
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+static JavaVM *g_jvm;
+static jobject gContext;
+
+//FIXME getting context
+void CALENetworkMonitorJNISetContext(JNIEnv *env, jobject context)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALENetworkMonitorJNISetContext");
+
+ if(context == NULL)
+ OIC_LOG_V(DEBUG, TAG, "context is null");
+
+ gContext = (*env)->NewGlobalRef(env, context);
+}
+
+//FIXME getting jvm
+void CALeNetworkMonitorJniInit(JNIEnv *env, JavaVM *jvm)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeNetworkMonitorJniInit");
+ g_jvm = jvm;
+}
+
+void CALEStartNetworkMonitor(JNIEnv *env, jobject obj)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEStartNetworkMonitor");
+
+ jclass jni_cls_CALeInterface = (*env)->FindClass(env, "com/iotivity/jar/CALeInterface");
+ if (!jni_cls_CALeInterface)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get CALeInterface class");
+ return;
+ }
+
+ jmethodID jni_mid_getActionStateIntentFilter = (*env)->GetStaticMethodID(env,
+ jni_cls_CALeInterface, "getActionStateIntentFilter",
+ "()Landroid/content/IntentFilter;");
+ if (!jni_mid_getActionStateIntentFilter)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get getActionStateIntentFilter method");
+ return;
+ }
+
+ jobject jni_obj_intentFilter = (*env)->CallStaticObjectMethod(env, jni_cls_CALeInterface,
+ jni_mid_getActionStateIntentFilter);
+ if (!jni_obj_intentFilter)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get jni_obj_intentFilter");
+ return;
+ }
+
+ jclass jni_cls_context = (*env)->FindClass(env, "Landroid/contect/Context");
+ if (!jni_cls_context)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get jni_cls_context class");
+ return;
+ }
+
+ jmethodID jni_mid_registerReceiver = (*env)->GetMethodID(env, jni_cls_context, "registerReceiver",
+ "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
+ if (!jni_mid_registerReceiver)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get jni_mid_registerReceiver method");
+ return;
+ }
+
+ jmethodID jni_obj_registerReceiver = (*env)->CallObjectMethod(env, gContext, jni_mid_registerReceiver, obj, jni_obj_intentFilter);
+ if (!jni_obj_registerReceiver)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get jni_obj_registerReceiver method");
+ return;
+ }
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeStateChangedCallback
+(JNIEnv *env, jobject obj, jint status)
+{
+ // STATE_ON:12, STATE_OFF:10
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - State Changed Callback(%d)", status);
+}
#include <jni.h>
#include <stdio.h>
+#include <string.h>
#include <android/log.h>
+#include "caleserver.h"
+#include "caleutils.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "uthreadpool.h"
+#include "uarraylist.h"
#include "com_iotivity_jar_CALeInterface.h"
-#define LOG_TAG "jni_BLE_Server"
+#define TAG PCF("CA_LE_SERVER")
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+/* Service UUID */
+//static const char *OIC_GATT_SERVICE_UUID = "000018f3-0000-1000-8000-00805f9b34fb";
+//static const char *OIC_GATT_CHARACTERISTIC_RESPONSE_UUID = "00002af5-0000-1000-8000-00805f9b34fb"; //read
+//static const char *OIC_GATT_CHARACTERISTIC_REQUEST_UUID = "00002af6-0000-1000-8000-00805f9b34fb"; //write
+
static const char *OIC_GATT_SERVICE_UUID = "713d0000-503e-4c75-ba94-3148f18d941e";
-static const char *OIC_GATT_CHARACTERISTIC_RESPONSE_UUID = "713d0002-503e-4c75-ba94-3148f18d941e";
-static const char *OIC_GATT_CHARACTERISTIC_REQUEST_UUID = "713d0003-503e-4c75-ba94-3148f18d941e";
+static const char *OIC_GATT_CHARACTERISTIC_RESPONSE_UUID = "713d0002-503e-4c75-ba94-3148f18d941e"; //read
+static const char *OIC_GATT_CHARACTERISTIC_REQUEST_UUID = "713d0003-503e-4c75-ba94-3148f18d941e"; //write
+static JavaVM *g_jvm;
static jobject gContext;
static jobject gBluetoothGattServer;
static jobject gBluetoothGattServerCallback;
-static jobject gAdvertiseCallback;
+static jobject gLeAdvertiseCallback;
+
+static CAPacketReceiveCallback gPacketReceiveCallback = NULL;
+static u_arraylist_t *gConnectedDeviceList = NULL;
+static u_thread_pool_t gThreadPoolHandle = NULL;
+
+static jboolean gIsStartServer;
+static jboolean gResposeInProgress;
-jobject getUuidFromString(JNIEnv *env, const char* uuid)
+//FIXME getting context
+void CALEServerJNISetContext(JNIEnv *env, jobject context)
{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerJNISetContext");
- LOGI("[BLE Server] getUuidFromString");
+ if(context == NULL)
+ OIC_LOG_V(DEBUG, TAG, "context is null");
- jclass jni_cid_UUID = (*env)->FindClass(env, "java/util/UUID");
- if (!jni_cid_UUID)
- {
- LOGI("[BLE Server] jni_cid_UUID is null");
- return NULL;
- }
+ gContext = (*env)->NewGlobalRef(env, context);
+}
+
+//FIXME getting jvm
+void CALeServerJniInit(JNIEnv *env, JavaVM *jvm)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeServerJniInit");
+ g_jvm = jvm;
+}
+
+jobject CALEServerSetResponseData(JNIEnv *env, jbyteArray responseData)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSetResponseData");
- jmethodID jni_mid_fromString = (*env)->GetStaticMethodID(env, jni_cid_UUID, "fromString",
- "(Ljava/lang/String;)Ljava/util/UUID;");
- if (!jni_mid_fromString)
+ jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothGattServer");
+
+ jclass jni_cid_bluetoothGattService = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothGattService");
+
+ jclass jni_cid_bluetoothGattCharacteristic = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothGattCharacteristic");
+
+ jmethodID jni_mid_getService = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattServer, "getService",
+ "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattService;");
+
+ jobject jni_obj_serviceUUID = CALEGetUuidFromString(env, OIC_GATT_SERVICE_UUID);
+
+ if (!gBluetoothGattServer)
{
- LOGI("[BLE Server] jni_mid_fromString is null");
+ OIC_LOG_V(DEBUG, TAG, "Check BluetoothGattServer status");
return NULL;
}
+ jobject jni_obj_bluetoothGattService = (*env)->CallObjectMethod(env,
+ gBluetoothGattServer, jni_mid_getService, jni_obj_serviceUUID);
- jstring str_serviceUUID = (*env)->NewStringUTF(env, uuid);
- LOGI("[BLE Server] uuid: %s", uuid);
- jobject jni_obj_serviceUUID = (*env)->CallStaticObjectMethod(env, jni_cid_UUID,
- jni_mid_fromString, str_serviceUUID);
- if (!jni_obj_serviceUUID)
- {
- LOGI("[BLE][Server] jni_obj_serviceUUID is null");
- return NULL;
+ jmethodID jni_mid_getCharacteristic =
+ (*env)->GetMethodID(env, jni_cid_bluetoothGattService,
+ "getCharacteristic",
+ "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattCharacteristic;");
+
+ jobject jni_obj_responseUUID = CALEGetUuidFromString(env,
+ OIC_GATT_CHARACTERISTIC_RESPONSE_UUID);
+
+ jobject jni_obj_bluetoothGattCharacteristic = (*env)->CallObjectMethod(env,
+ jni_obj_bluetoothGattService, jni_mid_getCharacteristic,
+ jni_obj_responseUUID);
+
+ jmethodID jni_mid_setValue = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattCharacteristic, "setValue",
+ "([B)Z");
+
+ jboolean jni_boolean_setValue = (*env)->CallBooleanMethod(env,
+ jni_obj_bluetoothGattCharacteristic, jni_mid_setValue,
+ responseData);
+
+ if (jni_boolean_setValue == JNI_FALSE) {
+ OIC_LOG_V(DEBUG, TAG, "Fail to set response data");
}
- return jni_obj_serviceUUID;
+ return jni_obj_bluetoothGattCharacteristic;
}
-jobject getParcelUuid(JNIEnv *env, jobject uuid)
+jboolean CALEServerSendResponseData(JNIEnv *env, jobject device, jobject responseData)
{
- jclass jni_cid_ParcelUuid = (*env)->FindClass(env, "android/os/ParcelUuid");
- if (!jni_cid_ParcelUuid)
- {
- LOGI("[BLE Server] jni_cid_ParcelUuid is null");
- return;
- }
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendResponseData");
- jmethodID jni_mid_ParcelUuid = (*env)->GetMethodID(env, jni_cid_ParcelUuid, "<init>",
- "(Ljava/util/UUID;)V");
- if (!jni_mid_ParcelUuid)
- {
- LOGI("[BLE Server] jni_mid_ParcelUuid is null");
- return;
- }
+ jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothGattServer");
- jobject jni_ParcelUuid = (*env)->NewObject(env, jni_cid_ParcelUuid, jni_mid_ParcelUuid, uuid);
- if (!jni_ParcelUuid)
- {
- LOGI("[BLE Server] jni_ParcelUuid is null");
- return;
+ jmethodID jni_mid_notifyCharacteristicChanged =
+ (*env)->GetMethodID(env, jni_cid_bluetoothGattServer,
+ "notifyCharacteristicChanged",
+ "(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothGattCharacteristic;Z)Z");
+
+ jboolean jni_boolean_notifyCharacteristicChanged =
+ (*env)->CallBooleanMethod(env, gBluetoothGattServer,
+ jni_mid_notifyCharacteristicChanged, device, responseData,
+ JNI_TRUE);
+
+ if (jni_boolean_notifyCharacteristicChanged == JNI_FALSE) {
+ OIC_LOG_V(DEBUG, TAG, "Fail to notify characteristic");
}
- return jni_ParcelUuid;
+ return jni_boolean_notifyCharacteristicChanged;
}
-jobject setData(JNIEnv *env, char* responseData)
+jboolean CALEServerSendResponse(JNIEnv *env, jobject device, jint requestId, jint status,
+ jint offset, jbyteArray value)
{
- LOGI("[BLE Server] set response");
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendResponse");
jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
"android/bluetooth/BluetoothGattServer");
+
+ jmethodID jni_mid_sendResponse = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattServer, "sendResponse",
+ "(Landroid/bluetooth/BluetoothDevice;III[B)Z");
+
+ jboolean jni_boolean_sendResponse = (*env)->CallBooleanMethod(env, gBluetoothGattServer,
+ jni_mid_sendResponse, device, requestId, status, offset, value);
+
+ if (jni_boolean_sendResponse == JNI_FALSE) {
+ OIC_LOG_V(DEBUG, TAG, "Fail to send response for gatt characteristic write request");
+ }
+
+ return jni_boolean_sendResponse;
+}
+
+void LEServerStartAdvertise(JNIEnv *env, jobject advertiseCallback)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "LEServerStartAdvertise");
+
+ jclass jni_cid_AdvertiseSettings = (*env)->FindClass(env,
+ "android/bluetooth/le/AdvertiseSettings$Builder");
+
+ jclass jni_cid_AdvertiseDataBuilder = (*env)->FindClass(env,
+ "android/bluetooth/le/AdvertiseData$Builder");
+
+ jclass jni_cid_BTAdapter = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothAdapter");
+
+ jclass jni_cid_leAdvertiser = (*env)->FindClass(env,
+ "android/bluetooth/le/BluetoothLeAdvertiser");
+
+ jmethodID jni_mid_AdvertiseSettings = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseSettings, "<init>", "()V");
+
+ jmethodID jni_mid_setAdvertiseMode = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseSettings, "setAdvertiseMode",
+ "(I)Landroid/bluetooth/le/AdvertiseSettings$Builder;");
+
+ jmethodID jni_mid_setConnectable = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseSettings, "setConnectable",
+ "(Z)Landroid/bluetooth/le/AdvertiseSettings$Builder;");
+
+ jmethodID jni_mid_setTimeout = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseSettings, "setTimeout",
+ "(I)Landroid/bluetooth/le/AdvertiseSettings$Builder;");
+
+ jmethodID jni_mid_AdvertiseDataBuilder = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseDataBuilder, "<init>", "()V");
+
+ jmethodID jni_mid_addServiceUuid =
+ (*env)->GetMethodID(env, jni_cid_AdvertiseDataBuilder,
+ "addServiceUuid",
+ "(Landroid/os/ParcelUuid;)Landroid/bluetooth/le/AdvertiseData$Builder;");
+
+ jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env,
+ jni_cid_BTAdapter, "getDefaultAdapter",
+ "()Landroid/bluetooth/BluetoothAdapter;");
+
+ jmethodID jni_mid_getBluetoothLeAdvertiser = (*env)->GetMethodID(env,
+ jni_cid_BTAdapter, "getBluetoothLeAdvertiser",
+ "()Landroid/bluetooth/le/BluetoothLeAdvertiser;");
+
+ jmethodID jni_mid_build_LeAdvertiseSettings = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseSettings, "build",
+ "()Landroid/bluetooth/le/AdvertiseSettings;");
+
+ jmethodID jni_mid_build_LeAdvertiseData = (*env)->GetMethodID(env,
+ jni_cid_AdvertiseDataBuilder, "build",
+ "()Landroid/bluetooth/le/AdvertiseData;");
+
+ jmethodID jni_mid_startAdvertising =
+ (*env)->GetMethodID(env, jni_cid_leAdvertiser, "startAdvertising",
+ "(Landroid/bluetooth/le/AdvertiseSettings;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseCallback;)V");
+
+ jobject jni_AdvertiseSettings = (*env)->NewObject(env,
+ jni_cid_AdvertiseSettings, jni_mid_AdvertiseSettings);
+
+ jobject jni_obj_setAdvertiseMode = (*env)->CallObjectMethod(env,
+ jni_AdvertiseSettings, jni_mid_setAdvertiseMode, 0); // 0: Low power, 1: Balanced
+
+ jobject jni_obj_setConnectable = (*env)->CallObjectMethod(env,
+ jni_AdvertiseSettings, jni_mid_setConnectable, JNI_TRUE);
+
+ jobject jni_obj_setTimeout = (*env)->CallObjectMethod(env,
+ jni_AdvertiseSettings, jni_mid_setTimeout, 0); //A value of 0 will disable the time limit
+
+ jobject jni_AdvertiseDataBuilder = (*env)->NewObject(env,
+ jni_cid_AdvertiseDataBuilder, jni_mid_AdvertiseDataBuilder);
+
+ jobject jni_obj_serviceUUID = CALEGetUuidFromString(env, OIC_GATT_SERVICE_UUID);
+
+ jobject jni_ParcelUuid = CALEGetParcelUuid(env, jni_obj_serviceUUID);
+
+ jobject jni_obj_addServiceUuid = (*env)->CallObjectMethod(env,
+ jni_AdvertiseDataBuilder, jni_mid_addServiceUuid, jni_ParcelUuid);
+
+ jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env,
+ jni_cid_BTAdapter, jni_mid_getDefaultAdapter);
+
+ jobject jni_obj_getBluetoothLeAdvertiser = (*env)->CallObjectMethod(env,
+ jni_obj_BTAdapter, jni_mid_getBluetoothLeAdvertiser);
+
+ jobject jni_obj_build_LeAdvertiseSettings = (*env)->CallObjectMethod(env,
+ jni_AdvertiseSettings, jni_mid_build_LeAdvertiseSettings);
+
+ jobject jni_obj_build_LeAdvertiseData = (*env)->CallObjectMethod(env,
+ jni_AdvertiseDataBuilder, jni_mid_build_LeAdvertiseData);
+
+ (*env)->CallVoidMethod(env, jni_obj_getBluetoothLeAdvertiser,
+ jni_mid_startAdvertising, jni_obj_build_LeAdvertiseSettings,
+ jni_obj_build_LeAdvertiseData, advertiseCallback);
+
+ OIC_LOG_V(DEBUG, TAG, "Advertising started!!");
+}
+
+jboolean CALEStartGattServer(JNIEnv *env, jobject gattServerCallback)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEStartGattServer");
+
+ if(gIsStartServer)
+ OIC_LOG_V(DEBUG, TAG, "Gatt server already started");
+
+ gBluetoothGattServerCallback = (*env)->NewGlobalRef(env, gattServerCallback);
+
+ // open gatt server
+ jobject bluetoothGattServer = CALEServerOpenGattServer(env);
+ gBluetoothGattServer = (*env)->NewGlobalRef(env, bluetoothGattServer);
+
+ // create gatt service
+ jobject bluetoothGattService = CALEServerCreateGattService(env);
+
+ // add gatt service
+ return CALEServerAddGattService(env, bluetoothGattServer, bluetoothGattService);
+}
+
+jobject CALEServerOpenGattServer(JNIEnv *env)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEServerOpenGattServer");
+
+ jclass jni_cid_context = (*env)->FindClass(env, "android/content/Context");
+
+ jclass jni_cid_bluetoothManager = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothManager");
+
+ jfieldID jni_fid_bluetoothService = (*env)->GetStaticFieldID(env,
+ jni_cid_context, "BLUETOOTH_SERVICE", "Ljava/lang/String;");
+
+ jmethodID jni_mid_getSystemService = (*env)->GetMethodID(env,
+ jni_cid_context, "getSystemService",
+ "(Ljava/lang/String;)Ljava/lang/Object;");
+
+ jmethodID jni_mid_openGattServer =
+ (*env)->GetMethodID(env, jni_cid_bluetoothManager, "openGattServer",
+ "(Landroid/content/Context;Landroid/bluetooth/BluetoothGattServerCallback;)Landroid/bluetooth/BluetoothGattServer;");
+
+ jobject jni_obj_bluetoothService = (*env)->GetStaticObjectField(env,
+ jni_cid_context, jni_fid_bluetoothService);
+
+ jobject jni_obj_bluetoothManager = (*env)->CallObjectMethod(env, gContext,
+ jni_mid_getSystemService, jni_obj_bluetoothService);
+
+ jobject jni_obj_bluetoothGattServer = (*env)->CallObjectMethod(env,
+ jni_obj_bluetoothManager, jni_mid_openGattServer, gContext,
+ gBluetoothGattServerCallback);
+
+ return jni_obj_bluetoothGattServer;
+}
+
+jobject CALEServerCreateGattService(JNIEnv *env)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEServerCreateGattService");
+
jclass jni_cid_bluetoothGattService = (*env)->FindClass(env,
"android/bluetooth/BluetoothGattService");
+
jclass jni_cid_bluetoothGattCharacteristic = (*env)->FindClass(env,
"android/bluetooth/BluetoothGattCharacteristic");
- LOGI("[BLE Server] get classes");
- jmethodID jni_mid_getService = (*env)->GetMethodID(env, jni_cid_bluetoothGattService,
- "getService", "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattService;");
- LOGI("[BLE Server] get methodID");
+ jfieldID jni_fid_serviceType = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothGattService, "SERVICE_TYPE_PRIMARY", "I");
- jobject jni_obj_serviceUUID = getUuidFromString(env, OIC_GATT_SERVICE_UUID);
- LOGI("[BLE Server] get serviceUUID");
+ jfieldID jni_fid_readProperties = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothGattCharacteristic, "PROPERTY_READ", "I");
- if (!gBluetoothGattServer)
+ jfieldID jni_fid_writeProperties = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothGattCharacteristic, "PROPERTY_WRITE", "I");
+
+ jfieldID jni_fid_readPermissions = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothGattCharacteristic, "PERMISSION_READ", "I");
+
+ jfieldID jni_fid_writePermissions = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothGattCharacteristic, "PERMISSION_WRITE", "I");
+
+ jmethodID jni_mid_bluetoothGattService = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattService, "<init>", "(Ljava/util/UUID;I)V");
+
+ jmethodID jni_mid_addCharacteristic = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattService, "addCharacteristic",
+ "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z");
+
+ jmethodID jni_mid_bluetoothGattCharacteristic = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattCharacteristic, "<init>",
+ "(Ljava/util/UUID;II)V");
+
+ jobject jni_obj_serviceUUID = CALEGetUuidFromString(env, OIC_GATT_SERVICE_UUID);
+
+ jobject jni_obj_serviceType = (*env)->GetStaticObjectField(env,
+ jni_cid_bluetoothGattService, jni_fid_serviceType);
+
+ jobject jni_bluetoothGattService = (*env)->NewObject(env,
+ jni_cid_bluetoothGattService, jni_mid_bluetoothGattService,
+ jni_obj_serviceUUID, jni_obj_serviceType);
+
+ jobject jni_obj_readUuid = CALEGetUuidFromString(env,
+ OIC_GATT_CHARACTERISTIC_RESPONSE_UUID);
+
+ jint jni_int_readProperties = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothGattCharacteristic, jni_fid_readProperties);
+
+ jint jni_int_readPermissions = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothGattCharacteristic, jni_fid_readPermissions);
+
+ jobject jni_readCharacteristic = (*env)->NewObject(env,
+ jni_cid_bluetoothGattCharacteristic,
+ jni_mid_bluetoothGattCharacteristic, jni_obj_readUuid,
+ jni_int_readProperties, jni_int_readPermissions);
+
+ jboolean jni_boolean_addReadCharacteristic = (*env)->CallBooleanMethod(env,
+ jni_bluetoothGattService, jni_mid_addCharacteristic,
+ jni_readCharacteristic);
+
+ jobject jni_obj_writeUuid = CALEGetUuidFromString(env,
+ OIC_GATT_CHARACTERISTIC_REQUEST_UUID);
+
+ jint jni_int_writeProperties = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothGattCharacteristic, jni_fid_writeProperties);
+
+ jint jni_int_writePermissions = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothGattCharacteristic, jni_fid_writePermissions);
+
+ jobject jni_writeCharacteristic = (*env)->NewObject(env,
+ jni_cid_bluetoothGattCharacteristic,
+ jni_mid_bluetoothGattCharacteristic, jni_obj_writeUuid,
+ jni_int_writeProperties, jni_int_writePermissions);
+
+ jboolean jni_boolean_addWriteCharacteristic = (*env)->CallBooleanMethod(env,
+ jni_bluetoothGattService, jni_mid_addCharacteristic,
+ jni_writeCharacteristic);
+
+ if (jni_boolean_addWriteCharacteristic == JNI_FALSE)
{
- LOGI("[BLE Server] gBluetoothGattServer is null");
+ OIC_LOG_V(DEBUG, TAG, "Fail to add jni_boolean_addReadCharacteristic");
return NULL;
}
- jobject jni_obj_bluetoothGattService = (*env)->CallObjectMethod(env, gBluetoothGattServer,
- jni_mid_getService, jni_obj_serviceUUID);
- LOGI("[BLE Server] jni_obj_bluetoothGattService");
+ return jni_bluetoothGattService;
+}
- jmethodID jni_mid_getCharacteristic = (*env)->GetMethodID(env, jni_cid_bluetoothGattServer,
- "getCharacteristic",
- "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothGattCharacteristic;");
+jboolean CALEServerAddGattService(JNIEnv *env, jobject bluetoothGattServer,
+ jobject bluetoothGattService)
+{
- jobject jni_obj_responseUUID = getUuidFromString(env, OIC_GATT_CHARACTERISTIC_RESPONSE_UUID);
- jobject jni_obj_bluetoothGattCharacteristic = (*env)->CallObjectMethod(env,
- jni_obj_bluetoothGattService, jni_mid_getCharacteristic, jni_obj_responseUUID);
- LOGI("[BLE Server] jni_obj_bluetoothGattCharacteristic");
+ OIC_LOG_V(DEBUG, TAG, "CALEServerAddGattService");
- jmethodID jni_mid_setValue = (*env)->GetMethodID(env, jni_cid_bluetoothGattCharacteristic,
- "setValue", "(Ljava/land/String;)Z");
- jstring str_responseData = (*env)->NewStringUTF(env, responseData);
- jboolean jni_boolean_setValue = (*env)->CallBooleanMethod(env,
- jni_obj_bluetoothGattCharacteristic, jni_mid_setValue, str_responseData);
+ jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothGattServer");
- return jni_obj_bluetoothGattCharacteristic;
-}
+ jmethodID jni_mid_addService = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattServer, "addService",
+ "(Landroid/bluetooth/BluetoothGattService;)Z");
-jboolean sendData(JNIEnv *env, jobject device, jobject responseData)
-{
+ jboolean jni_boolean_addService = (*env)->CallBooleanMethod(env, bluetoothGattServer,
+ jni_mid_addService, bluetoothGattService);
- if (!device)
+ if (jni_boolean_addService == JNI_FALSE)
{
- LOGI("[BLE Server] device is null");
- return JNI_FALSE;
+ OIC_LOG_V(DEBUG, TAG, "Fail to add gatt service");
}
- if (!responseData)
- {
- LOGI("[BLE Server] responseData is null");
- return JNI_FALSE;
- }
+ return jni_boolean_addService;
+}
+
+jboolean CALEServerConnect(JNIEnv *env, jobject bluetoothDevice)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEConnect");
jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
"android/bluetooth/BluetoothGattServer");
- jmethodID jni_mid_notifyCharacteristicChanged =
- (*env)->GetMethodID(env, jni_cid_bluetoothGattServer, "notifyCharacteristicChanged",
- "(Landroid/bluetooth/BluetoothDevice;Landroid/bluetooth/BluetoothGattCharacteristic;Z)Z");
- jboolean jni_boolean_notifyCharacteristicChanged = (*env)->CallBooleanMethod(env,
- gBluetoothGattServer, jni_mid_notifyCharacteristicChanged, device, responseData,
- JNI_TRUE);
+ jmethodID jni_mid_connect = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattServer, "connect",
+ "(Landroid/bluetooth/BluetoothDevice;Z)Z");
+
+ jboolean jni_boolean_connect = (*env)->CallBooleanMethod(env,
+ gBluetoothGattServer, jni_mid_connect, bluetoothDevice, JNI_FALSE);
- if (!jni_boolean_notifyCharacteristicChanged)
- return JNI_FALSE;
+ if(jni_boolean_connect == JNI_FALSE) {
+ OIC_LOG_V(DEBUG, TAG, "Fail to connect");
+ }
- return JNI_TRUE;
+ return jni_boolean_connect;
}
-jboolean sendResponse(JNIEnv *env, jobject device, jint requestId, jint status, jint offset)
+void CALEServerDisconnect(JNIEnv *env, jobject bluetoothDevice)
{
+ OIC_LOG_V(DEBUG, TAG, "CALEDisconnect");
+
jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
"android/bluetooth/BluetoothGattServer");
- jmethodID jni_mid_sendResponse = (*env)->GetMethodID(env, jni_cid_bluetoothGattServer,
- "sendResponse", "(Landroid/bluetooth/BluetoothDevice;III)Z");
- (*env)->CallVoidMethod(env, gBluetoothGattServer, jni_mid_sendResponse, device, requestId,
- status, offset);
- LOGI("[BLE Server] sendResponse");
+ jmethodID jni_mid_cancelConnection = (*env)->GetMethodID(env,
+ jni_cid_bluetoothGattServer, "cancelConnection",
+ "(Landroid/bluetooth/BluetoothDevice;)V");
+
+ (*env)->CallVoidMethod(env, gBluetoothGattServer, jni_mid_cancelConnection,
+ bluetoothDevice);
}
-void startAdvertize(JNIEnv *env, jobject advertiseCallback)
+jboolean CALEServerSend(JNIEnv *env, jobject bluetoothDevice, jbyteArray responseData)
{
- jclass jni_cid_AdvertiseSettings = (*env)->FindClass(env,
- "android/bluetooth/le/AdvertiseSettings$Builder");
+ OIC_LOG_V(DEBUG, TAG, "CALESend");
+
+ jobject responseChar = CALEServerSetResponseData(env, responseData);
- jmethodID jni_mid_AdvertiseSettings = (*env)->GetMethodID(env, jni_cid_AdvertiseSettings,
- "<init>", "()V");
+ jboolean result = CALEServerSendResponseData(env, bluetoothDevice, responseChar);
- jobject jni_AdvertiseSettings = (*env)->NewObject(env, jni_cid_AdvertiseSettings,
- jni_mid_AdvertiseSettings);
- if (!jni_AdvertiseSettings)
+ if(result == JNI_FALSE)
{
- LOGI("[BLE Server] jni_AdvertiseSettings is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Fail to send response data");
}
- jmethodID jni_mid_setConnectable = (*env)->GetMethodID(env, jni_cid_AdvertiseSettings,
- "setConnectable", "(Z)Landroid/bluetooth/le/AdvertiseSettings$Builder;");
+ return result;
+}
+
+void CALeServerCreateJniInterfaceObject()
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeServerCreateJniInterfaceObject");
- jobject jni_obj_setConnectable = (*env)->CallObjectMethod(env, jni_AdvertiseSettings,
- jni_mid_setConnectable, JNI_TRUE);
- if (!jni_obj_setConnectable)
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
{
- LOGI("[BLE][Server] jni_obj_setConnectable is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
}
- jmethodID jni_mid_setTimeout = (*env)->GetMethodID(env, jni_cid_AdvertiseSettings, "setTimeout",
- "(I)Landroid/bluetooth/le/AdvertiseSettings$Builder;");
+ // initialize le server code
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+}
- jobject jni_obj_setTimeout = (*env)->CallObjectMethod(env, jni_AdvertiseSettings,
- jni_mid_setTimeout, 10000);
- if (!jni_obj_setTimeout)
+void CALEServerInitialize(u_thread_pool_t handle)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerInitialize");
+
+ gResposeInProgress = JNI_FALSE;
+
+ gThreadPoolHandle = handle;
+
+ CALEServerCreateCachedDeviceList();
+
+ //CALeServerCreateJniInterfaceObject();
+}
+
+void CALEServerTerminate()
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerTerminate");
+
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
{
- LOGI("[BLE][Server] jni_obj_setTimeout is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
}
- jclass jni_cid_AdvertiseDataBuilder = (*env)->FindClass(env,
- "android/bluetooth/le/AdvertiseData$Builder");
+ CALEServerRemoveAllDevices(env);
- jmethodID jni_mid_AdvertiseDataBuilder = (*env)->GetMethodID(env, jni_cid_AdvertiseDataBuilder,
- "<init>", "()V");
+ if(gLeAdvertiseCallback)
+ {
+ (*env)->DeleteGlobalRef(env, gLeAdvertiseCallback);
+ }
- jobject jni_AdvertiseDataBuilder = (*env)->NewObject(env, jni_cid_AdvertiseDataBuilder,
- jni_mid_AdvertiseDataBuilder);
- if (!jni_AdvertiseDataBuilder)
+ if(gBluetoothGattServer)
{
- LOGI("[BLE Server] jni_AdvertiseDataBuilder is null");
- return;
+ (*env)->DeleteGlobalRef(env, gBluetoothGattServer);
}
- jobject jni_obj_serviceUUID = getUuidFromString(env, OIC_GATT_SERVICE_UUID);
- jobject jni_ParcelUuid = getParcelUuid(env, jni_obj_serviceUUID);
- jmethodID jni_mid_addServiceUuid = (*env)->GetMethodID(env, jni_cid_AdvertiseDataBuilder,
- "addServiceUuid",
- "(Landroid/os/ParcelUuid;)Landroid/bluetooth/le/AdvertiseData$Builder;");
+ if(gBluetoothGattServerCallback)
+ {
+ (*env)->DeleteGlobalRef(env, gBluetoothGattServerCallback);
+ }
- jobject jni_obj_addServiceUuid = (*env)->CallObjectMethod(env, jni_AdvertiseDataBuilder,
- jni_mid_addServiceUuid, jni_ParcelUuid);
- if (!jni_obj_addServiceUuid)
+ if(gContext)
{
- LOGI("[BLE Server] jni_obj_addServiceUuid is null");
- return;
+ (*env)->DeleteGlobalRef(env, gContext);
}
- jclass jni_cid_BTAdapter = (*env)->FindClass(env, "android/bluetooth/BluetoothAdapter");
- jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env, jni_cid_BTAdapter,
- "getDefaultAdapter", "()Landroid/bluetooth/BluetoothAdapter;");
- jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env, jni_cid_BTAdapter,
- jni_mid_getDefaultAdapter);
- if (!jni_obj_BTAdapter)
+ gIsStartServer = FALSE;
+ gResposeInProgress = FALSE;
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+}
+
+int32_t CALEServerSendUnicastMessage(const char* address, const char* data, uint32_t dataLen)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendUnicastMessage(%s, %s)", address, data);
+
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
{
- LOGI("[BLE Server] jni_obj_BTAdapter is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
}
- jmethodID jni_mid_getBluetoothLeAdvertiser = (*env)->GetMethodID(env, jni_cid_BTAdapter,
- "getBluetoothLeAdvertiser", "()Landroid/bluetooth/le/BluetoothLeAdvertiser;");
+ CALEServerSendUnicastMessageImpl(env, address, data, dataLen);
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+
+ return 0;
+}
- jobject jni_obj_getBluetoothLeAdvertiser = (*env)->CallObjectMethod(env, jni_obj_BTAdapter,
- jni_mid_getBluetoothLeAdvertiser);
- if (!jni_obj_getBluetoothLeAdvertiser)
+int32_t CALEServerSendMulticastMessage(const char* data, uint32_t dataLen)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendMulticastMessage(%s)", data);
+
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
{
- LOGI("[BLE Server] jni_obj_getBluetoothLeAdvertiser is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
}
- jmethodID jni_mid_build_LeAdvertiseSettings = (*env)->GetMethodID(env,
- jni_cid_AdvertiseSettings, "build", "()Landroid/bluetooth/le/AdvertiseSettings;");
+ CALEServerSendMulticastMessageImpl(env, data, dataLen);
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+
+ return 0;
+}
+
+int32_t CALEServerStartUnicastServer(const char* address)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerStartUnicastServer(%s)", address);
+
+ return 0;
+}
+
+int32_t CALEServerStartMulticastServer()
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerStartMulticastServer");
- jobject jni_obj_build_LeAdvertiseSettings = (*env)->CallObjectMethod(env, jni_AdvertiseSettings,
- jni_mid_build_LeAdvertiseSettings);
- if (!jni_obj_build_LeAdvertiseSettings)
+ if(gIsStartServer)
{
- LOGI("[BLE Server] jni_obj_build_LeAdvertiseSettings is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "server is already started..it will be skipped");
+ return 0;
+ }
+
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
+ }
+
+ gIsStartServer = TRUE;
+
+ // start gatt server
+ if( CALEStartGattServer(env, gBluetoothGattServerCallback) == JNI_FALSE) {
+ OIC_LOG_V(DEBUG, TAG, "Fail to start gatt server");
+ return -1;
}
- jmethodID jni_mid_build_LeAdvertiseData = (*env)->GetMethodID(env, jni_cid_AdvertiseDataBuilder,
- "build", "()Landroid/bluetooth/le/AdvertiseData;");
+ // start advertise
+ LEServerStartAdvertise(env, gLeAdvertiseCallback);
+
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+
+ return 0;
+}
+
+int32_t CALEServerStopUnicastServer(int32_t serverID)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerStopUnicastServer");
+
+ return 0;
+}
+
+int32_t CALEServerStopMulticastServer(int32_t serverID)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerStopMulticastServer");
+ gIsStartServer = FALSE;
+
+ // stop advertise
+
+ return 0;
+}
+
+void CALEServerSetCallback(CAPacketReceiveCallback callback)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerSetCallback");
+ gPacketReceiveCallback = callback;
+}
+
+void CALEServerGetInterfaceInfo(CALocalConnectivity_t **info, uint32_t* size)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerGetInterfaceInfo");
+ return;
+}
+
+void CALEServerGetLocalAddress(char* address)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerGetLocalAddress");
- jobject jni_obj_build_LeAdvertiseData = (*env)->CallObjectMethod(env, jni_AdvertiseDataBuilder,
- jni_mid_build_LeAdvertiseData);
- if (!jni_obj_build_LeAdvertiseData)
+ jboolean isAttached = FALSE;
+ JNIEnv* env;
+ jint res = (*g_jvm)->GetEnv(g_jvm, (void**)&env, JNI_VERSION_1_6);
+ if(res != JNI_OK)
{
- LOGI("[BLE Server] jni_obj_build_LeAdvertiseData is null");
- return;
+ OIC_LOG_V(DEBUG, TAG, "Could not get JNIEnv pointer");
+ res = (*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL);
+ if(res != JNI_OK)
+ {
+ OIC_LOG_V(DEBUG, TAG, "AttachCurrentThread failed");
+ return;
+ }
+ isAttached = TRUE;
}
- jclass jni_cid_leAdvertiser = (*env)->FindClass(env,
- "android/bluetooth/le/BluetoothLeAdvertiser");
+ jstring jni_address = CALEGetLocalDeviceAddress(env);
+ const char* localAddress = (*env)->GetStringUTFChars(env, jni_address, NULL);
+ memcpy(address, localAddress, strlen(localAddress));
- jmethodID jni_mid_startAdvertising =
- (*env)->GetMethodID(env, jni_cid_leAdvertiser, "startAdvertising",
- "(Landroid/bluetooth/le/AdvertiseSettings;Landroid/bluetooth/le/AdvertiseData;Landroid/bluetooth/le/AdvertiseCallback;)V");
+ OIC_LOG_V(DEBUG, TAG, "Local Address : %s", address);
+ if(isAttached)
+ (*g_jvm)->DetachCurrentThread(g_jvm);
+}
- (*env)->CallVoidMethod(env, jni_obj_getBluetoothLeAdvertiser, jni_mid_startAdvertising,
- jni_obj_build_LeAdvertiseSettings, jni_obj_build_LeAdvertiseData, advertiseCallback);
+int32_t CALEServerSendUnicastMessageImpl(JNIEnv *env, const char* address, const char* data,
+ uint32_t dataLen)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendUnicastMessageImpl, address: %s, data: %s", address, data);
+
+ // 1. get device object with address from cache
+ // 2. connect to the gatt client device
+ // 3. write a characteristic for response
+ // 4. notify it
+ // 5. disconnect
- LOGI("[BLE Server] jni start Advertising");
+ jobject jni_obj_bluetoothDevice = NULL;
+
+ if(gConnectedDeviceList == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "gConnectedDeviceList is null");
+ }
+
+ if(gConnectedDeviceList)
+ {
+ jint index;
+ for (index = 0; index < u_arraylist_length(gConnectedDeviceList); index++)
+ {
+ OIC_LOG(DEBUG, TAG, "check device address");
+ jobject jarrayObj = (jobject) u_arraylist_get(gConnectedDeviceList, index);
+ if(!jarrayObj)
+ {
+ OIC_LOG(DEBUG, TAG, "jarrayObj is null");
+ return -1;
+ }
+
+ jstring jni_setAddress = CALEGetAddressFromBTDevice(env, jarrayObj);
+ if(!jni_setAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "jni_setAddress is null");
+ return -1;
+ }
+ const char* setAddress = (*env)->GetStringUTFChars(env, jni_setAddress, NULL);
+
+ if(jarrayObj == NULL) {
+ OIC_LOG(DEBUG, TAG, "jarrayObj is null");
+ }
+
+ if(!strcmp(setAddress, address))
+ {
+ OIC_LOG(DEBUG, TAG, "device address matched");
+ jni_obj_bluetoothDevice = jarrayObj;
+ break;
+ }
+ jni_obj_bluetoothDevice = jarrayObj;
+ }
+
+ if(jni_obj_bluetoothDevice)
+ {
+ jbyteArray jni_bytearr_data = (*env)->NewByteArray(env, dataLen);
+ (*env)->SetByteArrayRegion(env, jni_bytearr_data, 0, dataLen, (jbyte*)data);
+
+ CALEServerSend(env, jni_obj_bluetoothDevice, jni_bytearr_data);
+
+ // set progress
+ gResposeInProgress = JNI_TRUE;
+ } else {
+ OIC_LOG(DEBUG, TAG, "jni_obj_bluetoothDevice is null");
+ }
+ }
}
-jobject openGattServer(JNIEnv *env)
+int32_t CALEServerSendMulticastMessageImpl(JNIEnv *env, const char* data, uint32_t dataLen)
{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerSendMulticastMessageImpl, send to, data: %s", data);
- jclass jni_cid_context = (*env)->FindClass(env, "android/content/Context");
- jfieldID jni_fid_bluetoothService = (*env)->GetStaticFieldID(env, jni_cid_context,
- "BLUETOOTH_SERVICE", "Ljava/lang/String;");
- jobject jni_obj_bluetoothService = (*env)->GetStaticObjectField(env, jni_cid_context,
- jni_fid_bluetoothService);
+ gResposeInProgress = JNI_FALSE;
- jmethodID jni_mid_getSystemService = (*env)->GetMethodID(env, jni_cid_context,
- "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
- jobject jni_obj_bluetoothManager = (*env)->CallObjectMethod(env, gContext,
- jni_mid_getSystemService, jni_obj_bluetoothService);
+ if(!gConnectedDeviceList)
+ {
+ OIC_LOG(DEBUG, TAG, "gConnectedDeviceList is null");
+ return 0;
+ }
- jclass jni_cid_bluetoothManager = (*env)->FindClass(env, "android/bluetooth/BluetoothManager");
- jmethodID jni_mid_openGattServer =
- (*env)->GetMethodID(env, jni_cid_bluetoothManager, "openGattServer",
- "(Landroid/content/Context;Landroid/bluetooth/BluetoothGattServerCallback;)Landroid/bluetooth/BluetoothGattServer;");
- jobject jni_obj_bluetoothGattServer = (*env)->CallObjectMethod(env, jni_obj_bluetoothManager,
- jni_mid_openGattServer, gContext, gBluetoothGattServerCallback);
+ // 1. get all the device objects from cache
+ // 2. connect to the gatt client devices
+ // 3. write a characteristic for response
+ // 4. notify it to every devices
+ // 5. disconnect
- return jni_obj_bluetoothGattServer;
+ jint index;
+ for (index = 0; index < u_arraylist_length(gConnectedDeviceList); index++)
+ {
+ jobject jarrayObj = (jobject) u_arraylist_get(gConnectedDeviceList, index);
+ if(!jarrayObj)
+ {
+ OIC_LOG(DEBUG, TAG, "jarrayObj is null");
+ return;
+ }
+
+ CALEServerConnect(env, jarrayObj);
+
+ gResposeInProgress = JNI_TRUE;
+
+ sleep(1);
+ }
+
+ return 0;
}
-jobject createGattService(JNIEnv *env)
+JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CARegisterLeGattServerCallback
+ (JNIEnv *env, jobject obj, jobject callback)
{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Register Le Gatt Server Callback");
- jclass jni_cid_bluetoothGattService = (*env)->FindClass(env,
- "android/bluetooth/BluetoothGattService");
- jmethodID jni_mid_bluetoothGattService = (*env)->GetMethodID(env, jni_cid_bluetoothGattService,
- "<init>", "(Ljava/util/UUID;I)V");
+ gBluetoothGattServerCallback = (*env)->NewGlobalRef(env, callback);
+}
- // service uuid object
- jobject jni_obj_serviceUUID = getUuidFromString(env, OIC_GATT_SERVICE_UUID);
+JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CARegisterBluetoothLeAdvertiseCallback
+ (JNIEnv *env, jobject obj, jobject callback)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Register Le Advertise Callback");
- // service type object
- jfieldID jni_fid_serviceType = (*env)->GetStaticFieldID(env, jni_cid_bluetoothGattService,
- "SERVICE_TYPE_PRIMARY", "I");
- jobject jni_obj_serviceType = (*env)->GetStaticObjectField(env, jni_cid_bluetoothGattService,
- jni_fid_serviceType);
+ gLeAdvertiseCallback = (*env)->NewGlobalRef(env, callback);
+}
+
+JNIEXPORT void JNICALL Java_com_iotivity_jar_CALeInterface_CALeGattServerConnectionStateChangeCallback
+ (JNIEnv *env, jobject obj, jobject device, jint status, jint newState)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Gatt Server ConnectionStateChange Callback");
+
+ OIC_LOG_V(DEBUG, TAG, "New connection State: %d", newState);
- jobject jni_bluetoothGattService = (*env)->NewObject(env, jni_cid_bluetoothGattService,
- jni_mid_bluetoothGattService, jni_obj_serviceUUID, jni_obj_serviceType);
- if (!jni_bluetoothGattService)
+ if (!device)
{
- LOGI("[BLE Server] fail to create gatt service instance!");
- return NULL;
+ OIC_LOG(DEBUG, TAG, "device is null");
+ return;
}
- jclass jni_cid_bluetoothGattCharacteristic = (*env)->FindClass(env,
- "android/bluetooth/BluetoothGattCharacteristic");
- jmethodID jni_mid_bluetoothGattCharacteristic = (*env)->GetMethodID(env,
- jni_cid_bluetoothGattCharacteristic, "<init>", "(Ljava/util/UUID;II)V");
+ jclass jni_cid_bluetoothProfile = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothProfile");
- // characteristic uuid for response
- jobject jni_obj_readUuid = getUuidFromString(env, OIC_GATT_CHARACTERISTIC_RESPONSE_UUID);
+ jfieldID jni_fid_state_connected = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothProfile, "STATE_CONNECTED", "I");
- // characteristic properties
- jfieldID jni_fid_readProperties = (*env)->GetStaticFieldID(env,
- jni_cid_bluetoothGattCharacteristic, "PROPERTY_READ", "I");
- jobject jni_obj_readProperties = (*env)->GetStaticObjectField(env,
- jni_cid_bluetoothGattCharacteristic, jni_fid_readProperties);
+ jfieldID jni_fid_state_disconnected = (*env)->GetStaticFieldID(env,
+ jni_cid_bluetoothProfile, "STATE_DISCONNECTED", "I");
- // characteristic permissions
- jfieldID jni_fid_readPermissions = (*env)->GetStaticFieldID(env,
- jni_cid_bluetoothGattCharacteristic, "PERMISSION_READ", "I");
- jobject jni_obj_readPermissions = (*env)->GetStaticObjectField(env,
- jni_cid_bluetoothGattCharacteristic, jni_fid_readPermissions);
+ // STATE_CONNECTED
+ jint jni_int_state_connected = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothProfile, jni_fid_state_connected);
+
+ // STATE_DISCONNECTED
+ jint jni_int_state_disconnected = (*env)->GetStaticIntField(env,
+ jni_cid_bluetoothProfile, jni_fid_state_disconnected);
+
+ if(newState == jni_int_state_connected) {
+
+ OIC_LOG_V(DEBUG, TAG, "LE CONNECTED");
+
+ jstring jni_remoteAddress = CALEGetAddressFromBTDevice(env, device);
+ if (!jni_remoteAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "jni_remoteAddress is null");
+ return;
+ }
+
+ const char* remoteAddress = (*env)->GetStringUTFChars(env, jni_remoteAddress, NULL);
+
+ if (gConnectedDeviceList == NULL)
+ {
+ OIC_LOG_V(DEBUG, TAG, "gConnectedDeviceList is null");
+ }
+
+ if(CALEServerIsDeviceInList(env, remoteAddress) == JNI_FALSE)
+ {
+ OIC_LOG_V(DEBUG, TAG, "add connected device to cache");
+ CALEServerAddDeviceToList(env, device);
+ }
+
+ }
+ else if(newState == jni_int_state_disconnected) {
+ OIC_LOG_V(DEBUG, TAG, "LE DISCONNECTED");
+ }
+}
- jobject jni_readCharacteristic = (*env)->NewObject(env, jni_cid_bluetoothGattCharacteristic,
- jni_mid_bluetoothGattCharacteristic, jni_obj_readUuid, jni_obj_readProperties,
- jni_obj_readPermissions);
- if (!jni_readCharacteristic)
+void CALEServerCreateCachedDeviceList()
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerCreateCachedDeviceList");
+
+ // create new object array
+ if (gConnectedDeviceList == NULL)
{
- LOGI("[BLE Server] fail to create Response Characteristic");
- return NULL;
+ OIC_LOG_V(DEBUG, TAG, "Create device list");
+
+ gConnectedDeviceList = u_arraylist_create();
}
+}
- jobject jni_obj_writeUuid = getUuidFromString(env, OIC_GATT_CHARACTERISTIC_REQUEST_UUID);
+jboolean CALEServerIsDeviceInList(JNIEnv *env, const char* remoteAddress)
+{
+ OIC_LOG(DEBUG, TAG, "CALEServerIsDeviceInList");
- // characteristic properties
- jfieldID jni_fid_writeProperties = (*env)->GetStaticFieldID(env,
- jni_cid_bluetoothGattCharacteristic, "PROPERTY_WRITE", "I");
- jobject jni_obj_writeProperties = (*env)->GetStaticObjectField(env,
- jni_cid_bluetoothGattCharacteristic, jni_fid_writeProperties);
+ if(gConnectedDeviceList == NULL)
+ OIC_LOG(DEBUG, TAG, "list is null");
- // characteristic permissions
- jfieldID jni_fid_writePermissions = (*env)->GetStaticFieldID(env,
- jni_cid_bluetoothGattCharacteristic, "PERMISSION_WRITE", "I");
- jobject jni_obj_writePermissions = (*env)->GetStaticObjectField(env,
- jni_cid_bluetoothGattCharacteristic, jni_fid_writePermissions);
+ uint32_t len = u_arraylist_length(gConnectedDeviceList);
+ OIC_LOG_V(DEBUG, TAG, "list length: %d", len);
- jobject jni_writeCharacteristic = (*env)->NewObject(env, jni_cid_bluetoothGattCharacteristic,
- jni_mid_bluetoothGattCharacteristic, jni_obj_writeUuid, jni_obj_writeProperties,
- jni_obj_writePermissions);
- if (!jni_writeCharacteristic)
+ uint32_t index;
+ for (index = 0; index < u_arraylist_length(gConnectedDeviceList); index++)
{
- LOGI("[BLE Server] fail to create Request Characteristic");
- return NULL;
+ jobject jarrayObj = (jobject) u_arraylist_get(gConnectedDeviceList, index);
+
+ if (!jarrayObj)
+ {
+ OIC_LOG(DEBUG, TAG, "jarrayObj is null");
+ return JNI_TRUE;
+ }
+
+ jstring jni_setAddress = CALEGetAddressFromBTDevice(env, jarrayObj);
+ if (!jni_setAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "jni_setAddress is null");
+ return JNI_TRUE;
+ }
+
+ const char* setAddress = (*env)->GetStringUTFChars(env, jni_setAddress,
+ NULL);
+
+ if (!strcmp(remoteAddress, setAddress))
+ {
+ OIC_LOG_V(DEBUG, TAG, "the device is already set");
+ return JNI_TRUE;
+ }
+ else
+ {
+ continue;
+ }
}
- jmethodID jni_mid_addCharacteristic = (*env)->GetMethodID(env, jni_cid_bluetoothGattService,
- "addCharacteristic", "(Landroid/bluetooth/BluetoothGattCharacteristic;)Z");
- jboolean jni_boolean_addReadCharacteristic = (*env)->CallBooleanMethod(env,
- jni_bluetoothGattService, jni_mid_addCharacteristic, jni_readCharacteristic);
- if (jni_boolean_addReadCharacteristic == JNI_FALSE)
+ OIC_LOG_V(DEBUG, TAG, "no device in list");
+ return JNI_FALSE;
+}
+
+void CALEServerAddDeviceToList(JNIEnv *env, jobject device)
+{
+ if (device == NULL)
{
- LOGI("[BLE Server] fail to add jni_boolean_addReadCharacteristic!!");
- return NULL;
+ OIC_LOG(DEBUG, TAG, "device is null");
+ return;
}
- jboolean jni_boolean_addWriteCharacteristic = (*env)->CallBooleanMethod(env,
- jni_bluetoothGattService, jni_mid_addCharacteristic, jni_writeCharacteristic);
- if (jni_boolean_addWriteCharacteristic == JNI_FALSE)
+ if (gConnectedDeviceList == NULL)
{
- LOGI("[BLE Server] fail to add jni_boolean_addReadCharacteristic!!");
- return NULL;
+ OIC_LOG(DEBUG, TAG, "list is null");
+ return;
}
- LOGI("[BLE Server] jni gatt characteristic added!!");
- return jni_bluetoothGattService;
+ jstring jni_remoteAddress = CALEGetAddressFromBTDevice(env, device);
+ if (!jni_remoteAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "jni_remoteAddress is null");
+ return;
+ }
+
+ const char* remoteAddress = (*env)->GetStringUTFChars(env,
+ jni_remoteAddress, NULL);
+
+ if (CALEServerIsDeviceInList(env, remoteAddress) == JNI_FALSE)
+ {
+ jobject gdevice = (*env)->NewGlobalRef(env, device);
+ u_arraylist_add(gConnectedDeviceList, gdevice);
+ OIC_LOG_V(DEBUG, TAG, "Set Object to Array as Element");
+ }
}
-void startGattServer(JNIEnv *env, jobject gattServerCallback)
+void CALEServerRemoveAllDevices(JNIEnv *env)
{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerRemoveAllDevices");
- gBluetoothGattServerCallback = (*env)->NewGlobalRef(env, gattServerCallback);
+ if (!gConnectedDeviceList)
+ {
+ OIC_LOG(DEBUG, TAG, "no deviceList");
+ return;
+ }
- // open gatt server
- jobject bluetoothGattServer = openGattServer(env);
+ uint32_t index;
+ for (index = 0; index < u_arraylist_length(gConnectedDeviceList); index++)
+ {
+ jobject jarrayObj = (jobject) u_arraylist_get(gConnectedDeviceList, index);
- gBluetoothGattServer = (*env)->NewGlobalRef(env, bluetoothGattServer);
- LOGI("[BLE Server] Bluetooth Gatt Server started!!");
+ if (jarrayObj)
+ {
+ (*env)->DeleteGlobalRef(env, jarrayObj);
+ }
+ }
- // get bluetooth gatt service
- jobject bluetoothGattService = createGattService(env);
+ OICFree(gConnectedDeviceList);
+ gConnectedDeviceList = NULL;
+ return;
+}
- jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
- "android/bluetooth/BluetoothGattServer");
- jmethodID jni_mid_addService = (*env)->GetMethodID(env, jni_cid_bluetoothGattServer,
- "addService", "(Landroid/bluetooth/BluetoothGattService;)Z");
- jboolean jni_boolean_addService = (*env)->CallBooleanMethod(env, bluetoothGattServer,
- jni_mid_addService, bluetoothGattService);
+void CALEServerRemoveDevice(JNIEnv *env, jstring address)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEServerRemoveDevice");
+
+ if (!gConnectedDeviceList)
+ {
+ OIC_LOG(DEBUG, TAG, "no deviceList");
+ return;
+ }
- LOGI("[BLE Server] jni gatt service added!!");
+ uint32_t index;
+ for (index = 0; index < u_arraylist_length(gConnectedDeviceList); index++)
+ {
+ jobject jarrayObj = (jobject) u_arraylist_get(gConnectedDeviceList, index);
+
+ if (jarrayObj)
+ {
+ jstring jni_setAddress = CALEGetAddressFromBTDevice(env, jarrayObj);
+ if (!jni_setAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "wrong device address");
+ continue;
+ }
+ const char* setAddress = (*env)->GetStringUTFChars(env, jni_setAddress,
+ NULL);
+ const char* remoteAddress = (*env)->GetStringUTFChars(env, address,
+ NULL);
+
+ if (!strcmp(setAddress, remoteAddress))
+ {
+ OIC_LOG_V(DEBUG, TAG, "device address : %s", remoteAddress);
+ (*env)->DeleteGlobalRef(env, jarrayObj);
+
+ CALEServerReorderinglist(index);
+ return;
+ }
+ }
+ }
+ OIC_LOG(DEBUG, TAG, "no target object");
+ return;
}
-void connect(JNIEnv *env, jobject bluetoothDevice)
+void CALEServerReorderinglist(uint32_t index)
{
+ if (index >= gConnectedDeviceList->length)
+ return;
- jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
- "android/bluetooth/BluetoothGattServer");
- jmethodID jni_mid_connect = (*env)->GetMethodID(env, jni_cid_bluetoothGattServer, "connect",
- "(Landroid/bluetooth/BluetoothDevice;Z)Z");
- jboolean jni_boolean_connect = (*env)->CallBooleanMethod(env, gBluetoothGattServer,
- jni_mid_connect, bluetoothDevice, JNI_FALSE);
- LOGI("[BLE Server] connection requested!!");
+ if (index < gConnectedDeviceList->length - 1)
+ {
+ memmove(&gConnectedDeviceList->data[index], &gConnectedDeviceList->data[index + 1],
+ (gConnectedDeviceList->length - index - 1) * sizeof(void *));
+ }
+
+ gConnectedDeviceList->size--;
+ gConnectedDeviceList->length--;
}
-void disconnect(JNIEnv *env, jobject bluetoothDevice)
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerServiceAddedCallback
+(JNIEnv *env, jobject obj, jint status, jobject gattService)
{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Gatt Service Added Callback(%d)", status);
+}
- jclass jni_cid_bluetoothGattServer = (*env)->FindClass(env,
- "android/bluetooth/BluetoothGattServer");
- jmethodID jni_mid_cancelConnection = (*env)->GetMethodID(env, jni_cid_bluetoothGattServer,
- "cancelConnection", "(Landroid/bluetooth/BluetoothDevice;)V");
- (*env)->CallVoidMethod(env, gBluetoothGattServer, jni_mid_cancelConnection, bluetoothDevice);
- LOGI("[BLE Server] disconnection requested!!");
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerCharacteristicReadRequestCallback
+(JNIEnv *env, jobject obj, jobject device, jint requestId, jint offset, jobject characteristic, jbyteArray data)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Gatt Server Characteristic Read Request Callback");
+
+ CALEServerSendResponse(env, device, requestId, 0, offset, NULL);
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerCharacteristicWriteRequestCallback
+(JNIEnv *env, jobject obj, jobject device, jint requestId, jobject characteristic, jbyteArray data,
+ jboolean preparedWrite, jboolean responseNeeded, jint offset, jbyteArray value)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Gatt Server Characteristic Write Request Callback");
+
+ CALEServerSendResponse(env, device, requestId, 0, offset, value);
+
+ if(data == NULL)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Reqeust data is null");
+ return;
+ }
+
+ jboolean isCopy;
+ char* requestData = (char*)(*env)->GetByteArrayElements(env, data, &isCopy);
+
+ jstring jni_address = CALEGetAddressFromBTDevice(env, device);
+ const char* address = (*env)->GetStringUTFChars(env, jni_address, NULL);
+ OIC_LOG_V(DEBUG, TAG, "remote device address : %s", address);
+
+ gPacketReceiveCallback(address, requestData);
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerDescriptorReadRequestCallback
+(JNIEnv *env, jobject obj, jobject device, jint requestId, jint offset, jobject descriptor)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface_CALeGattServerDescriptorReadRequestCallback");
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerDescriptorWriteRequestCallback
+(JNIEnv *env, jobject obj, jobject device, jint requestId, jobject descriptor,
+ jboolean preparedWrite, jboolean responseNeeded, jint offset, jbyteArray value)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface_CALeGattServerDescriptorWriteRequestCallback");
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerExecuteWriteCallback
+(JNIEnv *env, jobject obj, jobject device, jint requestId, jboolean execute)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface_CALeGattServerExecuteWriteCallback");
+
+ CALEServerSendResponse(env, device, requestId, 0, 0, NULL);
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeGattServerNotificationSentCallback
+(JNIEnv *env, jobject obj, jobject device, jint status)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - Gatt Server Notification Sent Callback");
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeAdvertiseStartSuccessCallback
+(JNIEnv *env, jobject obj, jobject settingsInEffect)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - LE Advertise Start Success Callback");
+}
+
+JNIEXPORT void JNICALL
+Java_com_iotivity_jar_CALeInterface_CALeAdvertiseStartFailureCallback
+(JNIEnv *env, jobject obj, jint errorCode)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALeInterface - LE Advertise Start Failure Callback(%)", errorCode);
}
--- /dev/null
+#include <jni.h>
+#include <stdio.h>
+#include <android/log.h>
+#include "caleutils.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "uthreadpool.h"
+#include "uarraylist.h"
+
+#define TAG PCF("CA_LE_UTILS")
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+#define METHODID_OBJECTNONPARAM "()Landroid/bluetooth/BluetoothAdapter;"
+//#define METHODID_INTNONPARAM "()I"
+#define METHODID_STRINGNONPARAM "()Ljava/lang/String;"
+//#define METHODID_OBJECT_STRINGUUIDPARAM "(Ljava/lang/String;Ljava/util/UUID;)Ljava/lang/Object;"
+//#define METHODID_ONRESPONSE_PARAM "(Ljava/lang/String;)V"
+#define CLASSPATH_BT_ADPATER "android/bluetooth/BluetoothAdapter"
+//#define CLASSPATH_BT_UUID "java/util/UUID"
+
+jobject CALEGetUuidFromString(JNIEnv *env, const char* uuid)
+{
+
+ OIC_LOG_V(DEBUG, TAG, "CALEGetUuidFromString");
+
+ jclass jni_cid_UUID = (*env)->FindClass(env, "java/util/UUID");
+
+ jmethodID jni_mid_fromString = (*env)->GetStaticMethodID(env, jni_cid_UUID,
+ "fromString", "(Ljava/lang/String;)Ljava/util/UUID;");
+
+ jstring str_uuid = (*env)->NewStringUTF(env, uuid);
+
+ jobject jni_obj_uuid = (*env)->CallStaticObjectMethod(env, jni_cid_UUID,
+ jni_mid_fromString, str_uuid);
+ if (!jni_obj_uuid)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Fail to get jni uuid object");
+ return NULL;
+ }
+
+ return jni_obj_uuid;
+}
+
+jobject CALEGetParcelUuid(JNIEnv *env, jobject uuid)
+{
+ OIC_LOG_V(DEBUG, TAG, "CALEGetParcelUuid");
+
+ jclass jni_cid_ParcelUuid = (*env)->FindClass(env, "android/os/ParcelUuid");
+
+ jmethodID jni_mid_ParcelUuid = (*env)->GetMethodID(env, jni_cid_ParcelUuid,
+ "<init>", "(Ljava/util/UUID;)V");
+
+ jobject jni_ParcelUuid = (*env)->NewObject(env, jni_cid_ParcelUuid,
+ jni_mid_ParcelUuid, uuid);
+ if (!jni_ParcelUuid)
+ {
+ OIC_LOG_V(DEBUG, TAG, "Fail to get jni ParcelUuid");
+ return NULL;
+ }
+
+ return jni_ParcelUuid;
+}
+
+jstring CALEGetAddressFromBTDevice(JNIEnv *env, jobject bluetoothDevice)
+{
+ OIC_LOG(DEBUG, TAG, "CALEGetAddressFromBTDevice");
+
+ jclass jni_cid_device_list = (*env)->FindClass(env,
+ "android/bluetooth/BluetoothDevice");
+ jmethodID jni_mid_getAddress = (*env)->GetMethodID(env, jni_cid_device_list,
+ "getAddress", "()Ljava/lang/String;");
+ jstring jni_address = (jstring)(*env)->CallObjectMethod(env,
+ bluetoothDevice, jni_mid_getAddress);
+ if (!jni_address)
+ {
+ OIC_LOG(DEBUG, TAG, "jni_address is null");
+ return 0;
+ }
+ return jni_address;
+}
+
+jstring CALEGetLocalDeviceAddress(JNIEnv* env)
+{
+ jclass jni_cid_BTAdapter = (*env)->FindClass(env, CLASSPATH_BT_ADPATER);
+ if (!jni_cid_BTAdapter)
+ {
+ OIC_LOG(DEBUG, TAG, "getAddress: jni_cid_BTAdapter is null");
+ return NULL;
+ }
+
+ jmethodID jni_mid_getDefaultAdapter = (*env)->GetStaticMethodID(env,
+ jni_cid_BTAdapter, "getDefaultAdapter", METHODID_OBJECTNONPARAM);
+ if (!jni_mid_getDefaultAdapter)
+ {
+ OIC_LOG(DEBUG, TAG, "getAddress: jni_mid_getDefaultAdapter is null");
+ return NULL;
+ }
+
+ jmethodID jni_mid_getAddress = (*env)->GetMethodID(env, jni_cid_BTAdapter,
+ "getAddress", METHODID_STRINGNONPARAM);
+ if (!jni_mid_getAddress)
+ {
+ OIC_LOG(DEBUG, TAG, "getAddress: jni_mid_getAddress is null");
+ return NULL;
+ }
+
+ jobject jni_obj_BTAdapter = (*env)->CallStaticObjectMethod(env,
+ jni_cid_BTAdapter, jni_mid_getDefaultAdapter);
+ if (!jni_obj_BTAdapter)
+ {
+ OIC_LOG(DEBUG, TAG, "getAddress: jni_obj_BTAdapter is null");
+ return NULL;
+ }
+
+ jstring jni_str_address = (jstring)(*env)->CallObjectMethod(env,
+ jni_obj_BTAdapter, jni_mid_getAddress);
+ if (!jni_str_address)
+ {
+ OIC_LOG(DEBUG, TAG, "getAddress: jni_str_address is null");
+ return NULL;
+ }
+
+ return jni_str_address;
+}
static CANetworkChangeCallback networkCallback = NULL;
static bool gServerRunning = false;
#define BLE_ADDRESS "DB:F7:EB:B5:0F:07"
-#define TAG "CALEADAPTER"
+#define TAG "LAD"
#define COAP_MAX_PDU_SIZE 320
OIC_LOG(DEBUG, TAG, "IN");
if (NULL == registerCallback || NULL == reqRespCallback || NULL == netCallback)
{
- OIC_LOG(ERROR, TAG, "Invalid Parameter");
+ OIC_LOG(ERROR, TAG, "error");
return CA_STATUS_INVALID_PARAM;
}
}
uint32_t CASendLENotification(const CARemoteEndpoint_t *endpoint, void *data,
- uint32_t dataLen)
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, TAG, "IN");
OIC_LOG(DEBUG, TAG, "OUT");
if (CA_STATUS_OK != result)
{
- return CA_STATUS_FAILED;
+ return CA_STATUS_FAILED;
}
int32_t index = 0;
- if(!CAIsBleConnected())
+ if (!CAIsBleConnected())
{
- OIC_LOG(DEBUG, TAG, "ble is not connected");
+ OIC_LOG(DEBUG, TAG, "le not conn");
return CA_STATUS_FAILED;
}
CAWriteBleData((unsigned char *)header, CA_HEADER_LENGTH);
- int32_t iter = value_length/CA_SUPPORTED_BLE_MTU_SIZE;
+ int32_t iter = value_length / CA_SUPPORTED_BLE_MTU_SIZE;
for (index = 0; index < iter; index++)
{
CAWriteBleData((unsigned char *)(char_value + (index * CA_SUPPORTED_BLE_MTU_SIZE)),
- (unsigned char) CA_SUPPORTED_BLE_MTU_SIZE);
+ (unsigned char) CA_SUPPORTED_BLE_MTU_SIZE);
CABleDoEvents();
}
CAWriteBleData((unsigned char *)(char_value + (index * CA_SUPPORTED_BLE_MTU_SIZE)),
- (unsigned char) value_length%CA_SUPPORTED_BLE_MTU_SIZE);
+ (unsigned char) value_length % CA_SUPPORTED_BLE_MTU_SIZE);
CABleDoEvents();
- OIC_LOG(DEBUG, TAG, "ble_write_bytes had been done");
+ OIC_LOG(DEBUG, TAG, "writebytes done");
OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
}
uint32_t CASendLEUnicastData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLen)
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, TAG, "IN");
if (NULL == remoteEndpoint)
{
- OIC_LOG(ERROR, TAG, "Invalid Input Parameter");
+ OIC_LOG(ERROR, TAG, "error");
return CA_STATUS_INVALID_PARAM;
}
CAUpdateCharacteristicsInGattServer((char *)data, dataLen);
OIC_LOG(DEBUG, TAG, "IN");
if (NULL == data || 0 == dataLen)
{
- OIC_LOG(ERROR, TAG, "Invalid Parameter");
+ OIC_LOG(ERROR, TAG, "error");
return CA_STATUS_INVALID_PARAM;
}
CAUpdateCharacteristicsInGattServer((char *)data, dataLen);
if (NULL == info || NULL == size)
{
- OIC_LOG(DEBUG, TAG, "Invalid Parameter");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_STATUS_INVALID_PARAM;
}
(*info) = CAAdapterCreateLocalEndpoint(CA_LE, BLE_ADDRESS);
if (NULL == (*info))
{
- OIC_LOG(DEBUG, TAG, "Out of memory");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_MEMORY_ALLOC_FAILED;
}
if (NULL == gCoapBuffer)
{
OIC_LOG(DEBUG, TAG, "IN");
- gCoapBuffer = (char *)OICMalloc(COAP_MAX_PDU_SIZE);
+ char headerArray[2] = "";
+ while (CAIsBleDataAvailable() && dataLen < 2)
+ {
+ headerArray[dataLen++] = CAReadBleData();
+ }
+
+ packetDataLen = CAParseHeader(headerArray);
+
+ if (packetDataLen > COAP_MAX_PDU_SIZE)
+ {
+ OIC_LOG(ERROR, TAG, "error");
+ return;
+ }
+ gCoapBuffer = (char *)OICMalloc(packetDataLen);
if (NULL == gCoapBuffer)
{
OIC_LOG(DEBUG, TAG, "error");
return;
}
- while (CAIsBleDataAvailable() && dataLen < 2)
- {
- gCoapBuffer[dataLen++] = CAReadBleData();
- }
- packetDataLen = CAParseHeader(gCoapBuffer);
OIC_LOG(DEBUG, TAG, "OUT");
- memset(gCoapBuffer, 0, COAP_MAX_PDU_SIZE);
+ memset(gCoapBuffer, 0, packetDataLen);
dataLen = 0;
}
OIC_LOG(DEBUG, TAG, "IN");
while (CAIsBleDataAvailable())
{
- OIC_LOG(DEBUG, TAG, "Inside While loop");
+ OIC_LOG(DEBUG, TAG, "In While loop");
gCoapBuffer[dataLen++] = CAReadBleData();
if (dataLen == packetDataLen)
{
}
else
{
- OIC_LOG(DEBUG, TAG, "No Data on BLE server");
+ OIC_LOG(DEBUG, TAG, "NoData");
}
return;
}
#include "oic_malloc.h"
#include "caadapterutils.h"
-#define TAG "CLS"
+#define TAG "LES"
CAResult_t CAInitializeBle()
{
// Set your BLE Shield name here, max. length 10
ble_set_name("SAMSUNG");
- OIC_LOG(DEBUG, TAG, "BLE Name Set is completed");
+ OIC_LOG(DEBUG, TAG, "LEName Set");
ble_begin();
static bool gDataReceiverHandlerState = false;
/**
- * @var isHeaderAvailable
- * @brief to differentiate btw header and data packet.
- */
-static CABool_t isHeaderAvailable = false;
-
-/**
- * @var gNetworkPacketReceivedServerCallback
- * @brief Maintains the callback to be notified on receival of network packets from other
- * BLE devices
- */
-static CANetworkPacketReceivedCallback gNetworkPacketReceivedServerCallback = NULL;
-
-/**
* @var gSendQueueHandle
* @brief Queue to process the outgoing packets from GATTServer.
*/
-//static CAAdapterMessageQueue_t *gSendQueueHandle = NULL;
-
static CAQueueingThread_t *gSendQueueHandle = NULL;
/**
int32_t CALERegisterNetworkNotifications(CANetworkChangeCallback netCallback);
+void CASetBleAdapterThreadPoolHandle(u_thread_pool_t handle);
+
#ifdef __TIZEN__
void CALEDeviceStateChangedCb(int32_t result, bt_adapter_state_e adapter_state,
void *user_data);
static uint32_t recvDataLen = 0;
static uint32_t totalDataLen = 0;
static char *defragData = NULL;
- static isHeaderAvailable = false;
+ static bool isHeaderAvailable = false;
static CARemoteEndpoint_t *remoteEndpoint = NULL;
u_mutex_lock(gBleClientReceiveDataMutex);
static uint32_t recvDataLen = 0;
static uint32_t totalDataLen = 0;
static char *defragData = NULL;
- static isHeaderAvailable = false;
+ static bool isHeaderAvailable = false;
static CARemoteEndpoint_t *remoteEndpoint = NULL;
u_mutex_lock(gBleClientReceiveDataMutex);
return;
}
char *header = (char *) OICMalloc(sizeof(char) * CA_HEADER_LENGTH);
- VERIFY_NON_NULL_VOID(*header, CALEADAPTER_TAG, "Malloc failed");
+ VERIFY_NON_NULL_VOID(header, CALEADAPTER_TAG, "Malloc failed");
char *dataSegment = (char *) OICMalloc(sizeof(char) * bleData->dataLen + CA_HEADER_LENGTH);
if (NULL == dataSegment)
}
memset(data, 0x0, valueLen + 1);
- strncpy(data, value, valueLen);
+ strncpy(data, (char *)value, valueLen);
uint32_t sentLength = 0;
return;
}
-void CABleGattCharacteristicWriteCb(bt_gatt_attribute_h handle)
+void CABleGattCharacteristicWriteCb(int32_t result, void *userData)
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN ");
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "OUT");
}
-CABool_t CABleGattCharacteristicsDiscoveredCb(int32_t result,
+bool CABleGattCharacteristicsDiscoveredCb(int32_t result,
int32_t inputIndex, int32_t total,
bt_gatt_attribute_h characteristic, void *userData)
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "OUT");
}
-CABool_t CABleGattPrimaryServiceCb(bt_gatt_attribute_h service, int32_t index, int32_t count,
+bool CABleGattPrimaryServiceCb(bt_gatt_attribute_h service, int32_t index, int32_t count,
void *userData)
{
OIC_LOG(DEBUG, TZ_BLE_CLIENT_TAG, "IN");
VERIFY_NON_NULL_RET(value, TZ_BLE_CLIENT_TAG, "malloc failed", CA_STATUS_FAILED);
memset(value, 0x0, (dataLen + 1));
- strncpy(value, data, dataLen);
+ strncpy((char *)value, data, dataLen);
OIC_LOG_V(DEBUG, TZ_BLE_CLIENT_TAG, "Updating the data of length [%d] to [%s] ", dataLen,
bleServiceInfo->bdAddress);
* @fn CABleGattCharacteristicWriteCb
* @brief This is the callback which will be called after the characteristics changed.
*
-* @param[in] handle The attribute handle of characteristic
+* @param[in] result result of write value
+* @param[in] userData user context
*
* @return void
*
*/
-void CABleGattCharacteristicWriteCb(bt_gatt_attribute_h handle);
+void CABleGattCharacteristicWriteCb(int32_t result, void *userData);
/**
* @fn CABleGattDescriptorDiscoveredCb
* @return 0 on failure and 1 on success.
*
*/
-CABool_t CABleGattCharacteristicsDiscoveredCb(int32_t result, int32_t inputIndex, int32_t total,
+bool CABleGattCharacteristicsDiscoveredCb(int32_t result, int32_t inputIndex, int32_t total,
bt_gatt_attribute_h characteristic, void *userData);
/**
* @return 0 on failure and 1 on success.
*
*/
-CABool_t CABleGattPrimaryServiceCb(bt_gatt_attribute_h service, int32_t index, int32_t count,
+bool CABleGattPrimaryServiceCb(bt_gatt_attribute_h service, int32_t index, int32_t count,
void *userData);
/**
*/
#define TZ_BLE_SERVER_TAG "TZ_BLE_GATT_SERVER"
-///TODO:: Currently keeping as single service. Later service path will be added in list for \
-///TODO:: supporting multiple services.
+/* TODO:: Currently keeping as single service. Later service path will be added in list for
+ * supporting multiple services */
#define CA_BLE_SERVICE_UUID "713d0000-503e-4c75-ba94-3148f18d941e"
memset(data, 0x0, charValueLen + 1);
- strncpy(data, charValue, charValueLen);
+ strncpy(data, (char *)charValue, charValueLen);
uint32_t sentLength = 0;
*
*/
CAResult_t CAAddNewCharacteristicsToGattServer(const char *svcPath, char *charUUID,
- char *charValue, int32_t charValueLen,
- int32_t read);
+ char *charValue, int32_t charValueLen,
+ int32_t read);
/**
* @fn CARemoveCharacteristicsFromGattServer
return CA_STATUS_OK;
}
-CAResult_t CACreateRemoteEndpoint(const CAURI_t uri, CARemoteEndpoint_t **remoteEndpoint)
+CAResult_t CACreateRemoteEndpoint(const CAURI_t uri,
+ const CAConnectivityType_t connectivityType,CARemoteEndpoint_t **remoteEndpoint)
{
OIC_LOG_V(DEBUG, TAG, "CACreateRemoteEndpoint");
- CARemoteEndpoint_t *remote = CACreateRemoteEndpointUriInternal(uri);
+ CARemoteEndpoint_t *remote = CACreateRemoteEndpointUriInternal(uri, connectivityType);
*remoteEndpoint = remote;
return CA_STATUS_OK;
}
-CAResult_t CACreateRemoteEndpoint(const CAURI_t uri, CARemoteEndpoint_t **remoteEndpoint)
+CAResult_t CACreateRemoteEndpoint(const CAURI_t uri,
+ const CAConnectivityType_t connectivityType,CARemoteEndpoint_t **remoteEndpoint)
{
OIC_LOG(DEBUG, TAG, "IN");
- CARemoteEndpoint_t *remote = CACreateRemoteEndpointUriInternal(uri);
+ CARemoteEndpoint_t *remote = CACreateRemoteEndpointUriInternal(uri,connectivityType);
*remoteEndpoint = remote;
#define TAG "CAIFCNT_ST"
-#define MEMORY_ALLOC_CHECK(arg) { if (arg == NULL) {OIC_LOG_V(DEBUG, TAG, "Out of memory"); goto memory_error_exit;} }
+#define MEMORY_ALLOC_CHECK(arg) { if (arg == NULL) {OIC_LOG_V(DEBUG, TAG, "Out of memory");\
+ goto memory_error_exit;} }
#define CA_CONNECTIVITY_TYPE_NUM 4
OIC_LOG(DEBUG, TAG, "OUT");
}
-static void CAReceivedPacketCallback(CARemoteEndpoint_t *endpoint, void *data, uint32_t dataLen)
+static void CAReceivedPacketCallback(CARemoteEndpoint_t *endpoint, void *data,
+ uint32_t dataLen)
{
OIC_LOG(DEBUG, TAG, "IN");
static CARequestCallback gRequestHandler = NULL;
static CAResponseCallback gResponseHandler = NULL;
+static void CATimeoutCallback(const CARemoteEndpoint_t *endpoint, void *pdu, uint32_t size)
+{
+ CARemoteEndpoint_t* ep = CACloneRemoteEndpoint(endpoint);
+ if (ep == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "memory allocation failed !");
+ return;
+ }
+
+ CAResponseInfo_t* resInfo = (CAResponseInfo_t*) OICMalloc(sizeof(CAResponseInfo_t));
+
+ if (resInfo == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "memory allocation failed !");
+ CADestroyRemoteEndpointInternal(ep);
+ return;
+ }
+ memset(resInfo, 0, sizeof(CAResponseInfo_t));
+
+ CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
+ uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
+
+ resInfo->result = CA_RETRANSMIT_TIMEOUT;
+ resInfo->info.type = type;
+ resInfo->info.messageId = messageId;
+
+ CAData_t *cadata = (CAData_t *) OICMalloc(sizeof(CAData_t));
+ if (cadata == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "memory allocation failed !");
+ CADestroyRemoteEndpointInternal(ep);
+ OICFree(resInfo);
+ return;
+ }
+ memset(cadata, 0, sizeof(CAData_t));
+
+ cadata->type = SEND_TYPE_UNICAST;
+ cadata->remoteEndpoint = ep;
+ cadata->requestInfo = NULL;
+ cadata->responseInfo = resInfo;
+
+ CAQueueingThreadAddData(&gReceiveThread, cadata, sizeof(CAData_t));
+}
+
static void CAReceiveThreadProcess(void *threadData)
{
// TODO
OIC_LOG_V(DEBUG, TAG, "responseInfo is available..");
pdu = (coap_pdu_t *) CAGeneratePdu(data->remoteEndpoint->resourceUri,
- data->responseInfo->result, data->responseInfo->info);
+ data->responseInfo->result,
+ data->responseInfo->info);
}
else
{
// for retransmission
CARetransmissionSentData(&gRetransmissionContext, data->remoteEndpoint, pdu->hdr,
- pdu->length);
+ pdu->length);
//For Unicast , data will be deleted by adapters
CADestroyRemoteEndpointInternal(data->remoteEndpoint);
}
-
+ coap_delete_pdu(pdu);
}
else if (type == SEND_TYPE_MULTICAST)
{
res = CASendMulticastData(pdu->hdr, pdu->length);
}
+ coap_delete_pdu(pdu);
}
else
{
// for retransmission
CARetransmissionReceivedData(&gRetransmissionContext, endpoint, pdu->hdr, pdu->length);
}
+ OICFree(pdu);
}
static void CANetworkChangedCallback(CALocalConnectivity_t *info, CANetworkStatus_t status)
return CA_MEMORY_ALLOC_FAILED;
}
-CAResult_t CADetachRequestToAllMessage(const CAGroupEndpoint_t* object,
+CAResult_t CADetachRequestToAllMessage(const CAGroupEndpoint_t *object,
const CARequestInfo_t *request)
{
// ToDo
CAAddress_t addr;
memset(&addr, 0, sizeof(CAAddress_t));
- CARemoteEndpoint_t* remoteEndpoint = CACreateRemoteEndpointInternal(object->resourceUri, addr,
+ CARemoteEndpoint_t *remoteEndpoint = CACreateRemoteEndpointInternal(object->resourceUri, addr,
object->connectivityType);
// clone request info
// }
// retransmission initialize
- CARetransmissionInitialize(&gRetransmissionContext, gThreadPoolHandle, CASendUnicastData, NULL);
+ CARetransmissionInitialize(&gRetransmissionContext, gThreadPoolHandle, CASendUnicastData,
+ CATimeoutCallback, NULL);
// start retransmission
res = CARetransmissionStart(&gRetransmissionContext);
#include "oic_malloc.h"
#include "caadapterutils.h"
-#define TAG1 "CAMH_ST"
+#define TAG "CAMH_ST"
-#define MEMORY_ALLOC_CHECK(arg) { if (arg == NULL) {OIC_LOG_V(DEBUG, TAG1, "Out of memory"); goto memory_error_exit;} }
+#define MEMORY_ALLOC_CHECK(arg) { if (arg == NULL) {OIC_LOG_V(DEBUG, TAG, "Out of memory");\
+ goto memory_error_exit;} }
#define MAX_THREAD_POOL_SIZE 10
#define CA_MAX_RT_ARRAY_SIZE 3
static void CAProcessData(CAData_t *data)
{
- OIC_LOG(DEBUG, TAG1, "IN");
- VERIFY_NON_NULL_VOID(data, TAG1, "data");
- VERIFY_NON_NULL_VOID(data->remoteEndpoint, TAG1, "remoteendpoint");
+ OIC_LOG(DEBUG, TAG, "IN");
+ VERIFY_NON_NULL_VOID(data, TAG, "data");
+ VERIFY_NON_NULL_VOID(data->remoteEndpoint, TAG, "remoteendpoint");
CAResult_t res = CA_STATUS_FAILED;
if (data->requestInfo != NULL)
{
- OIC_LOG_V(DEBUG, TAG1, "reqInfo avlbl");
+ OIC_LOG_V(DEBUG, TAG, "reqInfo avlbl");
pdu = (coap_pdu_t *) CAGeneratePdu(data->remoteEndpoint->resourceUri,
data->requestInfo->method, data->requestInfo->info);
}
else if (data->responseInfo != NULL)
{
- OIC_LOG_V(DEBUG, TAG1, "resInfo avlbl");
+ OIC_LOG_V(DEBUG, TAG, "resInfo avlbl");
pdu = (coap_pdu_t *) CAGeneratePdu(data->remoteEndpoint->resourceUri,
data->responseInfo->result, data->responseInfo->info);
}
else
{
- OIC_LOG(DEBUG, TAG1, "request info, response info is empty");
+ OIC_LOG(DEBUG, TAG, "request info, response info is empty");
}
// interface controller function call.
if (NULL != pdu)
{
- OIC_LOG_V(DEBUG, TAG1, "payload: %s", pdu->data);
+ OIC_LOG_V(DEBUG, TAG, "payload: %s", pdu->data);
- OIC_LOG_V(DEBUG, TAG1, "code: %d", pdu->hdr->code);
+ OIC_LOG_V(DEBUG, TAG, "code: %d", pdu->hdr->code);
- OIC_LOG_V(DEBUG, TAG1, "buffer: %s", pdu->hdr);
+ OIC_LOG_V(DEBUG, TAG, "buffer: %s", pdu->hdr);
res = CASendUnicastData(data->remoteEndpoint, pdu->hdr, pdu->length);
// for retransmission
CARetransmissionSentData(&gRetransmissionContext, data->remoteEndpoint, pdu->hdr,
- pdu->length);
+ pdu->length);
}
}
else if (type == SEND_TYPE_MULTICAST)
{
- OIC_LOG(DEBUG, TAG1, "both requestInfo & responseInfo is not available");
+ OIC_LOG(DEBUG, TAG, "both requestInfo & responseInfo is not available");
coap_pdu_t *pdu = NULL;
CAInfo_t info;
if (NULL != pdu)
{
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - payload : %s", pdu->data);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - payload : %s", pdu->data);
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - type : %d", pdu->hdr->type);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - type : %d", pdu->hdr->type);
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - code : %d", pdu->hdr->code);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - code : %d", pdu->hdr->code);
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - id : %d", pdu->hdr->id);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - id : %d", pdu->hdr->id);
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - token : %s", pdu->hdr->token);
- OIC_LOG_V(DEBUG, TAG1, "PDU Maker - buffer data : %s", pdu->hdr);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - token : %s", pdu->hdr->token);
+ OIC_LOG_V(DEBUG, TAG, "PDU Maker - buffer data : %s", pdu->hdr);
res = CASendMulticastData(pdu->hdr, pdu->length);
}
}
else
{
- OIC_LOG(DEBUG, TAG1, "unknown type!");
+ OIC_LOG(DEBUG, TAG, "unknown type!");
}
if (gHandlerCallback != NULL)
{
gHandlerCallback("", res);
}
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
}
-static void CAReceivedPacketCallback(CARemoteEndpoint_t *endpoint, void *data, uint32_t dataLen)
+static void CATimeoutCallback(const CARemoteEndpoint_t *endpoint, void *pdu, uint32_t size)
{
- OIC_LOG(DEBUG, TAG1, "IN");
- VERIFY_NON_NULL_VOID(data, TAG1, "data");
+ OIC_LOG(DEBUG, TAG, "IN");
+ CARemoteEndpoint_t* ep = CACloneRemoteEndpoint(endpoint);
+ if (ep == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "error");
+ return;
+ }
+
+ CAResponseInfo_t* resInfo = (CAResponseInfo_t*) OICMalloc(sizeof(CAResponseInfo_t));
+
+ if (resInfo == NULL)
+ {
+ OIC_LOG(DEBUG, TAG, "error");
+ CADestroyRemoteEndpointInternal(ep);
+ return;
+ }
+ memset(resInfo, 0, sizeof(CAResponseInfo_t));
+
+ CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
+ uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
+
+ resInfo->result = CA_RETRANSMIT_TIMEOUT;
+ resInfo->info.type = type;
+ resInfo->info.messageId = messageId;
+
+ if (gResponseHandler)
+ {
+ gResponseHandler(ep, resInfo);
+ }
+ CADestroyRemoteEndpointInternal(ep);
+ OICFree(resInfo);
+ OIC_LOG(DEBUG, TAG, "OUT");
+}
+
+static void CAReceivedPacketCallback(CARemoteEndpoint_t *endpoint, void *data,
+ uint32_t dataLen)
+{
+ OIC_LOG(DEBUG, TAG, "IN");
+ VERIFY_NON_NULL_VOID(data, TAG, "data");
coap_pdu_t *pdu;
uint32_t code = CA_NOT_FOUND;
ReqInfo = (CARequestInfo_t *) OICMalloc(sizeof(CARequestInfo_t));
if (ReqInfo == NULL)
{
- OIC_LOG(DEBUG, TAG1, "CAReceivedPacketCallback, Memory allocation failed !");
+ OIC_LOG(DEBUG, TAG, "CAReceivedPacketCallback, Memory allocation failed !");
coap_delete_pdu(pdu);
return;
}
uint32_t i;
for (i = 0; i < ReqInfo->info.numOptions; i++)
{
- OIC_LOG_V(DEBUG, TAG1, "Request- optionID: %d", ReqInfo->info.options[i].optionID);
+ OIC_LOG_V(DEBUG, TAG, "Request- optionID: %d", ReqInfo->info.options[i].optionID);
- OIC_LOG_V(DEBUG, TAG1, "Request- list: %s", ReqInfo->info.options[i].optionData);
+ OIC_LOG_V(DEBUG, TAG, "Request- list: %s", ReqInfo->info.options[i].optionData);
}
}
if (NULL != ReqInfo->info.payload)
{
- OIC_LOG_V(DEBUG, TAG1, "Request- payload: %s", ReqInfo->info.payload);
+ OIC_LOG_V(DEBUG, TAG, "Request- payload: %s", ReqInfo->info.payload);
}
- OIC_LOG_V(DEBUG, TAG1, "Request- code: %d", ReqInfo->method);
- OIC_LOG_V(DEBUG, TAG1, "Request- token : %s", ReqInfo->info.token);
+ OIC_LOG_V(DEBUG, TAG, "Request- code: %d", ReqInfo->method);
+ OIC_LOG_V(DEBUG, TAG, "Request- token : %s", ReqInfo->info.token);
if (NULL != endpoint)
{
endpoint->resourceUri = (char *) OICMalloc(strlen(uri) + 1);
if (endpoint->resourceUri == NULL)
{
- OIC_LOG(DEBUG, TAG1, "CAReceivedPacketCallback, Memory allocation failed !");
+ OIC_LOG(DEBUG, TAG, "CAReceivedPacketCallback, Memory allocation failed !");
coap_delete_pdu(pdu);
OICFree(ReqInfo);
return;
}
memset(endpoint->resourceUri, 0, strlen(uri) + 1);
memcpy(endpoint->resourceUri, uri, strlen(uri));
- OIC_LOG_V(DEBUG, TAG1, "URI : %s", endpoint->resourceUri);
+ OIC_LOG_V(DEBUG, TAG, "URI : %s", endpoint->resourceUri);
}
if (ReqInfo)
CAResponseInfo_t *ResInfo = (CAResponseInfo_t *) OICMalloc(sizeof(CAResponseInfo_t));
if (ResInfo == NULL)
{
- OIC_LOG(DEBUG, TAG1, "CAReceivedPacketCallback, Memory allocation failed !");
+ OIC_LOG(DEBUG, TAG, "CAReceivedPacketCallback, Memory allocation failed !");
coap_delete_pdu(pdu);
return;
}
uint32_t i;
for (i = 0; i < ResInfo->info.numOptions; i++)
{
- OIC_LOG_V(DEBUG, TAG1, "optionID: %d", ResInfo->info.options[i].optionID);
+ OIC_LOG_V(DEBUG, TAG, "optionID: %d", ResInfo->info.options[i].optionID);
- OIC_LOG_V(DEBUG, TAG1, "list: %s", ResInfo->info.options[i].optionData);
+ OIC_LOG_V(DEBUG, TAG, "list: %s", ResInfo->info.options[i].optionData);
}
if (NULL != ResInfo->info.payload)
{
- OIC_LOG_V(DEBUG, TAG1, "payload: %s", ResInfo->info.payload);
+ OIC_LOG_V(DEBUG, TAG, "payload: %s", ResInfo->info.payload);
}
- OIC_LOG_V(DEBUG, TAG1, "code: %d", ResInfo->result);
+ OIC_LOG_V(DEBUG, TAG, "code: %d", ResInfo->result);
}
if (NULL != endpoint)
endpoint->resourceUri = (char *) OICMalloc(strlen(uri) + 1);
if (endpoint->resourceUri == NULL)
{
- OIC_LOG(DEBUG, TAG1, "CAReceivedPacketCallback, Memory allocation failed !");
+ OIC_LOG(DEBUG, TAG, "CAReceivedPacketCallback, Memory allocation failed !");
coap_delete_pdu(pdu);
OICFree(ResInfo);
return;
}
memset(endpoint->resourceUri, 0, strlen(uri) + 1);
memcpy(endpoint->resourceUri, uri, strlen(uri));
- OIC_LOG_V(DEBUG, TAG1, "URI : %s", endpoint->resourceUri);
+ OIC_LOG_V(DEBUG, TAG, "URI : %s", endpoint->resourceUri);
}
if (ResInfo != NULL)
if (endpoint && endpoint->resourceUri)
{
OICFree(endpoint->resourceUri);
+ endpoint->resourceUri = NULL;
}
- OIC_LOG(DEBUG, TAG1, "OUT");
+ free(pdu);
+ OIC_LOG(DEBUG, TAG, "OUT");
}
static void CANetworkChangedCallback(CALocalConnectivity_t *info, CANetworkStatus_t status)
{
- OIC_LOG(DEBUG, TAG1, "IN");
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "IN");
+ OIC_LOG(DEBUG, TAG, "OUT");
}
void CAHandleRequestResponseCallbacks()
{
CAReadData();
- CARetransmissionBaseRoutine((void*)&gRetransmissionContext);
+ CARetransmissionBaseRoutine((void *)&gRetransmissionContext);
}
-CAResult_t CADetachRequestMessage(const CARemoteEndpoint_t *object, const CARequestInfo_t *request)
+CAResult_t CADetachRequestMessage(const CARemoteEndpoint_t *object,
+ const CARequestInfo_t *request)
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
- VERIFY_NON_NULL(object, TAG1, "object");
- VERIFY_NON_NULL(request, TAG1, "request");
+ VERIFY_NON_NULL(object, TAG, "object");
+ VERIFY_NON_NULL(request, TAG, "request");
CARemoteEndpoint_t *remoteEndpoint = NULL;
CARequestInfo_t *requestInfo = NULL;
// If max retransmission queue is reached, then don't handle new request
if (CA_MAX_RT_ARRAY_SIZE == u_arraylist_length(gRetransmissionContext.dataList))
{
- OIC_LOG(DEBUG, TAG1, "max RT queue size rchd");
+ OIC_LOG(DEBUG, TAG, "max RT queue size rchd");
return CA_SEND_FAILED;
}
CADestroyRemoteEndpoint(remoteEndpoint);
CADestroyRequestInfoInternal(requestInfo);
OICFree(data);
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
// memory error label.
{
OICFree(data);
}
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_MEMORY_ALLOC_FAILED;
}
CAResult_t CADetachResponseMessage(const CARemoteEndpoint_t *object,
const CAResponseInfo_t *response)
{
- OIC_LOG(DEBUG, TAG1, "IN");
- VERIFY_NON_NULL(object, TAG1, "object");
- VERIFY_NON_NULL(response, TAG1, "response");
+ OIC_LOG(DEBUG, TAG, "IN");
+ VERIFY_NON_NULL(object, TAG, "object");
+ VERIFY_NON_NULL(response, TAG, "response");
CAResponseInfo_t *responseInfo = NULL;
CARemoteEndpoint_t *remoteEndpoint = NULL;
CAData_t *data = (CAData_t *) OICMalloc(sizeof(CAData_t));
CADestroyRemoteEndpoint(remoteEndpoint);
CADestroyResponseInfoInternal(responseInfo);
OICFree(data);
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
// memory error label.
{
OICFree(data);
}
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_MEMORY_ALLOC_FAILED;
}
const CAHeaderOption_t *options,
uint8_t numOptions)
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
if (resourceUri == NULL)
{
return CA_STATUS_FAILED;
CADestroyRemoteEndpoint(remoteEndpoint);
OICFree(data);
OICFree(ReqInfo);
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
// memory error label.
{
OICFree(data);
}
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_MEMORY_ALLOC_FAILED;
}
void CASetMessageHandlerCallback(CAMessageHandlerCallback callback)
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
gHandlerCallback = callback;
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
}
-void CASetRequestResponseCallbacks(CARequestCallback ReqHandler, CAResponseCallback RespHandler)
+void CASetRequestResponseCallbacks(CARequestCallback ReqHandler,
+ CAResponseCallback RespHandler)
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
gRequestHandler = ReqHandler;
gResponseHandler = RespHandler;
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
}
CAResult_t CAInitializeMessageHandler()
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
CASetPacketReceivedCallback(CAReceivedPacketCallback);
CASetNetworkChangeCallback(CANetworkChangedCallback);
CAResult_t res;
// retransmission initialize
- CARetransmissionInitialize(&gRetransmissionContext, CASendUnicastData, NULL);
+ CARetransmissionInitialize(&gRetransmissionContext, CASendUnicastData, CATimeoutCallback, NULL);
CAInitializeAdapters();
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
}
void CATerminateMessageHandler()
{
- OIC_LOG(DEBUG, TAG1, "IN");
+ OIC_LOG(DEBUG, TAG, "IN");
// terminate interface adapters by controller
CATerminateAdapters();
CARetransmissionStop(&gRetransmissionContext);
CARetransmissionDestroy(&gRetransmissionContext);
- OIC_LOG(DEBUG, TAG1, "OUT");
+ OIC_LOG(DEBUG, TAG, "OUT");
}
}\r
\r
coap_pdu_t *CACreatePDUforRequestWithPayload(const code_t code, coap_list_t *options,\r
- const char *payload, const CAInfo_t info)\r
+ const char *payload, const CAInfo_t info)\r
{\r
OIC_LOG(DEBUG, TAG, "CACreatePDUforRequestWithPayload IN");\r
\r
coap_insert(optlist,\r
CACreateNewOptionNode(COAP_OPTION_URI_PORT,\r
coap_encode_var_bytes(portbuf, uri.port), portbuf),\r
- CAOrderOpts);\r
+ CAOrderOpts);\r
}\r
\r
if (uri.path.length)\r
\r
coap_insert(optlist,\r
CACreateNewOptionNode(info.options[i].optionID,\r
- info.options[i].optionLength,\r
+ info.options[i].optionLength,\r
info.options[i].optionData), CAOrderOpts);\r
}\r
}\r
return cnt;\r
}\r
\r
-CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void* pdu, uint32_t size)\r
+CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void *pdu, uint32_t size)\r
{\r
// pdu minimum size is 4 byte.\r
if (size < 4)\r
return CA_MSG_NONCONFIRM;\r
\r
- coap_hdr_t* hdr = (coap_hdr_t*) pdu;\r
+ coap_hdr_t *hdr = (coap_hdr_t *) pdu;\r
\r
return (CAMessageType_t) hdr->type;\r
}\r
\r
-uint16_t CAGetMessageIdFromPduBinaryData(const void* pdu, uint32_t size)\r
+uint16_t CAGetMessageIdFromPduBinaryData(const void *pdu, uint32_t size)\r
{\r
// pdu minimum size is 4 byte.\r
if (size < 4)\r
return 0;\r
\r
- coap_hdr_t* hdr = (coap_hdr_t*) pdu;\r
+ coap_hdr_t *hdr = (coap_hdr_t *) pdu;\r
\r
return ntohs(hdr->id);\r
}\r
#include "logger.h"
#include "oic_malloc.h"
-#define TAG "CA"
+#define TAG "CPM"
#define CA_MAX_TOKEN_LEN (8)
#define CA_FLAGS_BLOCK 0x01
coapUri = (char *) OICMalloc(length + coapHeaderLength + 1);
if (coapUri == NULL)
{
- OIC_LOG(ERROR, TAG, "Mem alloc failed");
+ OIC_LOG(ERROR, TAG, "error");
return NULL;
}
memset(coapUri, 0, length + coapHeaderLength + 1);
}
coap_pdu_t *CACreatePDUforRequestWithPayload(const code_t code, coap_list_t *options,
- const char *payload, const CAInfo_t info)
+ const char *payload, const CAInfo_t info)
{
OIC_LOG(DEBUG, TAG, "IN");
prng((unsigned char * )&message_id, sizeof(unsigned short));
++message_id;
- OIC_LOG_V(DEBUG, TAG, "gen msg id(%d)", message_id);
+ OIC_LOG_V(DEBUG, TAG, "gen msg id=%d", message_id);
}
else
{
uint32_t len = strlen(payload);
if ((CAFlags & CA_FLAGS_BLOCK) == 0)
{
- OIC_LOG_V(DEBUG, TAG, "add data, payload: %s", payload);
+ OIC_LOG_V(DEBUG, TAG, "add data,payload:%s", payload);
coap_add_data(pdu, len, (const unsigned char *) payload);
}
else
{
- OIC_LOG_V(DEBUG, TAG, "add block, payload: %s", payload);
+ OIC_LOG_V(DEBUG, TAG, "add block,payload: %s", payload);
coap_add_block(pdu, len, (const unsigned char *) payload, CABlock.num, CABlock.szx);
}
}
if (!(pdu = coap_new_pdu()))
{
- OIC_LOG(ERROR, TAG, "mem alloc failed");
+ OIC_LOG(ERROR, TAG, "error");
return NULL;
}
coap_insert(optlist,
CACreateNewOptionNode(COAP_OPTION_URI_PORT,
coap_encode_var_bytes(portbuf, uri.port), portbuf),
- CAOrderOpts);
+ CAOrderOpts);
}
if (uri.path.length)
coap_insert(optlist,
CACreateNewOptionNode(info.options[i].optionID,
- info.options[i].optionLength,
+ info.options[i].optionLength,
info.options[i].optionData), CAOrderOpts);
}
}
option = coap_malloc(sizeof(coap_option) + length);
if (!option)
{
- OIC_LOG(ERROR, TAG, "mem alloc failed");
+ OIC_LOG(ERROR, TAG, "error");
return NULL;
}
memset(option, 0, sizeof(coap_option) + length);
coap_free(option);
return NULL;
}
- //coap_free(option);
+ coap_free(option);
OIC_LOG(DEBUG, TAG, "OUT");
return node;
}
outInfo->options = (CAHeaderOption_t *) OICMalloc(sizeof(CAHeaderOption_t) * count);
if (outInfo->options == NULL)
{
- OIC_LOG(DEBUG, TAG, "Mem alloc failed");
+ OIC_LOG(DEBUG, TAG, "error");
return;
}
memset(outInfo->options, 0, sizeof(CAHeaderOption_t) * count);
outInfo->token = (char *) OICMalloc(CA_MAX_TOKEN_LEN);
if (outInfo->token == NULL)
{
- OIC_LOG(DEBUG, TAG, "Mem alloc failed");
+ OIC_LOG(DEBUG, TAG, "error");
OICFree(outInfo->options);
return;
}
outInfo->payload = (char *) OICMalloc(strlen((const char *) pdu->data) + 1);
if (outInfo->payload == NULL)
{
- OIC_LOG(DEBUG, TAG, "Mem alloc failed");
+ OIC_LOG(DEBUG, TAG, "error");
OICFree(outInfo->options);
OICFree(outInfo->token);
return;
char *temp = (char *) OICMalloc(sizeof(char) * (CA_MAX_TOKEN_LEN + 1));
if (temp == NULL)
{
- OIC_LOG(DEBUG, TAG, "Mem alloc failed");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_MEMORY_ALLOC_FAILED;
}
memset(temp, 0, sizeof(char) * (CA_MAX_TOKEN_LEN + 1));
return cnt;
}
-CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void* pdu, uint32_t size)
+CAMessageType_t CAGetMessageTypeFromPduBinaryData(const void *pdu, uint32_t size)
{
// pdu minimum size is 4 byte.
if (size < 4)
return CA_MSG_NONCONFIRM;
- coap_hdr_t* hdr = (coap_hdr_t*) pdu;
+ coap_hdr_t *hdr = (coap_hdr_t *) pdu;
return (CAMessageType_t) hdr->type;
}
-uint16_t CAGetMessageIdFromPduBinaryData(const void* pdu, uint32_t size)
+uint16_t CAGetMessageIdFromPduBinaryData(const void *pdu, uint32_t size)
{
// pdu minimum size is 4 byte.
if (size < 4)
return 0;
- coap_hdr_t* hdr = (coap_hdr_t*) pdu;
+ coap_hdr_t *hdr = (coap_hdr_t *) pdu;
return ntohs(hdr->id);
}
return isIp;
}
-CARemoteEndpoint_t *CACreateRemoteEndpointUriInternal(const CAURI_t uri)
+CARemoteEndpoint_t *CACreateRemoteEndpointUriInternal(const CAURI_t uri,
+ const CAConnectivityType_t connectivityType)
{
// support URI type
// coap://10.11.12.13:4545/resource_uri
// resource uri
CAURI_t resourceUri = pResourceUri;
- // connectivity type
- CAConnectivityType_t type;
-
- if (resType == 1)
- {
- type = CA_WIFI;
- }
- else
- {
- type = CA_EDR;
- }
-
- CARemoteEndpoint_t *remoteEndpoint = CACreateRemoteEndpointInternal(resourceUri, address, type);
+ CARemoteEndpoint_t *remoteEndpoint = CACreateRemoteEndpointInternal(resourceUri, address, connectivityType);
remoteEndpoint->isSecured = secured;
OICFree(cloneUri);
{
/** last sent time. microseconds **/
uint64_t timeStamp;
+ /** timeout value. milliseconds **/
+ uint32_t timeout;
/** retransmission count **/
uint8_t triedCount;
/** coap PDU message id **/
uint16_t messageId;
/** remote endpoint **/
- CARemoteEndpoint_t* endpoint;
+ CARemoteEndpoint_t *endpoint;
/** coap PDU **/
- void* pdu;
+ void *pdu;
/** coap PDU size**/
uint32_t size;
} CARetransmissionData_t;
uint64_t getCurrentTimeInMicroSeconds();
/**
- * timeout routine
- * 2sec -> 4sec -> 8sec -> 16sec
+ * @brief check timeout routine
+ * @param currentTime [IN]microseconds
+ * @param timeStamp [IN]microseconds
+ * @param timeoutValue [IN]milliseconds
+ * @param triedCount [IN]
+ * @return CA_TRUE(timeout) or CA_FALSE
*
* microseconds
*/
-static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint8_t triedCount)
+static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint32_t timeoutValue,
+ uint8_t triedCount)
{
// #1. calculate timeout
- uint64_t timeOut = (2 << triedCount) * 1000000;
+ uint64_t timeout = (timeoutValue << triedCount) * (uint64_t) 1000;
- if (currentTime >= timeStamp + timeOut)
+ if (currentTime >= timeStamp + timeout)
{
- OIC_LOG_V(DEBUG, TAG, "%d seconds time out!!, tried count(%d)",
- (2 << triedCount), triedCount);
+ OIC_LOG_V(DEBUG, TAG, "%d milliseconds time out!!, tried count(%d)",
+ (timeoutValue << triedCount), triedCount);
return CA_TRUE;
}
return CA_FALSE;
}
+/**
+ * @brief timeout value is
+ * between DEFAULT_ACK_TIMEOUT and (DEFAULT_ACK_TIMEOUT * DEFAULT_RANDOM_FACTOR) second.
+ * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
+ * @return milliseconds.
+ */
+static uint32_t CAGetTimeoutValue()
+{
+ return (DEFAULT_ACK_TIMEOUT * 1000) + ((1000 * (rand() & 0xFF)) >> 8);
+}
+
static void CACheckRetransmissionList(CARetransmission_t *context)
{
// mutex lock
for (i = 0; i < len; i++)
{
- CARetransmissionData_t* retData = u_arraylist_get(context->dataList, i);
+ CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
if (retData == NULL)
continue;
currentTime = getCurrentTimeInMicroSeconds();
- if (CACheckTimeout(currentTime, retData->timeStamp, retData->triedCount))
+ if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
{
// #2. if time's up, send the data.
if (context->dataSendMethod != NULL)
{
- OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
- retData->messageId);
+ OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
+ retData->messageId);
context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
}
// #4. if tried count is max, remove the retransmission data from list.
if (retData->triedCount >= context->config.tryingCount)
{
- CARetransmissionData_t* removedData = u_arraylist_remove(context->dataList, i);
+ CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
+
+ OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
+ message id(%d)", removedData->messageId);
- OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!\
- message id(%d)", removedData->messageId);
+ // callback for retransmit timeout
+ if (context->timeoutCallback != NULL)
+ {
+ context->timeoutCallback(removedData->endpoint, removedData->pdu,
+ removedData->size);
+ }
CADestroyRemoteEndpointInternal(removedData->endpoint);
OICFree(removedData->pdu);
// wait
u_cond_timed_wait(context->threadCond, context->threadMutex,
- RETRANSMISSION_CHECK_PERIOD);
+ RETRANSMISSION_CHECK_PERIOD);
}
// mutex unlock
}
-CAResult_t CARetransmissionInitialize(CARetransmission_t* context, u_thread_pool_t handle,
- CADataSendMethod_t retransmissionSendMethod, CARetransmissionConfig_t* config)
+CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
+ CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
+ CARetransmissionConfig_t* config)
{
if (context == NULL)
{
{
// setDefault
cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
- cfg.tryingCount = DEFAULT_RETRANSMISSION_COUNT;
+ cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
}
else
{
context->threadMutex = u_mutex_new();
context->threadCond = u_cond_new();
context->dataSendMethod = retransmissionSendMethod;
+ context->timeoutCallback = timeoutCallback;
context->config = cfg;
context->isStop = CA_FALSE;
context->dataList = u_arraylist_create();
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionStart(CARetransmission_t* context)
+CAResult_t CARetransmissionStart(CARetransmission_t *context)
{
if (context == NULL)
{
}
CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
- context);
+ context);
if (res != CA_STATUS_OK)
{
return res;
}
-CAResult_t CARetransmissionSentData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint,
- const void* pdu, uint32_t size)
+CAResult_t CARetransmissionSentData(CARetransmission_t *context,
+ const CARemoteEndpoint_t* endpoint,const void* pdu, uint32_t size)
{
if (context == NULL || endpoint == NULL || pdu == NULL)
{
// #0. check support connectivity type
if (!(context->config.supportType & endpoint->connectivityType))
{
- OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
- endpoint->connectivityType);
+ OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
+ endpoint->connectivityType);
return CA_STATUS_OK;
}
}
// create retransmission data
- CARetransmissionData_t* retData = (CARetransmissionData_t*) OICMalloc(
- sizeof(CARetransmissionData_t));
+ CARetransmissionData_t *retData = (CARetransmissionData_t *) OICMalloc(
+ sizeof(CARetransmissionData_t));
if (retData == NULL)
{
memset(retData, 0, sizeof(CARetransmissionData_t));
// copy PDU data
- void* pduData = (void*) OICMalloc(sizeof(int8_t) * size);
+ void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
if (pduData == NULL)
{
OICFree(retData);
memcpy(pduData, pdu, sizeof(int8_t) * size);
// clone remote endpoint
- CARemoteEndpoint_t* remoteEndpoint = CACloneRemoteEndpoint(endpoint);
+ CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
if (remoteEndpoint == NULL)
{
OICFree(retData);
// #2. add additional information. (time stamp, retransmission count...)
retData->timeStamp = getCurrentTimeInMicroSeconds();
+ retData->timeout = CAGetTimeoutValue();
retData->triedCount = 0;
retData->messageId = messageId;
retData->endpoint = remoteEndpoint;
u_mutex_lock(context->threadMutex);
// #3. add data into list
- u_arraylist_add(context->dataList, (void*) retData);
+ u_arraylist_add(context->dataList, (void *) retData);
// notity the thread
u_cond_signal(context->threadCond);
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
+CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint, const void *pdu, uint32_t size)
{
if (context == NULL || endpoint == NULL || pdu == NULL)
{
// #0. check support connectivity type
if (!(context->config.supportType & endpoint->connectivityType))
{
- OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
- endpoint->connectivityType);
+ OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
+ endpoint->connectivityType);
return CA_STATUS_OK;
}
// find index
for (i = 0; i < len; i++)
{
- CARetransmissionData_t* retData = u_arraylist_get(context->dataList, i);
+ CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
if (retData == NULL)
continue;
// found index
if ((retData->endpoint->connectivityType == endpoint->connectivityType)
- && retData->messageId == messageId)
+ && retData->messageId == messageId)
break;
}
// #2. remove data from list
if (i < len)
{
- CARetransmissionData_t* removedData = u_arraylist_remove(context->dataList, i);
+ CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)", messageId);
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionStop(CARetransmission_t* context)
+CAResult_t CARetransmissionStop(CARetransmission_t *context)
{
if (context == NULL)
{
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionDestroy(CARetransmission_t* context)
+CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
{
if (context == NULL)
{
memset(&getTs, 0, sizeof(getTs));
clock_gettime(CLOCK_MONOTONIC, &getTs);
- currentTime = (getTs.tv_sec * 1000000000 + getTs.tv_nsec)/1000;
- OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
+ currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
+ OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
#else
currentTime = g_get_monotonic_time();
#endif
/** coap PDU message id **/
uint16_t messageId;
/** remote endpoint **/
- CARemoteEndpoint_t* endpoint;
+ CARemoteEndpoint_t *endpoint;
/** coap PDU **/
- void* pdu;
+ void *pdu;
/** coap PDU size**/
uint32_t size;
} CARetransmissionData_t;
OIC_LOG_V(DEBUG, TAG, "len=%d", len);
for (i = 0; i < len; i++)
{
- CARetransmissionData_t* retData =
- (CARetransmissionData_t*)u_arraylist_get(gRetransmissionPtr->dataList, i);
+ CARetransmissionData_t *retData =
+ (CARetransmissionData_t *)u_arraylist_get(gRetransmissionPtr->dataList, i);
if (retData == NULL)
continue;
{
OIC_LOG(DEBUG, TAG, "RTdata-Success");
- // #2. if time's up, send the data.
+ // #2. if time's up, send the data.
if (gRetransmissionPtr->dataSendMethod != NULL)
{
OIC_LOG_V(DEBUG, TAG, "retry CON data-msgid=%d", retData->messageId);
// #4. if tried count is max, remove the retransmission data from list.
if (retData->triedCount >= gRetransmissionPtr->config.tryingCount)
{
- CARetransmissionData_t* removedData =
- (CARetransmissionData_t*)u_arraylist_remove(gRetransmissionPtr->dataList, i);
+ CARetransmissionData_t *removedData =
+ (CARetransmissionData_t *)u_arraylist_remove(gRetransmissionPtr->dataList, i);
if (NULL == removedData)
{
OIC_LOG(DEBUG, TAG, "Removed data is NULL");
}
OIC_LOG(DEBUG, TAG, "max trycount rchd");
OIC_LOG_V(DEBUG, TAG, "max trycount, remove retransmission CON data!!, messageid=%d",
- removedData->messageId);
+ removedData->messageId);
+
+ // callback for retransmit timeout
+ if (gRetransmissionPtr->timeoutCallback != NULL)
+ {
+ gRetransmissionPtr->timeoutCallback(removedData->endpoint, removedData->pdu,
+ removedData->size);
+ }
CADestroyRemoteEndpointInternal(removedData->endpoint);
OICFree(removedData->pdu);
gRcvAction.check();
}
-CAResult_t CARetransmissionInitialize(CARetransmission_t* context,
- CADataSendMethod_t retransmissionSendMethod,
- CARetransmissionConfig_t* config)
+CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
+ CADataSendMethod_t retransmissionSendMethod,
+ CATimeoutCallback_t timeoutCallback,
+ CARetransmissionConfig_t *config)
{
OIC_LOG(DEBUG, TAG, "IN");
if (context == NULL)
// set send thread data
context->dataSendMethod = retransmissionSendMethod;
+ context->timeoutCallback = timeoutCallback;
context->config = cfg;
context->isStop = CA_FALSE;
context->dataList = u_arraylist_create();
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionSentData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint,
- const void* pdu, uint32_t size)
+CAResult_t CARetransmissionSentData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint,
+ const void *pdu, uint32_t size)
{
OIC_LOG(DEBUG, TAG, "IN");
if (context == NULL || endpoint == NULL || pdu == NULL)
{
- OIC_LOG_V(DEBUG, TAG, "error");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_STATUS_INVALID_PARAM;
}
// #0. check support connectivity type
if (!(context->config.supportType & endpoint->connectivityType))
{
+ OIC_LOG(DEBUG, TAG, "error");
OIC_LOG_V(DEBUG, TAG, "not supported conntype=%d", endpoint->connectivityType);
return CA_STATUS_OK;
}
if (type != CA_MSG_CONFIRM)
{
+ OIC_LOG(DEBUG, TAG, "error");
return CA_STATUS_OK;
}
// create retransmission data
- CARetransmissionData_t* retData = (CARetransmissionData_t*) OICMalloc(
- sizeof(CARetransmissionData_t));
+ CARetransmissionData_t *retData = (CARetransmissionData_t *) OICMalloc(
+ sizeof(CARetransmissionData_t));
if (retData == NULL)
{
- OIC_LOG_V(DEBUG, TAG, "error");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_MEMORY_ALLOC_FAILED;
}
memset(retData, 0, sizeof(CARetransmissionData_t));
// copy PDU data
- void* pduData = (void*) OICMalloc(sizeof(int8_t) * size);
+ void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
if (pduData == NULL)
{
OICFree(retData);
- OIC_LOG_V(DEBUG, TAG, "error");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_MEMORY_ALLOC_FAILED;
}
memset(pduData, 0, sizeof(int8_t) * size);
memcpy(pduData, pdu, sizeof(int8_t) * size);
// clone remote endpoint
- CARemoteEndpoint_t* remoteEndpoint = CACloneRemoteEndpoint(endpoint);
+ CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
if (remoteEndpoint == NULL)
{
OICFree(retData);
OICFree(pduData);
- OIC_LOG_V(DEBUG, TAG, "memory error!!");
+ OIC_LOG(DEBUG, TAG, "error");
return CA_MEMORY_ALLOC_FAILED;
}
retData->size = size;
// #3. add data into list
- u_arraylist_add(context->dataList, (void*) retData);
+ u_arraylist_add(context->dataList, (void *) retData);
// #4. Initiate Re-transmission for added entry
gRetransmissionPtr = context;
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
- const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
+CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
+ const CARemoteEndpoint_t *endpoint, const void *pdu, uint32_t size)
{
OIC_LOG(DEBUG, TAG, "IN");
if (context == NULL || endpoint == NULL || pdu == NULL)
// find index
for (i = 0; i < len; i++)
{
- CARetransmissionData_t* retData =
- (CARetransmissionData_t*)u_arraylist_get(context->dataList, i);
+ CARetransmissionData_t *retData =
+ (CARetransmissionData_t *)u_arraylist_get(context->dataList, i);
if (retData == NULL)
continue;
// found index
if ((retData->endpoint->connectivityType == endpoint->connectivityType)
- && retData->messageId == messageId)
+ && retData->messageId == messageId)
break;
}
// #2. remove data from list
if (i < len)
{
- CARetransmissionData_t* removedData =
- (CARetransmissionData_t*)u_arraylist_remove(context->dataList, i);
+ CARetransmissionData_t *removedData =
+ (CARetransmissionData_t *)u_arraylist_remove(context->dataList, i);
if (NULL == removedData)
{
OIC_LOG(DEBUG, TAG, "Removed data is NULL");
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionStop(CARetransmission_t* context)
+CAResult_t CARetransmissionStop(CARetransmission_t *context)
{
OIC_LOG(DEBUG, TAG, "IN");
if (context == NULL)
return CA_STATUS_OK;
}
-CAResult_t CARetransmissionDestroy(CARetransmission_t* context)
+CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
{
OIC_LOG(DEBUG, TAG, "IN");
if (context == NULL)
OIC_LOG(DEBUG, TAG, "IN");
uint64_t currentTime = 0;
-/*
-#ifdef __ANDROID__
- struct timespec getTs;
+ /*
+ #ifdef __ANDROID__
+ struct timespec getTs;
- memset(&getTs, 0, sizeof(getTs));
- clock_gettime(CLOCK_MONOTONIC, &getTs);
+ memset(&getTs, 0, sizeof(getTs));
+ clock_gettime(CLOCK_MONOTONIC, &getTs);
- currentTime = (getTs.tv_sec * 1000000000 + getTs.tv_nsec)/1000;
- OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
-#else if __ARDUINO__
- currentTime = micros();
-#else
- currentTime = g_get_monotonic_time();
-*/
+ currentTime = (getTs.tv_sec * 1000000000 + getTs.tv_nsec)/1000;
+ OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
+ #else if __ARDUINO__
+ currentTime = micros();
+ #else
+ currentTime = g_get_monotonic_time();
+ */
#ifdef __ARDUINO__
currentTime = micros();
#include <w5100.h>
#include <EthernetUdp.h>
#include <IPAddress.h>
-#include <TimedAction.h>
#include "logger.h"
#include "cacommon.h"
#include <w5100.h>
#include <EthernetUdp.h>
#include <IPAddress.h>
-#include <TimedAction.h>
#include "logger.h"
#include "cacommon.h"
CAResult_t CAEthernetStartUnicastServer(const char *localAddress, int16_t *port,
const bool forceStart, int32_t *serverFD);
-static int32_t CAArduinoRecvData(int32_t sockFd, uint8_t *buf, uint32_t bufLen,
+static int32_t CAArduinoRecvData(int32_t sockFd, uint8_t **buf, uint32_t bufLen,
uint8_t *senderAddr, uint16_t *senderPort);
static CAResult_t CAArduinoGetInterfaceAddress(char *address, int32_t addrLen);
static void CAArduinoCheckData();
static CAEthernetPacketReceivedCallback gPacketReceivedCallback = NULL;
static int32_t gUnicastSocket = 0;
static int32_t gMulticastSocket = 0;
-static bool gServerRunning = false;
-static TimedAction gRcvAction = TimedAction(3000, CAArduinoCheckData);
/**
* @var gUnicastPort
gUnicastSocket = *serverFD;
OIC_LOG_V(DEBUG, MOD_NAME, "gUnicastPort: %d", gUnicastPort);
OIC_LOG_V(DEBUG, MOD_NAME, "gUnicastSocket: %d", gUnicastSocket);
-
- // start thread to monitor socket here
- if (!gServerRunning)
- {
- gRcvAction.enable();
- gServerRunning = true;
- }
OIC_LOG(DEBUG, MOD_NAME, "OUT");
return CA_STATUS_OK;
}
CAResult_t CAEthernetStartMulticastServer(const char *localAddress, const char *multicastAddress,
- const int16_t multicastPort, int32_t *serverFD)
+ const int16_t multicastPort, int32_t *serverFD)
{
OIC_LOG(DEBUG, MOD_NAME, "IN");
if (CAArduinoInitMulticastUdpSocket(multicastAddress, &multicastPort, &multicastPort,
gMulticastSocket = *serverFD;
OIC_LOG_V(DEBUG, MOD_NAME, "gMulticastPort: %d", multicastPort);
OIC_LOG_V(DEBUG, MOD_NAME, "gMulticastSocket: %d", gMulticastSocket);
- // start thread to monitor socket here
- if (!gServerRunning)
- {
- gRcvAction.enable();
- gServerRunning = true;
- }
-
OIC_LOG(DEBUG, MOD_NAME, "OUT");
return CA_STATUS_OK;
}
CAResult_t CAEthernetStopUnicastServer()
{
OIC_LOG(DEBUG, MOD_NAME, "IN");
- // terminate server thread
- // Stop thread if both server stopped
- if (gMulticastSocket == 0)
- {
- gRcvAction.disable();
- gServerRunning = false;
- }
-
close(gUnicastSocket);
gUnicastSocket = 0;
OIC_LOG(DEBUG, MOD_NAME, "OUT");
CAResult_t CAEthernetStopMulticastServer()
{
OIC_LOG(DEBUG, MOD_NAME, "IN");
- // terminate server thread
- // Stop thread if both server stopped
- if (gUnicastSocket == 0)
- {
- gRcvAction.disable();
- gServerRunning = false;
- }
-
close(gMulticastSocket);
gMulticastSocket = 0;
OIC_LOG(DEBUG, MOD_NAME, "OUT");
if (gPacketReceivedCallback)
{
gPacketReceivedCallback(ipAddress, port, data, dataLength);
- OIC_LOG(DEBUG, MOD_NAME, "NNP");
}
OIC_LOG(DEBUG, MOD_NAME, "OUT");
}
void CAArduinoCheckData()
{
- void *data = malloc(COAP_MAX_PDU_SIZE);
- if (NULL == data)
- {
- return;
- }
- memset(data, 0, COAP_MAX_PDU_SIZE);
+ void *data = NULL;
int32_t dataLen = 0;
uint8_t senderAddr[4] = { 0 };
char addr[IPNAMESIZE] = {0};
if (gUnicastSocket)
{
- dataLen = CAArduinoRecvData(gUnicastSocket, (uint8_t *)data, COAP_MAX_PDU_SIZE, senderAddr,
+ dataLen = CAArduinoRecvData(gUnicastSocket, (uint8_t **)&data, COAP_MAX_PDU_SIZE, senderAddr,
&senderPort);
if (dataLen < 0)
{
}
else if (dataLen > 0)
{
- OIC_LOG(DEBUG, MOD_NAME, "Data recvd on unicast server");
+ OIC_LOG(DEBUG, MOD_NAME, "unicast data recvd");
sprintf(addr, "%d.%d.%d.%d", senderAddr[0], senderAddr[1], senderAddr[2], senderAddr[3]);
CAPacketReceivedCallback(addr, senderPort, data, dataLen);
}
- else
+
+ if(data)
{
- OIC_LOG(DEBUG, MOD_NAME, "No data");
+ OICFree(data);
+ data = NULL;
}
}
if (gMulticastSocket)
{
- dataLen = CAArduinoRecvData(gMulticastSocket, (uint8_t *)data, COAP_MAX_PDU_SIZE, senderAddr,
- &senderPort);
+ dataLen = CAArduinoRecvData(gMulticastSocket, (uint8_t **)&data, COAP_MAX_PDU_SIZE,
+ senderAddr, &senderPort);
if (dataLen < 0)
{
CAEthernetStopMulticastServer();
sprintf(addr, "%d.%d.%d.%d", senderAddr[0], senderAddr[1], senderAddr[2], senderAddr[3]);
CAPacketReceivedCallback(addr, senderPort, data, dataLen);
}
+
+ if(data)
+ {
+ OICFree(data);
+ data = NULL;
+ }
}
- OICFree(data);
}
/// Retrieve any available data from UDP socket. This is a non-blocking call.
-int32_t CAArduinoRecvData(int32_t sockFd, uint8_t *buf, uint32_t bufLen, uint8_t *senderAddr,
- uint16_t *senderPort)
+int32_t CAArduinoRecvData(int32_t sockFd, uint8_t **buf, uint32_t bufLen,
+ uint8_t *senderAddr, uint16_t *senderPort)
{
- OIC_LOG(DEBUG, MOD_NAME, "IN");
+ //OIC_LOG(DEBUG, MOD_NAME, "IN");
/**Bug : When there are multiple UDP packets in Wiznet buffer, W5100.getRXReceivedSize
* will not return correct length of the first packet.
* Fix : Use the patch provided for arduino/libraries/Ethernet/utility/socket.cpp
*/
- VERIFY_NON_NULL(buf, MOD_NAME, "Invalid buf");
- VERIFY_NON_NULL(senderAddr, MOD_NAME, "Invalid senderAddr");
- VERIFY_NON_NULL(senderPort, MOD_NAME, "Invalid senderPort");
+ VERIFY_NON_NULL(buf, MOD_NAME, "buf");
+ VERIFY_NON_NULL(senderAddr, MOD_NAME, "senderAddr");
+ VERIFY_NON_NULL(senderPort, MOD_NAME, "senderPort");
int32_t ret = 0;
uint16_t recvLen = W5100.getRXReceivedSize(sockFd);
if (recvLen == 0)
{
- OIC_LOG_V(DEBUG, MOD_NAME, "rcvd %d", recvLen);
return recvLen;
}
+ OIC_LOG_V(DEBUG, MOD_NAME, "rcvd %d", recvLen);
+ recvLen = recvLen > COAP_MAX_PDU_SIZE ? COAP_MAX_PDU_SIZE:recvLen;
+
+ *buf = (uint8_t *)OICMalloc(recvLen + 1);
+ if (NULL == *buf)
+ {
+ OIC_LOG(DEBUG, MOD_NAME, "Out of memory!");
+ return 0;
+ }
+ memset(*buf, 0, recvLen + 1);
// Read available data.
- ret = recvfrom(sockFd, buf, bufLen, senderAddr, senderPort);
+ ret = recvfrom(sockFd, *buf, bufLen, senderAddr, senderPort);
OIC_LOG(DEBUG, MOD_NAME, "OUT");
return ret;
}
void CAEthernetPullData()
{
- gRcvAction.check();
+ CAArduinoCheckData();
}
/// Retrieves the IP address assigned to Arduino Ethernet shield
static CANetworkPacketReceivedCallback gNetworkPacketCallback = NULL;
/**
- * @var gEthernetNetworkChangeCallback
+ * @var gNetworkChangeCb
* @brief Network Changed Callback to CA
*/
static CANetworkChangeCallback gNetworkChangeCallback = NULL;
static CAResult_t CAEthernetInitializeQueueHandles();
static void CAEthernetDeinitializeQueueHandles();
static void CAEthernetNotifyNetworkChange(const char *address, const int16_t port,
- const CANetworkStatus_t status);
+ const CANetworkStatus_t status);
static void CAEthernetConnectionStateCB(const char *ipAddress,
- const CANetworkStatus_t status);
+ const CANetworkStatus_t status);
static void CAEthernetPacketReceivedCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength, const CABool_t isSecured);
+ const void *data, const uint32_t dataLength, const CABool_t isSecured);
#ifdef __WITH_DTLS__
static uint32_t CAEthernetPacketSendCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength);
+ const void *data, const uint32_t dataLength);
#endif
+
static CAResult_t CAEthernetStopServers();
static void CAEthernetSendDataThread(void *threadData);
static CAEthernetData *CACreateEthernetData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLength);
-void CAFreeEthernetData(CAEthernetData *ethernetData);
+ uint32_t dataLength);
+void CAFreeEthernetData(CAEthernetData *EthernetData);
CAResult_t CAEthernetInitializeQueueHandles()
}
void CAEthernetNotifyNetworkChange(const char *address, const int16_t port,
- const CANetworkStatus_t status)
+ const CANetworkStatus_t status)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory");
return;
}
- localEndpoint->addressInfo.IP.port = port;
if (NULL != gNetworkChangeCallback)
{
}
void CAEthernetConnectionStateCB(const char *ipAddress,
- const CANetworkStatus_t status)
+ const CANetworkStatus_t status)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
int16_t multicastPort = CA_MCAST_PORT;
int32_t multicastFd = 0;
ret = CAEthernetStartMulticastServer("0.0.0.0", CA_MULTICAST_IP, multicastPort,
- &multicastFd);
+ &multicastFd);
if (CA_STATUS_OK == ret)
{
OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Multicast server started on %d port",
#ifdef __WITH_DTLS__
uint32_t CAEthernetPacketSendCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength)
+ const void *data, const uint32_t dataLength)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
#endif
void CAEthernetPacketReceivedCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength, const CABool_t isSecured)
+ const void *data, const uint32_t dataLength, const CABool_t isSecured)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Address: %s, port:%d, data:%s", ipAddress, port, data);
}
CAResult_t CAInitializeEthernet(CARegisterConnectivityCallback registerCallback,
- CANetworkPacketReceivedCallback networkPacketCallback,
- CANetworkChangeCallback netCallback, u_thread_pool_t handle)
+ CANetworkPacketReceivedCallback networkPacketCallback,
+ CANetworkChangeCallback netCallback, u_thread_pool_t handle)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
VERIFY_NON_NULL(registerCallback, ETHERNET_ADAPTER_TAG, "registerCallback");
CAEthernetSetPacketReceiveCallback(CAEthernetPacketReceivedCB);
#ifdef __WITH_DTLS__
CAAdapterNetDtlsInit();
+
CADTLSSetAdapterCallbacks(CAEthernetPacketReceivedCB, CAEthernetPacketSendCB, DTLS_ETHERNET);
#endif
return CA_STATUS_FAILED;
}
+ // Start send queue thread
+ if (CA_STATUS_OK != CAQueueingThreadStart(gSendQueueHandle))
+ {
+ OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Start Send Data Thread");
+ return CA_STATUS_FAILED;
+ }
+
gStartUnicastServerRequested = true;
bool retVal = CAEthernetIsConnected();
if (false == retVal)
char *ifcName;
char *ifcAdrs;
ret = CAEthernetGetInterfaceInfo(&ifcName, &ifcAdrs);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Failed to get ethernet interface info [%d]", ret);
return ret;
// Address is hardcoded as we are using Single Interface
unicastPort = CA_SECURE_PORT;
ret = CAEthernetStartUnicastServer(ifcAdrs, &unicastPort, false, true, &serverFd);
+
if (CA_STATUS_OK == ret)
{
OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Secure Unicast server started on %d port", unicastPort);
gSecureUnicastServerport = unicastPort;
}
#endif
+
OICFree(ifcName);
OICFree(ifcAdrs);
+
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
- return ret;
+ return ret;;
}
CAResult_t CAStartEthernetListeningServer()
char *ifcName;
char *ifcAdrs;
ret = CAEthernetGetInterfaceInfo(&ifcName, &ifcAdrs);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Failed to get ethernet interface info [%d]", ret);
return ret;
}
uint32_t CASendEthernetUnicastData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLength)
+ uint32_t dataLength)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
return dataSize;
}
- // Create ethernetData to add to queue
- CAEthernetData *ethernetData = CACreateEthernetData(NULL, data, dataLength);
- if (!ethernetData)
+ // Create EthernetData to add to queue
+ CAEthernetData *EthernetData = CACreateEthernetData(NULL, data, dataLength);
+ if (!EthernetData)
{
OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to create ethernetData!");
return CA_MEMORY_ALLOC_FAILED;
}
// Add message to send queue
- CAQueueingThreadAddData(gSendQueueHandle, ethernetData, sizeof(CAEthernetData));
+ CAQueueingThreadAddData(gSendQueueHandle, EthernetData, sizeof(CAEthernetData));
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
return dataLength;
// Stop ethernet adapter
CAStopEthernet();
+ // Terminate Ethernet server
+ CAEthernetTerminateServer();
+
// Terminate network monitor
CAEthernetSetConnectionStateChangeCallback(NULL);
CAEthernetTerminateNetworkMonitor();
if (!ethernetData->remoteEndpoint->isSecured)
{
CAEthernetSendData(address, port, ethernetData->data, ethernetData->dataLen, false,
- ethernetData->remoteEndpoint->isSecured);
+ ethernetData->remoteEndpoint->isSecured);
}
else
{
}
#else
CAEthernetSendData(address, port, ethernetData->data, ethernetData->dataLen, false,
- ethernetData->remoteEndpoint->isSecured);
+ ethernetData->remoteEndpoint->isSecured);
#endif
}
else
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "Send Multicast Data is called");
CAEthernetSendData(CA_MULTICAST_IP, CA_MCAST_PORT, ethernetData->data,
- ethernetData->dataLen, true, false);
+ ethernetData->dataLen, true, false);
}
//Free ethernet data
}
CAEthernetData *CACreateEthernetData(const CARemoteEndpoint_t *remoteEndpoint, void *data,
- uint32_t dataLength)
+ uint32_t dataLength)
{
CAEthernetData *ethernetData = (CAEthernetData *) OICMalloc(sizeof(CAEthernetData));
if (!ethernetData)
static void CAEthernetNotifyNetworkChange(const char *address, const int16_t port,
- const CANetworkStatus_t status);
+ const CANetworkStatus_t status);
static void CAEthernetConnectionStateCB(const char *ipAddress,
const CANetworkStatus_t status);
static void CAEthernetPacketReceivedCB(const char *ipAddress, const uint32_t port,
CALocalConnectivity_t *localEndpoint = CAAdapterCreateLocalEndpoint(CA_ETHERNET, address);
if (!localEndpoint)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory");
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory!");
return;
}
localEndpoint->addressInfo.IP.port = port;
ret = CAEthernetStartUnicastServer("0.0.0.0", &port, false, &serverFd);
if (CA_STATUS_OK == ret)
{
- OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Unicast server started on %d port", port);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "unicast started:%d", port);
CAEthernetSetUnicastSocket(serverFd);
CAEthernetSetUnicastPort(port);
gUnicastServerport = port;
}
else
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to start Unicast server [%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
}
}
ret = CAEthernetStartMulticastServer("0.0.0.0", CA_MULTICAST_IP, multicastPort, &serverFd);
if (CA_STATUS_OK == ret)
{
- OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Multicast server started on %d port", multicastPort);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "multicast started:%d", multicastPort);
gIsMulticastServerStarted = true;
}
else
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to start Multicast server [%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
}
}
ret = CAEthernetStopServers();
if (CA_STATUS_OK != ret)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Stop Servers![%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
return;
}
}
const void *data, const uint32_t dataLength)
{
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "IN");
- OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Address: %s, port:%d, data:%s", ipAddress, port, data);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "sddress:%s", ipAddress);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "port:%s", port);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "data:%s", data);
/* CA is freeing this memory */
CARemoteEndpoint_t *endPoint = CAAdapterCreateRemoteEndpoint(CA_ETHERNET, ipAddress, NULL);
if (NULL == endPoint)
{
- OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory");
+ OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory!");
return;
}
endPoint->addressInfo.IP.port = port;
- void *buf = OICMalloc(dataLength + 1);
- if (NULL == buf)
- {
- OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Out of memory");
- CAAdapterFreeRemoteEndpoint(endPoint);
- return;
- }
- memcpy(buf, data, dataLength);
- memset(buf + dataLength, 0, 1);
if (gNetworkPacketCallback)
{
- gNetworkPacketCallback(endPoint, buf, dataLength);
+ gNetworkPacketCallback(endPoint, data, dataLength);
}
+ CADestroyRemoteEndpoint(endPoint);
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
}
CAResult_t ret = CAEthernetInitializeNetworkMonitor();
if (CA_STATUS_OK != ret)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to initialize n/w monitor![%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
return ret;
}
CAEthernetSetConnectionStateChangeCallback(CAEthernetConnectionStateCB);
ret = CAEthernetInitializeServer();
if (CA_STATUS_OK != ret)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to initialize server![%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
CATerminateEthernet();
return ret;
}
EthernetHandler.terminate = CATerminateEthernet;
registerCallback(EthernetHandler, CA_ETHERNET);
- OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "IntializeEthernet is Success");
+ OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "success");
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
return CA_STATUS_OK;
}
CAResult_t ret = CAEthernetStartNetworkMonitor();
if (CA_STATUS_OK != ret)
{
- OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Start n/w monitor");
+ OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Failed");
}
gStartUnicastServerRequested = true;
bool retVal = CAEthernetIsConnected();
if (false == retVal)
{
- OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Ethernet is not Connected");
+ OIC_LOG(ERROR, ETHERNET_ADAPTER_TAG, "Failed");
return ret;
}
ret = CAEthernetStartUnicastServer("0.0.0.0", &unicastPort, false, &serverFd);
if (CA_STATUS_OK == ret)
{
- OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "Unicast server started on %d port", unicastPort);
+ OIC_LOG_V(DEBUG, ETHERNET_ADAPTER_TAG, "unicast started:%d", unicastPort);
CAEthernetSetUnicastSocket(serverFd);
CAEthernetSetUnicastPort(unicastPort);
gUnicastServerport = unicastPort;
if (false == retVal)
{
OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG,
- "Failed to Start Multicast Server, Ethernet not Connected");
+ "No ethernet");
return CA_ADAPTER_NOT_ENABLED;
}
ret = CAEthernetStartMulticastServer("0.0.0.0", CA_MULTICAST_IP, multicastPort, &serverFD);
if (CA_STATUS_OK == ret)
{
- OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "Multicast Server is Started Successfully");
+ OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "multicast success");
gIsMulticastServerStarted = true;
}
VERIFY_NON_NULL_RET(data, ETHERNET_ADAPTER_TAG, "data", dataSize);
if (dataLength == 0)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Invalid Data Length");
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Invalid length");
return dataSize;
}
VERIFY_NON_NULL_RET(data, ETHERNET_ADAPTER_TAG, "data", dataSize);
if (dataLength == 0)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Invalid Data Length");
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Invalid length");
return dataSize;
}
bool retVal = CAEthernetIsConnected();
if (false == retVal)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG,
- "Failed to get interface address, Ethernet not Connected", CA_ADAPTER_NOT_ENABLED);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "No ethernet", CA_ADAPTER_NOT_ENABLED);
return CA_ADAPTER_NOT_ENABLED;
}
CAResult_t ret = CAEthernetGetInterfaceInfo(&ipAddress, &ifcName);
if (CA_STATUS_OK != ret)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to get interface info [%d]", ret);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", ret);
return ret;
}
(*info) = CAAdapterCreateLocalEndpoint(CA_ETHERNET, ipAddress);
if (NULL == (*info))
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to create Local Endpoint!",
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed",
CA_MEMORY_ALLOC_FAILED);
OICFree(ipAddress);
OICFree(ifcName);
OICFree(ipAddress);
OICFree(ifcName);
- OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "GetEthernetInterfaceInformation success");
+ OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "success");
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
return CA_STATUS_OK;
}
CAResult_t result = CAEthernetStopUnicastServer();
if (CA_STATUS_OK != result)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Stop Unicast Server![%d]", result);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", result);
return result;
}
CAEthernetSetUnicastSocket(-1);
result = CAEthernetStopMulticastServer();
if (CA_STATUS_OK != result)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Stop Multicast Server![%d]", result);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", result);
return result;
}
gIsMulticastServerStarted = false;
CAResult_t result = CAEthernetStopServers();
if (CA_STATUS_OK != result)
{
- OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "Failed to Stop Servers![%d]", result);
+ OIC_LOG_V(ERROR, ETHERNET_ADAPTER_TAG, "FAILED:%d", result);
}
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
CAEthernetSetConnectionStateChangeCallback(NULL);
CAEthernetTerminateNetworkMonitor();
- OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "nw monitor terminated");
CAEthernetSetPacketReceiveCallback(NULL);
- OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "TerminateEthernet Success");
+ OIC_LOG(INFO, ETHERNET_ADAPTER_TAG, "Terminated Ethernet");
OIC_LOG(DEBUG, ETHERNET_ADAPTER_TAG, "OUT");
return;
}
void CAEthernetSetUnicastSocket(const int32_t socketFD)
{
OIC_LOG(DEBUG, ETHERNET_CLIENT_TAG, "IN");
+
gUnicastServerSocketDescClient = socketFD;
}
void CAEthernetSetSecureUnicastSocket(const int32_t socketFD)
{
OIC_LOG(DEBUG, ETHERNET_CLIENT_TAG, "IN");
+
gUnicastServerSecureSocketDescClient = socketFD;
}
#endif
#endif
return len;
}
-
* @brief This methods gets local interface name and IP address information.
*/
static void CAEthernetGetInterfaceInformation(char **interfaceName, char **ipAddress,
- char **subnetMask);
+ char **subnetMask);
static void CANetworkMonitorThread(void *threadData);
}
u_mutex_lock(gEthernetNetInfoMutex);
- CAEthernetGetInterfaceInformation(&gEthernetInterfaceName, &gEthernetIPAddress, &gEthernetSubnetMask);
+ CAEthernetGetInterfaceInformation(&gEthernetInterfaceName, &gEthernetIPAddress,
+ &gEthernetSubnetMask);
u_mutex_unlock(gEthernetNetInfoMutex);
nwConnectivityStatus = (gEthernetIPAddress) ? CA_INTERFACE_UP : CA_INTERFACE_DOWN;
// Get the interface and ipaddress information from cache
u_mutex_lock(gEthernetNetInfoMutex);
- if(gEthernetInterfaceName == NULL || gEthernetIPAddress == NULL)
+ if (gEthernetInterfaceName == NULL || gEthernetIPAddress == NULL)
{
OIC_LOG_V(DEBUG, ETHERNET_MONITOR_TAG, "Network not enabled");
return CA_ADAPTER_NOT_ENABLED;
}
*interfaceName = (gEthernetInterfaceName) ? strndup(gEthernetInterfaceName,
- strlen(gEthernetInterfaceName)) : NULL;
- *ipAddress = (gEthernetIPAddress) ? strndup(gEthernetIPAddress,strlen(gEthernetIPAddress))
- : NULL;
+ strlen(gEthernetInterfaceName)) : NULL;
+ *ipAddress = (gEthernetIPAddress) ? strndup(gEthernetIPAddress, strlen(gEthernetIPAddress))
+ : NULL;
u_mutex_unlock(gEthernetNetInfoMutex);
VERIFY_NON_NULL(subnetMask, ETHERNET_MONITOR_TAG, "subnet mask");
u_mutex_lock(gEthernetNetInfoMutex);
- if(NULL == gEthernetSubnetMask)
+ if (NULL == gEthernetSubnetMask)
{
OIC_LOG_V(DEBUG, ETHERNET_MONITOR_TAG, "There is no subnet mask information!");
return CA_STATUS_FAILED;
}
- *subnetMask = (gEthernetSubnetMask) ? strndup(gEthernetSubnetMask,strlen(gEthernetSubnetMask))
- : NULL;
+ *subnetMask = (gEthernetSubnetMask) ? strndup(gEthernetSubnetMask, strlen(gEthernetSubnetMask))
+ : NULL;
u_mutex_unlock(gEthernetNetInfoMutex);
OIC_LOG_V(DEBUG, ETHERNET_MONITOR_TAG, "OUT");
gNetworkChangeCb = callback;
}
-void CAEthernetGetInterfaceInformation(char **interfaceName, char **ipAddress, char ** subnetMask)
+void CAEthernetGetInterfaceInformation(char **interfaceName, char **ipAddress, char **subnetMask)
{
struct ifaddrs *ifa = NULL;
struct ifaddrs *ifp = NULL;
if (-1 == getifaddrs(&ifp))
{
OIC_LOG_V(ERROR, ETHERNET_MONITOR_TAG, "Failed to get interface list!, Error code: %s",
- strerror(errno));
+ strerror(errno));
return;
}
continue;
}
- if (!strncasecmp(ifa->ifa_name,matchName,strlen(matchName)))
+ if (!strncasecmp(ifa->ifa_name, matchName, strlen(matchName)))
{
// get the interface ip address
if (0 != getnameinfo(ifa->ifa_addr, len, interfaceAddress,
- sizeof(interfaceAddress), NULL, 0, NI_NUMERICHOST))
+ sizeof(interfaceAddress), NULL, 0, NI_NUMERICHOST))
{
- OIC_LOG_V(ERROR, ETHERNET_MONITOR_TAG, "Failed to get IPAddress, Error code: %s",
- strerror(errno));
- break;
+ OIC_LOG_V(ERROR, ETHERNET_MONITOR_TAG, "Failed to get IPAddress, Error code: %s",
+ strerror(errno));
+ break;
}
// get the interface subnet mask
if (0 != getnameinfo(ifa->ifa_netmask, len, interfaceSubnetMask,
- sizeof(interfaceSubnetMask), NULL, 0, NI_NUMERICHOST))
+ sizeof(interfaceSubnetMask), NULL, 0, NI_NUMERICHOST))
{
- OIC_LOG_V(ERROR, ETHERNET_MONITOR_TAG, "Failed to get subnet mask, Error code: %s",
- strerror(errno));
- break;
+ OIC_LOG_V(ERROR, ETHERNET_MONITOR_TAG, "Failed to get subnet mask, Error code: %s",
+ strerror(errno));
+ break;
}
// set interface name
bool *stopFlag;
int32_t socket_fd;
CAAdapterServerType_t type;
-}CAAdapterReceiveThreadContext_t;
+} CAAdapterReceiveThreadContext_t;
static void CAReceiveHandler(void *data)
{
continue;
}
- if(!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))
+ if (!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))
{
OIC_LOG(DEBUG, ETHERNET_SERVER_TAG, "Packet received from different subnet, Ignore!");
continue;
}
OICFree(netMask);
- switch(ctx->type)
+ switch (ctx->type)
{
case CA_UNICAST_SERVER:
case CA_MULTICAST_SERVER:
case CA_SECURED_UNICAST_SERVER:
{
CAResult_t ret = CAAdapterNetDtlsDecrypt(srcIPAddress,
- srcPort,
- (uint8_t *)recvBuffer,
- recvLen, DTLS_ETHERNET);
+ srcPort,
+ (uint8_t *)recvBuffer,
+ recvLen, DTLS_ETHERNET);
OIC_LOG_V(DEBUG, ETHERNET_SERVER_TAG, "CAAdapterNetDtlsDecrypt returns [%d]", ret);
}
break;
OIC_LOG(DEBUG, ETHERNET_SERVER_TAG, "OUT");
}
-static CAResult_t CACreateSocket(int32_t* socketFD, const char *localIp, int16_t *port,
+static CAResult_t CACreateSocket(int32_t *socketFD, const char *localIp, int16_t *port,
const bool forceStart)
{
int32_t sock = -1;
memset((char *) &sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(serverPort);
- if(localIp)
+ if (localIp)
{
sockAddr.sin_addr.s_addr = inet_addr(localIp);
}
OIC_LOG(DEBUG, ETHERNET_SERVER_TAG, "IN");
CAResult_t ret = CACreateSocket(serverFD, localAddress, port, forceStart);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG(ERROR, ETHERNET_SERVER_TAG, "Failed to create unicast socket");
return ret;
* Thread context will be freed by thread on exit.
*/
CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
+ if (!ctx)
{
OIC_LOG(ERROR, ETHERNET_SERVER_TAG, "Out of memory!");
close(*serverFD);
ctx->stopFlag = &gStopUnicast;
ctx->socket_fd = *serverFD;
- ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER:CA_UNICAST_SERVER;
+ ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER : CA_UNICAST_SERVER;
if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))
{
OIC_LOG(ERROR, ETHERNET_SERVER_TAG, "Failed to create read thread!");
gStopUnicast = false;
if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gUnicastServerSocketFD))
+ &gUnicastServerSocketFD))
{
OIC_LOG_V(ERROR, ETHERNET_SERVER_TAG, "Failed to start unicast server!");
gUnicastServerSocketFD = -1;
gStopSecureUnicast = false;
if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gSecureUnicastServerSocketFD))
+ &gSecureUnicastServerSocketFD))
{
OIC_LOG_V(ERROR, ETHERNET_SERVER_TAG, "Failed to start unicast server!");
gSecureUnicastServerSocketFD = -1;
}
CAResult_t ret = CACreateSocket(&gMulticastServerSocketFD, multicastAddress, &port, true);
- if(ret != CA_STATUS_OK)
+ if (ret != CA_STATUS_OK)
{
OIC_LOG(ERROR, ETHERNET_SERVER_TAG, "Failed to create multicast socket");
u_mutex_unlock(gMutexMulticastServer);
* Thread context will be freed by thread on exit.
*/
CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
+ if (!ctx)
{
OIC_LOG(ERROR, ETHERNET_SERVER_TAG, "Out of memory!");
close(gMulticastServerSocketFD);
}
*serverFD = gMulticastServerSocketFD;
- strncpy(gMulticastServerInterface, localAddress, sizeof(gMulticastServerInterface));
+ strncpy(gMulticastServerInterface, localAddress, sizeof(IPNAMESIZE));
u_mutex_unlock(gMutexMulticastServer);
OIC_LOG(DEBUG, ETHERNET_SERVER_TAG, "OUT");
}
CAResult_t CAEthernetGetUnicastServerInfo(const bool isSecured, char **ipAddress, int16_t *port,
- int32_t *serverFD)
+ int32_t *serverFD)
{
OIC_LOG(DEBUG, ETHERNET_SERVER_TAG, "IN");
* @var gUnicastServerSocketDescClient
* @brief socket descriptor for unicast server
*/
-static int gUnicastServerSocketDescClient = -1;
+static int32_t gUnicastServerSocketDescClient = -1;
-void CAWiFiSetUnicastSocket(const int32_t socketFD)
-{
- OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "IN");
-
- gUnicastServerSocketDescClient = socketFD;
-}
+#ifdef __WITH_DTLS__
+/**
+ * @var gUnicastServerSocketDescClient
+ * @brief socket descriptor for secure unicast server
+ */
+static int32_t gUnicastServerSecureSocketDescClient = -1;
+#endif
-uint32_t CAWiFiSendData(const char *remoteAddress, const uint32_t port,
- const void *data, const uint32_t dataLength, bool isMulticast)
+static uint32_t CASendData(const char *remoteAddress, const uint32_t port,
+ const void *data, const uint32_t dataLength, int32_t sockfd)
{
OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "IN");
return 0;
}
- if (0 > gUnicastServerSocketDescClient)
+ if (0 > sockfd)
{
OIC_LOG(ERROR, WIFI_CLIENT_TAG, "Unicast Server is not running !");
return 0;
return 0;
}
- int32_t sendDataLength = sendto(gUnicastServerSocketDescClient, data, dataLength, 0,
+ int32_t sendDataLength = sendto(sockfd, data, dataLength, 0,
(struct sockaddr *)&destAddr, sizeof(destAddr));
if (sendDataLength == -1)
{
return sendDataLength;
}
+void CAWiFiSetUnicastSocket(const int32_t socketFD)
+{
+ OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "IN");
+
+ gUnicastServerSocketDescClient = socketFD;
+
+ OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "OUT");
+}
+
+#ifdef __WITH_DTLS__
+void CAWiFiSetSecureUnicastSocket(const int32_t socketFD)
+{
+ OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "IN");
+
+ gUnicastServerSecureSocketDescClient = socketFD;
+
+ OIC_LOG(DEBUG, WIFI_CLIENT_TAG, "OUT");
+}
+#endif
+uint32_t CAWiFiSendData(const char *remoteAddress, const uint32_t port,
+ const void *data, const uint32_t dataLength,
+ CABool_t isMulticast, CABool_t isSecured)
+{
+ uint32_t len = 0;
+
+#ifdef __WITH_DTLS__
+ if (CA_TRUE == isSecured)
+ {
+ len = CASendData(remoteAddress, port,
+ data, dataLength, gUnicastServerSecureSocketDescClient);
+ }
+ else
+ {
+#endif
+ len = CASendData(remoteAddress, port,
+ data, dataLength, gUnicastServerSocketDescClient);
+#ifdef __WITH_DTLS__
+ }
+#endif
+ return len;
+}
+
+
gNetworkChangeCb(gWifiIPAddress, nwConnectivityStatus);
}
+ OICFree(interfaceName);
+ OICFree(ipAddress);
}
JNIEXPORT void JNICALL Java_com_iotivity_jar_CAWiFiInterface_CAWiFiStateEnabled
#include "pdu.h"
#include "caadapterutils.h"
+#ifdef __WITH_DTLS__
+#include "caadapternetdtls.h"
+#endif
#include "umutex.h"
/**
#define CA_UDP_BIND_RETRY_COUNT 10
/**
- * @var gUnicastServerSocketDescriptor
- * @brief socket descriptor for unicast server
+ * @var gUnicastServerSocketFD
+ * @brief Unicast server socket descriptor
*/
-static int32_t gUnicastServerSocketDescriptor = -1;
+static int32_t gUnicastServerSocketFD = -1;
/**
- * @var gUnicastServerSocketDescriptor
- * @brief socket descriptor for unicast server
+ * @var gMutexUnicastServer
+ * @brief Mutex to synchronize unicast server
*/
-static char *gUnicastServerAddress = NULL;
+static u_mutex gMutexUnicastServer = NULL;
/**
- * @var gUnicastServerSocketDescriptor
- * @brief socket descriptor for unicast server
+ * @var gStopUnicast
+ * @brief Flag to control the Receive Unicast Data Thread
*/
-static int16_t gUnicastServerPort = -1;
+static bool gStopUnicast = false;
/**
- * @var gMutexUnicastServerSocketDescriptor
- * @brief Mutex for socket descriptor for unicast server
+ * @var gMulticastServerSocketFD
+ * @brief socket descriptor for multicast server
*/
-static u_mutex gMutexUnicastServerSocketDescriptor = NULL;
+static int32_t gMulticastServerSocketFD = -1;
+
/**
- * @var gMulticastServerSocketDescriptor
- * @brief socket descriptor for multicast server
+ * @var gMutexMulticastServer
+ * @brief Mutex to synchronize secure multicast server
*/
-static int32_t gMulticastServerSocketDescriptor = -1;
+static u_mutex gMutexMulticastServer = NULL;
/**
- * @var gMutexMulticastServerSocketDescriptor
- * @brief Mutex for socket descriptor for Multicast server
+ * @var gStopMulticast
+ * @brief Flag to control the Receive Multicast Data Thread
*/
-static u_mutex gMutexMulticastServerSocketDescriptor = NULL;
+static bool gStopMulticast = false;
+#ifdef __WITH_DTLS__
/**
- * @var gThreadPool
- * @brief ThreadPool for storing u_thread_pool_t handle passed from adapter
+ * @var gSecureUnicastServerSocketFD
+ * @brief Secure unicast server socket descriptor
*/
-static u_thread_pool_t gThreadPool = NULL;
+static int32_t gSecureUnicastServerSocketFD = -1;
/**
- * @var gMReq
- * @brief ip_mreq structure passed to join a multicast group
+ * @var gMutexUnicastServer
+ * @brief Mutex to synchronize secure unicast server
*/
-static struct ip_mreq gMReq;
+static u_mutex gMutexSecureUnicastServer = NULL;
/**
- * @var gStopUnicast
- * @brief Flag to control the Receive Unicast Data Thread
+ * @var gStopSecureUnicast
+ * @brief Flag to control the unicast secure data receive thread
*/
-static bool gStopUnicast = false;
+static bool gStopSecureUnicast = false;
/**
- * @var gMutexStopUnicast
- * @brief Mutex for gStopUnicast
+ * @var gSecureUnicastRecvBuffer
+ * @brief Character buffer used for receiving unicast data from network
*/
-static u_mutex gMutexStopUnicast = NULL;
+static char gSecureUnicastRecvBuffer[COAP_MAX_PDU_SIZE] = {0};
+#endif
/**
- * @var gStopMulticast
- * @brief Flag to control the Receive Multicast Data Thread
+ * @var gThreadPool
+ * @brief ThreadPool for storing u_thread_pool_t handle passed from adapter
*/
-static bool gStopMulticast = false;
+static u_thread_pool_t gThreadPool = NULL;
/**
- * @var gMutexStopMulticast
- * @brief Mutex for gStopMulticast
+ * @var gMulticastMemberReq
+ * @brief ip_mreq structure passed to join a multicast group
*/
-static u_mutex gMutexStopMulticast = NULL;
+static struct ip_mreq gMulticastMemberReq;
/**
* @var gPacketReceivedCallback
*/
static char gMulticastRecvBuffer[COAP_MAX_PDU_SIZE] = {0};
-/**
- * @fn CAWiFiServerCreateMutex
- * @brief Creates and initializes mutex
- */
-static CAResult_t CAWiFiServerCreateMutex(void);
+static void CAUnicastReceiveHandler(void *data)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "[CAUnicastReceiveHandler] IN");
-/**
- * @fn CAWiFiServerDestroyMutex
- * @brief Releases all created mutex
- */
-static void CAWiFiServerDestroyMutex(void);
+ fd_set reads;
+ struct timeval timeout;
-/**
- * @fn CAReceiveThreadForMulticast
- * @brief Handler thread for receiving data on multicast server
- */
-static void *CAReceiveThreadForMulticast(void *data);
+ while (!gStopUnicast)
+ {
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
-/**
- * @fn CAWiFiReceiveThreadForUnicast
- * @brief Handler thread for receiving data on unicast server
- */
-static void *CAWiFiReceiveThreadForUnicast(void *data);
+ FD_ZERO(&reads);
+ FD_SET(gUnicastServerSocketFD, &reads);
-CAResult_t CAWiFiInitializeServer(const u_thread_pool_t threadPool)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+ int32_t ret = select(gUnicastServerSocketFD + 1, &reads, NULL, NULL, &timeout);
+ if (gStopUnicast)
+ {
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Stop request already received for unicast server");
+ break;
+ }
+ if (ret < 0)
+ {
+ OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
+ continue;
+ }
+ if (!FD_ISSET(gUnicastServerSocketFD, &reads))
+ {
+ continue;
+ }
- //Input validation
- VERIFY_NON_NULL(threadPool, WIFI_SERVER_TAG, "Thread pool handle is NULL");
+ memset(gUnicastRecvBuffer, 0, sizeof(char)*COAP_MAX_PDU_SIZE);
- //Initialize mutex
- if (CA_STATUS_OK != CAWiFiServerCreateMutex())
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create mutex!");
- return CA_STATUS_FAILED;
- }
+ // Read data from socket
+ struct sockaddr_in srcSockAddress;
+ int32_t recvLen;
+ socklen_t srcAddressLen = sizeof(srcSockAddress);
+ if (-1 == (recvLen = recvfrom(gUnicastServerSocketFD, gUnicastRecvBuffer,
+ COAP_MAX_PDU_SIZE, 0, (struct sockaddr *) &srcSockAddress,
+ &srcAddressLen)))
+ {
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
+ continue;
+ }
+ else if (0 == recvLen)
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Unicast server socket is shutdown !");
- gThreadPool = threadPool;
+ // Notify upper layer this exception
+ if (gExceptionCallback)
+ {
+ gExceptionCallback(CA_UNICAST_SERVER);
+ }
+ return;
+ }
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
+ const char *srcIPAddress = NULL;
+ int32_t srcPort = -1;
-void CAWiFiTerminateServer(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+ srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
+ srcPort = ntohs(srcSockAddress.sin_port);
- gThreadPool = NULL;
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
+ srcIPAddress, srcPort);
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
+ gUnicastRecvBuffer, recvLen);
- //Destroy mutex
- CAWiFiServerDestroyMutex();
+ // Notify data to upper layer
+ if (gPacketReceivedCallback)
+ {
+ gPacketReceivedCallback(srcIPAddress, srcPort, gUnicastRecvBuffer, recvLen, false);
+ }
+ }
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "[CAUnicastReceiveHandler] OUT");
}
-CAResult_t CAWiFiStartMulticastServer(const char *localAddress, const char *multicastAddress,
- const int16_t multicastPort, int32_t *serverFD)
+#ifdef __WITH_DTLS__
+static void CASecureUnicastReceiveHandler(void *data)
{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "[CASecureUnicastReceiveHandler] IN");
+
+ fd_set reads;
+ struct timeval timeout;
- if (gMulticastServerSocketDescriptor != -1)
+ while (!gStopSecureUnicast)
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast Server is already running!");
- return CA_SERVER_STARTED_ALREADY;
- }
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
- VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "Local address is NULL");
- VERIFY_NON_NULL(multicastAddress, WIFI_SERVER_TAG, "Multicast address is NULL");
+ FD_ZERO(&reads);
+ FD_SET(gSecureUnicastServerSocketFD, &reads);
- if (0 >= multicastPort)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: Multicast port is invalid!");
- return CA_STATUS_INVALID_PARAM;
- }
+ int32_t ret = select(gSecureUnicastServerSocketFD + 1, &reads, NULL, NULL, &timeout);
+ if (gStopSecureUnicast)
+ {
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Stop request already received for secure server");
+ break;
+ }
- // Create a datagram socket on which to recv/send.
- u_mutex_lock(gMutexStopMulticast);
- gStopMulticast = false;
- u_mutex_unlock(gMutexStopMulticast);
+ if (ret < 0)
+ {
+ OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
+ continue;
+ }
- u_mutex_lock(gMutexMulticastServerSocketDescriptor);
+ if (!FD_ISSET(gSecureUnicastServerSocketFD, &reads))
+ {
+ continue;
+ }
- // create a UDP socket
- if ((gMulticastServerSocketDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to Create Socket, Error code: %s",
- strerror(errno));
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
- }
+ memset(gUnicastRecvBuffer, 0, sizeof(char)*COAP_MAX_PDU_SIZE);
- // Make the socket non-blocking
- if (-1 == fcntl(gMulticastServerSocketDescriptor, F_SETFL, O_NONBLOCK))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set non-block mode, Error code: %s",
- strerror(errno));
- close(gMulticastServerSocketDescriptor);
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_STATUS_FAILED;
- }
+ // Read data from socket
+ struct sockaddr_in srcSockAddress;
+ int32_t recvLen;
+ socklen_t srcAddressLen = sizeof(srcSockAddress);
+ if (-1 == (recvLen = recvfrom(gSecureUnicastServerSocketFD, gSecureUnicastRecvBuffer,
+ COAP_MAX_PDU_SIZE, 0, (struct sockaddr *) &srcSockAddress,
+ &srcAddressLen)))
+ {
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
+ continue;
+ }
+ else if (0 == recvLen)
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Unicast server socket is shutdown !");
- OIC_LOG(INFO, WIFI_SERVER_TAG, "socket creation success");
+ // Notify upper layer this exception
+ if (gExceptionCallback)
+ {
+ gExceptionCallback(CA_UNICAST_SERVER);
+ }
+ return;
+ }
- int32_t setOptionOn = 1;
- if (-1 == setsockopt(gMulticastServerSocketDescriptor, SOL_SOCKET, SO_REUSEADDR,
- (char *) &setOptionOn,
- sizeof(setOptionOn)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to setsockopt for SO_REUSEADDR, Error code: %s",
- strerror(errno));
- close(gMulticastServerSocketDescriptor);
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
- }
+ const char *srcIPAddress = NULL;
+ int32_t srcPort = -1;
- struct sockaddr_in sockAddr;
- memset((char *) &sockAddr, 0, sizeof(sockAddr));
+ srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
+ srcPort = ntohs(srcSockAddress.sin_port);
- sockAddr.sin_family = AF_INET;
- sockAddr.sin_port = htons(multicastPort);
- sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
+ srcIPAddress, srcPort);
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
+ gSecureUnicastRecvBuffer, recvLen);
- //bind socket to multicast port
- if (-1 == bind(gMulticastServerSocketDescriptor, (struct sockaddr *) &sockAddr,
- sizeof(sockAddr)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to Bind Socket! Return Code[%d]",
- CA_SOCKET_OPERATION_FAILED);
- close(gMulticastServerSocketDescriptor);
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
+ CAResult_t result = CAAdapterNetDtlsDecrypt(srcIPAddress,
+ srcPort,
+ (uint8_t *)gSecureUnicastRecvBuffer,
+ recvLen);
+
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, " CAAdapterNetDtlsDecrypt returns [%d]", result);
}
- OIC_LOG(INFO, WIFI_SERVER_TAG, "socket bind success");
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "[CASecureUnicastReceiveHandler] OUT");
+}
+#endif
- //Add membership to receiving socket (join group)
- memset(&gMReq, 0, sizeof(struct ip_mreq));
- gMReq.imr_interface.s_addr = htonl(INADDR_ANY);
- inet_aton(multicastAddress, &gMReq.imr_multiaddr);
+static void CAMulticastReceiveHandler(void *data)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- if (-1 == setsockopt(gMulticastServerSocketDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (char *) &gMReq,
- sizeof(struct ip_mreq)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to add to multicast group, Error code: %s\n",
- strerror(errno));
- close(gMulticastServerSocketDescriptor);
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
- }
+ fd_set reads;
+ struct timeval timeout;
- /**
- * The task to listen to data from multicastcast socket is added to the thread pool.
- * This is a blocking call is made where we try to receive some data.. We will keep waiting until some data is received.
- * This task will be terminated when thread pool is freed on stopping the adapters.
- */
- if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, (void *) CAReceiveThreadForMulticast,
- (void *)NULL))
+ while (!gStopMulticast)
{
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "[testThreadPool] thread_pool_add_task failed!");
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
- close(gMulticastServerSocketDescriptor);
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_STATUS_FAILED;
- }
+ FD_ZERO(&reads);
+ FD_SET(gMulticastServerSocketFD, &reads);
- *serverFD = gMulticastServerSocketDescriptor;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
+ int32_t ret = select(gMulticastServerSocketFD + 1, &reads, NULL, NULL, &timeout);
+ if (gStopMulticast)
+ {
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Stop request already received for multicast server");
+ break;
+ }
+ if ( ret < 0)
+ {
+ OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
+ continue;
+ }
+ if (!FD_ISSET(gMulticastServerSocketFD, &reads))
+ {
+ continue;
+ }
- OIC_LOG(INFO, WIFI_SERVER_TAG, "thread_pool_add_task done");
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast Server Started Successfully");
+ memset(gMulticastRecvBuffer, 0, sizeof(char)*COAP_MAX_PDU_SIZE);
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
+ // Read data from socket
+ struct sockaddr_in srcSockAddress;
+ int32_t recvLen;
+ socklen_t srcAddressLen = sizeof(srcSockAddress);
+ if (-1 == (recvLen = recvfrom(gMulticastServerSocketFD, gMulticastRecvBuffer,
+ COAP_MAX_PDU_SIZE, 0, (struct sockaddr *) &srcSockAddress,
+ &srcAddressLen)))
+ {
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
+ continue;
+ }
+ else if (0 == recvLen)
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast socket is shutdown!\n");
-}
+ // Notify upper layer this exception
+ if (gExceptionCallback)
+ {
+ gExceptionCallback(CA_MULTICAST_SERVER);
+ }
+ return;
+ }
-CAResult_t CAWiFiStartUnicastServer(const char *localAddress, int16_t *port,
- const bool forceStart, int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+ const char *srcIPAddress = NULL;
+ int32_t srcPort = -1;
- if (gUnicastServerSocketDescriptor != -1)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already! Return Code[%d]",
- CA_SERVER_STARTED_ALREADY);
- return CA_SERVER_STARTED_ALREADY;
- }
+ srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
+ srcPort = ntohs(srcSockAddress.sin_port);
+
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
+ srcIPAddress, srcPort);
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
+ gMulticastRecvBuffer, recvLen);
- VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "Invalid argument : localAddress is NULL");
- VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "Invalid argument : port is NULL");
- if (0 >= port)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: port is invalid!");
- return CA_STATUS_INVALID_PARAM;
+ // Notify data to upper layer
+ if (gPacketReceivedCallback)
+ {
+ gPacketReceivedCallback(srcIPAddress, srcPort, gMulticastRecvBuffer, recvLen, false);
+ }
}
- u_mutex_lock(gMutexStopUnicast);
- gStopUnicast = false;
- u_mutex_unlock(gMutexStopUnicast);
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+}
+
+static CAResult_t CAStartUnicastServer(const char *localAddress, int16_t *port,
+ const bool forceStart, u_thread_func receiveThread,
+ int32_t *serverFD)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ int32_t socketFD = -1;
- u_mutex_lock(gMutexUnicastServerSocketDescriptor);
// Create a UDP socket
- if ((gUnicastServerSocketDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
+ if (-1 == (socketFD = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to Create Socket, Error code: %s",
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to create Socket, Error code: %s",
strerror(errno));
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
+ return CA_STATUS_FAILED;
}
// Make the socket non-blocking
- if (-1 == fcntl(gUnicastServerSocketDescriptor, F_SETFL, O_NONBLOCK))
+ if (-1 == fcntl(socketFD, F_SETFL, O_NONBLOCK))
{
OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set non-block mode, Error code: %s",
strerror(errno));
- close(gUnicastServerSocketDescriptor);
- gUnicastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
+
+ close(socketFD);
return CA_STATUS_FAILED;
}
- OIC_LOG(INFO, WIFI_SERVER_TAG, "socket creation success");
-
if (true == forceStart)
{
int32_t setOptionOn = 1;
- if (-1 == setsockopt(gUnicastServerSocketDescriptor, SOL_SOCKET, SO_REUSEADDR,
+ if (-1 == setsockopt(socketFD, SOL_SOCKET, SO_REUSEADDR,
(char *) &setOptionOn,
sizeof(setOptionOn)))
{
OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set SO_REUSEADDR! Error code: %s",
strerror(errno));
- close(gUnicastServerSocketDescriptor);
- gUnicastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
+
+ close(socketFD);
+ return CA_STATUS_FAILED;
}
}
sockAddr.sin_port = htons(serverPort);
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- //Trying for bind in a loop
int16_t i;
for (i = 0; i < CA_UDP_BIND_RETRY_COUNT; i++)
{
- if (-1 == bind(gUnicastServerSocketDescriptor, (struct sockaddr *) &sockAddr,
+ if (-1 == bind(socketFD, (struct sockaddr *) &sockAddr,
sizeof(sockAddr)))
{
if (false == forceStart)
break;
}
- if (false == isBound)
+ if (false == isBound)
+ {
+ close(socketFD);
+ return CA_STATUS_FAILED;
+ }
+
+ *port = serverPort;
+ *serverFD = socketFD;
+
+ /**
+ * The task to listen for data from unicast socket is added to the thread pool.
+ * This is a blocking call is made where we try to receive some data..
+ * We will keep waiting until some data is received.
+ * This task will be terminated when thread pool is freed on stopping the adapters.
+ */
+ if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, receiveThread, NULL))
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create read thread!");
+
+ close(socketFD);
+ return CA_STATUS_FAILED;
+ }
+
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ return CA_STATUS_OK;
+}
+
+static void CAWiFiServerDestroyMutex(void)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ if (gMutexUnicastServer)
+ {
+ u_mutex_free(gMutexUnicastServer);
+ gMutexUnicastServer = NULL;
+ }
+
+#ifdef __WITH_DTLS__
+ if (gMutexSecureUnicastServer)
+ {
+ u_mutex_free(gMutexSecureUnicastServer);
+ gMutexSecureUnicastServer = NULL;
+ }
+#endif
+
+ if (gMutexMulticastServer)
+ {
+ u_mutex_free(gMutexMulticastServer);
+ gMutexMulticastServer = NULL;
+ }
+
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+}
+
+static CAResult_t CAWiFiServerCreateMutex(void)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ gMutexUnicastServer = u_mutex_new();
+ if (!gMutexUnicastServer)
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
+ return CA_STATUS_FAILED;
+ }
+
+#ifdef __WITH_DTLS__
+ gMutexSecureUnicastServer = u_mutex_new();
+ if (!gMutexSecureUnicastServer)
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
+ return CA_STATUS_FAILED;
+ }
+#endif
+
+ gMutexMulticastServer = u_mutex_new();
+ if (!gMutexMulticastServer)
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
+
+ CAWiFiServerDestroyMutex();
+ return CA_STATUS_FAILED;
+ }
+
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ return CA_STATUS_OK;
+}
+
+CAResult_t CAWiFiInitializeServer(const u_thread_pool_t threadPool)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ // Input validation
+ VERIFY_NON_NULL(threadPool, WIFI_SERVER_TAG, "Thread pool handle is NULL");
+
+ // Initialize mutex
+ if (CA_STATUS_OK != CAWiFiServerCreateMutex())
+ {
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create mutex!");
+ return CA_STATUS_FAILED;
+ }
+
+ gThreadPool = threadPool;
+
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ return CA_STATUS_OK;
+}
+
+void CAWiFiTerminateServer(void)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ gThreadPool = NULL;
+
+ // Destroy mutex
+ CAWiFiServerDestroyMutex();
+
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+}
+
+CAResult_t CAWiFiStartUnicastServer(const char *localAddress, int16_t *port,
+ const bool forceStart, const CABool_t isSecured,
+ int32_t *serverFD)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ // Input validation
+ VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "localAddress");
+ VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "port");
+ VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "server socket FD");
+
+ if (0 >= *port)
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: port is invalid!");
+ return CA_STATUS_INVALID_PARAM;
+ }
+
+ *serverFD = -1;
+ if (CA_FALSE == isSecured)
+ {
+ u_mutex_lock(gMutexUnicastServer);
+ if (-1 != gUnicastServerSocketFD)
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",
+ CA_SERVER_STARTED_ALREADY);
+
+ *serverFD = gUnicastServerSocketFD;
+ u_mutex_unlock(gMutexUnicastServer);
+ return CA_SERVER_STARTED_ALREADY;
+ }
+
+ gStopUnicast = false;
+ if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart,
+ CAUnicastReceiveHandler, &gUnicastServerSocketFD))
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
+ gUnicastServerSocketFD = -1;
+ u_mutex_unlock(gMutexUnicastServer);
+ return CA_STATUS_FAILED;
+ }
+
+ *serverFD = gUnicastServerSocketFD;
+ u_mutex_unlock(gMutexUnicastServer);
+ }
+#ifdef __WITH_DTLS__
+ else // Start unicast server for secured communication
+ {
+ u_mutex_lock(gMutexSecureUnicastServer);
+ if (-1 != gSecureUnicastServerSocketFD)
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",
+ CA_SERVER_STARTED_ALREADY);
+
+ *serverFD = gSecureUnicastServerSocketFD;
+ u_mutex_unlock(gMutexSecureUnicastServer);
+ return CA_SERVER_STARTED_ALREADY;
+ }
+
+ gStopSecureUnicast = false;
+ if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart,
+ CASecureUnicastReceiveHandler, &gSecureUnicastServerSocketFD))
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
+ gSecureUnicastServerSocketFD = -1;
+ u_mutex_unlock(gMutexSecureUnicastServer);
+ return CA_STATUS_FAILED;
+ }
+
+ *serverFD = gSecureUnicastServerSocketFD;
+ u_mutex_unlock(gMutexSecureUnicastServer);
+ }
+#endif
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ return CA_STATUS_OK;
+}
+
+CAResult_t CAWiFiStartMulticastServer(const char *localAddress, const char *multicastAddress,
+ const int16_t multicastPort, int32_t *serverFD)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+
+ // Input validation
+ VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "Local address is NULL");
+ VERIFY_NON_NULL(multicastAddress, WIFI_SERVER_TAG, "Multicast address is NULL");
+
+ if (0 >= multicastPort)
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to bind Socket! Return code[%d]",
- CA_SOCKET_OPERATION_FAILED);
- close(gUnicastServerSocketDescriptor);
- gUnicastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
- return CA_SOCKET_OPERATION_FAILED;
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: Multicast port is invalid!");
+ return CA_STATUS_INVALID_PARAM;
}
- OIC_LOG(INFO, WIFI_SERVER_TAG, "socket bind success");
+ u_mutex_lock(gMutexMulticastServer);
- socklen_t len;
- char *serverAddress = NULL;
- if (-1 != getsockname(gUnicastServerSocketDescriptor, (struct sockaddr *)&sockAddr, &len))
+ if (gMulticastServerSocketFD != -1)
{
- serverPort = ntohs(sockAddr.sin_port);
- serverAddress = inet_ntoa(sockAddr.sin_addr);
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast Server is already running!");
+ return CA_SERVER_STARTED_ALREADY;
}
- /**
- * The task to listen for data from unicast socket is added to the thread pool.
- * This is a blocking call is made where we try to receive some data..
- * We will keep waiting until some data is received.
- * This task will be terminated when thread pool is freed on stopping the adapters.
- */
- if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, (void *) CAWiFiReceiveThreadForUnicast,
- (void *) NULL))
+ if ((gMulticastServerSocketFD = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "[testThreadPool] thread_pool_add_task failed!");
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to Create Socket, Error code: %s",
+ strerror(errno));
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_STATUS_FAILED;
+ }
- close(gUnicastServerSocketDescriptor);
- gUnicastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
+ // Make the socket non-blocking
+ if (-1 == fcntl(gMulticastServerSocketFD, F_SETFL, O_NONBLOCK))
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set non-block mode, Error code: %s",
+ strerror(errno));
+ close(gMulticastServerSocketFD);
+ gMulticastServerSocketFD = -1;
+ u_mutex_unlock(gMutexMulticastServer);
return CA_STATUS_FAILED;
}
- //Free the server address previously stored
- OICFree(gUnicastServerAddress);
- gUnicastServerAddress = NULL;
- gUnicastServerPort = serverPort;
- gUnicastServerAddress = (serverAddress) ? strndup(serverAddress, strlen(serverAddress)) :
- NULL;
- *port = serverPort;
- *serverFD = gUnicastServerSocketDescriptor;
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "socket creation success");
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Unicast Server Started Successfully");
- return CA_STATUS_OK;
-}
+ int32_t setOptionOn = 1;
+ if (-1 == setsockopt(gMulticastServerSocketFD, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &setOptionOn,
+ sizeof(setOptionOn)))
+ {
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to setsockopt for SO_REUSEADDR, Error code: %s",
+ strerror(errno));
+ close(gMulticastServerSocketFD);
+ gMulticastServerSocketFD = -1;
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_STATUS_FAILED;
+ }
-CAResult_t CAWiFiStopMulticastServer(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
+ struct sockaddr_in sockAddr;
+ memset((char *) &sockAddr, 0, sizeof(sockAddr));
- u_mutex_lock(gMutexMulticastServerSocketDescriptor);
+ sockAddr.sin_family = AF_INET;
+ sockAddr.sin_port = htons(multicastPort);
+ sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- if (gMulticastServerSocketDescriptor == -1)
+ // bind socket to multicast port
+ if (-1 == bind(gMulticastServerSocketFD, (struct sockaddr *) &sockAddr,
+ sizeof(sockAddr)))
{
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast Server is not yet Started");
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- return CA_SERVER_NOT_STARTED;
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to Bind Socket!");
+ close(gMulticastServerSocketFD);
+ gMulticastServerSocketFD = -1;
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_STATUS_FAILED;
}
- u_mutex_lock(gMutexStopMulticast);
- gStopMulticast = true;
+ // Add membership to receiving socket (join group)
+ memset(&gMulticastMemberReq, 0, sizeof(struct ip_mreq));
+ gMulticastMemberReq.imr_interface.s_addr = htonl(INADDR_ANY);
+ inet_aton(multicastAddress, &gMulticastMemberReq.imr_multiaddr);
- // leave the group after you are done
- if (-1 == setsockopt(gMulticastServerSocketDescriptor, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (char *)&gMReq,
+ if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *) &gMulticastMemberReq,
sizeof(struct ip_mreq)))
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "cannot leave multicast group, Error code: %s\n",
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to add to multicast group, Error code: %s\n",
strerror(errno));
+ close(gMulticastServerSocketFD);
+ gMulticastServerSocketFD = -1;
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_STATUS_FAILED;
}
- // close the socket
- if (-1 == close(gMulticastServerSocketDescriptor))
+ /**
+ * The task to listen to data from multicastcast socket is added to the thread pool.
+ * This is a blocking call is made where we try to receive some data.
+ * We will keep waiting until some data is received.
+ * This task will be terminated when thread pool is freed on stopping the adapters.
+ */
+ gStopMulticast = false;
+ if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAMulticastReceiveHandler, NULL))
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast Server socket close failed, Error code: %s\n",
- strerror(errno));
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
- u_mutex_unlock(gMutexStopMulticast);
- return CA_SOCKET_OPERATION_FAILED;
- }
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "thread_pool_add_task failed!");
- u_mutex_unlock(gMutexStopMulticast);
-
- gMulticastServerSocketDescriptor = -1;
- u_mutex_unlock(gMutexMulticastServerSocketDescriptor);
+ close(gMulticastServerSocketFD);
+ gMulticastServerSocketFD = -1;
+ gStopMulticast = true;
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_STATUS_FAILED;
+ }
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast Server Stopped Successfully");
+ *serverFD = gMulticastServerSocketFD;
+ u_mutex_unlock(gMutexMulticastServer);
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
return CA_STATUS_OK;
-
}
CAResult_t CAWiFiStopUnicastServer()
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- u_mutex_lock(gMutexUnicastServerSocketDescriptor);
- if (gUnicastServerSocketDescriptor == -1)
+ u_mutex_lock(gMutexUnicastServer);
+ if (-1 == gUnicastServerSocketFD)
{
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Unicast Server is not yet Started");
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Unicast server is not yet started");
+ u_mutex_unlock(gMutexUnicastServer);
return CA_SERVER_NOT_STARTED;
}
- u_mutex_lock(gMutexStopUnicast);
+
gStopUnicast = true;
// close the socket
- if (-1 == close(gUnicastServerSocketDescriptor))
+ if (-1 == close(gUnicastServerSocketFD))
{
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server socket close failed, Error code: %s\n",
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to close socket, Error code: %s\n",
strerror(errno));
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
- u_mutex_unlock(gMutexStopUnicast);
- return CA_SOCKET_OPERATION_FAILED;
+ u_mutex_unlock(gMutexUnicastServer);
+ return CA_STATUS_FAILED;
}
- u_mutex_unlock(gMutexStopUnicast);
- gUnicastServerSocketDescriptor = -1;
-
- u_mutex_unlock(gMutexUnicastServerSocketDescriptor);
-
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Unicast Server Stopped Successfully");
- return CA_STATUS_OK;
-}
-
-CAResult_t CAWiFiGetUnicastServerInfo(char **ipAddress, int16_t *port, int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- //Input validation
- VERIFY_NON_NULL(ipAddress, WIFI_SERVER_TAG, "ipAddress holder is NULL");
- VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "port holder is NULL");
- VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "serverID holder is NULL");
-
- *ipAddress = gUnicastServerAddress;
- *port = gUnicastServerPort;
- *serverFD = gUnicastServerSocketDescriptor;
+ gUnicastServerSocketFD = -1;
+ u_mutex_unlock(gMutexUnicastServer);
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Unicast server stopped successfully");
return CA_STATUS_OK;
}
-void CAWiFiSetPacketReceiveCallback(CAWiFiPacketReceivedCallback callback)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gPacketReceivedCallback = callback;
-}
-
-void CAWiFiSetExceptionCallback(CAWiFiExceptionCallback callback)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gExceptionCallback = callback;
-}
-
-CAResult_t CAWiFiServerCreateMutex(void)
+#ifdef __WITH_DTLS__
+CAResult_t CAWiFiStopSecureUnicastServer()
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- gMutexUnicastServerSocketDescriptor = u_mutex_new();
- if (!gMutexUnicastServerSocketDescriptor)
+ u_mutex_lock(gMutexSecureUnicastServer);
+ if (-1 == gSecureUnicastServerSocketFD)
{
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
- return CA_STATUS_FAILED;
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Secure unicast server is not yet started");
+ u_mutex_unlock(gMutexSecureUnicastServer);
+ return CA_SERVER_NOT_STARTED;
}
- gMutexMulticastServerSocketDescriptor = u_mutex_new();
- if (!gMutexMulticastServerSocketDescriptor)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
-
- CAWiFiServerDestroyMutex();
- return CA_STATUS_FAILED;
- }
+ gStopSecureUnicast = true;
- gMutexStopUnicast = u_mutex_new();
- if (!gMutexStopUnicast)
+ // close the socket
+ if (-1 == close(gSecureUnicastServerSocketFD))
{
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
-
- CAWiFiServerDestroyMutex();
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to close the socket, Error code: %s\n",
+ strerror(errno));
+ u_mutex_unlock(gMutexSecureUnicastServer);
return CA_STATUS_FAILED;
}
- gMutexStopMulticast = u_mutex_new();
- if (!gMutexStopMulticast)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
-
- CAWiFiServerDestroyMutex();
- return CA_STATUS_FAILED;
- }
+ gSecureUnicastServerSocketFD = -1;
+ u_mutex_unlock(gMutexSecureUnicastServer);
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Secure unicast server stopped successfully");
return CA_STATUS_OK;
}
+#endif
-void CAWiFiServerDestroyMutex(void)
+CAResult_t CAWiFiStopMulticastServer(void)
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- if (gMutexUnicastServerSocketDescriptor)
- {
- u_mutex_free(gMutexUnicastServerSocketDescriptor);
- gMutexUnicastServerSocketDescriptor = NULL;
- }
+ u_mutex_lock(gMutexMulticastServer);
- if (gMutexMulticastServerSocketDescriptor)
+ if (gMulticastServerSocketFD == -1)
{
- u_mutex_free(gMutexMulticastServerSocketDescriptor);
- gMutexMulticastServerSocketDescriptor = NULL;
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast server is not yet started");
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_SERVER_NOT_STARTED;
}
- if (gMutexStopUnicast)
+ gStopMulticast = true;
+
+ // leave the group after you are done
+ if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&gMulticastMemberReq,
+ sizeof(struct ip_mreq)))
{
- u_mutex_free(gMutexStopUnicast);
- gMutexStopUnicast = NULL;
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to leave multicast group, Error code: %s\n",
+ strerror(errno));
}
- if (gMutexStopMulticast)
+ // close the socket
+ if (-1 == close(gMulticastServerSocketFD))
{
- u_mutex_free(gMutexStopMulticast);
- gMutexStopMulticast = NULL;
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to close the socket, Error code: %s\n",
+ strerror(errno));
+ u_mutex_unlock(gMutexMulticastServer);
+ return CA_SOCKET_OPERATION_FAILED;
}
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
+ gMulticastServerSocketFD = -1;
+ u_mutex_unlock(gMutexMulticastServer);
+
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast server stopped successfully");
+ return CA_STATUS_OK;
}
-void *CAWiFiReceiveThreadForUnicast(void *data)
+CAResult_t CAWiFiGetUnicastServerInfo(const CABool_t isSecured, char **ipAddress,
+ int16_t *port, int32_t *serverFD)
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- fd_set reads;
- struct timeval timeout;
+ // Input validation
+ VERIFY_NON_NULL(ipAddress, WIFI_SERVER_TAG, "IP address");
+ VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "Port");
+ VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "Server ID");
- //keep listening for data
- while (!gStopUnicast)
+ struct sockaddr_in sockAddr;
+ socklen_t len = sizeof(struct sockaddr_in);
+ if (-1 == getsockname(gUnicastServerSocketFD, (struct sockaddr *)&sockAddr, &len))
{
- //OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Waiting for data..");
-
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- FD_ZERO(&reads);
- FD_SET(gUnicastServerSocketDescriptor, &reads);
-
- int32_t ret = select(gUnicastServerSocketDescriptor + 1, &reads, NULL, NULL, &timeout);
- if (gStopUnicast)
- {
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Stop Unicast is called");
- break;
- }
- if (ret < 0)
- {
- OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
- continue;
- }
- if (!FD_ISSET(gUnicastServerSocketDescriptor, &reads))
- {
- //OIC_LOG(DEBUG, WIFI_SERVER_TAG, "No data to read");
- continue;
- }
-
- memset(gUnicastRecvBuffer, 0, sizeof(char)*COAP_MAX_PDU_SIZE);
-
- //Read data from socket
- struct sockaddr_in srcSockAddress;
- int32_t recvLen;
- socklen_t srcAddressLen = sizeof(srcSockAddress);
- if (-1 == (recvLen = recvfrom(gUnicastServerSocketDescriptor, gUnicastRecvBuffer,
- COAP_MAX_PDU_SIZE, 0, (struct sockaddr *) &srcSockAddress,
- &srcAddressLen)))
- {
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
- continue;
- }
- else if (0 == recvLen)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Unicast server socket is shutdown !");
-
- //Notify upper layer this exception
- if (gExceptionCallback)
- {
- gExceptionCallback(CA_UNICAST_SERVER);
- }
- return NULL;
- }
-
- const char *srcIPAddress = NULL;
- int32_t srcPort = -1;
-
- srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
- srcPort = ntohs(srcSockAddress.sin_port);
-
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
- srcIPAddress, srcPort);
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
- gUnicastRecvBuffer, recvLen);
-
- //Notify data to upper layer
- if (gPacketReceivedCallback)
- {
- gPacketReceivedCallback(srcIPAddress, srcPort, gUnicastRecvBuffer, recvLen);
- }
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed in getsockname [%s]!", strerror(errno));
+ return CA_STATUS_FAILED;
}
+
+ const char *serverAddress = inet_ntoa(sockAddr.sin_addr);
+ *ipAddress = (serverAddress) ? strndup(serverAddress, strlen(serverAddress)) : NULL;
+ *port = ntohs(sockAddr.sin_port);
+#ifdef __WITH_DTLS__
+ *serverFD = (CA_TRUE == isSecured) ? gSecureUnicastServerSocketFD : gUnicastServerSocketFD;
+#else
+ *serverFD = gUnicastServerSocketFD;
+#endif
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return NULL;
+ return CA_STATUS_OK;
}
-void *CAReceiveThreadForMulticast(void *data)
+void CAWiFiSetPacketReceiveCallback(CAWiFiPacketReceivedCallback callback)
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- fd_set reads;
- struct timeval timeout;
-
- //keep listening for data
- while (!gStopMulticast)
- {
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- FD_ZERO(&reads);
- FD_SET(gMulticastServerSocketDescriptor, &reads);
-
- int32_t ret = select(gMulticastServerSocketDescriptor + 1, &reads, NULL, NULL, &timeout);
- if (gStopMulticast)
- {
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Stop Multicast is called");
- break;
- }
- if ( ret < 0)
- {
- OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
- continue;
- }
- if (!FD_ISSET(gMulticastServerSocketDescriptor, &reads))
- {
- continue;
- }
-
- memset(gMulticastRecvBuffer, 0, sizeof(char)*COAP_MAX_PDU_SIZE);
-
- //Read data from socket
- struct sockaddr_in srcSockAddress;
- int32_t recvLen;
- socklen_t srcAddressLen = sizeof(srcSockAddress);
- if (-1 == (recvLen = recvfrom(gMulticastServerSocketDescriptor, gMulticastRecvBuffer,
- COAP_MAX_PDU_SIZE, 0, (struct sockaddr *) &srcSockAddress,
- &srcAddressLen)))
- {
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
- continue;
- }
- else if (0 == recvLen)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast socket is shutdown, returning from \
- thread\n");
-
- //Notify upper layer this exception
- if (gExceptionCallback)
- {
- gExceptionCallback(CA_MULTICAST_SERVER);
- }
- return NULL;
- }
-
- const char *srcIPAddress = NULL;
- int32_t srcPort = -1;
-
- srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
- srcPort = ntohs(srcSockAddress.sin_port);
-
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
- srcIPAddress, srcPort);
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
- gMulticastRecvBuffer, recvLen);
-
+ gPacketReceivedCallback = callback;
+}
- //Notify data to upper layer
- if (gPacketReceivedCallback)
- {
- gPacketReceivedCallback(srcIPAddress, srcPort, gMulticastRecvBuffer, recvLen);
- }
- }
+void CAWiFiSetExceptionCallback(CAWiFiExceptionCallback callback)
+{
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return NULL;
+ gExceptionCallback = callback;
}
const CABool_t isSecured);
#ifdef __WITH_DTLS__
static uint32_t CAWiFiPacketSendCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength);
+ const void *data, const uint32_t dataLength);
#endif
static CAResult_t CAWiFiStopServers();
static void CAWiFiSendDataThread(void *threadData);
if (CA_STATUS_OK != ret || NULL == address)
{
OIC_LOG_V(ERROR, WIFI_ADAPTER_TAG, "Failed to get interface info [%d]", ret);
- OICFree(address);
- OICFree(ifcName);
+ if(address)
+ {
+ OICFree(address);
+ }
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
return;
}
// Notify network change to CA
CAWiFiNotifyNetworkChange(address, port, status);
- OICFree(address);
- OICFree(ifcName);
+ if(address)
+ {
+ OICFree(address);
+ }
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
}
else
{
#ifdef __WITH_DTLS__
uint32_t CAWiFiPacketSendCB(const char *ipAddress, const uint32_t port,
- const void *data, const uint32_t dataLength)
+ const void *data, const uint32_t dataLength)
{
OIC_LOG(DEBUG, WIFI_ADAPTER_TAG, "IN");
return CA_STATUS_OK;
}
- char *ifcName;
- char *ifcAdrs;
+ char *ifcName = NULL;
+ char *ifcAdrs = NULL;
ret = CAWiFiGetInterfaceInfo(&ifcName, &ifcAdrs);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG_V(DEBUG, WIFI_ADAPTER_TAG, "Failed to get wifi interface info [%d]", ret);
return ret;
gSecureUnicastServerport = unicastPort;
}
#endif
- OICFree(ifcName);
- OICFree(ifcAdrs);
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
+ if(ifcAdrs)
+ {
+ OICFree(ifcAdrs);
+ }
OIC_LOG(DEBUG, WIFI_ADAPTER_TAG, "OUT");
- return ret;;
+ return ret;
}
CAResult_t CAStartWIFIListeningServer()
return CA_STATUS_OK;
}
- char *ifcName;
- char *ifcAdrs;
+ char *ifcName = NULL;
+ char *ifcAdrs = NULL;
ret = CAWiFiGetInterfaceInfo(&ifcName, &ifcAdrs);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG_V(DEBUG, WIFI_ADAPTER_TAG, "Failed to get wifi interface info [%d]", ret);
return ret;
gIsMulticastServerStarted = true;
}
- OICFree(ifcName);
- OICFree(ifcAdrs);
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
+ if(ifcAdrs)
+ {
+ OICFree(ifcAdrs);
+ }
OIC_LOG(DEBUG, WIFI_ADAPTER_TAG, "OUT");
return ret;
}
{
OIC_LOG_V(ERROR, WIFI_ADAPTER_TAG, "Failed to get interface info [%d]", ret);
- OICFree(netInfo);
- OICFree(ipAddress);
- OICFree(ifcName);
+ if(netInfo)
+ {
+ OICFree(netInfo);
+ }
+ if(ipAddress)
+ {
+ OICFree(ipAddress);
+ }
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
return ret;
}
{
OIC_LOG_V(ERROR, WIFI_ADAPTER_TAG, "Failed to create Local Endpoint!",
CA_MEMORY_ALLOC_FAILED);
- OICFree(netInfo);
- OICFree(ipAddress);
- OICFree(ifcName);
+ if(netInfo)
+ {
+ OICFree(netInfo);
+ }
+ if(ipAddress)
+ {
+ OICFree(ipAddress);
+ }
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
return CA_MEMORY_ALLOC_FAILED;
}
#endif
*info = netInfo;
- OICFree(ipAddress);
- OICFree(ifcName);
+ if(ipAddress)
+ {
+ OICFree(ipAddress);
+ }
+ if(ifcName)
+ {
+ OICFree(ifcName);
+ }
CAAdapterFreeLocalEndpoint(endpoint);
OIC_LOG(INFO, WIFI_ADAPTER_TAG, "GetWIFIInterfaceInformation success");
// Stop wifi adapter
CAStopWIFI();
+ // Terminate wifi server
+ CAWiFiTerminateServer();
+
// Terminate network monitor
CAWiFiSetConnectionStateChangeCallback(NULL);
CAWiFiTerminateNetworkMonitor();
* @brief This methods gets local interface name and IP address information.
*/
static void CAWiFiGetInterfaceInformation(char **interfaceName, char **ipAddress,
- char **subnetMask);
+ char **subnetMask);
static void CANetworkMonitorThread(void *threadData);
// Get the interface and ipaddress information from cache
u_mutex_lock(gWifiNetInfoMutex);
- if(gWifiInterfaceName == NULL || gWifiIPAddress == NULL)
+ if (gWifiInterfaceName == NULL || gWifiIPAddress == NULL)
{
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "Network not enabled");
return CA_ADAPTER_NOT_ENABLED;
}
- *interfaceName = (gWifiInterfaceName) ? strndup(gWifiInterfaceName,strlen(gWifiInterfaceName))
- : NULL;
- *ipAddress = (gWifiIPAddress) ? strndup(gWifiIPAddress,strlen(gWifiIPAddress))
- : NULL;
+ *interfaceName = (gWifiInterfaceName) ? strndup(gWifiInterfaceName, strlen(gWifiInterfaceName))
+ : NULL;
+ *ipAddress = (gWifiIPAddress) ? strndup(gWifiIPAddress, strlen(gWifiIPAddress))
+ : NULL;
u_mutex_unlock(gWifiNetInfoMutex);
VERIFY_NON_NULL(subnetMask, WIFI_MONITOR_TAG, "subnet mask");
u_mutex_lock(gWifiNetInfoMutex);
- if(NULL == gWifiSubnetMask)
+ if (NULL == gWifiSubnetMask)
{
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "There is no subnet mask information!");
return CA_STATUS_FAILED;
}
- *subnetMask = (gWifiSubnetMask) ? strndup(gWifiSubnetMask,strlen(gWifiSubnetMask))
- : NULL;
+ *subnetMask = (gWifiSubnetMask) ? strndup(gWifiSubnetMask, strlen(gWifiSubnetMask))
+ : NULL;
u_mutex_unlock(gWifiNetInfoMutex);
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "OUT");
if (-1 == getifaddrs(&ifp))
{
OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get interface list!, Error code: %s",
- strerror(errno));
+ strerror(errno));
return;
}
continue;
}
- if (!strncasecmp(ifa->ifa_name,matchName,strlen(matchName)))
+ if (!strncasecmp(ifa->ifa_name, matchName, strlen(matchName)))
{
// get the interface ip address
if (0 != getnameinfo(ifa->ifa_addr, len, interfaceAddress,
- sizeof(interfaceAddress), NULL, 0, NI_NUMERICHOST))
+ sizeof(interfaceAddress), NULL, 0, NI_NUMERICHOST))
{
- OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get IPAddress, Error code: %s",
- strerror(errno));
- break;
+ OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get IPAddress, Error code: %s",
+ strerror(errno));
+ break;
}
// get the interface subnet mask
if (0 != getnameinfo(ifa->ifa_netmask, len, interfaceSubnetMask,
- sizeof(interfaceSubnetMask), NULL, 0, NI_NUMERICHOST))
+ sizeof(interfaceSubnetMask), NULL, 0, NI_NUMERICHOST))
{
- OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get subnet mask, Error code: %s",
- strerror(errno));
- break;
+ OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get subnet mask, Error code: %s",
+ strerror(errno));
+ break;
}
// set interface name
-/******************************************************************
-*
-* Copyright 2014 Samsung Electronics All Rights Reserved.
-*
-*
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-******************************************************************/
-#include "cawifiinterface.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/select.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <errno.h>
-
-#include "pdu.h"
-#include "caadapterutils.h"
-#ifdef __WITH_DTLS__
-#include "caadapternetdtls.h"
-#endif
-#include "umutex.h"
-
-/**
- * @def WIFI_SERVER_TAG
- * @brief Logging tag for module name
- */
-#define WIFI_SERVER_TAG "WIFI_SERVER"
-
-/**
- * @def CA_UDP_BIND_RETRY_COUNT
- * @brief Retry count in case of socket bind failure.
- */
-#define CA_UDP_BIND_RETRY_COUNT 10
-
-/**
- * @def IPNAMESIZE
- * @brief max length for ip
- */
-#define IPNAMESIZE 16
-
-/**
- * @var gUnicastServerSocketFD
- * @brief Unicast server socket descriptor
- */
-static int32_t gUnicastServerSocketFD = -1;
-
-/**
- * @var gMutexUnicastServer
- * @brief Mutex to synchronize unicast server
- */
-static u_mutex gMutexUnicastServer = NULL;
-
-/**
- * @var gStopUnicast
- * @brief Flag to control the Receive Unicast Data Thread
- */
-static bool gStopUnicast = false;
-
-/**
- * @var gMulticastServerSocketFD
- * @brief socket descriptor for multicast server
- */
-static int32_t gMulticastServerSocketFD = -1;
-
-/**
- * @var gMutexMulticastServer
- * @brief Mutex to synchronize secure multicast server
- */
-static u_mutex gMutexMulticastServer = NULL;
-
-/**
- * @var gStopMulticast
- * @brief Flag to control the Receive Multicast Data Thread
- */
-static bool gStopMulticast = false;
-
-#ifdef __WITH_DTLS__
-/**
- * @var gSecureUnicastServerSocketFD
- * @brief Secure unicast server socket descriptor
- */
-static int32_t gSecureUnicastServerSocketFD = -1;
-
-/**
- * @var gMutexSecureUnicastServer
- * @brief Mutex to synchronize secure unicast server
- */
-static u_mutex gMutexSecureUnicastServer = NULL;
-
-/**
- * @var gStopSecureUnicast
- * @brief Flag to control the unicast secure data receive thread
- */
-static bool gStopSecureUnicast = false;
-#endif
-
-/**
- * @var gThreadPool
- * @brief ThreadPool for storing u_thread_pool_t handle passed from adapter
- */
-static u_thread_pool_t gThreadPool = NULL;
-
-/**
- * @var gMulticastServerInterface
- * @brief Local interface on which multicast server is running
- */
-static char gMulticastServerInterface[IPNAMESIZE];
-
-/**
- * @var gMulticastMemberReq
- * @brief ip_mreq structure passed to join a multicast group
- */
-static struct ip_mreq gMulticastMemberReq;
-
-/**
- * @var gPacketReceivedCallback
- * @brief Callback for notifying the upper layer on receival data from remote OIC device
- */
-static CAWiFiPacketReceivedCallback gPacketReceivedCallback = NULL;
-
-/**
- * @var gExceptionCallback
- * @brief Callback for notifying the upper layer when unicast/multicast server encounters exception
- */
-static CAWiFiExceptionCallback gExceptionCallback = NULL;
-
-/**
- @brief Thread context information for unicast, multicast and secured unicast server
- */
-typedef struct
-{
- bool *stopFlag;
- int32_t socket_fd;
- CAAdapterServerType_t type;
-}CAAdapterReceiveThreadContext_t;
-
-static void CAReceiveHandler(void *data)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
- // Input validation
- VERIFY_NON_NULL_VOID(data, WIFI_SERVER_TAG, "Invalid thread context");
-
- CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)data;
- fd_set reads;
- struct timeval timeout;
- char recvBuffer[COAP_MAX_PDU_SIZE] = {0};
-
- while (true != *(ctx->stopFlag))
- {
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- FD_ZERO(&reads);
- FD_SET(ctx->socket_fd, &reads);
-
- int32_t ret = select(ctx->socket_fd + 1, &reads, NULL, NULL, &timeout);
- if (*(ctx->stopFlag) == true)
- {
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Stop request received for [%d] server", ctx->type);
- break;
- }
- if (ret < 0)
- {
- OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));
- continue;
- }
- if (!FD_ISSET(ctx->socket_fd, &reads))
- {
- continue;
- }
-
- memset(recvBuffer, 0, sizeof(recvBuffer));
-
- // Read data from socket
- struct sockaddr_in srcSockAddress;
- int32_t recvLen;
- socklen_t srcAddressLen = sizeof(srcSockAddress);
- if (-1 == (recvLen = recvfrom(ctx->socket_fd, recvBuffer,
- sizeof(recvBuffer), 0, (struct sockaddr *) &srcSockAddress,
- &srcAddressLen)))
- {
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));
- continue;
- }
- else if (0 == recvLen)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Server socket shutdown [%d]!", ctx->type);
-
- // Notify upper layer this exception
- if (gExceptionCallback)
- {
- gExceptionCallback(ctx->type);
- }
- OICFree(ctx);
- return;
- }
-
- const char *srcIPAddress = NULL;
- int32_t srcPort = -1;
-
- srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);
- srcPort = ntohs(srcSockAddress.sin_port);
-
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",
- srcIPAddress, srcPort);
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",
- recvBuffer, recvLen);
-
- char *netMask = NULL;
- if (CA_STATUS_OK != CAWiFiGetInterfaceSubnetMask(&netMask))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to get ethernet subnet");
- continue;
- }
-
- if(!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))
- {
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Packet received from different subnet, Ignore!");
- continue;
- }
-
- OICFree(netMask);
- switch(ctx->type)
- {
- case CA_UNICAST_SERVER:
- case CA_MULTICAST_SERVER:
- // Notify data to upper layer
- if (gPacketReceivedCallback)
- {
- gPacketReceivedCallback(srcIPAddress, srcPort, recvBuffer, recvLen, false);
- }
- break;
-#ifdef __WITH_DTLS__
- case CA_SECURED_UNICAST_SERVER:
- {
- CAResult_t ret = CAAdapterNetDtlsDecrypt(srcIPAddress,
- srcPort,
- (uint8_t *)recvBuffer,
- recvLen, DTLS_WIFI);
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "CAAdapterNetDtlsDecrypt returns [%d]", ret);
- }
- break;
-#endif //__WITH_DTLS__
- default:
- // Should never occur
- OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Invalid server type");
- OICFree(ctx);
- return;
- }
- }
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
-}
-
-static CAResult_t CAWiFiCreateSocket(int32_t* socketFD, const char *localIp, int16_t *port,
- const bool forceStart)
-{
- int32_t sock = -1;
- // Create a UDP socket
- if (-1 == (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to create Socket, Error code: %s",
- strerror(errno));
- return CA_STATUS_FAILED;
- }
-
- // Make the socket non-blocking
- if (-1 == fcntl(sock, F_SETFL, O_NONBLOCK))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set non-block mode, Error code: %s",
- strerror(errno));
-
- close(sock);
- return CA_STATUS_FAILED;
- }
-
- if (true == forceStart)
- {
- int32_t setOptionOn = 1;
- if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (char *) &setOptionOn,
- sizeof(setOptionOn)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set SO_REUSEADDR! Error code: %s",
- strerror(errno));
-
- close(sock);
- return CA_STATUS_FAILED;
- }
- }
-
- struct sockaddr_in sockAddr;
- bool isBound = false;
- int16_t serverPort = *port;
-
- memset((char *) &sockAddr, 0, sizeof(sockAddr));
- sockAddr.sin_family = AF_INET;
- sockAddr.sin_port = htons(serverPort);
- if(localIp)
- {
- sockAddr.sin_addr.s_addr = inet_addr(localIp);
- }
- else
- {
- sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- }
-
- int16_t i;
- for (i = 0; i < CA_UDP_BIND_RETRY_COUNT; i++)
- {
- if (-1 == bind(sock, (struct sockaddr *) &sockAddr,
- sizeof(sockAddr)))
- {
- if (false == forceStart)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to bind socket[%s]. Trying again..",
- strerror(errno));
-
- //Set the port to next one
- serverPort += 1;
- sockAddr.sin_port = htons(serverPort);
- continue;
- }
- else
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to bind socket[%s]!",
- strerror(errno));
- break;
- }
- }
-
- isBound = true;
- break;
- }
-
- if (false == isBound)
- {
- close(sock);
- return CA_STATUS_FAILED;
- }
-
- *port = serverPort;
- *socketFD = sock;
- return CA_STATUS_OK;
-}
-
-static CAResult_t CAWiFiCloseSocket(int32_t *socketFD)
-{
- if (-1 == *socketFD)
- {
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Server not running");
- return CA_SERVER_NOT_STARTED;
- }
-
- // close the socket
- if (-1 == close(*socketFD))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to close the socket, Error code: %s\n",
- strerror(errno));
- return CA_STATUS_FAILED;
- }
-
- *socketFD = -1;
- return CA_STATUS_OK;
-}
-
-static CAResult_t CAStartUnicastServer(const char *localAddress, int16_t *port,
- const bool forceStart, bool isSecured, int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- CAResult_t ret = CAWiFiCreateSocket(serverFD, localAddress, port, forceStart);
- if(CA_STATUS_OK != ret)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create unicast socket");
- return ret;
- }
-
- /**
- * The task to listen for data from unicast socket is added to the thread pool.
- * This is a blocking call is made where we try to receive some data..
- * We will keep waiting until some data is received.
- * This task will be terminated when thread pool is freed on stopping the adapters.
- * Thread context will be freed by thread on exit.
- */
- CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");
- close(*serverFD);
- return CA_MEMORY_ALLOC_FAILED;
- }
-
- ctx->stopFlag = &gStopUnicast;
- ctx->socket_fd = *serverFD;
- ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER:CA_UNICAST_SERVER;
- if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create read thread!");
- OICFree((void *)ctx);
- close(*serverFD);
- return CA_STATUS_FAILED;
- }
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-static void CAWiFiServerDestroyMutex(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- if (gMutexUnicastServer)
- {
- u_mutex_free(gMutexUnicastServer);
- gMutexUnicastServer = NULL;
- }
-
-#ifdef __WITH_DTLS__
- if (gMutexSecureUnicastServer)
- {
- u_mutex_free(gMutexSecureUnicastServer);
- gMutexSecureUnicastServer = NULL;
- }
-#endif
-
- if (gMutexMulticastServer)
- {
- u_mutex_free(gMutexMulticastServer);
- gMutexMulticastServer = NULL;
- }
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
-}
-
-static CAResult_t CAWiFiServerCreateMutex(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gMutexUnicastServer = u_mutex_new();
- if (!gMutexUnicastServer)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
- return CA_STATUS_FAILED;
- }
-
-#ifdef __WITH_DTLS__
- gMutexSecureUnicastServer = u_mutex_new();
- if (!gMutexSecureUnicastServer)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
-
- CAWiFiServerDestroyMutex();
- return CA_STATUS_FAILED;
- }
-#endif
-
- gMutexMulticastServer = u_mutex_new();
- if (!gMutexMulticastServer)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");
-
- CAWiFiServerDestroyMutex();
- return CA_STATUS_FAILED;
- }
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-CAResult_t CAWiFiInitializeServer(const u_thread_pool_t threadPool)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- // Input validation
- VERIFY_NON_NULL(threadPool, WIFI_SERVER_TAG, "Thread pool handle is NULL");
-
- // Initialize mutex
- if (CA_STATUS_OK != CAWiFiServerCreateMutex())
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create mutex!");
- return CA_STATUS_FAILED;
- }
-
- gThreadPool = threadPool;
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-void CAWiFiTerminateServer(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gThreadPool = NULL;
-
- // Destroy mutex
- CAWiFiServerDestroyMutex();
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
-}
-
-CAResult_t CAWiFiStartUnicastServer(const char *localAddress, int16_t *port,
- const bool forceStart, const CABool_t isSecured,
- int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- // Input validation
- VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "localAddress");
- VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "port");
- VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "server socket FD");
-
- if (0 >= *port)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: port is invalid!");
- return CA_STATUS_INVALID_PARAM;
- }
-
- *serverFD = -1;
- if (CA_FALSE == isSecured)
- {
- u_mutex_lock(gMutexUnicastServer);
- if (-1 != gUnicastServerSocketFD)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",
- CA_SERVER_STARTED_ALREADY);
-
- *serverFD = gUnicastServerSocketFD;
- u_mutex_unlock(gMutexUnicastServer);
- return CA_SERVER_STARTED_ALREADY;
- }
-
- gStopUnicast = false;
- if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gUnicastServerSocketFD))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
- gUnicastServerSocketFD = -1;
- u_mutex_unlock(gMutexUnicastServer);
- return CA_STATUS_FAILED;
- }
-
- *serverFD = gUnicastServerSocketFD;
- u_mutex_unlock(gMutexUnicastServer);
- }
-#ifdef __WITH_DTLS__
- else // Start unicast server for secured communication
- {
- u_mutex_lock(gMutexSecureUnicastServer);
- if (-1 != gSecureUnicastServerSocketFD)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",
- CA_SERVER_STARTED_ALREADY);
-
- *serverFD = gSecureUnicastServerSocketFD;
- u_mutex_unlock(gMutexSecureUnicastServer);
- return CA_SERVER_STARTED_ALREADY;
- }
-
- gStopSecureUnicast = false;
- if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gSecureUnicastServerSocketFD))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
- gSecureUnicastServerSocketFD = -1;
- u_mutex_unlock(gMutexSecureUnicastServer);
- return CA_STATUS_FAILED;
- }
-
- *serverFD = gSecureUnicastServerSocketFD;
- u_mutex_unlock(gMutexSecureUnicastServer);
- }
-#endif
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-CAResult_t CAWiFiStartMulticastServer(const char *localAddress, const char *multicastAddress,
- const int16_t multicastPort, int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- // Input validation
- VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "Local address is NULL");
- VERIFY_NON_NULL(multicastAddress, WIFI_SERVER_TAG, "Multicast address is NULL");
-
- int16_t port = multicastPort;
- if (0 >= port)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: Multicast port is invalid!");
- return CA_STATUS_INVALID_PARAM;
- }
-
- u_mutex_lock(gMutexMulticastServer);
-
- if (gMulticastServerSocketFD != -1)
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast Server is already running!");
- u_mutex_unlock(gMutexMulticastServer);
- return CA_SERVER_STARTED_ALREADY;
- }
-
- CAResult_t ret = CAWiFiCreateSocket(&gMulticastServerSocketFD, multicastAddress, &port, true);
- if(ret != CA_STATUS_OK)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create multicast socket");
- u_mutex_unlock(gMutexMulticastServer);
- return ret;
- }
-
- // Add membership to receiving socket (join group)
- memset(&gMulticastMemberReq, 0, sizeof(struct ip_mreq));
- gMulticastMemberReq.imr_interface.s_addr = inet_addr(localAddress);
- inet_aton(multicastAddress, &gMulticastMemberReq.imr_multiaddr);
-
- if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (char *) &gMulticastMemberReq,
- sizeof(struct ip_mreq)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to add to multicast group, Error code: %s\n",
- strerror(errno));
- close(gMulticastServerSocketFD);
- gMulticastServerSocketFD = -1;
- u_mutex_unlock(gMutexMulticastServer);
- return CA_STATUS_FAILED;
- }
-
- /**
- * The task to listen to data from multicastcast socket is added to the thread pool.
- * This is a blocking call is made where we try to receive some data.
- * We will keep waiting until some data is received.
- * This task will be terminated when thread pool is freed on stopping the adapters.
- * Thread context will be freed by thread on exit.
- */
- CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");
- close(gMulticastServerSocketFD);
- gMulticastServerSocketFD = -1;
- return CA_MEMORY_ALLOC_FAILED;
- }
-
- ctx->stopFlag = &gStopMulticast;
- ctx->socket_fd = gMulticastServerSocketFD;
- ctx->type = CA_MULTICAST_SERVER;
-
- gStopMulticast = false;
- if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))
- {
- OIC_LOG(ERROR, WIFI_SERVER_TAG, "thread_pool_add_task failed!");
-
- close(gMulticastServerSocketFD);
- gMulticastServerSocketFD = -1;
- gStopMulticast = true;
- u_mutex_unlock(gMutexMulticastServer);
- return CA_STATUS_FAILED;
- }
-
- *serverFD = gMulticastServerSocketFD;
- strncpy(gMulticastServerInterface, localAddress, sizeof(gMulticastServerInterface));
- u_mutex_unlock(gMutexMulticastServer);
-
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-CAResult_t CAWiFiStopUnicastServer()
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- u_mutex_lock(gMutexUnicastServer);
- gStopUnicast = true;
- CAResult_t ret = CAWiFiCloseSocket(&gUnicastServerSocketFD);
- u_mutex_unlock(gMutexUnicastServer);
-
- OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Unicast server stopped [%d]", ret);
- return ret;
-}
-
-#ifdef __WITH_DTLS__
-CAResult_t CAWiFiStopSecureUnicastServer()
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- u_mutex_lock(gMutexSecureUnicastServer);
- gStopSecureUnicast = true;
- CAResult_t ret = CAWiFiCloseSocket(&gSecureUnicastServerSocketFD);
- u_mutex_unlock(gMutexSecureUnicastServer);
-
- OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Secured unicast server stopped [%d]", ret);
- return ret;
-}
-#endif
-
-CAResult_t CAWiFiStopMulticastServer(void)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- u_mutex_lock(gMutexMulticastServer);
-
- if (gMulticastServerSocketFD == -1)
- {
- OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast server is not yet started");
- u_mutex_unlock(gMutexMulticastServer);
- return CA_SERVER_NOT_STARTED;
- }
-
- gStopMulticast = true;
-
- // leave the group after you are done
- if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_DROP_MEMBERSHIP,
- (char *)&gMulticastMemberReq,
- sizeof(struct ip_mreq)))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to leave multicast group, Error code: %s\n",
- strerror(errno));
- }
-
- CAResult_t ret = CAWiFiCloseSocket(&gMulticastServerSocketFD);
- u_mutex_unlock(gMutexMulticastServer);
-
- OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Multicast server stopped [%d]", ret);
- return ret;
-}
-
-CAResult_t CAWiFiGetUnicastServerInfo(const CABool_t isSecured, char **ipAddress,
- int16_t *port, int32_t *serverFD)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- // Input validation
- VERIFY_NON_NULL(ipAddress, WIFI_SERVER_TAG, "IP address");
- VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "Port");
- VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "Server ID");
-
- struct sockaddr_in sockAddr;
- socklen_t len = sizeof(struct sockaddr_in);
- if (-1 == getsockname(gUnicastServerSocketFD, (struct sockaddr *)&sockAddr, &len))
- {
- OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed in getsockname [%s]!", strerror(errno));
- return CA_STATUS_FAILED;
- }
-
-
- const char *serverAddress = inet_ntoa(sockAddr.sin_addr);
- *ipAddress = (serverAddress) ? strndup(serverAddress, strlen(serverAddress)) : NULL;
- *port = ntohs(sockAddr.sin_port);
-#ifdef __WITH_DTLS__
- *serverFD = (CA_TRUE == isSecured) ? gSecureUnicastServerSocketFD : gUnicastServerSocketFD;
-#else
- *serverFD = gUnicastServerSocketFD;
-#endif
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
- return CA_STATUS_OK;
-}
-
-void CAWiFiSetPacketReceiveCallback(CAWiFiPacketReceivedCallback callback)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gPacketReceivedCallback = callback;
-}
-
-void CAWiFiSetExceptionCallback(CAWiFiExceptionCallback callback)
-{
- OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
-
- gExceptionCallback = callback;
-}
-
-
+/******************************************************************\r
+*\r
+* Copyright 2014 Samsung Electronics All Rights Reserved.\r
+*\r
+*\r
+*\r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+* you may not use this file except in compliance with the License.\r
+* You may obtain a copy of the License at\r
+*\r
+* http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+*\r
+******************************************************************/\r
+#include "cawifiinterface.h"\r
+\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#include <sys/select.h>\r
+#include <arpa/inet.h>\r
+#include <netinet/in.h>\r
+#include <errno.h>\r
+\r
+#include "pdu.h"\r
+#include "caadapterutils.h"\r
+#ifdef __WITH_DTLS__\r
+#include "caadapternetdtls.h"\r
+#endif\r
+#include "umutex.h"\r
+\r
+/**\r
+ * @def WIFI_SERVER_TAG\r
+ * @brief Logging tag for module name\r
+ */\r
+#define WIFI_SERVER_TAG "WIFI_SERVER"\r
+\r
+/**\r
+ * @def CA_UDP_BIND_RETRY_COUNT\r
+ * @brief Retry count in case of socket bind failure.\r
+ */\r
+#define CA_UDP_BIND_RETRY_COUNT 10\r
+\r
+/**\r
+ * @def IPNAMESIZE\r
+ * @brief max length for ip\r
+ */\r
+#define IPNAMESIZE 16\r
+\r
+/**\r
+ * @var gUnicastServerSocketFD\r
+ * @brief Unicast server socket descriptor\r
+ */\r
+static int32_t gUnicastServerSocketFD = -1;\r
+\r
+/**\r
+ * @var gMutexUnicastServer\r
+ * @brief Mutex to synchronize unicast server\r
+ */\r
+static u_mutex gMutexUnicastServer = NULL;\r
+\r
+/**\r
+ * @var gStopUnicast\r
+ * @brief Flag to control the Receive Unicast Data Thread\r
+ */\r
+static bool gStopUnicast = false;\r
+\r
+/**\r
+ * @var gMulticastServerSocketFD\r
+ * @brief socket descriptor for multicast server\r
+ */\r
+static int32_t gMulticastServerSocketFD = -1;\r
+\r
+/**\r
+ * @var gMutexMulticastServer\r
+ * @brief Mutex to synchronize secure multicast server\r
+ */\r
+static u_mutex gMutexMulticastServer = NULL;\r
+\r
+/**\r
+ * @var gStopMulticast\r
+ * @brief Flag to control the Receive Multicast Data Thread\r
+ */\r
+static bool gStopMulticast = false;\r
+\r
+#ifdef __WITH_DTLS__\r
+/**\r
+ * @var gSecureUnicastServerSocketFD\r
+ * @brief Secure unicast server socket descriptor\r
+ */\r
+static int32_t gSecureUnicastServerSocketFD = -1;\r
+\r
+/**\r
+ * @var gMutexSecureUnicastServer\r
+ * @brief Mutex to synchronize secure unicast server\r
+ */\r
+static u_mutex gMutexSecureUnicastServer = NULL;\r
+\r
+/**\r
+ * @var gStopSecureUnicast\r
+ * @brief Flag to control the unicast secure data receive thread\r
+ */\r
+static bool gStopSecureUnicast = false;\r
+#endif\r
+\r
+/**\r
+ * @var gThreadPool\r
+ * @brief ThreadPool for storing u_thread_pool_t handle passed from adapter\r
+ */\r
+static u_thread_pool_t gThreadPool = NULL;\r
+\r
+/**\r
+ * @var gMulticastServerInterface\r
+ * @brief Local interface on which multicast server is running\r
+ */\r
+static char gMulticastServerInterface[IPNAMESIZE];\r
+\r
+/**\r
+ * @var gMulticastMemberReq\r
+ * @brief ip_mreq structure passed to join a multicast group\r
+ */\r
+static struct ip_mreq gMulticastMemberReq;\r
+\r
+/**\r
+ * @var gPacketReceivedCallback\r
+ * @brief Callback for notifying the upper layer on receival data from remote OIC device\r
+ */\r
+static CAWiFiPacketReceivedCallback gPacketReceivedCallback = NULL;\r
+\r
+/**\r
+ * @var gExceptionCallback\r
+ * @brief Callback for notifying the upper layer when unicast/multicast server encounters exception\r
+ */\r
+static CAWiFiExceptionCallback gExceptionCallback = NULL;\r
+\r
+/**\r
+ @brief Thread context information for unicast, multicast and secured unicast server\r
+ */\r
+typedef struct\r
+{\r
+ bool *stopFlag;\r
+ int32_t socket_fd;\r
+ CAAdapterServerType_t type;\r
+} CAAdapterReceiveThreadContext_t;\r
+\r
+static void CAReceiveHandler(void *data)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+ // Input validation\r
+ VERIFY_NON_NULL_VOID(data, WIFI_SERVER_TAG, "Invalid thread context");\r
+\r
+ CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)data;\r
+ fd_set reads;\r
+ struct timeval timeout;\r
+ char recvBuffer[COAP_MAX_PDU_SIZE] = {0};\r
+\r
+ while (true != *(ctx->stopFlag))\r
+ {\r
+ timeout.tv_sec = 1;\r
+ timeout.tv_usec = 0;\r
+\r
+ FD_ZERO(&reads);\r
+ FD_SET(ctx->socket_fd, &reads);\r
+\r
+ int32_t ret = select(ctx->socket_fd + 1, &reads, NULL, NULL, &timeout);\r
+ if (*(ctx->stopFlag) == true)\r
+ {\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Stop request received for [%d] server", ctx->type);\r
+ break;\r
+ }\r
+ if (ret < 0)\r
+ {\r
+ OIC_LOG_V(FATAL, WIFI_SERVER_TAG, "select returned error %s", strerror(errno));\r
+ continue;\r
+ }\r
+ if (!FD_ISSET(ctx->socket_fd, &reads))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ memset(recvBuffer, 0, sizeof(recvBuffer));\r
+\r
+ // Read data from socket\r
+ struct sockaddr_in srcSockAddress;\r
+ int32_t recvLen;\r
+ socklen_t srcAddressLen = sizeof(srcSockAddress);\r
+ if (-1 == (recvLen = recvfrom(ctx->socket_fd, recvBuffer,\r
+ sizeof(recvBuffer), 0, (struct sockaddr *) &srcSockAddress,\r
+ &srcAddressLen)))\r
+ {\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "%s", strerror(errno));\r
+ continue;\r
+ }\r
+ else if (0 == recvLen)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Server socket shutdown [%d]!", ctx->type);\r
+\r
+ // Notify upper layer this exception\r
+ if (gExceptionCallback)\r
+ {\r
+ gExceptionCallback(ctx->type);\r
+ }\r
+ OICFree(ctx);\r
+ return;\r
+ }\r
+\r
+ const char *srcIPAddress = NULL;\r
+ int32_t srcPort = -1;\r
+\r
+ srcIPAddress = inet_ntoa(srcSockAddress.sin_addr);\r
+ srcPort = ntohs(srcSockAddress.sin_port);\r
+\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Received packet from %s:%d\n",\r
+ srcIPAddress, srcPort);\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Data: %s\t, DataLength: %d\n",\r
+ recvBuffer, recvLen);\r
+\r
+ char *netMask = NULL;\r
+ if (CA_STATUS_OK != CAWiFiGetInterfaceSubnetMask(&netMask))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to get ethernet subnet");\r
+ continue;\r
+ }\r
+\r
+ if (!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))\r
+ {\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Packet received from different subnet, Ignore!");\r
+ continue;\r
+ }\r
+\r
+ OICFree(netMask);\r
+ switch (ctx->type)\r
+ {\r
+ case CA_UNICAST_SERVER:\r
+ case CA_MULTICAST_SERVER:\r
+ // Notify data to upper layer\r
+ if (gPacketReceivedCallback)\r
+ {\r
+ gPacketReceivedCallback(srcIPAddress, srcPort, recvBuffer, recvLen, false);\r
+ }\r
+ break;\r
+#ifdef __WITH_DTLS__\r
+ case CA_SECURED_UNICAST_SERVER:\r
+ {\r
+ CAResult_t ret = CAAdapterNetDtlsDecrypt(srcIPAddress,\r
+ srcPort,\r
+ (uint8_t *)recvBuffer,\r
+ recvLen, DTLS_WIFI);\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "CAAdapterNetDtlsDecrypt returns [%d]", ret);\r
+ }\r
+ break;\r
+#endif //__WITH_DTLS__\r default:\r // Should never occur\r
+ OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "Invalid server type");\r
+ OICFree(ctx);\r
+ return;\r
+ }\r
+ }\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+}\r
+\r
+static CAResult_t CAWiFiCreateSocket(int32_t *socketFD, const char *localIp, int16_t *port,\r
+ const bool forceStart)\r
+{\r
+ int32_t sock = -1;\r
+ // Create a UDP socket\r
+ if (-1 == (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to create Socket, Error code: %s",\r
+ strerror(errno));\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ // Make the socket non-blocking\r
+ if (-1 == fcntl(sock, F_SETFL, O_NONBLOCK))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set non-block mode, Error code: %s",\r
+ strerror(errno));\r
+\r
+ close(sock);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ if (true == forceStart)\r
+ {\r
+ int32_t setOptionOn = 1;\r
+ if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,\r
+ (char *) &setOptionOn,\r
+ sizeof(setOptionOn)))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to set SO_REUSEADDR! Error code: %s",\r
+ strerror(errno));\r
+\r
+ close(sock);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+ }\r
+\r
+ struct sockaddr_in sockAddr;\r
+ bool isBound = false;\r
+ int16_t serverPort = *port;\r
+\r
+ memset((char *) &sockAddr, 0, sizeof(sockAddr));\r
+ sockAddr.sin_family = AF_INET;\r
+ sockAddr.sin_port = htons(serverPort);\r
+ if (localIp)\r
+ {\r
+ sockAddr.sin_addr.s_addr = inet_addr(localIp);\r
+ }\r
+ else\r
+ {\r
+ sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);\r
+ }\r
+\r
+ int16_t i;\r
+ for (i = 0; i < CA_UDP_BIND_RETRY_COUNT; i++)\r
+ {\r
+ if (-1 == bind(sock, (struct sockaddr *) &sockAddr,\r
+ sizeof(sockAddr)))\r
+ {\r
+ if (false == forceStart)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to bind socket[%s]. Trying again..",\r
+ strerror(errno));\r
+\r
+ //Set the port to next one\r
+ serverPort += 1;\r
+ sockAddr.sin_port = htons(serverPort);\r
+ continue;\r
+ }\r
+ else\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to bind socket[%s]!",\r
+ strerror(errno));\r
+ break;\r
+ }\r
+ }\r
+\r
+ isBound = true;\r
+ break;\r
+ }\r
+\r
+ if (false == isBound)\r
+ {\r
+ close(sock);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ *port = serverPort;\r
+ *socketFD = sock;\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+static CAResult_t CAWiFiCloseSocket(int32_t *socketFD)\r
+{\r
+ if (-1 == *socketFD)\r
+ {\r
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Server not running");\r
+ return CA_SERVER_NOT_STARTED;\r
+ }\r
+\r
+ // close the socket\r
+ if (-1 == close(*socketFD))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to close the socket, Error code: %s\n",\r
+ strerror(errno));\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ *socketFD = -1;\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+static CAResult_t CAStartUnicastServer(const char *localAddress, int16_t *port,\r
+ const bool forceStart, bool isSecured, int32_t *serverFD)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ CAResult_t ret = CAWiFiCreateSocket(serverFD, localAddress, port, forceStart);\r
+ if (CA_STATUS_OK != ret)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create unicast socket");\r
+ return ret;\r
+ }\r
+\r
+ /**\r
+ * The task to listen for data from unicast socket is added to the thread pool.\r
+ * This is a blocking call is made where we try to receive some data..\r
+ * We will keep waiting until some data is received.\r
+ * This task will be terminated when thread pool is freed on stopping the adapters.\r
+ * Thread context will be freed by thread on exit.\r
+ */\r
+ CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)\r
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));\r
+ if (!ctx)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");\r
+ close(*serverFD);\r
+ return CA_MEMORY_ALLOC_FAILED;\r
+ }\r
+\r
+ ctx->stopFlag = &gStopUnicast;\r
+ ctx->socket_fd = *serverFD;\r
+ ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER : CA_UNICAST_SERVER;\r
+ if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create read thread!");\r
+ OICFree((void *)ctx);\r
+ close(*serverFD);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+static void CAWiFiServerDestroyMutex(void)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ if (gMutexUnicastServer)\r
+ {\r
+ u_mutex_free(gMutexUnicastServer);\r
+ gMutexUnicastServer = NULL;\r
+ }\r
+\r
+#ifdef __WITH_DTLS__\r
+ if (gMutexSecureUnicastServer)\r
+ {\r
+ u_mutex_free(gMutexSecureUnicastServer);\r
+ gMutexSecureUnicastServer = NULL;\r
+ }\r
+#endif\r
+\r
+ if (gMutexMulticastServer)\r
+ {\r
+ u_mutex_free(gMutexMulticastServer);\r
+ gMutexMulticastServer = NULL;\r
+ }\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+}\r
+\r
+static CAResult_t CAWiFiServerCreateMutex(void)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ gMutexUnicastServer = u_mutex_new();\r
+ if (!gMutexUnicastServer)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+#ifdef __WITH_DTLS__\r
+ gMutexSecureUnicastServer = u_mutex_new();\r
+ if (!gMutexSecureUnicastServer)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");\r
+\r
+ CAWiFiServerDestroyMutex();\r
+ return CA_STATUS_FAILED;\r
+ }\r
+#endif\r
+\r
+ gMutexMulticastServer = u_mutex_new();\r
+ if (!gMutexMulticastServer)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to created mutex!");\r
+\r
+ CAWiFiServerDestroyMutex();\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+CAResult_t CAWiFiInitializeServer(const u_thread_pool_t threadPool)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ // Input validation\r
+ VERIFY_NON_NULL(threadPool, WIFI_SERVER_TAG, "Thread pool handle is NULL");\r
+\r
+ // Initialize mutex\r
+ if (CA_STATUS_OK != CAWiFiServerCreateMutex())\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create mutex!");\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ gThreadPool = threadPool;\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+void CAWiFiTerminateServer(void)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ gThreadPool = NULL;\r
+\r
+ // Destroy mutex\r
+ CAWiFiServerDestroyMutex();\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+}\r
+\r
+CAResult_t CAWiFiStartUnicastServer(const char *localAddress, int16_t *port,\r
+ const bool forceStart, const CABool_t isSecured,\r
+ int32_t *serverFD)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ // Input validation\r
+ VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "localAddress");\r
+ VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "port");\r
+ VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "server socket FD");\r
+\r
+ if (0 >= *port)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: port is invalid!");\r
+ return CA_STATUS_INVALID_PARAM;\r
+ }\r
+\r
+ *serverFD = -1;\r
+ if (CA_FALSE == isSecured)\r
+ {\r
+ u_mutex_lock(gMutexUnicastServer);\r
+ if (-1 != gUnicastServerSocketFD)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",\r
+ CA_SERVER_STARTED_ALREADY);\r
+\r
+ *serverFD = gUnicastServerSocketFD;\r
+ u_mutex_unlock(gMutexUnicastServer);\r
+ return CA_SERVER_STARTED_ALREADY;\r
+ }\r
+\r
+ gStopUnicast = false;\r
+ if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,\r
+ &gUnicastServerSocketFD))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");\r
+ gUnicastServerSocketFD = -1;\r
+ u_mutex_unlock(gMutexUnicastServer);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ *serverFD = gUnicastServerSocketFD;\r
+ u_mutex_unlock(gMutexUnicastServer);\r
+ }\r
+#ifdef __WITH_DTLS__\r
+ else // Start unicast server for secured communication\r
+ {\r
+ u_mutex_lock(gMutexSecureUnicastServer);\r
+ if (-1 != gSecureUnicastServerSocketFD)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Unicast Server is Started Already!",\r
+ CA_SERVER_STARTED_ALREADY);\r
+\r
+ *serverFD = gSecureUnicastServerSocketFD;\r
+ u_mutex_unlock(gMutexSecureUnicastServer);\r
+ return CA_SERVER_STARTED_ALREADY;\r
+ }\r
+\r
+ gStopSecureUnicast = false;\r
+ if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,\r
+ &gSecureUnicastServerSocketFD))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");\r
+ gSecureUnicastServerSocketFD = -1;\r
+ u_mutex_unlock(gMutexSecureUnicastServer);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ *serverFD = gSecureUnicastServerSocketFD;\r
+ u_mutex_unlock(gMutexSecureUnicastServer);\r
+ }\r
+#endif\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+CAResult_t CAWiFiStartMulticastServer(const char *localAddress, const char *multicastAddress,\r
+ const int16_t multicastPort, int32_t *serverFD)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ // Input validation\r
+ VERIFY_NON_NULL(localAddress, WIFI_SERVER_TAG, "Local address is NULL");\r
+ VERIFY_NON_NULL(multicastAddress, WIFI_SERVER_TAG, "Multicast address is NULL");\r
+\r
+ int16_t port = multicastPort;\r
+ if (0 >= port)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Invalid input: Multicast port is invalid!");\r
+ return CA_STATUS_INVALID_PARAM;\r
+ }\r
+\r
+ u_mutex_lock(gMutexMulticastServer);\r
+\r
+ if (gMulticastServerSocketFD != -1)\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Multicast Server is already running!");\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+ return CA_SERVER_STARTED_ALREADY;\r
+ }\r
+\r
+ CAResult_t ret = CAWiFiCreateSocket(&gMulticastServerSocketFD, multicastAddress, &port, true);\r
+ if (ret != CA_STATUS_OK)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create multicast socket");\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+ return ret;\r
+ }\r
+\r
+ // Add membership to receiving socket (join group)\r
+ memset(&gMulticastMemberReq, 0, sizeof(struct ip_mreq));\r
+ gMulticastMemberReq.imr_interface.s_addr = inet_addr(localAddress);\r
+ inet_aton(multicastAddress, &gMulticastMemberReq.imr_multiaddr);\r
+\r
+ if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP,\r
+ (char *) &gMulticastMemberReq,\r
+ sizeof(struct ip_mreq)))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to add to multicast group, Error code: %s\n",\r
+ strerror(errno));\r
+ close(gMulticastServerSocketFD);\r
+ gMulticastServerSocketFD = -1;\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ /**\r
+ * The task to listen to data from multicastcast socket is added to the thread pool.\r
+ * This is a blocking call is made where we try to receive some data.\r
+ * We will keep waiting until some data is received.\r
+ * This task will be terminated when thread pool is freed on stopping the adapters.\r
+ * Thread context will be freed by thread on exit.\r
+ */\r
+ CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)\r
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));\r
+ if (!ctx)\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");\r
+ close(gMulticastServerSocketFD);\r
+ gMulticastServerSocketFD = -1;\r
+ return CA_MEMORY_ALLOC_FAILED;\r
+ }\r
+\r
+ ctx->stopFlag = &gStopMulticast;\r
+ ctx->socket_fd = gMulticastServerSocketFD;\r
+ ctx->type = CA_MULTICAST_SERVER;\r
+\r
+ gStopMulticast = false;\r
+ if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))\r
+ {\r
+ OIC_LOG(ERROR, WIFI_SERVER_TAG, "thread_pool_add_task failed!");\r
+\r
+ close(gMulticastServerSocketFD);\r
+ gMulticastServerSocketFD = -1;\r
+ gStopMulticast = true;\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+ *serverFD = gMulticastServerSocketFD;\r
+ strncpy(gMulticastServerInterface, localAddress, sizeof(IPNAMESIZE));\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+CAResult_t CAWiFiStopUnicastServer()\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ u_mutex_lock(gMutexUnicastServer);\r
+ gStopUnicast = true;\r
+ CAResult_t ret = CAWiFiCloseSocket(&gUnicastServerSocketFD);\r
+ u_mutex_unlock(gMutexUnicastServer);\r
+\r
+ OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Unicast server stopped [%d]", ret);\r
+ return ret;\r
+}\r
+\r
+#ifdef __WITH_DTLS__\r
+CAResult_t CAWiFiStopSecureUnicastServer()\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ u_mutex_lock(gMutexSecureUnicastServer);\r
+ gStopSecureUnicast = true;\r
+ CAResult_t ret = CAWiFiCloseSocket(&gSecureUnicastServerSocketFD);\r
+ u_mutex_unlock(gMutexSecureUnicastServer);\r
+\r
+ OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Secured unicast server stopped [%d]", ret);\r
+ return ret;\r
+}\r
+#endif\r
+\r
+CAResult_t CAWiFiStopMulticastServer(void)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ u_mutex_lock(gMutexMulticastServer);\r
+\r
+ if (gMulticastServerSocketFD == -1)\r
+ {\r
+ OIC_LOG(INFO, WIFI_SERVER_TAG, "Multicast server is not yet started");\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+ return CA_SERVER_NOT_STARTED;\r
+ }\r
+\r
+ gStopMulticast = true;\r
+\r
+ // leave the group after you are done\r
+ if (-1 == setsockopt(gMulticastServerSocketFD, IPPROTO_IP, IP_DROP_MEMBERSHIP,\r
+ (char *)&gMulticastMemberReq,\r
+ sizeof(struct ip_mreq)))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to leave multicast group, Error code: %s\n",\r
+ strerror(errno));\r
+ }\r
+\r
+ CAResult_t ret = CAWiFiCloseSocket(&gMulticastServerSocketFD);\r
+ u_mutex_unlock(gMutexMulticastServer);\r
+\r
+ OIC_LOG_V(INFO, WIFI_SERVER_TAG, "Multicast server stopped [%d]", ret);\r
+ return ret;\r
+}\r
+\r
+CAResult_t CAWiFiGetUnicastServerInfo(const CABool_t isSecured, char **ipAddress,\r
+ int16_t *port, int32_t *serverFD)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ // Input validation\r
+ VERIFY_NON_NULL(ipAddress, WIFI_SERVER_TAG, "IP address");\r
+ VERIFY_NON_NULL(port, WIFI_SERVER_TAG, "Port");\r
+ VERIFY_NON_NULL(serverFD, WIFI_SERVER_TAG, "Server ID");\r
+\r
+ struct sockaddr_in sockAddr;\r
+ socklen_t len = sizeof(struct sockaddr_in);\r
+ if (-1 == getsockname(gUnicastServerSocketFD, (struct sockaddr *)&sockAddr, &len))\r
+ {\r
+ OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed in getsockname [%s]!", strerror(errno));\r
+ return CA_STATUS_FAILED;\r
+ }\r
+\r
+\r
+ const char *serverAddress = inet_ntoa(sockAddr.sin_addr);\r
+ *ipAddress = (serverAddress) ? strndup(serverAddress, strlen(serverAddress)) : NULL;\r
+ *port = ntohs(sockAddr.sin_port);\r
+#ifdef __WITH_DTLS__\r
+ *serverFD = (CA_TRUE == isSecured) ? gSecureUnicastServerSocketFD : gUnicastServerSocketFD;\r
+#else\r
+ *serverFD = gUnicastServerSocketFD;\r
+#endif\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");\r
+ return CA_STATUS_OK;\r
+}\r
+\r
+void CAWiFiSetPacketReceiveCallback(CAWiFiPacketReceivedCallback callback)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ gPacketReceivedCallback = callback;\r
+}\r
+\r
+void CAWiFiSetExceptionCallback(CAWiFiExceptionCallback callback)\r
+{\r
+ OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");\r
+\r
+ gExceptionCallback = callback;\r
+}\r
+\r
+\r
* @brief This methods gets local interface name and IP address information.
*/
static void CAWiFiGetInterfaceInformation(char **interfaceName, char **ipAddress,
- char **subnetMask);
+ char **subnetMask);
CAResult_t CAWiFiInitializeNetworkMonitor(const u_thread_pool_t threadPool)
{
return CA_STATUS_FAILED;
}
+ CAWiFiGetInterfaceInformation(&gWifiInterfaceName, &gWifiIPAddress, &gWifiSubnetMask);
+
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "OUT");
return CA_STATUS_OK;
}
u_mutex_lock(gWifiNetInfoMutex);
+ if (NULL == gWifiInterfaceName || NULL == gWifiIPAddress)
+ {
+ OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "Network not enabled");
+ u_mutex_unlock(gWifiNetInfoMutex);
+ return CA_ADAPTER_NOT_ENABLED;
+ }
+
if (gWifiInterfaceName && strlen(gWifiInterfaceName))
{
*interfaceName = strndup(gWifiInterfaceName, strlen(gWifiInterfaceName));
VERIFY_NON_NULL(subnetMask, WIFI_MONITOR_TAG, "subnet mask");
u_mutex_lock(gWifiNetInfoMutex);
- if(NULL == gWifiSubnetMask)
+ if (NULL == gWifiSubnetMask)
{
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "There is no subnet mask information!");
return CA_STATUS_FAILED;
}
- *subnetMask = (gWifiSubnetMask) ? strndup(gWifiSubnetMask,strlen(gWifiSubnetMask))
- : NULL;
+ *subnetMask = (gWifiSubnetMask) ? strndup(gWifiSubnetMask, strlen(gWifiSubnetMask))
+ : NULL;
u_mutex_unlock(gWifiNetInfoMutex);
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "OUT");
OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get access point! error num [%d]",
ret);
- OICFree(interfaceName);
+ OICFree(*interfaceName);
+ *interfaceName = NULL;
u_mutex_unlock(gWifiNetInfoMutex);
return;
}
{
OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get interface address! error num [%d]",
ret);
- OICFree(interfaceName);
+ OICFree(*interfaceName);
+ *interfaceName = NULL;
u_mutex_unlock(gWifiNetInfoMutex);
return;
}
if (subnetMask)
{
- if (WIFI_ERROR_NONE != (ret = wifi_ap_set_subnet_mask(accessPoint, WIFI_ADDRESS_FAMILY_IPV4,
+ if (WIFI_ERROR_NONE != (ret = wifi_ap_get_subnet_mask(accessPoint, WIFI_ADDRESS_FAMILY_IPV4,
subnetMask)))
{
OIC_LOG_V(ERROR, WIFI_MONITOR_TAG, "Failed to get interface address! error num [%d]",
ret);
- OICFree(ipAddress);
- OICFree(interfaceName);
+ OICFree(*ipAddress);
+ OICFree(*interfaceName);
+ *ipAddress = NULL;
+ *interfaceName = NULL;
u_mutex_unlock(gWifiNetInfoMutex);
return;
}
u_mutex_unlock(gWifiNetInfoMutex);
OIC_LOG_V(DEBUG, WIFI_MONITOR_TAG, "OUT");
-}
\ No newline at end of file
+}
bool *stopFlag;
int32_t socket_fd;
CAAdapterServerType_t type;
-}CAAdapterReceiveThreadContext_t;
+} CAAdapterReceiveThreadContext_t;
static void CAReceiveHandler(void *data)
{
continue;
}
- if(!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))
+ if (!CAAdapterIsSameSubnet(gMulticastServerInterface, srcIPAddress, netMask))
{
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "Packet received from different subnet, Ignore!");
continue;
}
OICFree(netMask);
- switch(ctx->type)
+ switch (ctx->type)
{
case CA_UNICAST_SERVER:
case CA_MULTICAST_SERVER:
case CA_SECURED_UNICAST_SERVER:
{
CAResult_t ret = CAAdapterNetDtlsDecrypt(srcIPAddress,
- srcPort,
- (uint8_t *)recvBuffer,
- recvLen, DTLS_WIFI);
+ srcPort,
+ (uint8_t *)recvBuffer,
+ recvLen, DTLS_WIFI);
OIC_LOG_V(DEBUG, WIFI_SERVER_TAG, "CAAdapterNetDtlsDecrypt returns [%d]", ret);
}
break;
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "OUT");
}
-static CAResult_t CAWiFiCreateSocket(int32_t* socketFD, const char *localIp, int16_t *port,
+static CAResult_t CAWiFiCreateSocket(int32_t *socketFD, const char *localIp, int16_t *port,
const bool forceStart)
{
int32_t sock = -1;
memset((char *) &sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(serverPort);
- if(localIp)
+ if (localIp)
{
sockAddr.sin_addr.s_addr = inet_addr(localIp);
}
OIC_LOG(DEBUG, WIFI_SERVER_TAG, "IN");
CAResult_t ret = CAWiFiCreateSocket(serverFD, localAddress, port, forceStart);
- if(CA_STATUS_OK != ret)
+ if (CA_STATUS_OK != ret)
{
OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create unicast socket");
return ret;
* Thread context will be freed by thread on exit.
*/
CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
+ if (!ctx)
{
OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");
close(*serverFD);
ctx->stopFlag = &gStopUnicast;
ctx->socket_fd = *serverFD;
- ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER:CA_UNICAST_SERVER;
+ ctx->type = isSecured ? CA_SECURED_UNICAST_SERVER : CA_UNICAST_SERVER;
if (CA_STATUS_OK != u_thread_pool_add_task(gThreadPool, CAReceiveHandler, (void *)ctx))
{
OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create read thread!");
gStopUnicast = false;
if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gUnicastServerSocketFD))
+ &gUnicastServerSocketFD))
{
OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
gUnicastServerSocketFD = -1;
gStopSecureUnicast = false;
if (CA_STATUS_OK != CAStartUnicastServer(localAddress, port, forceStart, isSecured,
- &gSecureUnicastServerSocketFD))
+ &gSecureUnicastServerSocketFD))
{
OIC_LOG_V(ERROR, WIFI_SERVER_TAG, "Failed to start unicast server!");
gSecureUnicastServerSocketFD = -1;
}
CAResult_t ret = CAWiFiCreateSocket(&gMulticastServerSocketFD, multicastAddress, &port, true);
- if(ret != CA_STATUS_OK)
+ if (ret != CA_STATUS_OK)
{
OIC_LOG(ERROR, WIFI_SERVER_TAG, "Failed to create multicast socket");
u_mutex_unlock(gMutexMulticastServer);
* Thread context will be freed by thread on exit.
*/
CAAdapterReceiveThreadContext_t *ctx = (CAAdapterReceiveThreadContext_t *)
- OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
- if(!ctx)
+ OICMalloc(sizeof(CAAdapterReceiveThreadContext_t));
+ if (!ctx)
{
OIC_LOG(ERROR, WIFI_SERVER_TAG, "Out of memory!");
close(gMulticastServerSocketFD);
{
uri = (char *) "123.123.123.123:1234/b/light";
- EXPECT_EQ(CA_STATUS_OK, CACreateRemoteEndpoint(uri, &tempRep));
+ EXPECT_EQ(CA_STATUS_OK, CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep));
CADestroyRemoteEndpoint(tempRep);
}
TEST(CreateRemoteEndpointTest, TC_07_Positive_02)
{
uri = (char *) "123.123.123.123:1234/b/light";
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
EXPECT_TRUE(tempRep != NULL);
{
uri = NULL;
- EXPECT_EQ(CA_STATUS_FAILED, CACreateRemoteEndpoint(uri, &tempRep));
+ EXPECT_EQ(CA_STATUS_FAILED, CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep));
CADestroyRemoteEndpoint(tempRep);
}
TEST(CreateRemoteEndpointTest, TC_09_Nagative_02)
{
uri = NULL;
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
if (tempRep != NULL)
{
TEST(DestroyRemoteEndpointTest, TC_10_Positive_01)
{
uri = (char *) "123.123.123.123:1234/b/light";
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
CADestroyRemoteEndpoint(tempRep);
{
uri = (char *) "123.123.123.123:1234/b/light";
memset(&requestInfo, 0, sizeof(CARequestInfo_t));
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
CAGenerateToken(&tempToken);
requestInfo.method = CA_GET;
requestInfo.info.token = tempToken;
{
uri = NULL;
memset(&requestInfo, 0, sizeof(CARequestInfo_t));
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
CAGenerateToken(&tempToken);
requestInfo.method = CA_GET;
requestInfo.info.token = tempToken;
TEST(SendResponseTest, TC_17_Positive_01)
{
uri = (char *) "123.123.123.123:1234/b/light";
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
memset(&responseData, 0, sizeof(CAInfo_t));
CAGenerateToken(&tempToken);
TEST(SendResponseTest, TC_18_Nagative_01)
{
uri = NULL;
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
memset(&responseData, 0, sizeof(CAInfo_t));
CAGenerateToken(&tempToken);
TEST(SendNotificationTest, TC_19_Positive_01)
{
uri = (char *) "123.123.123.123:1234/b/light";
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
memset(&responseData, 0, sizeof(CAInfo_t));
responseData.token = (char *) "client token";
TEST(SendNotificationTest, TC_20_Nagative_01)
{
uri = NULL;
- CACreateRemoteEndpoint(uri, &tempRep);
+ CACreateRemoteEndpoint(uri, CA_ETHERNET, &tempRep);
memset(&responseData, 0, sizeof(CAInfo_t));
responseData.token = (char *) "client token";