diff --git a/modules/libcom/.ci/travis-build.sh b/modules/libcom/.ci/travis-build.sh new file mode 100755 index 000000000..622979b9d --- /dev/null +++ b/modules/libcom/.ci/travis-build.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e -x + +# set RTEMS to eg. "4.9" or "4.10" +# requires qemu, bison, flex, texinfo, install-info +if [ -n "$RTEMS" ] +then + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 $EXTRA + +if [ "$TEST" != "NO" ] +then + make -j2 tapfiles + make -s test-results +fi diff --git a/modules/libcom/.ci/travis-prepare.sh b/modules/libcom/.ci/travis-prepare.sh new file mode 100755 index 000000000..9a4d23772 --- /dev/null +++ b/modules/libcom/.ci/travis-prepare.sh @@ -0,0 +1,123 @@ +#!/bin/sh +set -e -x + +CURDIR="$PWD" + +QDIR="$HOME/.cache/qemu" + +if [ -n "$RTEMS" -a "$TEST" = "YES" ] +then + git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu" + cd "$HOME/.build/qemu" + + HEAD=`git log -n1 --pretty=format:%H` + echo "HEAD revision $HEAD" + + [ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"` + echo "Cached revision $BUILT" + + if [ "$HEAD" != "$BUILT" ] + then + echo "Building QEMU" + git submodule --quiet update --init + + install -d "$HOME/.build/qemu/build" + cd "$HOME/.build/qemu/build" + + "$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror + make -j2 + make install + + echo "$HEAD" > "$HOME/.cache/qemu/built" + fi +fi + +cd "$CURDIR" + +cat << EOF > configure/RELEASE.local +EPICS_BASE=$HOME/.source/epics-base +EOF + +install -d "$HOME/.source" +cd "$HOME/.source" + +git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base +(cd epics-base && git log -n1 ) + +EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch` + +# requires wine and g++-mingw-w64-i686 +if [ "$WINE" = "32" ] +then + echo "Cross mingw32" + sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw + cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw +CMPLR_PREFIX=i686-w64-mingw32- +EOF + cat << EOF >> epics-base/configure/CONFIG_SITE +CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw +EOF +fi + +if [ "$STATIC" = "YES" ] +then + echo "Build static libraries/executables" + cat << EOF >> epics-base/configure/CONFIG_SITE +SHARED_LIBRARIES=NO +STATIC_BUILD=YES +EOF +fi + +case "$CMPLR" in +clang) + echo "Host compiler is clang" + cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH +GNU = NO +CMPLR_CLASS = clang +CC = clang +CCC = clang++ +EOF + + # hack + sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon + + clang --version + ;; +*) + echo "Host compiler is default" + gcc --version + ;; +esac + +cat <> epics-base/configure/CONFIG_SITE +USR_CPPFLAGS += $USR_CPPFLAGS +USR_CFLAGS += $USR_CFLAGS +USR_CXXFLAGS += $USR_CXXFLAGS +EOF + +# set RTEMS to eg. "4.9" or "4.10" +# requires qemu, bison, flex, texinfo, install-info +if [ -n "$RTEMS" ] +then + echo "Cross RTEMS${RTEMS} for pc386" + install -d /home/travis/.cache + curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \ + | tar -C /home/travis/.cache -xj + + sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS + cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS +RTEMS_VERSION=$RTEMS +RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386 +EOF + cat << EOF >> epics-base/configure/CONFIG_SITE +CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386 +EOF + + # find local qemu-system-i386 + export PATH="$HOME/.cache/qemu/usr/bin:$PATH" + echo -n "Using QEMU: " + type qemu-system-i386 || echo "Missing qemu" + EXTRA=RTEMS_QEMU_FIXUPS=YES +fi + +make -j2 -C epics-base $EXTRA diff --git a/modules/libcom/.travis.yml b/modules/libcom/.travis.yml new file mode 100644 index 000000000..8b1e2ab3e --- /dev/null +++ b/modules/libcom/.travis.yml @@ -0,0 +1,26 @@ +sudo: false +dist: trusty +language: c +compiler: + - gcc +addons: + apt: + packages: + - libreadline6-dev + - libncurses5-dev + - perl + - clang + - g++-mingw-w64-i686 +install: + - ./.ci/travis-prepare.sh +script: + - ./.ci/travis-build.sh +env: + - BRCORE=master + - CMPLR=clang + - USR_CXXFLAGS=-std=c++11 + - CMPLR=clang USR_CXXFLAGS=-std=c++11 + - WINE=32 TEST=NO STATIC=YES + - WINE=32 TEST=NO STATIC=NO + - RTEMS=4.10 TEST=NO + - RTEMS=4.9 TEST=NO diff --git a/modules/libcom/Makefile b/modules/libcom/Makefile new file mode 100644 index 000000000..442a6f7d1 --- /dev/null +++ b/modules/libcom/Makefile @@ -0,0 +1,25 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP = . +include $(TOP)/configure/CONFIG + +DIRS += configure src +src_DEPEND_DIRS = configure + +DIRS += RTEMS +RTEMS_DEPEND_DIRS = src + +DIRS += vxWorks +vxWorks_DEPEND_DIRS = src + +DIRS += test +test_DEPEND_DIRS = RTEMS vxWorks + +include $(TOP)/configure/RULES_TOP diff --git a/modules/libcom/RTEMS/Makefile b/modules/libcom/RTEMS/Makefile new file mode 100644 index 000000000..b0f3b6fd9 --- /dev/null +++ b/modules/libcom/RTEMS/Makefile @@ -0,0 +1,29 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP = .. +include $(TOP)/configure/CONFIG +include $(TOP)/configure/CONFIG_LIBCOM_VERSION + +INC += epicsRtemsInitHooks.h + +rtemsCom_SRCS += rtems_init.c +rtemsCom_SRCS += rtems_config.c +rtemsCom_SRCS += rtems_netconfig.c +rtemsCom_SRCS += rtems_util.c +rtemsCom_SRCS += setBootConfigFromNVRAM.c +rtemsCom_SRCS += epicsRtemsInitHookPre.c +rtemsCom_SRCS += epicsRtemsInitHookPost.c + +LIBRARY_RTEMS = rtemsCom + +# shared library ABI version. +SHRLIB_VERSION = $(EPICS_LIBCOM_MAJOR_VERSION).$(EPICS_LIBCOM_MINOR_VERSION).$(EPICS_LIBCOM_MAINTENANCE_VERSION) + +include $(TOP)/configure/RULES diff --git a/modules/libcom/RTEMS/epicsRtemsInitHookPost.c b/modules/libcom/RTEMS/epicsRtemsInitHookPost.c new file mode 100644 index 000000000..f589eb9cf --- /dev/null +++ b/modules/libcom/RTEMS/epicsRtemsInitHookPost.c @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Dummy version -- use if application does not provide its own version + */ +#include "epicsRtemsInitHooks.h" + +int +epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) +{ + return 0; +} diff --git a/modules/libcom/RTEMS/epicsRtemsInitHookPre.c b/modules/libcom/RTEMS/epicsRtemsInitHookPre.c new file mode 100644 index 000000000..08efe1fe2 --- /dev/null +++ b/modules/libcom/RTEMS/epicsRtemsInitHookPre.c @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Dummy version -- use if application does not provide its own version + */ +#include "epicsRtemsInitHooks.h" + +int +epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) +{ + return 0; +} diff --git a/modules/libcom/RTEMS/epicsRtemsInitHooks.h b/modules/libcom/RTEMS/epicsRtemsInitHooks.h new file mode 100644 index 000000000..b7f09c100 --- /dev/null +++ b/modules/libcom/RTEMS/epicsRtemsInitHooks.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Hooks into RTEMS startup code + */ +#include +#include + +extern char *env_nfsServer; +extern char *env_nfsPath; +extern char *env_nfsMountPoint; + +/* + * Return 0 for success, non-zero for failure (will cause panic) + */ +int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); +int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); diff --git a/modules/libcom/RTEMS/rtems_config.c b/modules/libcom/RTEMS/rtems_config.c new file mode 100644 index 000000000..147c08b10 --- /dev/null +++ b/modules/libcom/RTEMS/rtems_config.c @@ -0,0 +1,71 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS configuration for EPICS + * Author: W. Eric Norum + * norume@aps.anl.gov + * (630) 252-4793 + */ + +#include + +/* + *********************************************************************** + * RTEMS CONFIGURATION * + *********************************************************************** + */ +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#if __RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) +# define CONFIGURE_UNIFIED_WORK_AREAS +#else +# define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024) +#endif + +#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30) +#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500) +#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20) +#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5) +#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 + +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 150 +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_MAXIMUM_DRIVERS 8 + +#define CONFIGURE_MICROSECONDS_PER_TICK 20000 + +#define CONFIGURE_INIT_TASK_PRIORITY 80 + +#define CONFIGURE_MALLOC_STATISTICS 1 + +#define CONFIGURE_INIT +#define CONFIGURE_INIT_TASK_INITIAL_MODES (RTEMS_PREEMPT | \ + RTEMS_NO_TIMESLICE | \ + RTEMS_NO_ASR | \ + RTEMS_INTERRUPT_LEVEL(0)) +#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_FLOATING_POINT | RTEMS_LOCAL) +#define CONFIGURE_INIT_TASK_STACK_SIZE (16*1024) +rtems_task Init (rtems_task_argument argument); + +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_FILESYSTEM_NFS +#define CONFIGURE_FILESYSTEM_IMFS + +/* + * This should be made BSP dependent, not CPU dependent but I know of no + * appropriate conditionals to use. + * The new general time support makes including the RTC driverr less important. + */ +#if !defined(mpc604) && !defined(__mc68040__) && !defined(__mcf5200__) && !defined(mpc7455) && !defined(__arm__) && !defined(__nios2__)/* don't have RTC code */ +#define CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER +#endif + + +#include +#include diff --git a/modules/libcom/RTEMS/rtems_init.c b/modules/libcom/RTEMS/rtems_init.c new file mode 100644 index 000000000..82871da0d --- /dev/null +++ b/modules/libcom/RTEMS/rtems_init.c @@ -0,0 +1,668 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS startup task for EPICS + * Author: W. Eric Norum + * eric.norum@usask.ca + * (306) 966-5394 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "epicsExit.h" +#include "envDefs.h" +#include "errlog.h" +#include "logClient.h" +#include "osiUnistd.h" +#include "iocsh.h" +#include "osdTime.h" + +#include "epicsRtemsInitHooks.h" + +/* + * Prototypes for some functions not in header files + */ +void tzset(void); +int fileno(FILE *); +int main(int argc, char **argv); + +static void +logReset (void) +{ + void rtems_bsp_reset_cause(char *buf, size_t capacity) __attribute__((weak)); + void (*fp)(char *buf, size_t capacity) = rtems_bsp_reset_cause; + + if (fp) { + char buf[80]; + fp(buf, sizeof buf); + errlogPrintf ("Startup after %s.\n", buf); + } + else { + errlogPrintf ("Startup.\n"); + } +} + +/* + *********************************************************************** + * FATAL ERROR REPORTING * + *********************************************************************** + */ +/* + * Delay for a while, then terminate + */ +static void +delayedPanic (const char *msg) +{ + extern rtems_interval rtemsTicksPerSecond; + + rtems_task_wake_after (rtemsTicksPerSecond); + rtems_panic (msg); +} + +/* + * Log error and terminate + */ +void +LogFatal (const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + errlogVprintf (msg, ap); + va_end (ap); + delayedPanic (msg); +} + +/* + * Log RTEMS error and terminate + */ +void +LogRtemsFatal (const char *msg, rtems_status_code sc) +{ + errlogPrintf ("%s: %s\n", msg, rtems_status_text (sc)); + delayedPanic (msg); +} + +/* + * Log network error and terminate + */ +void +LogNetFatal (const char *msg, int err) +{ + errlogPrintf ("%s: %d\n", msg, err); + delayedPanic (msg); +} + +void * +mustMalloc(int size, const char *msg) +{ + void *p; + + if ((p = malloc (size)) == NULL) + LogFatal ("Can't allocate space for %s.\n", msg); + return p; +} + +/* + *********************************************************************** + * REMOTE FILE ACCESS * + *********************************************************************** + */ +#ifdef OMIT_NFS_SUPPORT +# include +#endif + +static int +initialize_local_filesystem(char **argv) +{ + extern char _DownloadLocation[] __attribute__((weak)); + extern char _FlashBase[] __attribute__((weak)); + extern char _FlashSize[] __attribute__((weak)); + + argv[0] = rtems_bsdnet_bootp_boot_file_name; + if (_FlashSize && (_DownloadLocation || _FlashBase)) { + extern char _edata[]; + size_t flashIndex = _edata - _DownloadLocation; + char *header = _FlashBase + flashIndex; + + if (memcmp(header + 257, "ustar ", 8) == 0) { + int fd; + printf ("***** Unpack in-memory file system (IMFS) *****\n"); + if (rtems_tarfs_load("/", (unsigned char *)header, (size_t)_FlashSize - flashIndex) != 0) { + printf("Can't unpack tar filesystem\n"); + return 0; + } + if ((fd = open(rtems_bsdnet_bootp_cmdline, 0)) >= 0) { + close(fd); + printf ("***** Found startup script (%s) in IMFS *****\n", rtems_bsdnet_bootp_cmdline); + argv[1] = rtems_bsdnet_bootp_cmdline; + return 1; + } + printf ("***** Startup script (%s) not in IMFS *****\n", rtems_bsdnet_bootp_cmdline); + } + } + return 0; +} + +#ifndef OMIT_NFS_SUPPORT +#if __RTEMS_MAJOR__>4 || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) +int +nfsMount(char *uidhost, char *path, char *mntpoint) +{ + int devl = strlen(uidhost) + strlen(path) + 2; + char *dev; + int rval = -1; + + if ((dev = malloc(devl)) == NULL) { + fprintf(stderr,"nfsMount: out of memory\n"); + return -1; + } + sprintf(dev, "%s:%s", uidhost, path); + printf("Mount %s on %s\n", dev, mntpoint); + if (rtems_mkdir(mntpoint, S_IRWXU | S_IRWXG | S_IRWXO)) + printf("Warning -- unable to make directory \"%s\"\n", mntpoint); + if (mount(dev, mntpoint, RTEMS_FILESYSTEM_TYPE_NFS, + RTEMS_FILESYSTEM_READ_WRITE, NULL)) { + perror("mount failed"); + } + else { + rval = 0; + } + free(dev); + return rval; +} +#define NFS_INIT +#else +#define NFS_INIT rpcUdpInit(); nfsInit(0,0); +#endif +#endif + +static void +initialize_remote_filesystem(char **argv, int hasLocalFilesystem) +{ +#ifdef OMIT_NFS_SUPPORT + printf ("***** Initializing TFTP *****\n"); +#if __RTEMS_MAJOR__>4 || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) + mount_and_make_target_path(NULL, + "/TFTP", + RTEMS_FILESYSTEM_TYPE_TFTPFS, + RTEMS_FILESYSTEM_READ_WRITE, + NULL); +#else + rtems_bsdnet_initialize_tftp_filesystem (); +#endif + if (!hasLocalFilesystem) { + char *path; + int pathsize = 200; + int l; + + path = mustMalloc(pathsize, "Command path name "); + strcpy (path, "/TFTP/BOOTP_HOST/epics/"); + l = strlen (path); + if (gethostname (&path[l], pathsize - l - 10) || (path[l] == '\0')) + { + LogFatal ("Can't get host name"); + } + strcat (path, "/st.cmd"); + argv[1] = path; + } +#else + char *server_name; + char *server_path; + char *mount_point; + char *cp; + int l = 0; + + printf ("***** Initializing NFS *****\n"); + NFS_INIT + if (env_nfsServer && env_nfsPath && env_nfsMountPoint) { + server_name = env_nfsServer; + server_path = env_nfsPath; + mount_point = env_nfsMountPoint; + cp = mount_point; + while ((cp = strchr(cp+1, '/')) != NULL) { + *cp = '\0'; + if ((mkdir (mount_point, 0755) != 0) + && (errno != EEXIST)) + LogFatal("Can't create directory \"%s\": %s.\n", + mount_point, strerror(errno)); + *cp = '/'; + } + argv[1] = rtems_bsdnet_bootp_cmdline; + } + else if (hasLocalFilesystem) { + return; + } + else { + /* + * Use first component of nvram/bootp command line pathname + * to set up initial NFS mount. A "/tftpboot/" is prepended + * if the pathname does not begin with a '/'. This allows + * NFS and TFTP to have a similar view of the remote system. + */ + if (rtems_bsdnet_bootp_cmdline[0] == '/') + cp = rtems_bsdnet_bootp_cmdline + 1; + else + cp = rtems_bsdnet_bootp_cmdline; + cp = strchr(cp, '/'); + if ((cp == NULL) + || ((l = cp - rtems_bsdnet_bootp_cmdline) == 0)) + LogFatal("\"%s\" is not a valid command pathname.\n", rtems_bsdnet_bootp_cmdline); + cp = mustMalloc(l + 20, "NFS mount paths"); + server_path = cp; + server_name = rtems_bsdnet_bootp_server_name; + if (rtems_bsdnet_bootp_cmdline[0] == '/') { + mount_point = server_path; + strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); + mount_point[l] = '\0'; + argv[1] = rtems_bsdnet_bootp_cmdline; + /* + * Its probably common to embed the mount point in the server + * name so, when this is occurring, dont clobber the mount point + * by appending the first node from the command path. This allows + * the mount point to be a different path then the server's mount + * path. + * + * This allows for example a line similar to as follows the DHCP + * configuration file. + * + * server-name "159.233@192.168.0.123:/vol/vol0/bootRTEMS"; + */ + if ( server_name ) { + const size_t allocSize = strlen ( server_name ) + 2; + char * const pServerName = mustMalloc( allocSize, + "NFS mount paths"); + char * const pServerPath = mustMalloc ( allocSize, + "NFS mount paths"); + const int scanfStatus = sscanf ( + server_name, + "%[^:] : / %s", + pServerName, + pServerPath + 1u ); + if ( scanfStatus == 2 ) { + pServerPath[0u]= '/'; + server_name = pServerName; + server_path = pServerPath; + } + else { + free ( pServerName ); + free ( pServerPath ); + } + } + } + else { + char *abspath = mustMalloc(strlen(rtems_bsdnet_bootp_cmdline)+2,"Absolute command path"); + strcpy(server_path, "/tftpboot/"); + mount_point = server_path + strlen(server_path); + strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); + mount_point[l] = '\0'; + mount_point--; + strcpy(abspath, "/"); + strcat(abspath, rtems_bsdnet_bootp_cmdline); + argv[1] = abspath; + } + } + errlogPrintf("nfsMount(\"%s\", \"%s\", \"%s\")\n", + server_name, server_path, mount_point); + nfsMount(server_name, server_path, mount_point); +#endif +} + +static +char rtems_etc_hosts[] = "127.0.0.1 localhost\n"; + +/* If it doesn't already exist, create /etc/hosts with an entry for 'localhost' */ +static +void fixup_hosts(void) +{ + FILE *fp; + int ret; + struct stat STAT; + + ret=stat("/etc/hosts", &STAT); + if(ret==0) + { + return; /* already exists, assume file */ + } else if(errno!=ENOENT) { + perror("error: fixup_hosts stat /etc/hosts"); + return; + } + + ret = mkdir("/etc", 0775); + if(ret!=0 && errno!=EEXIST) + { + perror("error: fixup_hosts create /etc"); + return; + } + + if((fp=fopen("/etc/hosts", "w"))==NULL) + { + perror("error: fixup_hosts create /etc/hosts"); + } + + if(fwrite(rtems_etc_hosts, 1, sizeof(rtems_etc_hosts)-1, fp)!=sizeof(rtems_etc_hosts)-1) + { + perror("error: failed to write /etc/hosts"); + } + + fclose(fp); +} + +/* + * Get to the startup script directory + * The TFTP filesystem requires a trailing '/' on chdir arguments. + */ +static void +set_directory (const char *commandline) +{ + const char *cp; + char *directoryPath; + int l; + + cp = strrchr(commandline, '/'); + if (cp == NULL) { + l = 0; + cp = "/"; + } + else { + l = cp - commandline; + cp = commandline; + } + directoryPath = mustMalloc(l + 2, "Command path directory "); + strncpy(directoryPath, cp, l); + directoryPath[l] = '/'; + directoryPath[l+1] = '\0'; + if (chdir (directoryPath) < 0) + LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno)); + else + errlogPrintf("chdir(\"%s\")\n", directoryPath); + free(directoryPath); +} + +/* + *********************************************************************** + * RTEMS/EPICS COMMANDS * + *********************************************************************** + */ +/* + * RTEMS status + */ +static void +rtems_netstat (unsigned int level) +{ + rtems_bsdnet_show_if_stats (); + rtems_bsdnet_show_mbuf_stats (); + if (level >= 1) { + rtems_bsdnet_show_inet_routes (); + } + if (level >= 2) { + rtems_bsdnet_show_ip_stats (); + rtems_bsdnet_show_icmp_stats (); + rtems_bsdnet_show_udp_stats (); + rtems_bsdnet_show_tcp_stats (); + } +} + +static const iocshArg netStatArg0 = { "level",iocshArgInt}; +static const iocshArg * const netStatArgs[1] = {&netStatArg0}; +static const iocshFuncDef netStatFuncDef = {"netstat",1,netStatArgs}; +static void netStatCallFunc(const iocshArgBuf *args) +{ + rtems_netstat(args[0].ival); +} + +static const iocshFuncDef heapSpaceFuncDef = {"heapSpace",0,NULL}; +static void heapSpaceCallFunc(const iocshArgBuf *args) +{ + rtems_malloc_statistics_t s; + double x; + + malloc_get_statistics(&s); + x = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); + if (x >= 1024*1024) + printf("Heap space: %.1f MB\n", x / (1024 * 1024)); + else + printf("Heap space: %.1f kB\n", x / 1024); +} + +#ifndef OMIT_NFS_SUPPORT +static const iocshArg nfsMountArg0 = { "[uid.gid@]host",iocshArgString}; +static const iocshArg nfsMountArg1 = { "server path",iocshArgString}; +static const iocshArg nfsMountArg2 = { "mount point",iocshArgString}; +static const iocshArg * const nfsMountArgs[3] = {&nfsMountArg0,&nfsMountArg1, + &nfsMountArg2}; +static const iocshFuncDef nfsMountFuncDef = {"nfsMount",3,nfsMountArgs}; +static void nfsMountCallFunc(const iocshArgBuf *args) +{ + char *cp = args[2].sval; + while ((cp = strchr(cp+1, '/')) != NULL) { + *cp = '\0'; + if ((mkdir (args[2].sval, 0755) != 0) && (errno != EEXIST)) { + printf("Can't create directory \"%s\": %s.\n", + args[2].sval, strerror(errno)); + return; + } + *cp = '/'; + } + nfsMount(args[0].sval, args[1].sval, args[2].sval); +} +#endif + +/* + * Register RTEMS-specific commands + */ +static void iocshRegisterRTEMS (void) +{ + iocshRegister(&netStatFuncDef, netStatCallFunc); + iocshRegister(&heapSpaceFuncDef, heapSpaceCallFunc); +#ifndef OMIT_NFS_SUPPORT + iocshRegister(&nfsMountFuncDef, nfsMountCallFunc); +#endif +} + +/* + * Set up the console serial line (no handshaking) + */ +static void +initConsole (void) +{ + struct termios t; + + if (tcgetattr (fileno (stdin), &t) < 0) { + printf ("tcgetattr failed: %s\n", strerror (errno)); + return; + } + t.c_iflag &= ~(IXOFF | IXON | IXANY); + if (tcsetattr (fileno (stdin), TCSANOW, &t) < 0) { + printf ("tcsetattr failed: %s\n", strerror (errno)); + return; + } +} + +/* + * Ensure that the configuration object files + * get pulled in from the library + */ +extern rtems_configuration_table Configuration; +extern struct rtems_bsdnet_config rtems_bsdnet_config; +const void *rtemsConfigArray[] = { + &Configuration, + &rtems_bsdnet_config +}; + +/* + * Hook to ensure that BSP cleanup code gets run on exit + */ +static void +exitHandler(void) +{ + rtems_shutdown_executive(0); +} + +/* + * RTEMS Startup task + */ +rtems_task +Init (rtems_task_argument ignored) +{ + int result; + char *argv[3] = { NULL, NULL, NULL }; + char *cp; + rtems_task_priority newpri; + rtems_status_code sc; + rtems_time_of_day now; + + /* + * Explain why we're here + */ + logReset(); + + /* + * Architecture-specific hooks + */ + if (epicsRtemsInitPreSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) + delayedPanic("epicsRtemsInitPreSetBootConfigFromNVRAM"); + if (rtems_bsdnet_config.bootp == NULL) { + extern void setBootConfigFromNVRAM(void); + setBootConfigFromNVRAM(); + } + if (epicsRtemsInitPostSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) + delayedPanic("epicsRtemsInitPostSetBootConfigFromNVRAM"); + + /* + * Override RTEMS configuration + */ + rtems_task_set_priority ( + RTEMS_SELF, + epicsThreadGetOssPriorityValue(epicsThreadPriorityIocsh), + &newpri); + + /* + * Create a reasonable environment + */ + initConsole (); + putenv ("TERM=xterm"); + putenv ("IOCSH_HISTSIZE=20"); + + /* + * Display some OS information + */ + printf("\n***** RTEMS Version: %s *****\n", + rtems_get_version_string()); + + /* + * Start network + */ + if ((cp = getenv("EPICS_TS_NTP_INET")) != NULL) + rtems_bsdnet_config.ntp_server[0] = cp; + if (rtems_bsdnet_config.network_task_priority == 0) + { + unsigned int p; + if (epicsThreadHighestPriorityLevelBelow(epicsThreadPriorityScanLow, &p) + == epicsThreadBooleanStatusSuccess) + { + rtems_bsdnet_config.network_task_priority = epicsThreadGetOssPriorityValue(p); + } + } + printf("\n***** Initializing network *****\n"); + rtems_bsdnet_initialize_network(); + initialize_remote_filesystem(argv, initialize_local_filesystem(argv)); + fixup_hosts(); + + /* + * More environment: iocsh prompt and hostname + */ + { + char hostname[1024]; + gethostname(hostname, 1023); + char *cp = mustMalloc(strlen(hostname)+3, "iocsh prompt"); + sprintf(cp, "%s> ", hostname); + epicsEnvSet ("IOCSH_PS1", cp); + epicsEnvSet("IOC_NAME", hostname); + } + + /* + * Use BSP-supplied time of day if available otherwise supply default time. + * It is very likely that other time synchronization facilities in EPICS + * will soon override this value. + */ + if (rtems_clock_get(RTEMS_CLOCK_GET_TOD,&now) != RTEMS_SUCCESSFUL) { + now.year = 2001; + now.month = 1; + now.day = 1; + now.hour = 0; + now.minute = 0; + now.second = 0; + now.ticks = 0; + if ((sc = rtems_clock_set (&now)) != RTEMS_SUCCESSFUL) + printf ("***** Can't set time: %s\n", rtems_status_text (sc)); + } + if (getenv("TZ") == NULL) { + const char *tzp = envGetConfigParamPtr(&EPICS_TIMEZONE); + if (tzp == NULL) { + printf("Warning -- no timezone information available -- times will be displayed as GMT.\n"); + } + else { + char tz[10]; + int minWest, toDst = 0, fromDst = 0; + if(sscanf(tzp, "%9[^:]::%d:%d:%d", tz, &minWest, &toDst, &fromDst) < 2) { + printf("Warning: EPICS_TIMEZONE (%s) unrecognizable -- times will be displayed as GMT.\n", tzp); + } + else { + char posixTzBuf[40]; + char *p = posixTzBuf; + p += sprintf(p, "%cST%d:%.2d", tz[0], minWest/60, minWest%60); + if (toDst != fromDst) + p += sprintf(p, "%cDT", tz[0]); + epicsEnvSet("TZ", posixTzBuf); + } + } + } + tzset(); + osdTimeRegister(); + + /* + * Run the EPICS startup script + */ + printf ("***** Preparing EPICS application *****\n"); + iocshRegisterRTEMS (); + set_directory (argv[1]); + epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]); + atexit(exitHandler); + errlogFlush(); + printf ("***** Starting EPICS application *****\n"); + result = main ((sizeof argv / sizeof argv[0]) - 1, argv); + printf ("***** IOC application terminating *****\n"); + epicsThreadSleep(1.0); + epicsExit(result); +} diff --git a/modules/libcom/RTEMS/rtems_netconfig.c b/modules/libcom/RTEMS/rtems_netconfig.c new file mode 100644 index 000000000..832a6646b --- /dev/null +++ b/modules/libcom/RTEMS/rtems_netconfig.c @@ -0,0 +1,120 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS network configuration for EPICS + * Author: W. Eric Norum + * eric.norum@usask.ca + * (306) 966-5394 + * + * This file can be copied to an application source dirctory + * and modified to override the values shown below. + */ +#include +#include +#include + +extern void rtems_bsdnet_loopattach(); +static struct rtems_bsdnet_ifconfig loopback_config = { + "lo0", /* name */ + (int (*)(struct rtems_bsdnet_ifconfig *, int))rtems_bsdnet_loopattach, /* attach function */ + NULL, /* link to next interface */ + "127.0.0.1", /* IP address */ + "255.0.0.0", /* IP net mask */ +}; + +/* + * The following conditionals select the network interface card. + * + * On RTEMS-pc386 targets all network drivers which support run-time + * probing are linked. + * On other targets the network interface specified by the board-support + * package is used. + * To use a different NIC for a particular application, copy this file to the + * application directory and make the appropriate changes. + */ +#if defined(__i386__) +extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int); +static struct rtems_bsdnet_ifconfig fxp_driver_config = { + "fxp1", /* name */ + rtems_fxp_attach, /* attach function */ + &loopback_config, /* link to next interface */ +}; +extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int); +static struct rtems_bsdnet_ifconfig e3c509_driver_config = { + "ep0", /* name */ + rtems_3c509_driver_attach, /* attach function */ + &fxp_driver_config, /* link to next interface */ +}; +#define FIRST_DRIVER_CONFIG &e3c509_driver_config +#else + +# if defined(__PPC) + /* + * FIXME: This really belongs in the BSP + */ +# ifndef RTEMS_BSP_NETWORK_DRIVER_NAME +# define RTEMS_BSP_NETWORK_DRIVER_NAME "dc1" +# endif +# ifndef RTEMS_BSP_NETWORK_DRIVER_ATTACH +# define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_dec21140_driver_attach + extern int rtems_dec21140_driver_attach(); +# endif +# endif + +static struct rtems_bsdnet_ifconfig bsp_driver_config = { + RTEMS_BSP_NETWORK_DRIVER_NAME, /* name */ + RTEMS_BSP_NETWORK_DRIVER_ATTACH, /* attach function */ + &loopback_config, /* link to next interface */ +}; +#define FIRST_DRIVER_CONFIG &bsp_driver_config + +#endif + +/* + * Allow configure/os/CONFIG_SITE.Common.RTEMS to provide domain name + */ +#ifdef RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME +# define XSTR(x) STR(x) +# define STR(x) #x +# define MY_DOMAINNAME XSTR(RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME) +#else +# define MY_DOMAINNAME NULL +#endif + +/* + * Allow non-BOOTP network configuration + */ +#ifndef MY_DO_BOOTP +# define MY_DO_BOOTP rtems_bsdnet_do_bootp +#endif + +/* + * Allow site- and BSP-specific network buffer space configuration. + * The macro values are specified in KBytes. + */ +#ifndef RTEMS_NETWORK_CONFIG_MBUF_SPACE +# define RTEMS_NETWORK_CONFIG_MBUF_SPACE 180 +#endif +#ifndef RTEMS_NETWORK_CONFIG_CLUSTER_SPACE +# define RTEMS_NETWORK_CONFIG_CLUSTER_SPACE 350 +#endif + +/* + * Network configuration + */ +struct rtems_bsdnet_config rtems_bsdnet_config = { + FIRST_DRIVER_CONFIG, /* Link to next interface */ + MY_DO_BOOTP, /* How to find network config */ + 10, /* If 0 then the network daemons will run at a */ + /* priority just less than the lowest-priority */ + /* EPICS scan thread. */ + /* If non-zero then the network daemons will run */ + /* at this *RTEMS* priority */ + RTEMS_NETWORK_CONFIG_MBUF_SPACE*1024, + RTEMS_NETWORK_CONFIG_CLUSTER_SPACE*1024, + NULL, /* Host name */ + MY_DOMAINNAME, /* Domain name */ +}; diff --git a/modules/libcom/RTEMS/rtems_util.c b/modules/libcom/RTEMS/rtems_util.c new file mode 100644 index 000000000..ca64f9650 --- /dev/null +++ b/modules/libcom/RTEMS/rtems_util.c @@ -0,0 +1,44 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS utilitiy routines for EPICS + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + * + * Supplies routines that are present in vxWorks but missing in RTEMS. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Like connect(), but with an explicit timeout + */ +int connectWithTimeout (int sfd, + struct sockaddr *addr, + int addrlen, + struct timeval *timeout) +{ + struct timeval sv; + socklen_t svlen = sizeof sv; + int ret; + + if (!timeout) + return connect (sfd, addr, addrlen); + if (getsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, &svlen) < 0) + return -1; + if (setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)timeout, sizeof *timeout) < 0) + return -1; + ret = connect (sfd, addr, addrlen); + setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, sizeof sv); + return ret; +} diff --git a/modules/libcom/RTEMS/setBootConfigFromNVRAM.c b/modules/libcom/RTEMS/setBootConfigFromNVRAM.c new file mode 100644 index 000000000..6a162c602 --- /dev/null +++ b/modules/libcom/RTEMS/setBootConfigFromNVRAM.c @@ -0,0 +1,370 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *env_nfsServer; +char *env_nfsPath; +char *env_nfsMountPoint; + +/* + * Split argument string of form nfs_server:nfs_export: + * The nfs_export component will be used as: + * - the path to the directory exported from the NFS server + * - the local mount point + * - a prefix of + * For example, the argument string: + * romeo:/export/users:smith/ioc/iocexample/st.cmd + * would: + * - mount /export/users from NFS server romeo on /export/users + * - chdir to /export/users/smith/ioc/iocexample + * - read commands from st.cmd + */ +static void +splitRtemsBsdnetBootpCmdline(void) +{ + char *cp1, *cp2, *cp3; + + if ((cp1 = rtems_bsdnet_bootp_cmdline) == NULL) + return; + if (((cp2 = strchr(cp1, ':')) != NULL) + && (((cp3 = strchr(cp2+1, ' ')) != NULL) + || ((cp3 = strchr(cp2+1, ':')) != NULL))) { + int l1 = cp2 - cp1; + int l2 = cp3 - cp2 - 1; + int l3 = strlen(cp3) - 1; + if (l1 && l2 && l3) { + *cp2++ = '\0'; + *cp3 = '\0'; + env_nfsServer = cp1; + env_nfsMountPoint = env_nfsPath = epicsStrDup(cp2); + *cp3 = '/'; + rtems_bsdnet_bootp_cmdline = cp2; + } + } +} + +/* + * Split NFS mount information of the form nfs_server:host_path:local_path + */ +static void +splitNfsMountPath(char *nfsString) +{ + char *cp2, *cp3; + + if (nfsString == NULL) + return; + if (((cp2 = strchr(nfsString, ':')) != NULL) + && (((cp3 = strchr(cp2+1, ' ')) != NULL) + || ((cp3 = strchr(cp2+1, ':')) != NULL))) { + int l1 = cp2 - nfsString; + int l2 = cp3 - cp2 - 1; + int l3 = strlen(cp3) - 1; + if (l1 && l2 && l3) { + *cp2++ = '\0'; + *cp3++ = '\0'; + env_nfsServer = nfsString; + env_nfsPath = cp2; + env_nfsMountPoint = cp3; + } + } +} + +#if defined(HAVE_MOTLOAD) + +/* + * Motorola MOTLOAD NVRAM Access + */ +static char * +gev(const char *parm, volatile char *nvp) +{ + const char *val; + const char *name; + char *ret; + char c; + + for (;;) { + if (*nvp == '\0') + return NULL; + name = parm; + while ((c = *nvp++) != '\0') { + if ((c == '=') && (*name == '\0')) { + val = (char *)nvp; + while (*nvp++ != '\0') + continue; + ret = malloc(nvp - val); + if (ret == NULL) + return NULL; + strcpy(ret, val); + return ret; + } + if (c != *name++) { + while (*nvp++ != '\0') + continue; + break; + } + } + } +} + +static char * +motScriptParm(const char *mot_script_boot, char parm) +{ + const char *cp; + char *ret; + int l; + + while (*mot_script_boot != '\0') { + if (isspace(*(unsigned char *)mot_script_boot) + && (*(mot_script_boot+1) == '-') + && (*(mot_script_boot+2) == parm)) { + mot_script_boot += 3; + cp = mot_script_boot; + while ((*mot_script_boot != '\0') && + !isspace(*(unsigned char *)mot_script_boot)) + mot_script_boot++; + l = mot_script_boot - cp; + ret = malloc(l+1); + if (ret == NULL) + return NULL; + strncpy(ret, cp, l); + *(ret+l) = '\0'; + return ret; + } + mot_script_boot++; + } + return NULL; +} + +void +setBootConfigFromNVRAM(void) +{ + char *cp; + const char *mot_script_boot; + char *nvp; + +# if defined(BSP_NVRAM_BASE_ADDR) + nvp = (volatile unsigned char *)(BSP_NVRAM_BASE_ADDR+0x70f8); +# elif defined(BSP_I2C_VPD_EEPROM_DEV_NAME) + char gev_buf[3592]; + int fd; + if ((fd = open(BSP_I2C_VPD_EEPROM_DEV_NAME, 0)) < 0) { + printf("Can't open %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); + return; + } + lseek(fd, 0x10f8, SEEK_SET); + if (read(fd, gev_buf, sizeof gev_buf) != sizeof gev_buf) { + printf("Can't read %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); + return; + } + close(fd); + nvp = gev_buf; +# else +# error "No way to read GEV!" +# endif + + if (rtems_bsdnet_config.bootp != NULL) + return; + mot_script_boot = gev("mot-script-boot", nvp); + if ((rtems_bsdnet_bootp_server_name = gev("mot-/dev/enet0-sipa", nvp)) == NULL) + rtems_bsdnet_bootp_server_name = motScriptParm(mot_script_boot, 's'); + if ((rtems_bsdnet_config.gateway = gev("mot-/dev/enet0-gipa", nvp)) == NULL) + rtems_bsdnet_config.gateway = motScriptParm(mot_script_boot, 'g'); + if ((rtems_bsdnet_config.ifconfig->ip_netmask = gev("mot-/dev/enet0-snma", nvp)) == NULL) + rtems_bsdnet_config.ifconfig->ip_netmask = motScriptParm(mot_script_boot, 'm'); + + rtems_bsdnet_config.name_server[0] = gev("rtems-dns-server", nvp); + if (rtems_bsdnet_config.name_server[0] == NULL) + rtems_bsdnet_config.name_server[0] = rtems_bsdnet_bootp_server_name; + cp = gev("rtems-dns-domainname", nvp); + if (cp) + rtems_bsdnet_config.domainname = cp; + + if ((rtems_bsdnet_config.ifconfig->ip_address = gev("mot-/dev/enet0-cipa", nvp)) == NULL) + rtems_bsdnet_config.ifconfig->ip_address = motScriptParm(mot_script_boot, 'c'); + rtems_bsdnet_config.hostname = gev("rtems-client-name", nvp); + if (rtems_bsdnet_config.hostname == NULL) + rtems_bsdnet_config.hostname = rtems_bsdnet_config.ifconfig->ip_address; + + if ((rtems_bsdnet_bootp_boot_file_name = gev("mot-/dev/enet0-file", nvp)) == NULL) + rtems_bsdnet_bootp_boot_file_name = motScriptParm(mot_script_boot, 'f'); + rtems_bsdnet_bootp_cmdline = gev("epics-script", nvp); + splitRtemsBsdnetBootpCmdline(); + splitNfsMountPath(gev("epics-nfsmount", nvp)); + rtems_bsdnet_config.ntp_server[0] = gev("epics-ntpserver", nvp); + if (rtems_bsdnet_config.ntp_server[0] == NULL) + rtems_bsdnet_config.ntp_server[0] = rtems_bsdnet_bootp_server_name; + if ((cp = gev("epics-tz", nvp)) != NULL) + epicsEnvSet("TZ", cp); +} + +#elif defined(HAVE_PPCBUG) +/* + * Motorola PPCBUG NVRAM Access + */ +struct ppcbug_nvram { + uint32_t PacketVersionIdentifier; + uint32_t NodeControlMemoryAddress; + uint32_t BootFileLoadAddress; + uint32_t BootFileExecutionAddress; + uint32_t BootFileExecutionDelay; + uint32_t BootFileLength; + uint32_t BootFileByteOffset; + uint32_t TraceBufferAddress; + uint32_t ClientIPAddress; + uint32_t ServerIPAddress; + uint32_t SubnetIPAddressMask; + uint32_t BroadcastIPAddressMask; + uint32_t GatewayIPAddress; + uint8_t BootpRarpRetry; + uint8_t TftpRarpRetry; + uint8_t BootpRarpControl; + uint8_t UpdateControl; + char BootFilenameString[64]; + char ArgumentFilenameString[64]; +}; + +static char *addr(char *cbuf, uint32_t addr) +{ + struct in_addr a; + if ((a.s_addr = addr) == 0) + return NULL; + return (char *)inet_ntop(AF_INET, &a, cbuf, INET_ADDRSTRLEN); +} + +void +setBootConfigFromNVRAM(void) +{ + static struct ppcbug_nvram nvram; + static char ip_address[INET_ADDRSTRLEN]; + static char ip_netmask[INET_ADDRSTRLEN]; + static char server[INET_ADDRSTRLEN]; + static char gateway[INET_ADDRSTRLEN]; + + if (rtems_bsdnet_config.bootp != NULL) + return; + + /* + * Get network configuation from PPCBUG. + * The 'correct' way to do this would be to issue a .NETCFIG PPCBUG + * system call. Unfortunately it is very difficult to issue such a + * call once RTEMS is up and running so we just copy from the 'known' + * location of the network configuration parameters. + * Care must be taken to access the NVRAM a byte at a time. + */ + +#if defined(NVRAM_INDIRECT) + { + volatile char *addrLo = (volatile char *)0x80000074; + volatile char *addrHi = (volatile char *)0x80000075; + volatile char *data = (volatile char *)0x80000077; + int addr = 0x1000; + char *d = (char *)&nvram; + + while (d < ((char *)&nvram + sizeof nvram)) { + *addrLo = addr & 0xFF; + *addrHi = (addr >> 8) & 0xFF; + *d++ = *data; + addr++; + } + } +#else + { + volatile char *s = (volatile char *)0xFFE81000; + char *d = (char *)&nvram; + + while (d < ((char *)&nvram + sizeof nvram)) + *d++ = *s++; + } +#endif + /* + * Assume that the boot server is also the name, log and ntp server! + */ + rtems_bsdnet_config.name_server[0] = + rtems_bsdnet_config.ntp_server[0] = + rtems_bsdnet_bootp_server_name = addr(server, nvram.ServerIPAddress); + rtems_bsdnet_bootp_server_address.s_addr = nvram.ServerIPAddress; + /* + * Nothing better to use as host name! + */ + rtems_bsdnet_config.ifconfig->ip_address = + rtems_bsdnet_config.hostname = addr(ip_address, nvram.ClientIPAddress); + + rtems_bsdnet_config.gateway = addr(gateway, nvram.GatewayIPAddress); + rtems_bsdnet_config.ifconfig->ip_netmask = addr(ip_netmask, nvram.SubnetIPAddressMask); + + rtems_bsdnet_bootp_boot_file_name = nvram.BootFilenameString; + rtems_bsdnet_bootp_cmdline = nvram.ArgumentFilenameString; + splitRtemsBsdnetBootpCmdline(); +} + +#elif defined(__mcf528x__) + +static char * +env(const char *parm, const char *defaultValue) +{ + const char *cp = bsp_getbenv(parm); + + if (!cp) { + if (!defaultValue) + return NULL; + cp = defaultValue; + printf ("%s environment variable missing -- using %s.\n", parm, cp); + } + return epicsStrDup(cp); +} + +void +setBootConfigFromNVRAM(void) +{ + const char *cp1; + + if (rtems_bsdnet_config.bootp != NULL) + return; + rtems_bsdnet_config.gateway = env("GATEWAY", NULL); + rtems_bsdnet_config.ifconfig->ip_netmask = env("NETMASK", "255.255.252.0"); + + rtems_bsdnet_bootp_server_name = env("SERVER", "192.168.0.1"); + rtems_bsdnet_config.name_server[0] = env("NAMESERVER", rtems_bsdnet_bootp_server_name); + rtems_bsdnet_config.ntp_server[0] = env("NTPSERVER", rtems_bsdnet_bootp_server_name); + cp1 = env("DOMAIN", NULL); + if (cp1 != NULL) + rtems_bsdnet_config.domainname = cp1; + rtems_bsdnet_config.hostname = env("HOSTNAME", "iocNobody"); + rtems_bsdnet_config.ifconfig->ip_address = env("IPADDR0", "192.168.0.2"); + rtems_bsdnet_bootp_boot_file_name = env("BOOTFILE", "uC5282App.boot"); + rtems_bsdnet_bootp_cmdline = env("CMDLINE", "epics/iocBoot/iocNobody/st.cmd"); + splitNfsMountPath(env("NFSMOUNT", NULL)); + if ((cp1 = env("TZ", NULL)) != NULL) + epicsEnvSet("TZ", cp1); +} + +#else +/* + * Placeholder for systems without NVRAM + */ +void +setBootConfigFromNVRAM(void) +{ + printf("SYSTEM HAS NO NON-VOLATILE RAM!\n"); + printf("YOU MUST USE SOME OTHER METHOD TO OBTAIN NETWORK CONFIGURATION\n"); +} +#endif diff --git a/modules/libcom/configure/CONFIG b/modules/libcom/configure/CONFIG new file mode 100644 index 000000000..774ed251c --- /dev/null +++ b/modules/libcom/configure/CONFIG @@ -0,0 +1,44 @@ +# CONFIG - Load build configuration data +# +# Do not make changes to this file! + +ifeq ($(strip $(EPICS_HOST_ARCH)),) + $(warning EPICS_HOST_ARCH is not set.) +endif + +# Allow user to override where the build rules come from +RULES = $(EPICS_BASE) + +# RELEASE files point to other application tops +include $(TOP)/configure/RELEASE +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common +ifdef T_A +-include $(TOP)/configure/RELEASE.Common.$(T_A) +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) +endif + +ifeq ($(strip $(RULES)),) + ifeq ($(strip $(EPICS_BASE)),) + $(warning Build error: EPICS_BASE not set after including RELEASE files.) + else + $(warning Build error: EPICS_BASE set but RULES variable empty.) + endif + $(error Makefiles loaded: $(MAKEFILE_LIST)) + # Die before the include of $(CONFIG)/CONFIG below does +endif + +BUILDING_LIBCOM = DEFINED + +CONFIG = $(RULES)/configure +include $(CONFIG)/CONFIG + +# Override the Base definition: +INSTALL_LOCATION = $(TOP) + +# CONFIG_SITE files contain other build configuration settings +include $(TOP)/configure/CONFIG_SITE +-include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common +ifdef T_A + -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) + -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) +endif diff --git a/modules/libcom/configure/CONFIG_LIBCOM_MODULE b/modules/libcom/configure/CONFIG_LIBCOM_MODULE new file mode 100644 index 000000000..175606cdb --- /dev/null +++ b/modules/libcom/configure/CONFIG_LIBCOM_MODULE @@ -0,0 +1,25 @@ +#************************************************************************* +# Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# Set EPICS_LIBCOM if necessary +ifndef EPICS_LIBCOM + EPICS_LIBCOM = $(if $(BUILDING_LIBCOM),$(INSTALL_LOCATION),$(EPICS_BASE)) + + # Paths to tools built here + EPICS_LIBCOM_HOST_BIN ?= $(EPICS_LIBCOM)/bin/$(EPICS_HOST_ARCH) +endif + +# Set location of locally generated tools +YACC = $(abspath $(EPICS_LIBCOM_HOST_BIN))/antelope$(HOSTEXE) +LEX = $(abspath $(EPICS_LIBCOM_HOST_BIN))/e_flex$(HOSTEXE) \ + -S$(EPICS_LIBCOM)/include/flex.skel.static + +# Default stack size for osiThread +OSITHREAD_USE_DEFAULT_STACK = NO +OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK + +BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK)) diff --git a/modules/libcom/configure/CONFIG_LIBCOM_VERSION b/modules/libcom/configure/CONFIG_LIBCOM_VERSION new file mode 100644 index 000000000..a4fa88918 --- /dev/null +++ b/modules/libcom/configure/CONFIG_LIBCOM_VERSION @@ -0,0 +1,4 @@ +EPICS_LIBCOM_MAJOR_VERSION = 3 +EPICS_LIBCOM_MINOR_VERSION = 17 +EPICS_LIBCOM_MAINTENANCE_VERSION = 1 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 diff --git a/modules/libcom/configure/CONFIG_SITE b/modules/libcom/configure/CONFIG_SITE new file mode 100644 index 000000000..d78c7f514 --- /dev/null +++ b/modules/libcom/configure/CONFIG_SITE @@ -0,0 +1,42 @@ +# CONFIG_SITE + +# Make any application-specific changes to the EPICS build +# configuration variables in this file. +# +# Host/target specific settings can be specified in files named +# CONFIG_SITE.$(EPICS_HOST_ARCH).Common +# CONFIG_SITE.Common.$(T_A) +# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) + +# CHECK_RELEASE controls the consistency checking of the support +# applications pointed to by the RELEASE* files. +# Normally CHECK_RELEASE should be set to YES. +# Set CHECK_RELEASE to NO to disable checking completely. +# Set CHECK_RELEASE to WARN to perform consistency checking but +# continue building even if conflicts are found. +CHECK_RELEASE = YES + +# Set this when you only want to compile this application +# for a subset of the cross-compiled target architectures +# that Base is built for. +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-ppc32 + +# To install files into a location other than $(TOP) define +# INSTALL_LOCATION here. +#INSTALL_LOCATION= + +# Set this when the IOC and build host use different paths +# to the install location. This may be needed to boot from +# a Microsoft FTP server say, or on some NFS configurations. +#IOCS_APPL_TOP = + +# For application debugging purposes, override the HOST_OPT and/ +# or CROSS_OPT settings from base/configure/CONFIG_SITE +#HOST_OPT = NO +#CROSS_OPT = NO + +# These allow developers to override the CONFIG_SITE variable +# settings without having to modify the configure/CONFIG_SITE +# file itself. +-include $(TOP)/../CONFIG_SITE.local +-include $(TOP)/configure/CONFIG_SITE.local diff --git a/modules/libcom/configure/Makefile b/modules/libcom/configure/Makefile new file mode 100644 index 000000000..85a7b5843 --- /dev/null +++ b/modules/libcom/configure/Makefile @@ -0,0 +1,15 @@ +#************************************************************************* +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP = .. + +include $(TOP)/configure/CONFIG + +TARGETS = $(CONFIG_TARGETS) +CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) + +CFG += CONFIG_LIBCOM_MODULE +CFG += CONFIG_LIBCOM_VERSION + +include $(TOP)/configure/RULES diff --git a/modules/libcom/configure/RELEASE b/modules/libcom/configure/RELEASE new file mode 100644 index 000000000..819b441e7 --- /dev/null +++ b/modules/libcom/configure/RELEASE @@ -0,0 +1,38 @@ +# RELEASE - Location of external support modules +# +# IF YOU CHANGE ANY PATHS in this file or make API changes to +# any modules it refers to, you should do a "make rebuild" in +# this application's top level directory. +# +# The EPICS build process does not check dependencies against +# any files from outside the application, so it is safest to +# rebuild it completely if any modules it depends on change. +# +# Host- or target-specific settings can be given in files named +# RELEASE.$(EPICS_HOST_ARCH).Common +# RELEASE.Common.$(T_A) +# RELEASE.$(EPICS_HOST_ARCH).$(T_A) +# +# This file is parsed by both GNUmake and an EPICS Perl script, +# so it may ONLY contain definititions of paths to other support +# modules, variable definitions that are used in module paths, +# and include statements that pull in other RELEASE files. +# Variables may be used before their values have been set. +# Build variables that are NOT used in paths should be set in +# the CONFIG_SITE file. + +# Variables and paths to dependent modules: +#MODULES = /path/to/modules +#MYMODULE = $(MODULES)/my-module + +# If building the EPICS modules individually, set these: +#EPICS_BASE = $(MODULES)/core-7.0.1 + +# Set RULES here if you want to use build rules from elsewhere: +#RULES = $(MODULES)/build-rules + +# These lines allow developers to override these RELEASE settings +# without having to modify this file directly. +-include $(TOP)/../RELEASE.local +-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local +-include $(TOP)/configure/RELEASE.local diff --git a/modules/libcom/configure/RULES b/modules/libcom/configure/RULES new file mode 100644 index 000000000..6d56e14e8 --- /dev/null +++ b/modules/libcom/configure/RULES @@ -0,0 +1,6 @@ +# RULES + +include $(CONFIG)/RULES + +# Library should be rebuilt because LIBOBJS may have changed. +$(LIBNAME): ../Makefile diff --git a/modules/libcom/configure/RULES_DIRS b/modules/libcom/configure/RULES_DIRS new file mode 100644 index 000000000..3ba269dcc --- /dev/null +++ b/modules/libcom/configure/RULES_DIRS @@ -0,0 +1,2 @@ +#RULES_DIRS +include $(CONFIG)/RULES_DIRS diff --git a/modules/libcom/configure/RULES_TOP b/modules/libcom/configure/RULES_TOP new file mode 100644 index 000000000..2b8cbc6da --- /dev/null +++ b/modules/libcom/configure/RULES_TOP @@ -0,0 +1,2 @@ +#RULES_TOP +include $(CONFIG)/RULES_TOP diff --git a/modules/libcom/src/Com.rc b/modules/libcom/src/Com.rc new file mode 100644 index 000000000..d5a5562b7 --- /dev/null +++ b/modules/libcom/src/Com.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Common Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Common Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "Com\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "Com.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/modules/libcom/src/Makefile b/modules/libcom/src/Makefile new file mode 100644 index 000000000..13d1409b8 --- /dev/null +++ b/modules/libcom/src/Makefile @@ -0,0 +1,84 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP = .. +include $(TOP)/configure/CONFIG + +# Uncomment this to remove the (benign) valgrind helper stubs +#USR_CFLAGS += -DNVALGRIND + +LIBCOM = $(TOP)/src + +INC += valgrind/valgrind.h + +INC += libComVersion.h +INC += libComVersionNum.h + +include $(LIBCOM)/as/Makefile +include $(LIBCOM)/bucketLib/Makefile +include $(LIBCOM)/calc/Makefile +include $(LIBCOM)/cvtFast/Makefile +include $(LIBCOM)/cppStd/Makefile +include $(LIBCOM)/cxxTemplates/Makefile +include $(LIBCOM)/dbmf/Makefile +include $(LIBCOM)/ellLib/Makefile +include $(LIBCOM)/env/Makefile +include $(LIBCOM)/error/Makefile +include $(LIBCOM)/fdmgr/Makefile +include $(LIBCOM)/flex/Makefile +include $(LIBCOM)/freeList/Makefile +include $(LIBCOM)/gpHash/Makefile +include $(LIBCOM)/iocsh/Makefile +include $(LIBCOM)/log/Makefile +include $(LIBCOM)/macLib/Makefile +include $(LIBCOM)/misc/Makefile +include $(LIBCOM)/osi/Makefile +include $(LIBCOM)/pool/Makefile +include $(LIBCOM)/ring/Makefile +include $(LIBCOM)/taskwd/Makefile +include $(LIBCOM)/timer/Makefile +include $(LIBCOM)/yacc/Makefile +include $(LIBCOM)/yajl/Makefile + +# Library to build: +LIBRARY=Com + +Com_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 + +Com_RCS = Com.rc + +ifeq ($(T_A),$(EPICS_HOST_ARCH)) + # Antelope & flex are needed to finish libCom + DELAY_INSTALL_LIBS = YES +endif + +EXPANDVARS += EPICS_LIBCOM_MAJOR_VERSION +EXPANDVARS += EPICS_LIBCOM_MINOR_VERSION +EXPANDVARS += EPICS_LIBCOM_MAINTENANCE_VERSION +EXPANDVARS += EPICS_LIBCOM_DEVELOPMENT_FLAG + +EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))") + +# shared library ABI version. +SHRLIB_VERSION = 3.17.0 + +include $(TOP)/configure/RULES + +include $(LIBCOM)/as/RULES +include $(LIBCOM)/env/RULES +include $(LIBCOM)/error/RULES +include $(LIBCOM)/flex/RULES +include $(LIBCOM)/misc/RULES +include $(LIBCOM)/osi/RULES +include $(LIBCOM)/yajl/RULES + +# Can't use EXPAND as generated headers must appear +# in O.Common, but EXPAND emits rules for O.$(T_A) +../O.Common/libComVersionNum.h: ../libComVersionNum.h@ + $(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ diff --git a/modules/libcom/src/as/Makefile b/modules/libcom/src/as/Makefile new file mode 100644 index 000000000..65f752018 --- /dev/null +++ b/modules/libcom/src/as/Makefile @@ -0,0 +1,22 @@ +#************************************************************************* +# Copyright (c) 2010 Brookhaven Science Associates, as Operator of +# Brookhaven National Lab. +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************ + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/as + +INC += asLib.h +INC += asTrapWrite.h + +Com_SRCS += asLib.c +Com_SRCS += asTrapWrite.c + +CLEANS += asLib.c asLib_lex.c diff --git a/modules/libcom/src/as/RULES b/modules/libcom/src/as/RULES new file mode 100644 index 000000000..f693cf37d --- /dev/null +++ b/modules/libcom/src/as/RULES @@ -0,0 +1,21 @@ +#************************************************************************* +# Copyright (c) 2010 Brookhaven Science Associates, as Operator of +# Brookhaven National Lab. +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************ + +# This is a Makefile fragment, see src/libCom/Makefile. + +# Extra rule since asLib_lex.c is included by asLib.c +asLib$(DEP): asLib_lex.c +asLib.c: asLib_lex.c + +# Ensure that lexer and parser are built before they are needed +asLib.c: $(INSTALL_HOST_BIN)/antelope$(HOSTEXE) +asLib_lex.c: $(INSTALL_HOST_BIN)/e_flex$(HOSTEXE) +asLib_lex.c: $(INSTALL_INCLUDE)/flex.skel.static diff --git a/modules/libcom/src/as/asLib.h b/modules/libcom/src/as/asLib.h new file mode 100644 index 000000000..95fed7a9a --- /dev/null +++ b/modules/libcom/src/as/asLib.h @@ -0,0 +1,244 @@ +/* asLib.h */ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 09-27-93*/ + +#ifndef INCasLibh +#define INCasLibh + +#include "shareLib.h" +#include "ellLib.h" +#include "errMdef.h" +#include "errlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct asgMember *ASMEMBERPVT; +typedef struct asgClient *ASCLIENTPVT; +typedef int (*ASINPUTFUNCPTR)(char *buf,int max_size); +typedef enum{ + asClientCOAR /*Change of access rights*/ + /*For now this is all*/ +} asClientStatus; + +typedef void (*ASCLIENTCALLBACK) (ASCLIENTPVT,asClientStatus); + +/* The following routines are macros with the following syntax +long asCheckGet(ASCLIENTPVT asClientPvt); +long asCheckPut(ASCLIENTPVT asClientPvt); +*/ +#define asCheckGet(asClientPvt) \ + (!asActive || ((asClientPvt)->access >= asREAD)) +#define asCheckPut(asClientPvt) \ + (!asActive || ((asClientPvt)->access >= asWRITE)) + +/* More convenience macros +void *asTrapWriteWithData(ASCLIENTPVT asClientPvt, + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data); +void asTrapWriteAfter(ASCLIENTPVT asClientPvt); +*/ +#define asTrapWriteWithData(asClientPvt, user, host, addr, type, count, data) \ + ((asActive && (asClientPvt)->trapMask) \ + ? asTrapWriteBeforeWithData((user), (host), (addr), (type), (count), (data)) \ + : 0) +#define asTrapWriteAfter(pvt) \ + if (pvt) asTrapWriteAfterWrite(pvt) + +/* This macro is for backwards compatibility, upgrade any code + calling it to use asTrapWriteWithData() instead ASAP: +void *asTrapWriteBefore(ASCLIENTPVT asClientPvt, + const char *userid, const char *hostid, void *addr); +*/ +#define asTrapWriteBefore(asClientPvt, user, host, addr) \ + asTrapWriteWithData(asClientPvt, user, host, addr, 0, 0, NULL) + + +epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); +epicsShareFunc long epicsShareAPI asInitFile( + const char *filename,const char *substitutions); +epicsShareFunc long epicsShareAPI asInitFP(FILE *fp,const char *substitutions); +/*caller must provide permanent storage for asgName*/ +epicsShareFunc long epicsShareAPI asAddMember( + ASMEMBERPVT *asMemberPvt,const char *asgName); +epicsShareFunc long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt); +/*caller must provide permanent storage for newAsgName*/ +epicsShareFunc long epicsShareAPI asChangeGroup( + ASMEMBERPVT *asMemberPvt,const char *newAsgName); +epicsShareFunc void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt); +epicsShareFunc void epicsShareAPI asPutMemberPvt( + ASMEMBERPVT asMemberPvt,void *userPvt); +/*client must provide permanent storage for user and host*/ +epicsShareFunc long epicsShareAPI asAddClient( + ASCLIENTPVT *asClientPvt,ASMEMBERPVT asMemberPvt, + int asl,const char *user,char *host); +/*client must provide permanent storage for user and host*/ +epicsShareFunc long epicsShareAPI asChangeClient( + ASCLIENTPVT asClientPvt,int asl,const char *user,char *host); +epicsShareFunc long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt); +epicsShareFunc void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt); +epicsShareFunc void epicsShareAPI asPutClientPvt( + ASCLIENTPVT asClientPvt,void *userPvt); +epicsShareFunc long epicsShareAPI asRegisterClientCallback( + ASCLIENTPVT asClientPvt, ASCLIENTCALLBACK pcallback); +epicsShareFunc long epicsShareAPI asComputeAllAsg(void); +/* following declared below after ASG is declared +epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); +*/ +epicsShareFunc long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt); +epicsShareFunc int epicsShareAPI asDump( + void (*memcallback)(ASMEMBERPVT,FILE *), + void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); +epicsShareFunc int epicsShareAPI asDumpFP(FILE *fp, + void (*memcallback)(ASMEMBERPVT,FILE *), + void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); +epicsShareFunc int epicsShareAPI asDumpUag(const char *uagname); +epicsShareFunc int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname); +epicsShareFunc int epicsShareAPI asDumpHag(const char *hagname); +epicsShareFunc int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname); +epicsShareFunc int epicsShareAPI asDumpRules(const char *asgname); +epicsShareFunc int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname); +epicsShareFunc int epicsShareAPI asDumpMem(const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients); +epicsShareFunc int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients); +epicsShareFunc int epicsShareAPI asDumpHash(void); +epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp); + +epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWithData( + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data); + +epicsShareFunc void epicsShareAPI asTrapWriteAfterWrite(void *pvt); + +#define S_asLib_clientsExist (M_asLib| 1) /*Client Exists*/ +#define S_asLib_noUag (M_asLib| 2) /*User Access Group does not exist*/ +#define S_asLib_noHag (M_asLib| 3) /*Host Access Group does not exist*/ +#define S_asLib_noAccess (M_asLib| 4) /*access security: no access allowed*/ +#define S_asLib_noModify (M_asLib| 5) /*access security: no modification allowed*/ +#define S_asLib_badConfig (M_asLib| 6) /*access security: bad configuration file*/ +#define S_asLib_badCalc (M_asLib| 7) /*access security: bad calculation espression*/ +#define S_asLib_dupAsg (M_asLib| 8) /*Duplicate Access Security Group */ +#define S_asLib_InitFailed (M_asLib| 9) /*access security: Init failed*/ +#define S_asLib_asNotActive (M_asLib|10) /*access security is not active*/ +#define S_asLib_badMember (M_asLib|11) /*access security: bad ASMEMBERPVT*/ +#define S_asLib_badClient (M_asLib|12) /*access security: bad ASCLIENTPVT*/ +#define S_asLib_badAsg (M_asLib|13) /*access security: bad ASG*/ +#define S_asLib_noMemory (M_asLib|14) /*access security: no Memory */ + +/*Private declarations */ +epicsShareExtern int asActive; + +/* definition of access rights*/ +typedef enum{asNOACCESS,asREAD,asWRITE} asAccessRights; + +struct gphPvt; + +/*Base pointers for access security*/ +typedef struct asBase{ + ELLLIST uagList; + ELLLIST hagList; + ELLLIST asgList; + struct gphPvt *phash; +} ASBASE; + +epicsShareExtern volatile ASBASE *pasbase; + +/*Defs for User Access Groups*/ +typedef struct{ + ELLNODE node; + char *user; +} UAGNAME; +typedef struct uag{ + ELLNODE node; + char *name; + ELLLIST list; /*list of UAGNAME*/ +} UAG; +/*Defs for Host Access Groups*/ +typedef struct{ + ELLNODE node; + char *host; +} HAGNAME; +typedef struct hag{ + ELLNODE node; + char *name; + ELLLIST list; /*list of HAGNAME*/ +} HAG; +/*Defs for Access SecurityGroups*/ +typedef struct { + ELLNODE node; + UAG *puag; +}ASGUAG; +typedef struct { + ELLNODE node; + HAG *phag; +}ASGHAG; +#define AS_TRAP_WRITE 1 +typedef struct{ + ELLNODE node; + asAccessRights access; + int level; + unsigned long inpUsed; /*bitmap of which inputs are used*/ + int result; /*Result of calc converted to TRUE/FALSE*/ + char *calc; + void *rpcl; + ELLLIST uagList; /*List of ASGUAG*/ + ELLLIST hagList; /*List of ASGHAG*/ + int trapMask; +} ASGRULE; +typedef struct{ + ELLNODE node; + char *inp; + void *capvt; + struct asg *pasg; + int inpIndex; +}ASGINP; + +typedef struct asg{ + ELLNODE node; + char *name; + ELLLIST inpList; + ELLLIST ruleList; + ELLLIST memberList; + double *pavalue; /*pointer to array of input values*/ + unsigned long inpBad; /*bitmap of which inputs are bad*/ + unsigned long inpChanged; /*bitmap of inputs that changed*/ +} ASG; +typedef struct asgMember { + ELLNODE node; + ASG *pasg; + ELLLIST clientList; + const char *asgName; + void *userPvt; +} ASGMEMBER; + +typedef struct asgClient { + ELLNODE node; + ASGMEMBER *pasgMember; + const char *user; + char *host; + void *userPvt; + ASCLIENTCALLBACK pcallback; + int level; + asAccessRights access; + int trapMask; +} ASGCLIENT; + +epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); +/*following is "friend" function*/ +epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size); +epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str); +epicsShareFunc void asFreeAll(ASBASE *pasbase); +#ifdef __cplusplus +} +#endif + +#endif /*INCasLibh*/ diff --git a/modules/libcom/src/as/asLib.y b/modules/libcom/src/as/asLib.y new file mode 100644 index 000000000..55d48ebad --- /dev/null +++ b/modules/libcom/src/as/asLib.y @@ -0,0 +1,230 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +%{ +static int yyerror(char *); +static int yy_start; +#include "asLibRoutines.c" +static int yyFailed = FALSE; +static int line_num=1; +static UAG *yyUag=NULL; +static HAG *yyHag=NULL; +static ASG *yyAsg=NULL; +static ASGRULE *yyAsgRule=NULL; +%} + +%start asconfig + +%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC +%token tokenINP +%token tokenINTEGER +%token tokenSTRING + +%union +{ + int Int; + char *Str; +} + +%% + +asconfig: asconfig asconfig_item + | asconfig_item + +asconfig_item: tokenUAG uag_head uag_body + | tokenUAG uag_head + | tokenHAG hag_head hag_body + | tokenHAG hag_head + | tokenASG asg_head asg_body + | tokenASG asg_head + ; + +uag_head: '(' tokenSTRING ')' + { + yyUag = asUagAdd($2); + if(!yyUag) yyerror(""); + free((void *)$2); + } + ; + +uag_body: '{' uag_user_list '}' + { + ; + } + ; + +uag_user_list: uag_user_list ',' uag_user_list_name + | uag_user_list_name + ; + +uag_user_list_name: tokenSTRING + { + if (asUagAddUser(yyUag,$1)) + yyerror(""); + free((void *)$1); + } + ; + +hag_head: '(' tokenSTRING ')' + { + yyHag = asHagAdd($2); + if(!yyHag) yyerror(""); + free((void *)$2); + } + ; + +hag_body: '{' hag_host_list '}' + ; + +hag_host_list: hag_host_list ',' hag_host_list_name + | hag_host_list_name + ; + +hag_host_list_name: tokenSTRING + { + if (asHagAddHost(yyHag,$1)) + yyerror(""); + free((void *)$1); + } + ; + +asg_head: '(' tokenSTRING ')' + { + yyAsg = asAsgAdd($2); + if(!yyAsg) yyerror(""); + free((void *)$2); + } + ; + +asg_body: '{' asg_body_list '}' + { + } + +asg_body_list: asg_body_list asg_body_item + | asg_body_item + +asg_body_item: inp_config | rule_config + ; + +inp_config: tokenINP '(' tokenSTRING ')' + { + if (asAsgAddInp(yyAsg,$3,$1)) + yyerror(""); + free((void *)$3); + } + ; + +rule_config: tokenRULE rule_head rule_body + | tokenRULE rule_head + +rule_head: rule_head_manditory rule_head_options + +rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING + { + asAccessRights rights; + + if((strcmp($4,"NONE")==0)) { + rights=asNOACCESS; + } else if((strcmp($4,"READ")==0)) { + rights=asREAD; + } else if((strcmp($4,"WRITE")==0)) { + rights=asWRITE; + } else { + yyerror("Access rights must be NONE, READ or WRITE"); + rights = asNOACCESS; + } + yyAsgRule = asAsgAddRule(yyAsg,rights,$2); + free((void *)$4); + } + ; + +rule_head_options: ')' + | rule_log_options + +rule_log_options: ',' tokenSTRING ')' + { + if((strcmp($2,"TRAPWRITE")==0)) { + long status; + status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE); + if(status) yyerror(""); + } else if((strcmp($2,"NOTRAPWRITE")!=0)) { + yyerror("Log options must be TRAPWRITE or NOTRAPWRITE"); + } + free((void *)$2); + } + ; + +rule_body: '{' rule_list '}' + ; + +rule_list: rule_list rule_list_item + | rule_list_item + ; + +rule_list_item: tokenUAG '(' rule_uag_list ')' + | tokenHAG '(' rule_hag_list ')' + | tokenCALC '(' tokenSTRING ')' + { + if (asAsgRuleCalc(yyAsgRule,$3)) + yyerror(""); + free((void *)$3); + } + ; + +rule_uag_list: rule_uag_list ',' rule_uag_list_name + | rule_uag_list_name + ; + +rule_uag_list_name: tokenSTRING + { + if (asAsgRuleUagAdd(yyAsgRule,$1)) + yyerror(""); + free((void *)$1); + } + ; + +rule_hag_list: rule_hag_list ',' rule_hag_list_name + | rule_hag_list_name + ; + +rule_hag_list_name: tokenSTRING + { + if (asAsgRuleHagAdd(yyAsgRule,$1)) + yyerror(""); + free((void *)$1); + } + ; +%% + +#include "asLib_lex.c" + +static int yyerror(char *str) +{ + if (strlen(str)) + errlogPrintf("%s at line %d\n", str, line_num); + else + errlogPrintf("Error at line %d\n", line_num); + yyFailed = TRUE; + return 0; +} +static int myParse(ASINPUTFUNCPTR inputfunction) +{ + static int FirstFlag = 1; + int rtnval; + + my_yyinput = &inputfunction; + if (!FirstFlag) { + line_num=1; + yyFailed = FALSE; + yyreset(); + yyrestart(NULL); + } + FirstFlag = 0; + rtnval = yyparse(); + if(rtnval!=0 || yyFailed) return(-1); else return(0); +} diff --git a/modules/libcom/src/as/asLibRoutines.c b/modules/libcom/src/as/asLibRoutines.c new file mode 100644 index 000000000..e7f1d90bf --- /dev/null +++ b/modules/libcom/src/as/asLibRoutines.c @@ -0,0 +1,1352 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 10-15-93 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "dbDefs.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "gpHash.h" +#include "freeList.h" +#include "macLib.h" +#include "postfix.h" +#include "asLib.h" + +static epicsMutexId asLock; +#define LOCK epicsMutexMustLock(asLock) +#define UNLOCK epicsMutexUnlock(asLock) + +/*following must be global because asCa nneeds it*/ +epicsShareDef ASBASE volatile *pasbase=NULL; +static ASBASE *pasbasenew=NULL; +epicsShareDef int asActive = FALSE; + +static void *freeListPvt = NULL; + + +#define DEFAULT "DEFAULT" + +/* Defined in asLib.y */ +static int myParse(ASINPUTFUNCPTR inputfunction); + +/*private routines */ +static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName); +static long asComputeAllAsgPvt(void); +static long asComputeAsgPvt(ASG *pasg); +static long asComputePvt(ASCLIENTPVT asClientPvt); +static UAG *asUagAdd(const char *uagName); +static long asUagAddUser(UAG *puag,const char *user); +static HAG *asHagAdd(const char *hagName); +static long asHagAddHost(HAG *phag,const char *host); +static ASG *asAsgAdd(const char *asgName); +static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex); +static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level); +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask); +static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name); +static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name); +static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc); + +/* + asInitialize can be called while access security is already active. + This is accomplished by doing the following: + + The version pointed to by pasbase is kept as is but locked against changes + A new version is created and pointed to by pasbasenew + If anything goes wrong. The original version is kept. This results is some + wasted space but at least things still work. + If the new access security configuration is successfully read then: + the old memberList is moved from old to new. + the old structures are freed. +*/ +static void asInitializeOnce(void *arg) +{ + asLock = epicsMutexMustCreate(); +} +long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction) +{ + ASG *pasg; + long status; + ASBASE *pasbaseold; + GPHENTRY *pgphentry; + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + static epicsThreadOnceId asInitializeOnceFlag = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,(void *)0); + LOCK; + pasbasenew = asCalloc(1,sizeof(ASBASE)); + if(!freeListPvt) freeListInitPvt(&freeListPvt,sizeof(ASGCLIENT),20); + ellInit(&pasbasenew->uagList); + ellInit(&pasbasenew->hagList); + ellInit(&pasbasenew->asgList); + asAsgAdd(DEFAULT); + status = myParse(inputfunction); + if(status) { + status = S_asLib_badConfig; + /*Not safe to call asFreeAll */ + UNLOCK; + return(status); + } + pasg = (ASG *)ellFirst(&pasbasenew->asgList); + while(pasg) { + pasg->pavalue = asCalloc(CALCPERFORM_NARGS, sizeof(double)); + pasg = (ASG *)ellNext(&pasg->node); + } + gphInitPvt(&pasbasenew->phash, 256); + /*Hash each uagname and each hagname*/ + puag = (UAG *)ellFirst(&pasbasenew->uagList); + while(puag) { + puagname = (UAGNAME *)ellFirst(&puag->list); + while(puagname) { + pgphentry = gphAdd(pasbasenew->phash,puagname->user,puag); + if(!pgphentry) { + errlogPrintf("Duplicated user '%s' in UAG '%s'\n", + puagname->user, puag->name); + } + puagname = (UAGNAME *)ellNext(&puagname->node); + } + puag = (UAG *)ellNext(&puag->node); + } + phag = (HAG *)ellFirst(&pasbasenew->hagList); + while(phag) { + phagname = (HAGNAME *)ellFirst(&phag->list); + while(phagname) { + pgphentry = gphAdd(pasbasenew->phash,phagname->host,phag); + if(!pgphentry) { + errlogPrintf("Duplicated host '%s' in HAG '%s'\n", + phagname->host, phag->name); + } + phagname = (HAGNAME *)ellNext(&phagname->node); + } + phag = (HAG *)ellNext(&phag->node); + } + pasbaseold = (ASBASE *)pasbase; + pasbase = (ASBASE volatile *)pasbasenew; + if(pasbaseold) { + ASG *poldasg; + ASGMEMBER *poldmem; + ASGMEMBER *pnextoldmem; + + poldasg = (ASG *)ellFirst(&pasbaseold->asgList); + while(poldasg) { + poldmem = (ASGMEMBER *)ellFirst(&poldasg->memberList); + while(poldmem) { + pnextoldmem = (ASGMEMBER *)ellNext(&poldmem->node); + ellDelete(&poldasg->memberList,&poldmem->node); + status = asAddMemberPvt(&poldmem,poldmem->asgName); + poldmem = pnextoldmem; + } + poldasg = (ASG *)ellNext(&poldasg->node); + } + asFreeAll(pasbaseold); + } + asActive = TRUE; + UNLOCK; + return(0); +} + +long epicsShareAPI asInitFile(const char *filename,const char *substitutions) +{ + FILE *fp; + long status; + + fp = fopen(filename,"r"); + if(!fp) { + errlogPrintf("asInitFile: Can't open file '%s'\n", filename); + return(S_asLib_badConfig); + } + status = asInitFP(fp,substitutions); + if(fclose(fp)==EOF) { + errMessage(0,"asInitFile: fclose failed!"); + if(!status) status = S_asLib_badConfig; + } + return(status); +} + +#define BUF_SIZE 200 +static char *my_buffer; +static char *my_buffer_ptr; +static FILE *stream; +static char *mac_input_buffer=NULL; +static MAC_HANDLE *macHandle = NULL; + +static int myInputFunction(char *buf, int max_size) +{ + int l,n; + char *fgetsRtn; + + if(*my_buffer_ptr==0) { + if(macHandle) { + fgetsRtn = fgets(mac_input_buffer,BUF_SIZE,stream); + if(fgetsRtn) { + n = macExpandString(macHandle,mac_input_buffer, + my_buffer,BUF_SIZE); + if(n<0) { + errlogPrintf("access security: macExpandString failed\n" + "input line: %s\n",mac_input_buffer); + return(0); + } + } + } else { + fgetsRtn = fgets(my_buffer,BUF_SIZE,stream); + } + if(fgetsRtn==NULL) return(0); + my_buffer_ptr = my_buffer; + } + l = strlen(my_buffer_ptr); + n = (l<=max_size ? l : max_size); + memcpy(buf,my_buffer_ptr,n); + my_buffer_ptr += n; + return(n); +} + +long epicsShareAPI asInitFP(FILE *fp,const char *substitutions) +{ + char buffer[BUF_SIZE]; + char mac_buffer[BUF_SIZE]; + long status; + char **macPairs; + + buffer[0] = 0; + my_buffer = buffer; + my_buffer_ptr = my_buffer; + stream = fp; + if(substitutions) { + if((status = macCreateHandle(&macHandle,NULL))) { + errMessage(status,"asInitFP: macCreateHandle error"); + return(status); + } + macParseDefns(macHandle,substitutions,&macPairs); + if(macPairs ==NULL) { + macDeleteHandle(macHandle); + macHandle = NULL; + } else { + macInstallMacros(macHandle,macPairs); + free(macPairs); + mac_input_buffer = mac_buffer; + } + } + status = asInitialize(myInputFunction); + if(macHandle) { + macDeleteHandle(macHandle); + macHandle = NULL; + } + return(status); +} + +long epicsShareAPI asAddMember(ASMEMBERPVT *pasMemberPvt,const char *asgName) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asAddMemberPvt(pasMemberPvt,asgName); + UNLOCK; + return(status); +} + +long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt) +{ + ASGMEMBER *pasgmember; + + if(!asActive) return(S_asLib_asNotActive); + pasgmember = *asMemberPvt; + if(!pasgmember) return(S_asLib_badMember); + LOCK; + if (ellCount(&pasgmember->clientList) > 0) { + UNLOCK; + return(S_asLib_clientsExist); + } + if(pasgmember->pasg) { + ellDelete(&pasgmember->pasg->memberList,&pasgmember->node); + } else { + errMessage(-1,"Logic error in asRemoveMember"); + UNLOCK; + return(-1); + } + free(pasgmember); + *asMemberPvt = NULL; + UNLOCK; + return(0); +} + +long epicsShareAPI asChangeGroup(ASMEMBERPVT *asMemberPvt,const char *newAsgName) +{ + ASGMEMBER *pasgmember; + long status; + + if(!asActive) return(S_asLib_asNotActive); + pasgmember = *asMemberPvt; + if(!pasgmember) return(S_asLib_badMember); + LOCK; + if(pasgmember->pasg) { + ellDelete(&pasgmember->pasg->memberList,&pasgmember->node); + } else { + errMessage(-1,"Logic error in asChangeGroup"); + UNLOCK; + return(-1); + } + status = asAddMemberPvt(asMemberPvt,newAsgName); + UNLOCK; + return(status); +} + +void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt) +{ + ASGMEMBER *pasgmember = asMemberPvt; + + if(!asActive) return(NULL); + if(!pasgmember) return(NULL); + return(pasgmember->userPvt); +} + +void epicsShareAPI asPutMemberPvt(ASMEMBERPVT asMemberPvt,void *userPvt) +{ + ASGMEMBER *pasgmember = asMemberPvt; + + if(!asActive) return; + if(!pasgmember) return; + pasgmember->userPvt = userPvt; + return; +} + +long epicsShareAPI asAddClient(ASCLIENTPVT *pasClientPvt,ASMEMBERPVT asMemberPvt, + int asl,const char *user,char *host) +{ + ASGMEMBER *pasgmember = asMemberPvt; + ASGCLIENT *pasgclient; + int len, i; + + long status; + if(!asActive) return(S_asLib_asNotActive); + if(!pasgmember) return(S_asLib_badMember); + pasgclient = freeListCalloc(freeListPvt); + if(!pasgclient) return(S_asLib_noMemory); + len = strlen(host); + for (i = 0; i < len; i++) { + host[i] = (char)tolower((int)host[i]); + } + *pasClientPvt = pasgclient; + pasgclient->pasgMember = asMemberPvt; + pasgclient->level = asl; + pasgclient->user = user; + pasgclient->host = host; + LOCK; + ellAdd(&pasgmember->clientList,&pasgclient->node); + status = asComputePvt(pasgclient); + UNLOCK; + return(status); +} + +long epicsShareAPI asChangeClient( + ASCLIENTPVT asClientPvt,int asl,const char *user,char *host) +{ + ASGCLIENT *pasgclient = asClientPvt; + long status; + int len, i; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + len = strlen(host); + for (i = 0; i < len; i++) { + host[i] = (char)tolower((int)host[i]); + } + LOCK; + pasgclient->level = asl; + pasgclient->user = user; + pasgclient->host = host; + status = asComputePvt(pasgclient); + UNLOCK; + return(status); +} + +long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt) +{ + ASGCLIENT *pasgclient = *asClientPvt; + ASGMEMBER *pasgMember; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + LOCK; + pasgMember = pasgclient->pasgMember; + if(!pasgMember) { + errMessage(-1,"asRemoveClient: No ASGMEMBER"); + UNLOCK; + return(-1); + } + ellDelete(&pasgMember->clientList,&pasgclient->node); + UNLOCK; + freeListFree(freeListPvt,pasgclient); + *asClientPvt = NULL; + return(0); +} + +long epicsShareAPI asRegisterClientCallback(ASCLIENTPVT asClientPvt, + ASCLIENTCALLBACK pcallback) +{ + ASGCLIENT *pasgclient = asClientPvt; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + LOCK; + pasgclient->pcallback = pcallback; + (*pasgclient->pcallback)(pasgclient,asClientCOAR); + UNLOCK; + return(0); +} + +void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt) +{ + ASGCLIENT *pasgclient = asClientPvt; + + if(!asActive) return(NULL); + if(!pasgclient) return(NULL); + return(pasgclient->userPvt); +} + +void epicsShareAPI asPutClientPvt(ASCLIENTPVT asClientPvt,void *userPvt) +{ + ASGCLIENT *pasgclient = asClientPvt; + if(!asActive) return; + if(!pasgclient) return; + LOCK; + pasgclient->userPvt = userPvt; + UNLOCK; +} + +long epicsShareAPI asComputeAllAsg(void) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputeAllAsgPvt(); + UNLOCK; + return(status); +} + +long epicsShareAPI asComputeAsg(ASG *pasg) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputeAsgPvt(pasg); + UNLOCK; + return(status); +} + +long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputePvt(asClientPvt); + UNLOCK; + return(status); +} + +/*The dump routines do not lock. Thus they may get inconsistant data.*/ +/*HOWEVER if they did lock and a user interrupts one of then then BAD BAD*/ +static const char *asAccessName[] = {"NONE","READ","WRITE"}; +static const char *asTrapOption[] = {"NOTRAPWRITE","TRAPWRITE"}; +static const char *asLevelName[] = {"ASL0","ASL1"}; +int epicsShareAPI asDump( + void (*memcallback)(struct asgMember *,FILE *), + void (*clientcallback)(struct asgClient *,FILE *), + int verbose) +{ + return asDumpFP(stdout,memcallback,clientcallback,verbose); +} + +int epicsShareAPI asDumpFP( + FILE *fp, + void (*memcallback)(struct asgMember *,FILE *), + void (*clientcallback)(struct asgClient *,FILE *), + int verbose) +{ + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(0); + puag = (UAG *)ellFirst(&pasbase->uagList); + if(!puag) fprintf(fp,"No UAGs\n"); + while(puag) { + fprintf(fp,"UAG(%s)",puag->name); + puagname = (UAGNAME *)ellFirst(&puag->list); + if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(puagname) { + fprintf(fp,"%s",puagname->user); + puagname = (UAGNAME *)ellNext(&puagname->node); + if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + puag = (UAG *)ellNext(&puag->node); + } + phag = (HAG *)ellFirst(&pasbase->hagList); + if(!phag) fprintf(fp,"No HAGs\n"); + while(phag) { + fprintf(fp,"HAG(%s)",phag->name); + phagname = (HAGNAME *)ellFirst(&phag->list); + if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(phagname) { + fprintf(fp,"%s",phagname->host); + phagname = (HAGNAME *)ellNext(&phagname->node); + if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + phag = (HAG *)ellNext(&phag->node); + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + int print_end_brace; + + fprintf(fp,"ASG(%s)",pasg->name); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + if(pasginp || pasgrule) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + while(pasginp) { + + fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); + if(verbose) { + if((pasg->inpBad & (1ul << pasginp->inpIndex))) + fprintf(fp," INVALID"); + else + fprintf(fp," VALID"); + fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); + } + fprintf(fp,"\n"); + pasginp = (ASGINP *)ellNext(&pasginp->node); + } + while(pasgrule) { + int print_end_brace; + + fprintf(fp,"\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasguag || pasghag || pasgrule->calc) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + if(pasguag) fprintf(fp,"\t\tUAG("); + while(pasguag) { + fprintf(fp,"%s",pasguag->puag->name); + pasguag = (ASGUAG *)ellNext(&pasguag->node); + if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasghag) fprintf(fp,"\t\tHAG("); + while(pasghag) { + fprintf(fp,"%s",pasghag->phag->name); + pasghag = (ASGHAG *)ellNext(&pasghag->node); + if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); + } + if(pasgrule->calc) { + fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); + if(verbose) + fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); + fprintf(fp,"\n"); + } + if(print_end_brace) fprintf(fp,"\t}\n"); + pasgrule = (ASGRULE *)ellNext(&pasgrule->node); + } + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + if(!verbose) pasgmember = NULL; + if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); + while(pasgmember) { + if(strlen(pasgmember->asgName)==0) + fprintf(fp,"\t\t"); + else + fprintf(fp,"\t\t%s",pasgmember->asgName); + if(memcallback) memcallback(pasgmember,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + fprintf(fp,"\t\t\t %s %s",pasgclient->user,pasgclient->host); + if(pasgclient->level>=0 && pasgclient->level<=1) + fprintf(fp," %s",asLevelName[pasgclient->level]); + else + fprintf(fp," Illegal Level %d",pasgclient->level); + if(pasgclient->access<=2) + fprintf(fp," %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); + else + fprintf(fp," Illegal Access %d",pasgclient->access); + if(clientcallback) clientcallback(pasgclient,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); + } + pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); + } + if(print_end_brace) fprintf(fp,"}\n"); + pasg = (ASG *)ellNext(&pasg->node); + } + return(0); +} + +int epicsShareAPI asDumpUag(const char *uagname) +{ + return asDumpUagFP(stdout,uagname); +} + +int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname) +{ + UAG *puag; + UAGNAME *puagname; + + if(!asActive) return(0); + puag = (UAG *)ellFirst(&pasbase->uagList); + if(!puag) fprintf(fp,"No UAGs\n"); + while(puag) { + if(uagname && strcmp(uagname,puag->name)!=0) { + puag = (UAG *)ellNext(&puag->node); + continue; + } + fprintf(fp,"UAG(%s)",puag->name); + puagname = (UAGNAME *)ellFirst(&puag->list); + if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(puagname) { + fprintf(fp,"%s",puagname->user); + puagname = (UAGNAME *)ellNext(&puagname->node); + if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + puag = (UAG *)ellNext(&puag->node); + } + return(0); +} + +int epicsShareAPI asDumpHag(const char *hagname) +{ + return asDumpHagFP(stdout,hagname); +} + +int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname) +{ + HAG *phag; + HAGNAME *phagname; + + if(!asActive) return(0); + phag = (HAG *)ellFirst(&pasbase->hagList); + if(!phag) fprintf(fp,"No HAGs\n"); + while(phag) { + if(hagname && strcmp(hagname,phag->name)!=0) { + phag = (HAG *)ellNext(&phag->node); + continue; + } + fprintf(fp,"HAG(%s)",phag->name); + phagname = (HAGNAME *)ellFirst(&phag->list); + if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(phagname) { + fprintf(fp,"%s",phagname->host); + phagname = (HAGNAME *)ellNext(&phagname->node); + if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + phag = (HAG *)ellNext(&phag->node); + } + return(0); +} + +int epicsShareAPI asDumpRules(const char *asgname) +{ + return asDumpRulesFP(stdout,asgname); +} + +int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname) +{ + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + + if(!asActive) return(0); + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + int print_end_brace; + + if(asgname && strcmp(asgname,pasg->name)!=0) { + pasg = (ASG *)ellNext(&pasg->node); + continue; + } + fprintf(fp,"ASG(%s)",pasg->name); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + if(pasginp || pasgrule) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + while(pasginp) { + + fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); + if ((pasg->inpBad & (1ul << pasginp->inpIndex))) + fprintf(fp," INVALID"); + fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); + fprintf(fp,"\n"); + pasginp = (ASGINP *)ellNext(&pasginp->node); + } + while(pasgrule) { + int print_end_brace; + + fprintf(fp,"\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasguag || pasghag || pasgrule->calc) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + if(pasguag) fprintf(fp,"\t\tUAG("); + while(pasguag) { + fprintf(fp,"%s",pasguag->puag->name); + pasguag = (ASGUAG *)ellNext(&pasguag->node); + if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasghag) fprintf(fp,"\t\tHAG("); + while(pasghag) { + fprintf(fp,"%s",pasghag->phag->name); + pasghag = (ASGHAG *)ellNext(&pasghag->node); + if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); + } + if(pasgrule->calc) { + fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); + fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); + fprintf(fp,"\n"); + } + if(print_end_brace) fprintf(fp,"\t}\n"); + pasgrule = (ASGRULE *)ellNext(&pasgrule->node); + } + if(print_end_brace) fprintf(fp,"}\n"); + pasg = (ASG *)ellNext(&pasg->node); + } + return(0); +} + +int epicsShareAPI asDumpMem(const char *asgname,void (*memcallback)(ASMEMBERPVT,FILE *), + int clients) +{ + return asDumpMemFP(stdout,asgname,memcallback,clients); +} + +int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients) +{ + ASG *pasg; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(0); + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + + if(asgname && strcmp(asgname,pasg->name)!=0) { + pasg = (ASG *)ellNext(&pasg->node); + continue; + } + fprintf(fp,"ASG(%s)\n",pasg->name); + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); + while(pasgmember) { + if(strlen(pasgmember->asgName)==0) + fprintf(fp,"\t\t"); + else + fprintf(fp,"\t\t%s",pasgmember->asgName); + if(memcallback) memcallback(pasgmember,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + if(!clients) pasgclient = NULL; + while(pasgclient) { + fprintf(fp,"\t\t\t %s %s", + pasgclient->user,pasgclient->host); + if(pasgclient->level>=0 && pasgclient->level<=1) + fprintf(fp," %s",asLevelName[pasgclient->level]); + else + fprintf(fp," Illegal Level %d",pasgclient->level); + if(pasgclient->access<=2) + fprintf(fp," %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); + else + fprintf(fp," Illegal Access %d",pasgclient->access); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); + } + pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); + } + pasg = (ASG *)ellNext(&pasg->node); + } + return(0); +} + +epicsShareFunc int epicsShareAPI asDumpHash(void) +{ + return asDumpHashFP(stdout); +} + +epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp) +{ + if(!asActive) return(0); + gphDumpFP(fp,pasbase->phash); + return(0); +} + +/*Start of private routines*/ +/* asCalloc is "friend" function */ +epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size) +{ + void *p; + + p=callocMustSucceed(nobj,size,"asCalloc"); + return(p); +} +epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str) +{ + size_t len = strlen((char *) str); + char *buf = asCalloc(1, len + 1); + strcpy(buf, (char *) str); + return buf; +} + +static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName) +{ + ASGMEMBER *pasgmember; + ASG *pgroup; + ASGCLIENT *pasgclient; + + if(*pasMemberPvt) { + pasgmember = *pasMemberPvt; + } else { + pasgmember = asCalloc(1,sizeof(ASGMEMBER)); + ellInit(&pasgmember->clientList); + *pasMemberPvt = pasgmember; + } + pasgmember->asgName = asgName; + pgroup = (ASG *)ellFirst(&pasbase->asgList); + while(pgroup) { + if(strcmp(pgroup->name,pasgmember->asgName)==0) goto got_it; + pgroup = (ASG *)ellNext(&pgroup->node); + } + /* Put it in DEFAULT*/ + pgroup = (ASG *)ellFirst(&pasbase->asgList); + while(pgroup) { + if(strcmp(pgroup->name,DEFAULT)==0) goto got_it; + pgroup = (ASG *)ellNext(&pgroup->node); + } + errMessage(-1,"Logic Error in asAddMember"); + return(-1); +got_it: + pasgmember->pasg = pgroup; + ellAdd(&pgroup->memberList,&pasgmember->node); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + asComputePvt((ASCLIENTPVT)pasgclient); + pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); + } + return(0); +} + +static long asComputeAllAsgPvt(void) +{ + ASG *pasg; + + if(!asActive) return(S_asLib_asNotActive); + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + asComputeAsgPvt(pasg); + pasg = (ASG *)ellNext(&pasg->node); + } + return(0); +} + +static long asComputeAsgPvt(ASG *pasg) +{ + ASGRULE *pasgrule; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(S_asLib_asNotActive); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + double result = pasgrule->result; /* set for VAL */ + long status; + + if(pasgrule->calc && (pasg->inpChanged & pasgrule->inpUsed)) { + status = calcPerform(pasg->pavalue,&result,pasgrule->rpcl); + if(status) { + pasgrule->result = 0; + errMessage(status,"asComputeAsg"); + } else { + pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0; + } + } + pasgrule = (ASGRULE *)ellNext(&pasgrule->node); + } + pasg->inpChanged = FALSE; + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + while(pasgmember) { + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + asComputePvt((ASCLIENTPVT)pasgclient); + pasgclient = (ASGCLIENT *)ellNext(&pasgclient->node); + } + pasgmember = (ASGMEMBER *)ellNext(&pasgmember->node); + } + return(0); +} + +static long asComputePvt(ASCLIENTPVT asClientPvt) +{ + asAccessRights access=asNOACCESS; + int trapMask=0; + ASGCLIENT *pasgclient = asClientPvt; + ASGMEMBER *pasgMember; + ASG *pasg; + ASGRULE *pasgrule; + asAccessRights oldaccess; + GPHENTRY *pgphentry; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + pasgMember = pasgclient->pasgMember; + if(!pasgMember) return(S_asLib_badMember); + pasg = pasgMember->pasg; + if(!pasg) return(S_asLib_badAsg); + oldaccess=pasgclient->access; + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + if(access == asWRITE) break; + if(access>=pasgrule->access) goto next_rule; + if(pasgclient->level > pasgrule->level) goto next_rule; + /*if uagList is empty then no need to check uag*/ + if(ellCount(&pasgrule->uagList)>0){ + ASGUAG *pasguag; + UAG *puag; + + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + while(pasguag) { + if((puag = pasguag->puag)) { + pgphentry = gphFind(pasbase->phash,pasgclient->user,puag); + if(pgphentry) goto check_hag; + } + pasguag = (ASGUAG *)ellNext(&pasguag->node); + } + goto next_rule; + } +check_hag: + /*if hagList is empty then no need to check hag*/ + if(ellCount(&pasgrule->hagList)>0) { + ASGHAG *pasghag; + HAG *phag; + + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + while(pasghag) { + if((phag = pasghag->phag)) { + pgphentry=gphFind(pasbase->phash,pasgclient->host,phag); + if(pgphentry) goto check_calc; + } + pasghag = (ASGHAG *)ellNext(&pasghag->node); + } + goto next_rule; + } +check_calc: + if(!pasgrule->calc + || (!(pasg->inpBad & pasgrule->inpUsed) && (pasgrule->result==1))) { + access = pasgrule->access; + trapMask = pasgrule->trapMask; + } +next_rule: + pasgrule = (ASGRULE *)ellNext(&pasgrule->node); + } + pasgclient->access = access; + pasgclient->trapMask = trapMask; + if(pasgclient->pcallback && oldaccess!=access) { + (*pasgclient->pcallback)(pasgclient,asClientCOAR); + } + return(0); +} + +void asFreeAll(ASBASE *pasbase) +{ + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + void *pnext; + + puag = (UAG *)ellFirst(&pasbase->uagList); + while(puag) { + puagname = (UAGNAME *)ellFirst(&puag->list); + while(puagname) { + pnext = ellNext(&puagname->node); + ellDelete(&puag->list,&puagname->node); + free(puagname); + puagname = pnext; + } + pnext = ellNext(&puag->node); + ellDelete(&pasbase->uagList,&puag->node); + free(puag); + puag = pnext; + } + phag = (HAG *)ellFirst(&pasbase->hagList); + while(phag) { + phagname = (HAGNAME *)ellFirst(&phag->list); + while(phagname) { + pnext = ellNext(&phagname->node); + ellDelete(&phag->list,&phagname->node); + free(phagname); + phagname = pnext; + } + pnext = ellNext(&phag->node); + ellDelete(&pasbase->hagList,&phag->node); + free(phag); + phag = pnext; + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + free(pasg->pavalue); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while(pasginp) { + pnext = ellNext(&pasginp->node); + ellDelete(&pasg->inpList,&pasginp->node); + free(pasginp); + pasginp = pnext; + } + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + free(pasgrule->calc); + free(pasgrule->rpcl); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + while(pasguag) { + pnext = ellNext(&pasguag->node); + ellDelete(&pasgrule->uagList,&pasguag->node); + free(pasguag); + pasguag = pnext; + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + while(pasghag) { + pnext = ellNext(&pasghag->node); + ellDelete(&pasgrule->hagList,&pasghag->node); + free(pasghag); + pasghag = pnext; + } + pnext = ellNext(&pasgrule->node); + ellDelete(&pasg->ruleList,&pasgrule->node); + free(pasgrule); + pasgrule = pnext; + } + pnext = ellNext(&pasg->node); + ellDelete(&pasbase->asgList,&pasg->node); + free(pasg); + pasg = pnext; + } + gphFreeMem(pasbase->phash); + free(pasbase); +} + +/*Beginning of routines called by lex code*/ +static UAG *asUagAdd(const char *uagName) +{ + UAG *pprev; + UAG *pnext; + UAG *puag; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (UAG *)ellFirst(&pasbase->uagList); + while(pnext) { + cmpvalue = strcmp(uagName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + errlogPrintf("Duplicate User Access Group named '%s'\n", uagName); + return(NULL); + } + pnext = (UAG *)ellNext(&pnext->node); + } + puag = asCalloc(1,sizeof(UAG)+strlen(uagName)+1); + ellInit(&puag->list); + puag->name = (char *)(puag+1); + strcpy(puag->name,uagName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->uagList,&puag->node); + } else { + pprev = (UAG *)ellPrevious(&pnext->node); + ellInsert(&pasbase->uagList,&pprev->node,&puag->node); + } + return(puag); +} + +static long asUagAddUser(UAG *puag,const char *user) +{ + UAGNAME *puagname; + + if(!puag) return(0); + puagname = asCalloc(1,sizeof(UAGNAME)+strlen(user)+1); + puagname->user = (char *)(puagname+1); + strcpy(puagname->user,user); + ellAdd(&puag->list,&puagname->node); + return(0); +} + +static HAG *asHagAdd(const char *hagName) +{ + HAG *pprev; + HAG *pnext; + HAG *phag; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (HAG *)ellFirst(&pasbase->hagList); + while(pnext) { + cmpvalue = strcmp(hagName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + errlogPrintf("Duplicate Host Access Group named '%s'\n", hagName); + return(NULL); + } + pnext = (HAG *)ellNext(&pnext->node); + } + phag = asCalloc(1,sizeof(HAG)+strlen(hagName)+1); + ellInit(&phag->list); + phag->name = (char *)(phag+1); + strcpy(phag->name,hagName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->hagList,&phag->node); + } else { + pprev = (HAG *)ellPrevious(&pnext->node); + ellInsert(&pasbase->hagList,&pprev->node,&phag->node); + } + return(phag); +} + +static long asHagAddHost(HAG *phag,const char *host) +{ + HAGNAME *phagname; + int len, i; + + if (!phag) return 0; + len = strlen(host); + phagname = asCalloc(1, sizeof(HAGNAME) + len + 1); + phagname->host = (char *)(phagname + 1); + for (i = 0; i < len; i++) { + phagname->host[i] = (char)tolower((int)host[i]); + } + ellAdd(&phag->list, &phagname->node); + return 0; +} + +static ASG *asAsgAdd(const char *asgName) +{ + ASG *pprev; + ASG *pnext; + ASG *pasg; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (ASG *)ellFirst(&pasbase->asgList); + while(pnext) { + cmpvalue = strcmp(asgName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + if(strcmp(DEFAULT,pnext->name)==0) { + if(ellCount(&pnext->inpList)==0 + && ellCount(&pnext->ruleList)==0) + return(pnext); + } + errlogPrintf("Duplicate Access Security Group named '%s'\n", asgName); + return(NULL); + } + pnext = (ASG *)ellNext(&pnext->node); + } + pasg = asCalloc(1,sizeof(ASG)+strlen(asgName)+1); + ellInit(&pasg->inpList); + ellInit(&pasg->ruleList); + ellInit(&pasg->memberList); + pasg->name = (char *)(pasg+1); + strcpy(pasg->name,asgName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->asgList,&pasg->node); + } else { + pprev = (ASG *)ellPrevious(&pnext->node); + ellInsert(&pasbase->asgList,&pprev->node,&pasg->node); + } + return(pasg); +} + +static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex) +{ + ASGINP *pasginp; + + if(!pasg) return(0); + pasginp = asCalloc(1,sizeof(ASGINP)+strlen(inp)+1); + pasginp->inp = (char *)(pasginp+1); + strcpy(pasginp->inp,inp); + pasginp->pasg = pasg; + pasginp->inpIndex = inpIndex; + ellAdd(&pasg->inpList,&pasginp->node); + return(0); +} + +static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level) +{ + ASGRULE *pasgrule; + + if(!pasg) return(0); + pasgrule = asCalloc(1,sizeof(ASGRULE)); + pasgrule->access = access; + pasgrule->trapMask = 0; + pasgrule->level = level; + ellInit(&pasgrule->uagList); + ellInit(&pasgrule->hagList); + ellAdd(&pasg->ruleList,&pasgrule->node); + return(pasgrule); +} + +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask) +{ + if(!pasgrule) return(0); + pasgrule->trapMask = trapMask; + return(0); +} + +static long asAsgRuleUagAdd(ASGRULE *pasgrule, const char *name) +{ + ASGUAG *pasguag; + UAG *puag; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + if (!pasgrule) + return 0; + + puag = (UAG *)ellFirst(&pasbase->uagList); + while (puag) { + if (strcmp(puag->name, name)==0) + break; + puag = (UAG *)ellNext(&puag->node); + } + if (!puag){ + errlogPrintf("No User Access Group named '%s' defined\n", name); + return S_asLib_noUag; + } + + pasguag = asCalloc(1, sizeof(ASGUAG)); + pasguag->puag = puag; + ellAdd(&pasgrule->uagList, &pasguag->node); + return 0; +} + +static long asAsgRuleHagAdd(ASGRULE *pasgrule, const char *name) +{ + ASGHAG *pasghag; + HAG *phag; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + if (!pasgrule) + return 0; + + phag = (HAG *)ellFirst(&pasbase->hagList); + while (phag) { + if (strcmp(phag->name, name)==0) + break; + phag = (HAG *)ellNext(&phag->node); + } + if (!phag){ + errlogPrintf("No Host Access Group named '%s' defined\n", name); + return S_asLib_noHag; + } + + pasghag = asCalloc(1, sizeof(ASGHAG)); + pasghag->phag = phag; + ellAdd(&pasgrule->hagList, &pasghag->node); + return 0; +} + +static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc) +{ + short err; + long status; + size_t insize; + unsigned long stores; + + if (!pasgrule) return 0; + insize = strlen(calc) + 1; + pasgrule->calc = asCalloc(1, insize); + strcpy(pasgrule->calc, calc); + pasgrule->rpcl = asCalloc(1, INFIX_TO_POSTFIX_SIZE(insize)); + status = postfix(pasgrule->calc, pasgrule->rpcl, &err); + if(status) { + free(pasgrule->calc); + free(pasgrule->rpcl); + pasgrule->calc = NULL; + pasgrule->rpcl = NULL; + status = S_asLib_badCalc; + errlogPrintf("%s in CALC expression '%s'\n", calcErrorStr(err), calc); + return status; + } + calcArgUsage(pasgrule->rpcl, &pasgrule->inpUsed, &stores); + /* Until someone proves stores are not dangerous, don't allow them */ + if (stores) { + free(pasgrule->calc); + free(pasgrule->rpcl); + pasgrule->calc = NULL; + pasgrule->rpcl = NULL; + status = S_asLib_badCalc; + errlogPrintf("Assignment operator used in CALC expression '%s'\n", + calc); + } + return(status); +} diff --git a/modules/libcom/src/as/asLib_lex.l b/modules/libcom/src/as/asLib_lex.l new file mode 100644 index 000000000..924105c14 --- /dev/null +++ b/modules/libcom/src/as/asLib_lex.l @@ -0,0 +1,94 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +newline "\n" +backslash "\\" +doublequote "\"" +comment "#" +whitespace [ \t\r] +escape {backslash}. +stringchar [^"\n\\] + +name [a-zA-Z0-9_\-+:.\[\]<>;] +digit [0-9] +punctuation [(){},] +link [A-L] + +%{ +static ASINPUTFUNCPTR *my_yyinput; +#undef YY_INPUT +#define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms)) + +static int yyreset(void) +{ + line_num=1; + BEGIN INITIAL; + return(0); +} + +%} + +%% + +UAG { return(tokenUAG); } +HAG { return(tokenHAG); } +ASG { return(tokenASG); } +RULE { return(tokenRULE); } +CALC { return(tokenCALC); } + +INP{link} { + yylval.Int = (unsigned char)yytext[3]; + yylval.Int -= 'A'; + return(tokenINP); +} + +{digit}+ { /*integer*/ + yylval.Int = atoi((char *)yytext); + return(tokenINTEGER); +} + +{name}+ { /*unquoted string*/ + yylval.Str=asStrdup(yytext); + return(tokenSTRING); +} + +{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */ + yylval.Str=asStrdup(yytext+1); + yylval.Str[strlen(yylval.Str)-1] = '\0'; + return(tokenSTRING); +} + +{doublequote}({stringchar}|{escape})*{newline} { /* bad string */ + yyerror("Newline in quoted string, closing quote missing"); +} + +{punctuation} { return(yytext[0]); } + +{newline} { line_num++; } + +{comment}.* ; +{whitespace} ; + +. { + char message[40]; + YY_BUFFER_STATE *dummy=0; + + if (isprint((int) yytext[0])) { + sprintf(message, "Invalid character '%c'", yytext[0]); + } + else { + sprintf(message, "Invalid character 0x%2.2x", yytext[0]); + } + yyerror(message); + + /*The following suppress compiler warning messages*/ + if (0) yyunput('c',(unsigned char *) message); + if (0) yy_switch_to_buffer(*dummy); +} + +%% diff --git a/modules/libcom/src/as/asTrapWrite.c b/modules/libcom/src/as/asTrapWrite.c new file mode 100644 index 000000000..544e9a211 --- /dev/null +++ b/modules/libcom/src/as/asTrapWrite.c @@ -0,0 +1,175 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*asTrapWrite.c */ +/* Author: Marty Kraimer Date: 07NOV2000 */ + +/* Matthias Clausen and Vladis Korobov at DESY + * implemented the first logging of Channel Access Puts + * This implementation uses many ideas from their implementation +*/ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "ellLib.h" +#include "freeList.h" +#include "epicsStdio.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "ellLib.h" + +#include "asLib.h" +#include "asTrapWrite.h" + +typedef struct listenerPvt { + ELLNODE node; + struct listener *plistener; + void *userPvt; +}listenerPvt; + +typedef struct listener{ + ELLNODE node; + asTrapWriteListener func; +}listener; + +typedef struct writeMessage { + ELLNODE node; + asTrapWriteMessage message; + ELLLIST listenerPvtList; +}writeMessage; + + +typedef struct asTrapWritePvt +{ + ELLLIST listenerList; + ELLLIST writeMessageList; + void *freeListWriteMessage; + void *freeListListenerPvt; + epicsMutexId lock; +}asTrapWritePvt; + +static asTrapWritePvt *pasTrapWritePvt = 0; + +static void asTrapWriteInit(void) +{ + pasTrapWritePvt = callocMustSucceed(1,sizeof(asTrapWritePvt),"asTrapWriteInit"); + ellInit(&pasTrapWritePvt->listenerList); + ellInit(&pasTrapWritePvt->writeMessageList); + freeListInitPvt( + &pasTrapWritePvt->freeListWriteMessage,sizeof(writeMessage),20); + freeListInitPvt( + &pasTrapWritePvt->freeListListenerPvt,sizeof(listenerPvt),20); + pasTrapWritePvt->lock = epicsMutexMustCreate(); +} + +asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func) +{ + listener *plistener; + if(pasTrapWritePvt==0) asTrapWriteInit(); + plistener = callocMustSucceed(1,sizeof(listener),"asTrapWriteRegisterListener"); + plistener->func = func; + epicsMutexMustLock(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->listenerList,&plistener->node); + epicsMutexUnlock(pasTrapWritePvt->lock); + return((asTrapWriteId)plistener); +} + +void epicsShareAPI asTrapWriteUnregisterListener(asTrapWriteId id) +{ + listener *plistener = (listener *)id; + writeMessage *pwriteMessage; + + if(pasTrapWritePvt==0) return; + epicsMutexMustLock(pasTrapWritePvt->lock); + pwriteMessage = (writeMessage *)ellFirst(&pasTrapWritePvt->writeMessageList); + while(pwriteMessage) { + listenerPvt *plistenerPvt + = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while(plistenerPvt) { + listenerPvt *pnext + = (listenerPvt *)ellNext(&plistenerPvt->node); + if(plistenerPvt->plistener == plistener) { + ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); + } + plistenerPvt = pnext; + } + pwriteMessage = (writeMessage *)ellNext(&pwriteMessage->node); + } + ellDelete(&pasTrapWritePvt->listenerList,&plistener->node); + free(plistener); + epicsMutexUnlock(pasTrapWritePvt->lock); +} + +void * epicsShareAPI asTrapWriteBeforeWithData( + const char *userid, const char *hostid, void *addr, + int dbrType, int no_elements, void *data) +{ + writeMessage *pwriteMessage; + listener *plistener; + + if (pasTrapWritePvt == 0 || + ellCount(&pasTrapWritePvt->listenerList) <= 0) return 0; + + pwriteMessage = (writeMessage *)freeListCalloc( + pasTrapWritePvt->freeListWriteMessage); + pwriteMessage->message.userid = userid; + pwriteMessage->message.hostid = hostid; + pwriteMessage->message.serverSpecific = addr; + pwriteMessage->message.dbrType = dbrType; + pwriteMessage->message.no_elements = no_elements; + pwriteMessage->message.data = data; + ellInit(&pwriteMessage->listenerPvtList); + + epicsMutexMustLock(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); + plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); + while (plistener) { + listenerPvt *plistenerPvt = (listenerPvt *)freeListCalloc( + pasTrapWritePvt->freeListListenerPvt); + + plistenerPvt->plistener = plistener; + pwriteMessage->message.userPvt = 0; + plistener->func(&pwriteMessage->message, 0); + plistenerPvt->userPvt = pwriteMessage->message.userPvt; + ellAdd(&pwriteMessage->listenerPvtList, &plistenerPvt->node); + plistener = (listener *)ellNext(&plistener->node); + } + epicsMutexUnlock(pasTrapWritePvt->lock); + return pwriteMessage; +} + +void epicsShareAPI asTrapWriteAfterWrite(void *pvt) +{ + writeMessage *pwriteMessage = (writeMessage *)pvt; + listenerPvt *plistenerPvt; + + if (pwriteMessage == 0 || + pasTrapWritePvt == 0) return; + + epicsMutexMustLock(pasTrapWritePvt->lock); + plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while (plistenerPvt) { + listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); + listener *plistener = plistenerPvt->plistener; + + pwriteMessage->message.userPvt = plistenerPvt->userPvt; + plistener->func(&pwriteMessage->message, 1); + ellDelete(&pwriteMessage->listenerPvtList, &plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt, plistenerPvt); + plistenerPvt = pnext; + } + ellDelete(&pasTrapWritePvt->writeMessageList, &pwriteMessage->node); + freeListFree(pasTrapWritePvt->freeListWriteMessage, pwriteMessage); + epicsMutexUnlock(pasTrapWritePvt->lock); +} diff --git a/modules/libcom/src/as/asTrapWrite.h b/modules/libcom/src/as/asTrapWrite.h new file mode 100644 index 000000000..b8033cb94 --- /dev/null +++ b/modules/libcom/src/as/asTrapWrite.h @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*asTrapWrite.h*/ +/* Author: Marty Kraimer Date: 07NOV2000 */ + +#ifndef INCasTrapWriteh +#define INCasTrapWriteh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct asTrapWriteMessage { + const char *userid; + const char *hostid; + void *serverSpecific; + void *userPvt; + int dbrType; /* Data type from ca/db_access.h, NOT dbFldTypes.h */ + int no_elements; + void *data; /* Might be NULL if no data is available */ +} asTrapWriteMessage; + + +typedef void *asTrapWriteId; +typedef void(*asTrapWriteListener)(asTrapWriteMessage *pmessage,int after); + +epicsShareFunc asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func); +epicsShareFunc void epicsShareAPI asTrapWriteUnregisterListener( + asTrapWriteId id); + +/* + * asTrapWriteListener is called before and after the write is performed. + * The listener can set userPvt on the before call and retrieve it after + * after = (0,1) (before,after) the put. + * + * Each asTrapWriteMessage can change or may be deleted after + * the user's asTrapWriteListener returns + * + * asTrapWriteListener delays the associated server thread so it must not + * do anything that causes to to block. +*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCasTrapWriteh*/ diff --git a/modules/libcom/src/bucketLib/Makefile b/modules/libcom/src/bucketLib/Makefile new file mode 100644 index 000000000..f66dc7d47 --- /dev/null +++ b/modules/libcom/src/bucketLib/Makefile @@ -0,0 +1,12 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/bucketLib +INC += bucketLib.h +Com_SRCS += bucketLib.c diff --git a/modules/libcom/src/bucketLib/bucketLib.c b/modules/libcom/src/bucketLib/bucketLib.c new file mode 100644 index 000000000..40df596a4 --- /dev/null +++ b/modules/libcom/src/bucketLib/bucketLib.c @@ -0,0 +1,522 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@atdiv.lanl.gov + * (505) 665 1831 + * Date: 9-93 + * + * NOTES: + * .01 Storage for identifier must persist until an item is deleted + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "freeList.h" /* bucketLib uses freeListLib inside the DLL */ +#include "bucketLib.h" + +/* + * these data type dependent routines are + * provided in the bucketLib.c + */ +typedef BUCKETID bucketHash(BUCKET *pb, const void *pId); +typedef ITEM **bucketCompare(ITEM **ppi, const void *pId); + +static bucketCompare bucketUnsignedCompare; +static bucketCompare bucketPointerCompare; +static bucketCompare bucketStringCompare; +static bucketHash bucketUnsignedHash; +static bucketHash bucketPointerHash; +static bucketHash bucketStringHash; + +typedef struct { + bucketHash *pHash; + bucketCompare *pCompare; + buckTypeOfId type; +}bucketSET; + +static bucketSET BSET[] = { + {bucketUnsignedHash, bucketUnsignedCompare, bidtUnsigned}, + {bucketPointerHash, bucketPointerCompare, bidtPointer}, + {bucketStringHash, bucketStringCompare, bidtString} +}; + +static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, + const void *pId, const void *pApp); +static void *bucketLookupItem(BUCKET *pb, bucketSET *pBSET, const void *pId); + + + +/* + * bucket id bit width + */ +#define BUCKETID_BIT_WIDTH (sizeof(BUCKETID)*CHAR_BIT) + +/* + * Maximum bucket size + */ +#define BUCKET_MAX_WIDTH 12 + + +/* + * bucketUnsignedCompare() + */ +static ITEM **bucketUnsignedCompare (ITEM **ppi, const void *pId) +{ + unsigned id; + unsigned *pItemId; + ITEM *pi; + + id = * (unsigned *) pId; + while ( (pi = *ppi) ) { + if (bidtUnsigned == pi->type) { + pItemId = (unsigned *) pi->pId; + if (id == *pItemId) { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketPointerCompare() + */ +static ITEM **bucketPointerCompare (ITEM **ppi, const void *pId) +{ + void *ptr; + void **pItemId; + ITEM *pi; + + ptr = * (void **) pId; + while ( (pi = *ppi) ) { + if (bidtPointer == pi->type ) { + pItemId = (void **) pi->pId; + if (ptr == *pItemId) { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketStringCompare () + */ +static ITEM **bucketStringCompare (ITEM **ppi, const void *pId) +{ + const char *pStr = pId; + ITEM *pi; + int status; + + while ( (pi = *ppi) ) { + if (bidtString == pi->type) { + status = strcmp (pStr, (char *)pi->pId); + if (status == '\0') { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketUnsignedHash () + */ +static BUCKETID bucketUnsignedHash (BUCKET *pb, const void *pId) +{ + const unsigned *pUId = (const unsigned *) pId; + unsigned src; + BUCKETID hashid; + + src = *pUId; + hashid = src; + src = src >> pb->hashIdNBits; + while (src) { + hashid = hashid ^ src; + src = src >> pb->hashIdNBits; + } + hashid = hashid & pb->hashIdMask; + + return hashid; +} + + +/* + * bucketPointerHash () + */ +static BUCKETID bucketPointerHash (BUCKET *pb, const void *pId) +{ + void * const *ppId = (void * const *) pId; + size_t src; + BUCKETID hashid; + + /* + * This makes the assumption that size_t + * can be used to hold a pointer value + * (this assumption may not port to all + * CPU architectures) + */ + src = (size_t) *ppId; + hashid = src; + src = src >> pb->hashIdNBits; + while(src){ + hashid = hashid ^ src; + src = src >> pb->hashIdNBits; + } + hashid = hashid & pb->hashIdMask; + + return hashid; +} + + +/* + * bucketStringHash () + */ +static BUCKETID bucketStringHash (BUCKET *pb, const void *pId) +{ + const char *pStr = (const char *) pId; + BUCKETID hashid; + unsigned i; + + hashid = 0; + i = 1; + while(*pStr){ + hashid += *pStr * i; + pStr++; + i++; + } + + hashid = hashid % (pb->hashIdMask+1); + + return hashid; +} + + + +/* + * bucketCreate() + */ +epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries) +{ + BUCKETID mask; + unsigned nbits; + BUCKET *pb; + + /* + * no absurd sized buckets + */ + if (nHashTableEntries<=1) { + fprintf (stderr, "Tiny bucket create failed\n"); + return NULL; + } + + /* + * count the number of bits in the bucket id + */ + if ( BUCKETID_BIT_WIDTH > 0 ) { + for (nbits=0; nbits=BUCKETID_BIT_WIDTH) { + fprintf ( + stderr, + "%s at %d: Requested index width=%d to large. max=%ld\n", + __FILE__, + __LINE__, + nbits, + (long)(BUCKETID_BIT_WIDTH-1)); + return NULL; + } + + pb = (BUCKET *) calloc(1, sizeof(*pb)); + if (!pb) { + return pb; + } + + pb->hashIdMask = mask; + pb->hashIdNBits = nbits; + freeListInitPvt(&pb->freeListPVT, sizeof(ITEM), 1024); + + pb->pTable = (ITEM **) calloc (mask+1, sizeof(*pb->pTable)); + if (!pb->pTable) { + freeListCleanup(pb->freeListPVT); + free (pb); + return NULL; + } + return pb; +} + + +/* + * bucketFree() + */ +epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb) +{ + /* + * deleting a bucket with entries in use + * will cause memory leaks and is not allowed + */ + assert (prb->nInUse==0); + + /* + * free the free list + */ + freeListCleanup(prb->freeListPVT); + free (prb->pTable); + free (prb); + + return S_bucket_success; +} + + +/* + * bucketAddItem() + */ +epicsShareFunc int epicsShareAPI + bucketAddItemUnsignedId(BUCKET *prb, const unsigned *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtUnsigned], pId, pApp); +} +epicsShareFunc int epicsShareAPI + bucketAddItemPointerId(BUCKET *prb, void * const *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtPointer], pId, pApp); +} +epicsShareFunc int epicsShareAPI + bucketAddItemStringId(BUCKET *prb, const char *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtString], pId, pApp); +} +static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, const void *pId, const void *pApp) +{ + BUCKETID hashid; + ITEM **ppi; + ITEM **ppiExists; + ITEM *pi; + + /* + * try to get it off the free list first. If + * that fails then malloc() + */ + pi = (ITEM *) freeListMalloc(prb->freeListPVT); + if (!pi) { + return S_bucket_noMemory; + } + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (prb, pId); + + pi->pApp = pApp; + pi->pId = pId; + pi->type = pBSET->type; + assert ((hashid & ~prb->hashIdMask) == 0); + ppi = &prb->pTable[hashid]; + /* + * Dont reuse a resource id ! + */ + ppiExists = (*pBSET->pCompare) (ppi, pId); + if (ppiExists) { + freeListFree(prb->freeListPVT,pi); + return S_bucket_idInUse; + } + pi->pItem = *ppi; + prb->pTable[hashid] = pi; + prb->nInUse++; + + return S_bucket_success; +} + +/* + * bucketLookupAndRemoveItem () + */ +static void *bucketLookupAndRemoveItem (BUCKET *prb, bucketSET *pBSET, const void *pId) +{ + BUCKETID hashid; + ITEM **ppi; + ITEM *pi; + void *pApp; + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (prb, pId); + + assert((hashid & ~prb->hashIdMask) == 0); + ppi = &prb->pTable[hashid]; + ppi = (*pBSET->pCompare) (ppi, pId); + if(!ppi){ + return NULL; + } + prb->nInUse--; + pi = *ppi; + *ppi = pi->pItem; + + pApp = (void *) pi->pApp; + + /* + * stuff it on the free list + */ + freeListFree(prb->freeListPVT, pi); + + return pApp; +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId); +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId); +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId); +} + + +/* + * bucketRemoveItem() + */ +epicsShareFunc int epicsShareAPI + bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId)?S_bucket_success:S_bucket_uknId; +} +epicsShareFunc int epicsShareAPI + bucketRemoveItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId)?S_bucket_success:S_bucket_uknId; +} +epicsShareFunc int epicsShareAPI + bucketRemoveItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId)?S_bucket_success:S_bucket_uknId; +} + + +/* + * bucketLookupItem() + */ +epicsShareFunc void * epicsShareAPI + bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupItem(prb, &BSET[bidtUnsigned], pId); +} +epicsShareFunc void * epicsShareAPI + bucketLookupItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupItem(prb, &BSET[bidtPointer], pId); +} +epicsShareFunc void * epicsShareAPI + bucketLookupItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupItem(prb, &BSET[bidtString], pId); +} +static void *bucketLookupItem (BUCKET *pb, bucketSET *pBSET, const void *pId) +{ + BUCKETID hashid; + ITEM **ppi; + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (pb, pId); + assert((hashid & ~pb->hashIdMask) == 0); + + /* + * at the bottom level just + * linear search for it. + */ + ppi = (*pBSET->pCompare) (&pb->pTable[hashid], pId); + if(ppi){ + return (void *) (*ppi)->pApp; + } + return NULL; +} + + + +/* + * bucketShow() + */ +epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb) +{ + ITEM **ppi; + ITEM *pi; + unsigned nElem; + double X; + double XX; + double mean; + double stdDev; + unsigned count; + unsigned maxEntries; + + printf( " Bucket entries in use = %d bytes in use = %ld\n", + pb->nInUse, + (long) (sizeof(*pb)+(pb->hashIdMask+1)* + sizeof(ITEM *)+pb->nInUse*sizeof(ITEM))); + + ppi = pb->pTable; + nElem = pb->hashIdMask+1; + X = 0.0; + XX = 0.0; + maxEntries = 0; + while (ppi < &pb->pTable[nElem]) { + pi = *ppi; + count = 0; + while (pi) { + count++; + pi = pi->pItem; + } + X += count; + XX += count*count; + if (count > maxEntries) maxEntries = count; + ppi++; + } + + mean = X/nElem; + stdDev = sqrt(XX/nElem - mean*mean); + printf( " Bucket entries/hash id - mean = %f std dev = %f max = %d\n", + mean, + stdDev, + maxEntries); + + return S_bucket_success; +} + + diff --git a/modules/libcom/src/bucketLib/bucketLib.h b/modules/libcom/src/bucketLib/bucketLib.h new file mode 100644 index 000000000..60d73b459 --- /dev/null +++ b/modules/libcom/src/bucketLib/bucketLib.h @@ -0,0 +1,91 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 9-93 + * + * NOTES: + * .01 Storage for identifier must persist until an item is deleted + */ + +#ifndef INCbucketLibh +#define INCbucketLibh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "errMdef.h" +#include "epicsTypes.h" +#include "shareLib.h" + +typedef unsigned BUCKETID; + +typedef enum {bidtUnsigned, bidtPointer, bidtString} buckTypeOfId; + +typedef struct item{ + struct item *pItem; + const void *pId; + const void *pApp; + buckTypeOfId type; +}ITEM; + +typedef struct bucket{ + ITEM **pTable; + void *freeListPVT; + unsigned hashIdMask; + unsigned hashIdNBits; + unsigned nInUse; +}BUCKET; + +epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries); +epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb); +epicsShareFunc int epicsShareAPI bucketShow (BUCKET *pb); + +/* + * !! Identifier must exist (and remain constant) at the specified address until + * the item is deleted from the bucket !! + */ +epicsShareFunc int epicsShareAPI bucketAddItemUnsignedId (BUCKET *prb, + const unsigned *pId, const void *pApp); +epicsShareFunc int epicsShareAPI bucketAddItemPointerId (BUCKET *prb, + void * const *pId, const void *pApp); +epicsShareFunc int epicsShareAPI bucketAddItemStringId (BUCKET *prb, + const char *pId, const void *pApp); + +epicsShareFunc int epicsShareAPI bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc int epicsShareAPI bucketRemoveItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc int epicsShareAPI bucketRemoveItemStringId (BUCKET *prb, const char *pId); + +epicsShareFunc void * epicsShareAPI bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc void * epicsShareAPI bucketLookupItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc void * epicsShareAPI bucketLookupItemStringId (BUCKET *prb, const char *pId); + +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId); + + +/* + * Status returned by bucketLib functions + */ +#define BUCKET_SUCCESS S_bucket_success +#define S_bucket_success 0 +#define S_bucket_noMemory (M_bucket | 1) /*Memory allocation failed*/ +#define S_bucket_idInUse (M_bucket | 2) /*Identifier already in use*/ +#define S_bucket_uknId (M_bucket | 3) /*Unknown identifier*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCbucketLibh*/ + diff --git a/modules/libcom/src/calc/Makefile b/modules/libcom/src/calc/Makefile new file mode 100644 index 000000000..493cb3245 --- /dev/null +++ b/modules/libcom/src/calc/Makefile @@ -0,0 +1,14 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/calc +INC += postfix.h +Com_SRCS += postfix.c +Com_SRCS += calcPerform.c + diff --git a/modules/libcom/src/calc/calcPerform.c b/modules/libcom/src/calc/calcPerform.c new file mode 100644 index 000000000..c0f4aebb8 --- /dev/null +++ b/modules/libcom/src/calc/calcPerform.c @@ -0,0 +1,504 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Julie Sander and Bob Dalesio + * Date: 07-27-87 + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiUnistd.h" +#include "dbDefs.h" +#include "epicsMath.h" +#include "epicsTypes.h" +#include "errlog.h" +#include "postfix.h" +#include "postfixPvt.h" + +static double calcRandom(void); +static int cond_search(const char **ppinst, int match); + +#ifndef PI +#define PI 3.14159265358979323 +#endif + +/* Turn off global optimization for 64-bit MSVC builds */ +#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +# pragma optimize("g", off) +#endif + +/* calcPerform + * + * Evalutate the postfix expression + */ +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *pinst) +{ + double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */ + double *ptop; /* stack pointer */ + double top; /* value from top of stack */ + epicsInt32 itop; /* integer from top of stack */ + epicsUInt32 utop; /* unsigned integer from top of stack */ + int op; + int nargs; + + /* initialize */ + ptop = stack; + + /* RPN evaluation loop */ + while ((op = *pinst++) != END_EXPRESSION){ + switch (op){ + + case LITERAL_DOUBLE: + memcpy(++ptop, pinst, sizeof(double)); + pinst += sizeof(double); + break; + + case LITERAL_INT: + memcpy(&itop, pinst, sizeof(epicsInt32)); + *++ptop = itop; + pinst += sizeof(epicsInt32); + break; + + case FETCH_VAL: + *++ptop = *presult; + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + *++ptop = parg[op - FETCH_A]; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + parg[op - STORE_A] = *ptop--; + break; + + case CONST_PI: + *++ptop = PI; + break; + + case CONST_D2R: + *++ptop = PI/180.; + break; + + case CONST_R2D: + *++ptop = 180./PI; + break; + + case UNARY_NEG: + *ptop = - *ptop; + break; + + case ADD: + top = *ptop--; + *ptop += top; + break; + + case SUB: + top = *ptop--; + *ptop -= top; + break; + + case MULT: + top = *ptop--; + *ptop *= top; + break; + + case DIV: + top = *ptop--; + *ptop /= top; + break; + + case MODULO: + itop = (epicsInt32) *ptop--; + if (itop) + *ptop = (epicsInt32) *ptop % itop; + else + *ptop = epicsNAN; + break; + + case POWER: + top = *ptop--; + *ptop = pow(*ptop, top); + break; + + case ABS_VAL: + *ptop = fabs(*ptop); + break; + + case EXP: + *ptop = exp(*ptop); + break; + + case LOG_10: + *ptop = log10(*ptop); + break; + + case LOG_E: + *ptop = log(*ptop); + break; + + case MAX: + nargs = *pinst++; + while (--nargs) { + top = *ptop--; + if (*ptop < top || isnan(top)) + *ptop = top; + } + break; + + case MIN: + nargs = *pinst++; + while (--nargs) { + top = *ptop--; + if (*ptop > top || isnan(top)) + *ptop = top; + } + break; + + case SQU_RT: + *ptop = sqrt(*ptop); + break; + + case ACOS: + *ptop = acos(*ptop); + break; + + case ASIN: + *ptop = asin(*ptop); + break; + + case ATAN: + *ptop = atan(*ptop); + break; + + case ATAN2: + top = *ptop--; + *ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */ + break; + + case COS: + *ptop = cos(*ptop); + break; + + case SIN: + *ptop = sin(*ptop); + break; + + case TAN: + *ptop = tan(*ptop); + break; + + case COSH: + *ptop = cosh(*ptop); + break; + + case SINH: + *ptop = sinh(*ptop); + break; + + case TANH: + *ptop = tanh(*ptop); + break; + + case CEIL: + *ptop = ceil(*ptop); + break; + + case FLOOR: + *ptop = floor(*ptop); + break; + + case FINITE: + nargs = *pinst++; + top = finite(*ptop); + while (--nargs) { + --ptop; + top = top && finite(*ptop); + } + *ptop = top; + break; + + case ISINF: + *ptop = isinf(*ptop); + break; + + case ISNAN: + nargs = *pinst++; + top = isnan(*ptop); + while (--nargs) { + --ptop; + top = top || isnan(*ptop); + } + *ptop = top; + break; + + case NINT: + top = *ptop; + *ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5); + break; + + case RANDOM: + *++ptop = calcRandom(); + break; + + case REL_OR: + top = *ptop--; + *ptop = *ptop || top; + break; + + case REL_AND: + top = *ptop--; + *ptop = *ptop && top; + break; + + case REL_NOT: + *ptop = ! *ptop; + break; + + /* For bitwise operations on values with bit 31 set, double values + * must first be cast to unsigned to correctly set that bit; the + * double value must be negative in that case. The result must be + * cast to a signed integer before converting to the double result. + */ + + case BIT_OR: + utop = *ptop--; + *ptop = (epicsInt32) ((epicsUInt32) *ptop | utop); + break; + + case BIT_AND: + utop = *ptop--; + *ptop = (epicsInt32) ((epicsUInt32) *ptop & utop); + break; + + case BIT_EXCL_OR: + utop = *ptop--; + *ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop); + break; + + case BIT_NOT: + utop = *ptop; + *ptop = (epicsInt32) ~utop; + break; + + /* The shift operators use signed integers, so a right-shift will + * extend the sign bit into the left-hand end of the value. The + * double-casting through unsigned here is important, see above. + */ + + case RIGHT_SHIFT: + utop = *ptop--; + *ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31); + break; + + case LEFT_SHIFT: + utop = *ptop--; + *ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31); + break; + + case NOT_EQ: + top = *ptop--; + *ptop = *ptop != top; + break; + + case LESS_THAN: + top = *ptop--; + *ptop = *ptop < top; + break; + + case LESS_OR_EQ: + top = *ptop--; + *ptop = *ptop <= top; + break; + + case EQUAL: + top = *ptop--; + *ptop = *ptop == top; + break; + + case GR_OR_EQ: + top = *ptop--; + *ptop = *ptop >= top; + break; + + case GR_THAN: + top = *ptop--; + *ptop = *ptop > top; + break; + + case COND_IF: + if (*ptop-- == 0.0 && + cond_search(&pinst, COND_ELSE)) return -1; + break; + + case COND_ELSE: + if (cond_search(&pinst, COND_END)) return -1; + break; + + case COND_END: + break; + + default: + errlogPrintf("calcPerform: Bad Opcode %d at %p\n", op, pinst-1); + return -1; + } + } + + /* The stack should now have one item on it, the expression value */ + if (ptop != stack + 1) + return -1; + *presult = *ptop; + return 0; +} +#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +# pragma optimize("", on) +#endif + + +epicsShareFunc long +calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) +{ + unsigned long inputs = 0; + unsigned long stores = 0; + char op; + while ((op = *pinst++) != END_EXPRESSION) { + switch (op) { + + case LITERAL_DOUBLE: + pinst += sizeof(double); + break; + case LITERAL_INT: + pinst += sizeof(epicsInt32); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + pinst++; + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + /* Don't claim to use an arg we already stored to */ + inputs |= (1 << (op - FETCH_A)) & ~stores; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + stores |= (1 << (op - STORE_A)); + break; + + default: + break; + } + } + if (pinputs) *pinputs = inputs; + if (pstores) *pstores = stores; + return 0; +} + +/* Generate a random number between 0 and 1 using the algorithm + * seed = (multy * seed) + addy Random Number Generator by Knuth + * SemiNumerical Algorithms + * Chapter 1 + * randy = seed / 65535.0 To normalize the number between 0 - 1 + */ +static unsigned short seed = 0xa3bf; +static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ +static unsigned short addy = 0x3141; + +static double calcRandom(void) +{ + seed = (seed * multy) + addy; + + /* between 0 - 1 */ + return (double) seed / 65535.0; +} + +/* Search the instruction stream for a matching operator, skipping any + * other conditional instructions found, and leave *ppinst pointing to + * the next instruction to be executed. + */ +static int cond_search(const char **ppinst, int match) +{ + const char *pinst = *ppinst; + int count = 1; + int op; + + while ((op = *pinst++) != END_EXPRESSION) { + if (op == match && --count == 0) { + *ppinst = pinst; + return 0; + } + switch (op) { + case LITERAL_DOUBLE: + pinst += sizeof(double); + break; + case LITERAL_INT: + pinst += sizeof(epicsInt32); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + pinst++; + break; + case COND_IF: + count++; + break; + } + } + return 1; +} diff --git a/modules/libcom/src/calc/postfix.c b/modules/libcom/src/calc/postfix.c new file mode 100644 index 000000000..463ceea82 --- /dev/null +++ b/modules/libcom/src/calc/postfix.c @@ -0,0 +1,626 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Subroutines used to convert an infix expression to a postfix expression + * + * Original Author: Bob Dalesio + * Date: 12-12-86 +*/ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsAssert.h" +#include "epicsStdlib.h" +#include "epicsString.h" +#include "epicsTypes.h" +#include "postfix.h" +#include "postfixPvt.h" +#include "shareLib.h" + + +/* declarations for postfix */ + +/* element types */ +typedef enum { + OPERAND, + LITERAL_OPERAND, + STORE_OPERATOR, + UNARY_OPERATOR, + VARARG_OPERATOR, + BINARY_OPERATOR, + SEPERATOR, + CLOSE_PAREN, + CONDITIONAL, + EXPR_TERMINATOR, +} element_type; + + +/* element table + * + * structure of an element + */ +typedef struct expression_element{ + char *name; /* character representation of an element */ + char in_stack_pri; /* priority on translation stack */ + char in_coming_pri; /* priority in input string */ + signed char runtime_effect; /* stack change, positive means push */ + element_type type; /* element type */ + rpn_opcode code; /* postfix opcode */ +} ELEMENT; + +/* + * NOTE: Keep these lists sorted. Elements are searched in reverse order, + * and where two names start with the same substring we must pick out the + * longest name first (hence the sort requirement). + * NOTE: All VARARG_OPERATORs have to be made known to the calcExprDump() + * routine at the end of this file. + */ +static const ELEMENT operands[] = { +/* name prio's stack element type opcode */ +{"!", 7, 8, 0, UNARY_OPERATOR, REL_NOT}, +{"(", 0, 8, 0, UNARY_OPERATOR, NOT_GENERATED}, +{"-", 7, 8, 0, UNARY_OPERATOR, UNARY_NEG}, +{".", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"0", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"0X", 0, 0, 1, LITERAL_OPERAND,LITERAL_INT}, +{"1", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"2", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"3", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"4", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"5", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"6", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"7", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"8", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"9", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"A", 0, 0, 1, OPERAND, FETCH_A}, +{"ABS", 7, 8, 0, UNARY_OPERATOR, ABS_VAL}, +{"ACOS", 7, 8, 0, UNARY_OPERATOR, ACOS}, +{"ASIN", 7, 8, 0, UNARY_OPERATOR, ASIN}, +{"ATAN", 7, 8, 0, UNARY_OPERATOR, ATAN}, +{"ATAN2", 7, 8, -1, UNARY_OPERATOR, ATAN2}, +{"B", 0, 0, 1, OPERAND, FETCH_B}, +{"C", 0, 0, 1, OPERAND, FETCH_C}, +{"CEIL", 7, 8, 0, UNARY_OPERATOR, CEIL}, +{"COS", 7, 8, 0, UNARY_OPERATOR, COS}, +{"COSH", 7, 8, 0, UNARY_OPERATOR, COSH}, +{"D", 0, 0, 1, OPERAND, FETCH_D}, +{"D2R", 0, 0, 1, OPERAND, CONST_D2R}, +{"E", 0, 0, 1, OPERAND, FETCH_E}, +{"EXP", 7, 8, 0, UNARY_OPERATOR, EXP}, +{"F", 0, 0, 1, OPERAND, FETCH_F}, +{"FINITE", 7, 8, 0, VARARG_OPERATOR,FINITE}, +{"FLOOR", 7, 8, 0, UNARY_OPERATOR, FLOOR}, +{"G", 0, 0, 1, OPERAND, FETCH_G}, +{"H", 0, 0, 1, OPERAND, FETCH_H}, +{"I", 0, 0, 1, OPERAND, FETCH_I}, +{"INF", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"ISINF", 7, 8, 0, UNARY_OPERATOR, ISINF}, +{"ISNAN", 7, 8, 0, VARARG_OPERATOR,ISNAN}, +{"J", 0, 0, 1, OPERAND, FETCH_J}, +{"K", 0, 0, 1, OPERAND, FETCH_K}, +{"L", 0, 0, 1, OPERAND, FETCH_L}, +{"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, +{"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"MAX", 7, 8, 0, VARARG_OPERATOR,MAX}, +{"MIN", 7, 8, 0, VARARG_OPERATOR,MIN}, +{"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, +{"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +{"PI", 0, 0, 1, OPERAND, CONST_PI}, +{"R2D", 0, 0, 1, OPERAND, CONST_R2D}, +{"RNDM", 0, 0, 1, OPERAND, RANDOM}, +{"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, +{"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, +{"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, +{"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, +{"VAL", 0, 0, 1, OPERAND, FETCH_VAL}, +{"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +}; + +static const ELEMENT operators[] = { +/* name prio's stack element type opcode */ +{"!=", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"#", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"%", 5, 5, -1, BINARY_OPERATOR,MODULO}, +{"&", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"&&", 2, 2, -1, BINARY_OPERATOR,REL_AND}, +{")", 0, 0, 0, CLOSE_PAREN, NOT_GENERATED}, +{"*", 5, 5, -1, BINARY_OPERATOR,MULT}, +{"**", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"+", 4, 4, -1, BINARY_OPERATOR,ADD}, +{",", 0, 0, 0, SEPERATOR, NOT_GENERATED}, +{"-", 4, 4, -1, BINARY_OPERATOR,SUB}, +{"/", 5, 5, -1, BINARY_OPERATOR,DIV}, +{":", 0, 0, -1, CONDITIONAL, COND_ELSE}, +{":=", 0, 0, -1, STORE_OPERATOR, STORE_A}, +{";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, +{"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, +{"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT}, +{"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, +{"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{">", 3, 3, -1, BINARY_OPERATOR,GR_THAN}, +{">=", 3, 3, -1, BINARY_OPERATOR,GR_OR_EQ}, +{">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT}, +{"?", 0, 0, -1, CONDITIONAL, COND_IF}, +{"AND", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"OR", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"XOR", 1, 1, -1, BINARY_OPERATOR,BIT_EXCL_OR}, +{"^", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"|", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"||", 1, 1, -1, BINARY_OPERATOR,REL_OR}, +}; + + +/* get_element + * + * find the next expression element in the infix expression + */ +static int + get_element(int opnd, const char **ppsrc, const ELEMENT **ppel) +{ + const ELEMENT *ptable, *pel; + + *ppel = NULL; + + while (isspace((int) (unsigned char) **ppsrc)) ++*ppsrc; + if (**ppsrc == '\0') return FALSE; + + if (opnd) { + ptable = operands; + pel = ptable + NELEMENTS(operands) - 1; + } else { + ptable = operators; + pel = ptable + NELEMENTS(operators) - 1; + } + + while (pel >= ptable) { + size_t len = strlen(pel->name); + + if (epicsStrnCaseCmp(*ppsrc, pel->name, len) == 0) { + *ppel = pel; + *ppsrc += len; + return TRUE; + } + --pel; + } + return FALSE; +} + + +/* postfix + * + * convert an infix expression to a postfix expression + */ +epicsShareFunc long + postfix(const char *psrc, char *pout, short *perror) +{ + ELEMENT stack[80]; + ELEMENT *pstacktop = stack; + const ELEMENT *pel; + int operand_needed = TRUE; + int runtime_depth = 0; + int cond_count = 0; + char * const pdest = pout; + char *pnext; + + if (psrc == NULL || *psrc == '\0' || + pout == NULL || perror == NULL) { + if (perror) *perror = CALC_ERR_NULL_ARG; + if (pout) *pout = END_EXPRESSION; + return -1; + } + + /* place the expression elements into postfix */ + *pout = END_EXPRESSION; + *perror = CALC_ERR_NONE; + + while (get_element(operand_needed, &psrc, &pel)) { + switch (pel->type) { + + case OPERAND: + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + operand_needed = FALSE; + break; + + case LITERAL_OPERAND: + runtime_depth += pel->runtime_effect; + + psrc -= strlen(pel->name); + if (pel->code == LITERAL_DOUBLE) { + double lit_d; + epicsInt32 lit_i; + + if (epicsParseDouble(psrc, &lit_d, &pnext)) { + *perror = CALC_ERR_BAD_LITERAL; + goto bad; + } + psrc = pnext; + lit_i = (epicsInt32) lit_d; + if (lit_d != (double) lit_i) { + *pout++ = pel->code; + memcpy(pout, &lit_d, sizeof(double)); + pout += sizeof(double); + } else { + *pout++ = LITERAL_INT; + memcpy(pout, &lit_i, sizeof(epicsInt32)); + pout += sizeof(epicsInt32); + } + } + else { + epicsUInt32 lit_ui; + + assert(pel->code == LITERAL_INT); + if (epicsParseUInt32(psrc, &lit_ui, 0, &pnext)) { + *perror = CALC_ERR_BAD_LITERAL; + goto bad; + } + psrc = pnext; + *pout++ = LITERAL_INT; + memcpy(pout, &lit_ui, sizeof(epicsUInt32)); + pout += sizeof(epicsUInt32); + } + + operand_needed = FALSE; + break; + + case STORE_OPERATOR: + if (pout == pdest || pstacktop > stack || + *--pout < FETCH_A || *pout > FETCH_L) { + *perror = CALC_ERR_BAD_ASSIGNMENT; + goto bad; + } + /* Convert fetch into a store on the stack */ + *++pstacktop = *pel; + pstacktop->code = STORE_A + *pout - FETCH_A; + runtime_depth -= 1; + operand_needed = TRUE; + break; + + case UNARY_OPERATOR: + case VARARG_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + break; + + case BINARY_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + + operand_needed = TRUE; + break; + + case SEPERATOR: + /* Move operators to the output until open paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_BAD_SEPERATOR; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + operand_needed = TRUE; + pstacktop->runtime_effect -= 1; + break; + + case CLOSE_PAREN: + /* Move operators to the output until matching paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_PAREN_NOT_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + pstacktop--; /* remove ( from stack */ + /* if there is a vararg operator before the opening paren, + it inherits the (opening) paren's stack effect */ + if ((pstacktop > stack) && + pstacktop->type == VARARG_OPERATOR) { + pstacktop->runtime_effect = (pstacktop+1)->runtime_effect; + /* check for no arguments */ + if (pstacktop->runtime_effect > 0) { + *perror = CALC_ERR_INCOMPLETE; + goto bad; + } + } + break; + + case CONDITIONAL: + /* Move operators of > priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri > pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Add new element to the output */ + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + + /* For : operator, also push COND_END code to stack */ + if (pel->name[0] == ':') { + if (--cond_count < 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + pstacktop++; + *pstacktop = *pel; + pstacktop->code = COND_END; + pstacktop->runtime_effect = 0; + } else { + cond_count++; + } + + operand_needed = TRUE; + break; + + case EXPR_TERMINATOR: + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (runtime_depth > 1) { + *perror = CALC_ERR_TOOMANY; + goto bad; + } + + operand_needed = TRUE; + break; + + default: + *perror = CALC_ERR_INTERNAL; + goto bad; + } + + if (runtime_depth < 0) { + *perror = CALC_ERR_UNDERFLOW; + goto bad; + } + if (runtime_depth >= CALCPERFORM_STACK) { + *perror = CALC_ERR_OVERFLOW; + goto bad; + } + } + + if (*psrc != '\0') { + *perror = CALC_ERR_SYNTAX; + goto bad; + } + + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + *pout = END_EXPRESSION; + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (operand_needed || runtime_depth != 1) { + *perror = CALC_ERR_INCOMPLETE; + goto bad; + } + return 0; + +bad: + *pdest = END_EXPRESSION; + return -1; +} + + +/* calcErrorStr + * + * Return a message string appropriate for the given error code + */ +epicsShareFunc const char * + calcErrorStr(short error) +{ + static const char *errStrs[] = { + "No error", + "Too many results returned", + "Badly formed numeric literal", + "Bad assignment target", + "Comma without enclosing parentheses", + "Close parenthesis found without open", + "Parenthesis still open at end of expression", + "Unbalanced conditional ?: operators", + "Incomplete expression, operand missing", + "Not enough operands provided", + "Runtime stack overflow", + "Syntax error, unknown operator/operand", + "NULL or empty input argument to postfix()", + "Internal error, unknown element type", + }; + + if (error < CALC_ERR_NONE || error > CALC_ERR_INTERNAL) + return NULL; + return errStrs[error]; +} + + +/* calcExprDump + * + * Disassemble the given postfix instructions to stdout + */ +epicsShareFunc void + calcExprDump(const char *pinst) +{ + static const char *opcodes[] = { + "End Expression", + /* Operands */ + "LITERAL_DOUBLE", "LITERAL_INT", "VAL", + "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", + "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", + /* Assignment */ + "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", + "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", + /* Trigonometry Constants */ + "CONST_PI", + "CONST_D2R", + "CONST_R2D", + /* Arithmetic */ + "UNARY_NEG", + "ADD", + "SUB", + "MULT", + "DIV", + "MODULO", + "POWER", + /* Algebraic */ + "ABS_VAL", + "EXP", + "LOG_10", + "LOG_E", + "MAX", + "MIN", + "SQU_RT", + /* Trigonometric */ + "ACOS", + "ASIN", + "ATAN", + "ATAN2", + "COS", + "COSH", + "SIN", + "SINH", + "TAN", + "TANH", + /* Numeric */ + "CEIL", + "FLOOR", + "FINITE", + "ISINF", + "ISNAN", + "NINT", + "RANDOM", + /* Boolean */ + "REL_OR", + "REL_AND", + "REL_NOT", + /* Bitwise */ + "BIT_OR", + "BIT_AND", + "BIT_EXCL_OR", + "BIT_NOT", + "RIGHT_SHIFT", + "LEFT_SHIFT", + /* Relationals */ + "NOT_EQ", + "LESS_THAN", + "LESS_OR_EQ", + "EQUAL", + "GR_OR_EQ", + "GR_THAN", + /* Conditional */ + "COND_IF", + "COND_ELSE", + "COND_END", + /* Misc */ + "NOT_GENERATED" + }; + char op; + double lit_d; + epicsInt32 lit_i; + + while ((op = *pinst) != END_EXPRESSION) { + switch (op) { + case LITERAL_DOUBLE: + memcpy(&lit_d, ++pinst, sizeof(double)); + printf("\tDouble %g\n", lit_d); + pinst += sizeof(double); + break; + case LITERAL_INT: + memcpy(&lit_i, ++pinst, sizeof(epicsInt32)); + printf("\tInteger %d (0x%x)\n", lit_i, lit_i); + pinst += sizeof(epicsInt32); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + printf("\t%s, %d arg(s)\n", opcodes[(int) op], *++pinst); + pinst++; + break; + default: + printf("\t%s\n", opcodes[(int) op]); + pinst++; + } + } +} diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h new file mode 100644 index 000000000..b90492012 --- /dev/null +++ b/modules/libcom/src/calc/postfix.h @@ -0,0 +1,89 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* postfix.h + * Original Author: Bob Dalesio + * Date: 9-21-88 + */ + +#ifndef INCpostfixh +#define INCpostfixh + +#include "shareLib.h" + +#define CALCPERFORM_NARGS 12 +#define CALCPERFORM_STACK 80 + +#define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6) +/* The above expression is an estimate of the maximum postfix buffer + * size needed for a given infix expression buffer (the argument must count + * the trailing nil byte in the input expression string). The actual size + * needed is never larger than this value, although it is actually a + * few bytes smaller for some sizes. + * + * The maximum expansion from infix to postfix is for the sub-expression + * .1?.1: + * which is 6 characters long and results in 21 bytes of postfix: + * .1 => LITERAL_DOUBLE + 8 byte value + * ? => COND_IF + * .1 => LITERAL_DOUBLE + 8 byte value + * : => COND_ELSE + * ... + * => COND_END + * For other short expressions the factor 21/6 always gives a big enough + * postfix buffer (proven by hand, look at '1+' and '.1+' as well). + */ + +/* These are not hard limits, just default sizes for the database */ +#define MAX_INFIX_SIZE 100 +#define MAX_POSTFIX_SIZE INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE) + + +/* Error numbers from postfix */ + +#define CALC_ERR_NONE 0 /* No error */ +#define CALC_ERR_TOOMANY 1 /* Too many results returned */ +#define CALC_ERR_BAD_LITERAL 2 /* Bad numeric literal */ +#define CALC_ERR_BAD_ASSIGNMENT 3 /* Bad assignment target */ +#define CALC_ERR_BAD_SEPERATOR 4 /* Comma without parentheses */ +#define CALC_ERR_PAREN_NOT_OPEN 5 /* Close parenthesis without open */ +#define CALC_ERR_PAREN_OPEN 6 /* Open parenthesis at end of expression */ +#define CALC_ERR_CONDITIONAL 7 /* Unbalanced conditional ?: operators */ +#define CALC_ERR_INCOMPLETE 8 /* Incomplete expression, operand missing */ +#define CALC_ERR_UNDERFLOW 9 /* Runtime stack would underflow */ +#define CALC_ERR_OVERFLOW 10 /* Runtime stack would overflow */ +#define CALC_ERR_SYNTAX 11 /* Syntax error */ +#define CALC_ERR_NULL_ARG 12 /* NULL or empty input argument */ +#define CALC_ERR_INTERNAL 13 /* Internal error, bad element type */ +/* Changes in the above errors must also be made in calcErrorStr() */ + + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc long + postfix(const char *pinfix, char *ppostfix, short *perror); + +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *ppostfix); + +epicsShareFunc long + calcArgUsage(const char *ppostfix, unsigned long *pinputs, unsigned long *pstores); + +epicsShareFunc const char * + calcErrorStr(short error); + +epicsShareFunc void + calcExprDump(const char *pinst); + +#ifdef __cplusplus +} +#endif + +#endif /* INCpostfixh */ diff --git a/modules/libcom/src/calc/postfixPvt.h b/modules/libcom/src/calc/postfixPvt.h new file mode 100644 index 000000000..53efb32e0 --- /dev/null +++ b/modules/libcom/src/calc/postfixPvt.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* postfixPvt.h + * Original Author: Bob Dalesio + * Date: 9-21-88 + */ + +/* Notes: + * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must + * be contiguous. + * 2. The LITERAL opcodes are followed by a binary representation of their + * values, but these are not aligned properly. + * 3. The var-arg functions MIN, MAX, FINITE and ISNAN are followed by + * a byte giving the number of arguments to process. + * 4. You can't use strlen() on an RPN buffer since the literal values + * can contain zero bytes. + */ + +#ifndef INCpostfixPvth +#define INCpostfixPvth + + +/* RPN opcodes */ +typedef enum { + END_EXPRESSION = 0, + /* Operands */ + LITERAL_DOUBLE, LITERAL_INT, FETCH_VAL, + FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, + FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, + /* Assignment */ + STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, + STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, + /* Trigonometry Constants */ + CONST_PI, + CONST_D2R, + CONST_R2D, + /* Arithmetic */ + UNARY_NEG, + ADD, + SUB, + MULT, + DIV, + MODULO, + POWER, + /* Algebraic */ + ABS_VAL, + EXP, + LOG_10, + LOG_E, + MAX, + MIN, + SQU_RT, + /* Trigonometric */ + ACOS, + ASIN, + ATAN, + ATAN2, + COS, + COSH, + SIN, + SINH, + TAN, + TANH, + /* Numeric */ + CEIL, + FLOOR, + FINITE, + ISINF, + ISNAN, + NINT, + RANDOM, + /* Boolean */ + REL_OR, + REL_AND, + REL_NOT, + /* Bitwise */ + BIT_OR, + BIT_AND, + BIT_EXCL_OR, + BIT_NOT, + RIGHT_SHIFT, + LEFT_SHIFT, + /* Relationals */ + NOT_EQ, + LESS_THAN, + LESS_OR_EQ, + EQUAL, + GR_OR_EQ, + GR_THAN, + /* Conditional */ + COND_IF, + COND_ELSE, + COND_END, + /* Misc */ + NOT_GENERATED +} rpn_opcode; + +#endif /* INCpostfixPvth */ diff --git a/modules/libcom/src/cppStd/Makefile b/modules/libcom/src/cppStd/Makefile new file mode 100644 index 000000000..782c4da25 --- /dev/null +++ b/modules/libcom/src/cppStd/Makefile @@ -0,0 +1,13 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/cppStd +INC += epicsAlgorithm.h +INC += epicsExcept.h + diff --git a/modules/libcom/src/cppStd/epicsAlgorithm.h b/modules/libcom/src/cppStd/epicsAlgorithm.h new file mode 100644 index 000000000..20850242d --- /dev/null +++ b/modules/libcom/src/cppStd/epicsAlgorithm.h @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// epicsAlgorithm.h +// Authors: Jeff Hill & Andrew Johnson + +#ifndef __EPICS_ALGORITHM_H__ +#define __EPICS_ALGORITHM_H__ + +#include "epicsMath.h" + +// The C++ standard only requires types to be less-than comparable, so +// the epicsMin and epicsMax templates only use operator < + +// epicsMin + +template +inline const T& epicsMin (const T& a, const T& b) +{ + return (b < a) ? b : a; +} + +// If b is a NaN the above template returns a, but should return NaN. +// These specializations ensure that epicsMin(x,NaN) == NaN + +template <> +inline const float& epicsMin (const float& a, const float& b) +{ + return (b < a) || isnan(b) ? b : a; +} + +template <> +inline const double& epicsMin (const double& a, const double& b) +{ + return (b < a) || isnan(b) ? b : a; +} + + +// epicsMax + +template +inline const T& epicsMax (const T& a, const T& b) +{ + return (a < b) ? b : a; +} + +// If b is a NaN the above template returns a, but should return NaN. +// These specializations ensure that epicsMax(x,NaN) == NaN + +template <> +inline const float& epicsMax (const float& a, const float& b) +{ + return (a < b) || isnan(b) ? b : a; +} + +template <> +inline const double& epicsMax (const double& a, const double& b) +{ + return (a < b) || isnan(b) ? b : a; +} + + +// epicsSwap + +template +inline void epicsSwap(T& a, T& b) +{ + T temp = a; + a = b; + b = temp; +} + +#endif // __EPICS_ALGORITHM_H__ diff --git a/modules/libcom/src/cppStd/epicsExcept.h b/modules/libcom/src/cppStd/epicsExcept.h new file mode 100644 index 000000000..e68999735 --- /dev/null +++ b/modules/libcom/src/cppStd/epicsExcept.h @@ -0,0 +1,71 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// Author: Andrew Johnson & Jeff Hill +// Date: December 2000 + +#ifndef __EPICS_EXCEPT_H__ +#define __EPICS_EXCEPT_H__ + +#define epicsThrowHere(exc) \ + throw locationException(exc, __FILE__, __LINE__) + +class sourceLocation { +public: // Functions + sourceLocation(const char *fileName, int lineNumber); +// sourceLocation(const sourceLocation&); Copy constructable +// sourceLocation& operator=(const sourceLocation&); Assignable + + const char *fileName() const; + int lineNumber() const; + +private: // Hide compiler-generated member functions + sourceLocation(); // default constructor + +private: // Data + const char *file; + int line; +}; + +template +class locationException : public T, public sourceLocation { +public: + locationException(const T& exc, const char *fileName, int lineNumber); +}; + + +/* Example: + * if (status) epicsThrowHere(std::logic_error("operation failed!")); + * try { ... } catch(sourceLocation& where) { ... } + */ + +// END OF DECLARATIONS + +// INLINE FUNCTIONS + +// sourceFileLocation +inline sourceLocation::sourceLocation (const char *fileName, int lineNumber) : + file(fileName), line(lineNumber) {} + +inline const char* sourceLocation::fileName () const { + return this->file; +} + +inline int sourceLocation::lineNumber () const { + return this->line; +} + +// locationException +template +inline locationException::locationException + (const char *fileName, int lineNumber, const E& exc) : + T(exc), sourceLocation(fileName, lineNumber) {} + + +#endif // __EPICS_EXCEPT_H__ diff --git a/modules/libcom/src/cvtFast/Makefile b/modules/libcom/src/cvtFast/Makefile new file mode 100644 index 000000000..473693c83 --- /dev/null +++ b/modules/libcom/src/cvtFast/Makefile @@ -0,0 +1,13 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/cvtFast +INC += cvtFast.h +Com_SRCS += cvtFast.c + diff --git a/modules/libcom/src/cvtFast/cvtFast.c b/modules/libcom/src/cvtFast/cvtFast.c new file mode 100644 index 000000000..106c1ba47 --- /dev/null +++ b/modules/libcom/src/cvtFast/cvtFast.c @@ -0,0 +1,523 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Fast numeric to string conversions + * + * Original Authors: + * Bob Dalesio, Mark Anderson and Marty Kraimer + * Date: 12 January 1993 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "cvtFast.h" +#include "epicsMath.h" +#include "epicsStdio.h" + +/* + * These routines convert numbers up to +/- 10,000,000. + * They defer to sprintf() for numbers requiring more than + * 8 places of precision. + */ +static epicsInt32 frac_multiplier[] = + {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; + +int cvtFloatToString(float flt_value, char *pdest, + epicsUInt16 precision) +{ + int got_one, i; + epicsInt32 whole, iplace, number, fraction, fplace; + float ftemp; + char *startAddr; + + /* can this routine handle this conversion */ + if (isnan(flt_value) || precision > 8 || + flt_value > 10000000.0 || flt_value < -10000000.0) { + if (precision > 8 || flt_value >= 1e8 || flt_value <= -1e8) { + if (precision > 12) precision = 12; /* FIXME */ + sprintf(pdest, "%*.*e", precision+6, precision, (double) flt_value); + } else { + if (precision > 3) precision = 3; /* FIXME */ + sprintf(pdest, "%.*f", precision, (double) flt_value); + } + return((int)strlen(pdest)); + } + startAddr = pdest; + + /* determine the sign */ + if (flt_value < 0){ + *pdest++ = '-'; + flt_value = -flt_value; + }; + + /* remove the whole number portion */ + whole = (epicsInt32)flt_value; + ftemp = flt_value - whole; + + /* multiplier to convert fractional portion to integer */ + fplace = frac_multiplier[precision]; + fraction = (epicsInt32)(ftemp * fplace * 10); + fraction = (fraction + 5) / 10; /* round up */ + + /* determine rounding into the whole number portion */ + if ((fraction / fplace) >= 1){ + whole++; + fraction -= fplace; + } + + /* whole numbers */ + got_one = 0; + for (iplace = 10000000; iplace >= 1; iplace /= 10){ + if (whole >= iplace){ + got_one = 1; + number = whole / iplace; + whole = whole - (number * iplace); + *pdest = number + '0'; + pdest++; + }else if (got_one){ + *pdest = '0'; + pdest++; + } + } + if (!got_one){ + *pdest = '0'; + pdest++; + } + + /* fraction */ + if (precision > 0){ + /* convert fractional portional to ASCII */ + *pdest = '.'; + pdest++; + for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ + number = fraction / fplace; + fraction -= number * fplace; + *pdest = number + '0'; + pdest++; + } + } + *pdest = 0; + + return((int)(pdest - startAddr)); +} + +int cvtDoubleToString( + double flt_value, + char *pdest, + epicsUInt16 precision) +{ + epicsUInt16 got_one,i; + epicsInt32 whole,iplace,number,fraction,fplace; + double ftemp; + char *startAddr; + + /* can this routine handle this conversion */ + if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) { + if (precision > 8 || flt_value > 1e16 || flt_value < -1e16) { + if(precision>17) precision=17; + sprintf(pdest,"%*.*e",precision+7,precision, + flt_value); + } else { + if(precision>3) precision=3; + sprintf(pdest,"%.*f",precision,flt_value); + } + return((int)strlen(pdest)); + } + startAddr = pdest; + + /* determine the sign */ + if (flt_value < 0){ + *pdest++ = '-'; + flt_value = -flt_value; + }; + + /* remove the whole number portion */ + whole = (epicsInt32)flt_value; + ftemp = flt_value - whole; + + /* multiplier to convert fractional portion to integer */ + fplace = frac_multiplier[precision]; + fraction = (epicsInt32)(ftemp * fplace * 10); + fraction = (fraction + 5) / 10; /* round up */ + + /* determine rounding into the whole number portion */ + if ((fraction / fplace) >= 1){ + whole++; + fraction -= fplace; + } + + /* whole numbers */ + got_one = 0; + for (iplace = 10000000; iplace >= 1; iplace /= 10){ + if (whole >= iplace){ + got_one = 1; + number = whole / iplace; + whole = whole - (number * iplace); + *pdest = number + '0'; + pdest++; + }else if (got_one){ + *pdest = '0'; + pdest++; + } + } + if (!got_one){ + *pdest = '0'; + pdest++; + } + + /* fraction */ + if (precision > 0){ + /* convert fractional portional to ASCII */ + *pdest = '.'; + pdest++; + for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ + number = fraction / fplace; + fraction -= number * fplace; + *pdest = number + '0'; + pdest++; + } + } + *pdest = 0; + + return((int)(pdest - startAddr)); +} + +/* + * These routines are provided for backwards compatibility, + * extensions such as MEDM, edm and histtool use them. + */ + +/* + * cvtFloatToExpString + * + * Converts a float to a %e formatted string + */ +int cvtFloatToExpString(float val, char *pdest, epicsUInt16 precision) +{ + return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val); +} + +/* + * cvtFloatToCompactString + * + * Converts a float to a %g formatted string. + * The result uses %f notation for 10e-4 < |value| < 10e+4, + * otherwise %e notation. + */ +int cvtFloatToCompactString(float val, char *pdest, epicsUInt16 precision) +{ + if ((val < 1.e4 && val > 1.e-4) || + (val > -1.e4 && val < -1.e-4) || + val == 0.0) + return cvtFloatToString(val, pdest, precision); + + return cvtFloatToExpString(val, pdest, precision); +} + + +/* + * cvtDoubleToExpString + * + * Converts a double to a %e formatted string + */ + +int cvtDoubleToExpString(double val, char *pdest, epicsUInt16 precision) +{ + return epicsSnprintf(pdest, MAX_STRING_SIZE, "%.*e", precision, val); +} + + +/* + * cvtDoubleToCompactString + * + * Converts a double to %g formatted string. + * The result uses %f notation for 10e-4 < |value| < 10e+4, + * otherwise %e notation. + */ +int cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 precision) +{ + if ((val < 1.e4 && val > 1.e-4) || + (val > -1.e4 && val < -1.e-4) || + val == 0.0) + return cvtDoubleToString(val, pdest, precision); + + return cvtDoubleToExpString(val, pdest, precision); +} + + +/* Integer conversion primitives */ + +static size_t + UInt32ToDec(epicsUInt32 val, char *pdest) +{ + int i; + char digit[10]; + size_t len; + + for (i = 0; val; i++) { + epicsUInt32 tenth = val / 10; + + digit[i] = val - tenth * 10 + '0'; + val = tenth; + } + len = i; + + while (i > 0) + *pdest++ = digit[--i]; + + *pdest = 0; + return len; +} + +static size_t + UInt32ToBase(epicsUInt32 val, char *pdest, int base) +{ + int i; + char digit, digits[32]; + size_t len; + + for (i = 0; val; i++) { + epicsUInt32 tenth = val / base; + + digit = val - tenth * base; + digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a'; + val = tenth; + } + len = i; + + while (i > 0) + *pdest++ = digits[--i]; + + *pdest = 0; + return len; +} + +static size_t + UInt64ToDec(epicsUInt64 val, char *pdest) +{ + int i; + char digit[20]; + size_t len; + + for (i = 0; val; i++) { + epicsUInt64 tenth = val / 10; + + digit[i] = val - tenth * 10 + '0'; + val = tenth; + } + + len = i; + while (i > 0) + *pdest++ = digit[--i]; + + *pdest = 0; + return len; +} + +static size_t + UInt64ToBase(epicsUInt64 val, char *pdest, int base) +{ + int i; + char digit, digits[64]; + size_t len; + + for (i = 0; val; i++) { + epicsUInt64 tenth = val / base; + + digit = val - tenth * base; + digits[i] = digit < 10 ? digit + '0' : digit - 10 + 'a'; + val = tenth; + } + len = i; + + while (i > 0) + *pdest++ = digits[--i]; + + *pdest = 0; + return len; +} + + +/* Integer conversion routines */ + +size_t + cvtUInt32ToString(epicsUInt32 val, char *pdest) +{ + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 1; + } + + return UInt32ToDec(val, pdest); +} + +size_t + cvtInt32ToString(epicsInt32 val, char *pdest) +{ + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 1; + } + + if (val > 0) + return UInt32ToDec(val, pdest); + + if (val == -0x80000000) { + strcpy(pdest, "-2147483648"); + return strlen(pdest); + } + + *pdest++ = '-'; + return 1 + UInt32ToDec(-val, pdest); +} + + +size_t + cvtUInt64ToString(epicsUInt64 val, char *pdest) +{ + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 1; + } + + return UInt64ToDec(val, pdest); +} + +size_t + cvtInt64ToString(epicsInt64 val, char *pdest) +{ + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 1; + } + + if (val > 0) + return UInt64ToDec(val, pdest); + + if (val == -0x8000000000000000LL) { + strcpy(pdest, "-9223372036854775808"); + return strlen(pdest); + } + + *pdest++ = '-'; + return 1 + UInt64ToDec(-val, pdest); +} + + +size_t + cvtInt32ToHexString(epicsInt32 val, char *pdest) +{ + if (val < 0) + *pdest++ = '-'; + + *pdest++ = '0'; + *pdest++ = 'x'; + + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 3; + } + + if (val > 0) + return 2 + UInt32ToBase(val, pdest, 16); + + if (val == -0x80000000) { + strcpy(pdest, "80000000"); + return 11; + } + + return 3 + UInt32ToBase(-val, pdest, 16); +} + +size_t + cvtUInt32ToHexString(epicsUInt32 val, char *pdest) +{ + *pdest++ = '0'; + *pdest++ = 'x'; + + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 3; + } + + return 2 + UInt32ToBase(val, pdest, 16); +} + +size_t + cvtInt32ToOctalString(epicsInt32 val, char *pdest) +{ + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 1; + } + + if (val > 0) { + *pdest++ = '0'; + return 1 + UInt32ToBase(val, pdest, 8); + } + + if (val == -0x80000000) { + strcpy(pdest, "-020000000000"); + return strlen(pdest); + } + + *pdest++ = '-'; + *pdest++ = '0'; + return 2 + UInt32ToBase(-val, pdest, 8); +} + +size_t + cvtInt64ToHexString(epicsInt64 val, char *pdest) +{ + if (val < 0) + *pdest++ = '-'; + + *pdest++ = '0'; + *pdest++ = 'x'; + + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 3; + } + + if (val > 0) + return 2 + UInt64ToBase(val, pdest, 16); + + if (val == -0x8000000000000000LL) { + strcpy(pdest, "8000000000000000"); + return 19; + } + + return 3 + UInt64ToBase(-val, pdest, 16); +} + +size_t + cvtUInt64ToHexString(epicsUInt64 val, char *pdest) +{ + *pdest++ = '0'; + *pdest++ = 'x'; + + if (val == 0) { + *pdest++ = '0'; + *pdest = 0; + return 3; + } + + return 2 + UInt64ToBase(val, pdest, 16); +} + diff --git a/modules/libcom/src/cvtFast/cvtFast.h b/modules/libcom/src/cvtFast/cvtFast.h new file mode 100644 index 000000000..db06dda53 --- /dev/null +++ b/modules/libcom/src/cvtFast/cvtFast.h @@ -0,0 +1,83 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Fast numeric to string conversions + * + * Original Authors: + * Bob Dalesio, Mark Anderson and Marty Kraimer + * Date: 12 January 1993 + */ + +#ifndef INCcvtFasth +#define INCcvtFasth + +#include + +#include "epicsTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * All functions return the number of characters in the output + */ +epicsShareFunc int + cvtFloatToString(float val, char *pdest, epicsUInt16 prec); +epicsShareFunc int + cvtDoubleToString(double val, char *pdest, epicsUInt16 prec); + +epicsShareFunc int + cvtFloatToExpString(float val, char *pdest, epicsUInt16 prec); +epicsShareFunc int + cvtDoubleToExpString(double val, char *pdest, epicsUInt16 prec); +epicsShareFunc int + cvtFloatToCompactString(float val, char *pdest, epicsUInt16 prec); +epicsShareFunc int + cvtDoubleToCompactString(double val, char *pdest, epicsUInt16 prec); + +epicsShareFunc size_t + cvtInt32ToString(epicsInt32 val, char *pdest); +epicsShareFunc size_t + cvtUInt32ToString(epicsUInt32 val, char *pdest); +epicsShareFunc size_t + cvtInt64ToString(epicsInt64 val, char *pdest); +epicsShareFunc size_t + cvtUInt64ToString(epicsUInt64 val, char *pdest); + +epicsShareFunc size_t + cvtInt32ToHexString(epicsInt32 val, char *pdest); +epicsShareFunc size_t + cvtUInt32ToHexString(epicsUInt32 val, char *pdest); +epicsShareFunc size_t + cvtInt32ToOctalString(epicsInt32 val, char *pdest); +epicsShareFunc size_t + cvtInt64ToHexString(epicsInt64 val, char *pdest); +epicsShareFunc size_t + cvtUInt64ToHexString(epicsUInt64 val, char *pdest); + +/* Support the original names */ + +#define cvtCharToString(val, str) cvtInt32ToString(val, str) +#define cvtUcharToString(val, str) cvtUInt32ToString(val, str) +#define cvtShortToString(val, str) cvtInt32ToString(val, str) +#define cvtUshortToString(val, str) cvtUInt32ToString(val, str) +#define cvtLongToString(val, str) cvtInt32ToString(val, str) +#define cvtUlongToString(val, str) cvtUInt32ToString(val, str) + +#define cvtLongToHexString(val, str) cvtInt32ToHexString(val, str) +#define cvtULongToHexString(val, str) cvtUInt32ToHexString(val, str) +#define cvtLongToOctalString(val, str) cvtInt32ToOctalString(val, str) + +#ifdef __cplusplus +} +#endif + +#endif /*INCcvtFasth*/ diff --git a/modules/libcom/src/cxxTemplates/Makefile b/modules/libcom/src/cxxTemplates/Makefile new file mode 100644 index 000000000..c67668f55 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/Makefile @@ -0,0 +1,20 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/cxxTemplates +INC += resourceLib.h +INC += tsDLList.h +INC += tsSLList.h +INC += tsMinMax.h +INC += tsFreeList.h +INC += epicsSingleton.h +INC += epicsGuard.h +Com_SRCS += resourceLib.cpp +Com_SRCS += epicsSingletonMutex.cpp + diff --git a/modules/libcom/src/cxxTemplates/README b/modules/libcom/src/cxxTemplates/README new file mode 100644 index 000000000..786c598b3 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/README @@ -0,0 +1,35 @@ + +C++ templates: +tsSLList.h - type safe single linked list template +tsDLList.h - type safe double linked list template +resourceLib.h - hash table template +tsFreeeList.h - free list allocator / deallocator + +the test subdir contains examples + +Since I am using templates the linked lists are type safe +(no casting of pointers ala ellList and dllList). +Also, the node class in embedded in the item on the +list (more efficient use of pool). + +The file resourceLib.h provides a core hashing library +"resTable " where "itemClass" objects +are stored in the hash table and "idClass" is the data type +of the key for the hash table. The identifier class provides +the hash alg. I have provided simple string "stringId" and +unsigned integer "uintId" key types in resourceLib.h. It +is easy to implement a new key class. + +There are examples under cxxTemplate/test. The list/hashing +templates all depend on a particular inheritance hierarchy. +If the inheritance hierarchy is wrong nothing will compile. +For instance, in tsDLList.h the template data type "T" +must derive from tsDLNode. Likewise, in tsSLList.h +"T" must derive from tsSLNode. Likewise, in resourceLib.h +class "T" (the type stored in the hash table) must derive +from class "ID" (the hash table key type) and also derive from +tsSLNode. + + + + diff --git a/modules/libcom/src/cxxTemplates/epicsGuard.h b/modules/libcom/src/cxxTemplates/epicsGuard.h new file mode 100644 index 000000000..f0aa401a5 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/epicsGuard.h @@ -0,0 +1,114 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsGuardh +#define epicsGuardh + +#ifndef assert // allow use of epicsAssert.h +# include +#endif + +/* + * Author: Jeffrey O. Hill + */ + +template < class T > class epicsGuardRelease; + +// Automatically applies and releases the mutex. +// This class is also useful in situations where +// C++ exceptions are possible. +template < class T > +class epicsGuard { +public: + typedef epicsGuardRelease release_t; + epicsGuard ( T & ); + void assertIdenticalMutex ( const T & ) const; + ~epicsGuard (); +private: + T * _pTargetMutex; + epicsGuard ( const epicsGuard & ); + epicsGuard & operator = ( const epicsGuard & ); + friend class epicsGuardRelease < T >; +}; + +// Automatically releases and reapplies the mutex. +// This class is also useful in situations where +// C++ exceptions are possible. +template < class T > +class epicsGuardRelease { +public: + typedef epicsGuard guard_t; + epicsGuardRelease ( epicsGuard < T > & ); + ~epicsGuardRelease (); +private: + epicsGuard < T > & _guard; + T * _pTargetMutex; + epicsGuardRelease ( const epicsGuardRelease & ); + epicsGuardRelease & operator = ( const epicsGuardRelease & ); +}; + +// same interface as epicsMutex +class epicsMutexNOOP { +public: + void lock (); + bool tryLock (); + void unlock (); + void show ( unsigned level ) const; +}; + +template < class T > +inline epicsGuard < T > :: epicsGuard ( T & mutexIn ) : + _pTargetMutex ( & mutexIn ) +{ + _pTargetMutex->lock (); +} + +template < class T > +inline epicsGuard < T > :: ~epicsGuard () +{ + _pTargetMutex->unlock (); +} + +template < class T > +inline void epicsGuard < T > :: assertIdenticalMutex ( + const T & mutexToVerify ) const +{ + assert ( _pTargetMutex == & mutexToVerify ); +} + +template < class T > +inline epicsGuardRelease < T > :: + epicsGuardRelease ( epicsGuard & guardIn ) : + _guard ( guardIn ), + _pTargetMutex ( guardIn._pTargetMutex ) +{ + // Setting the guard's _pTargetMutex to nill will + // allow assertIdenticalMutex to catch situations + // where a guard is being used and it has been + // released, and also situations where ~epicsGuard () + // runs and an epicsGuardRelease is still referencing + // the guard will be detected. + _guard._pTargetMutex = 0; + _pTargetMutex->unlock (); +} + +template < class T > +inline epicsGuardRelease < T > :: ~epicsGuardRelease () +{ + _pTargetMutex->lock (); + _guard._pTargetMutex = _pTargetMutex; +} + +inline void epicsMutexNOOP::lock () {} +inline bool epicsMutexNOOP::tryLock () { return true; } +inline void epicsMutexNOOP::unlock () {} +inline void epicsMutexNOOP::show ( unsigned ) const {} + +#endif // epicsGuardh diff --git a/modules/libcom/src/cxxTemplates/epicsSingleton.h b/modules/libcom/src/cxxTemplates/epicsSingleton.h new file mode 100644 index 000000000..2c63d3ff7 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/epicsSingleton.h @@ -0,0 +1,216 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + */ + +#ifndef epicsSingleton_h +#define epicsSingleton_h + +#include +#include + +#include "shareLib.h" +#include "epicsAssert.h" + +class epicsShareClass SingletonUntyped { +public: + SingletonUntyped (); + ~SingletonUntyped (); + typedef void * ( * PBuild ) (); + void incrRefCount ( PBuild ); + typedef void ( * PDestroy ) ( void * ); + void decrRefCount ( PDestroy ); + void * pInstance () const; +private: + void * _pInstance; + std :: size_t _refCount; + SingletonUntyped ( const SingletonUntyped & ); + SingletonUntyped & operator = ( const SingletonUntyped & ); +}; + +// This class exists for the purpose of avoiding file scope +// object chicken and egg problems. It implements thread safe +// lazy initialization. To avoid locking overhead retain a +// copy of the epicsSingleton :: reference for future use. +template < class TYPE > +class epicsSingleton { +public: + class reference { + public: + reference ( epicsSingleton & ); + reference ( const reference & ); + ~reference (); + // this somewhat convoluted reference of the return + // type ref through the epicsSingleton template is + // required for the archaic Tornado gnu compiler + typename epicsSingleton < TYPE > :: reference & + operator = ( const reference & ); + TYPE * operator -> (); + const TYPE * operator -> () const; + TYPE & operator * (); + const TYPE & operator * () const; + private: + epicsSingleton * _pSingleton; + }; + friend class reference; + epicsSingleton () {} + // mutex lock/unlock pair overhead incured + // when either of these are called + reference getReference (); + const reference getReference () const; +private: + SingletonUntyped _singletonUntyped; + static void * _build (); + static void _destroy ( void * ); + epicsSingleton ( const epicsSingleton & ); + epicsSingleton & operator = ( const epicsSingleton & ); +}; + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + reference ( epicsSingleton & es ): + _pSingleton ( & es ) +{ + es._singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); +} + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + reference ( const reference & ref ) : + _pSingleton ( ref._pSingleton ) +{ + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); +} + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + ~reference () +{ + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( & epicsSingleton < TYPE > :: _destroy ); +} + +template < class TYPE > +typename epicsSingleton < TYPE > :: reference & + epicsSingleton < TYPE > :: reference :: + operator = ( const reference & ref ) +{ + if ( _pSingleton != ref._pSingleton ) { + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( epicsSingleton < TYPE > :: _destroy ); + _pSingleton = ref._pSingleton; + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); + } + return *this; +} + +template < class TYPE > +inline TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () +{ + assert ( _pSingleton ); + return reinterpret_cast < TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline const TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () const +{ + assert ( _pSingleton ); + return reinterpret_cast < const TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () +{ + return * this->operator -> (); +} + +template < class TYPE > +inline const TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () const +{ + return * this->operator -> (); +} + +inline SingletonUntyped :: SingletonUntyped () : + _pInstance ( 0 ), _refCount ( 0 ) +{ +} + +inline void * SingletonUntyped :: pInstance () const +{ + return _pInstance; +} + +inline SingletonUntyped :: ~SingletonUntyped () +{ + // we dont assert fail on non-zero _refCount + // and or non nill _pInstance here because this + // is designed to tolarate situations where + // file scope epicsSingleton objects (which + // theoretically dont have storage lifespan + // issues) are deleted in a non-determanistic + // order +# if 0 + assert ( _refCount == 0 ); + assert ( _pInstance == 0 ); +# endif +} + +template < class TYPE > +void * epicsSingleton < TYPE > :: _build () +{ + return new TYPE (); +} + +template < class TYPE > +void epicsSingleton < TYPE > :: + _destroy ( void * pDestroyTypeless ) +{ + TYPE * pDestroy = + reinterpret_cast < TYPE * > ( pDestroyTypeless ); + delete pDestroy; +} + +template < class TYPE > +inline typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () +{ + return reference ( * this ); +} + +template < class TYPE > +inline const typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () const +{ + epicsSingleton < TYPE > * pConstCastAway = + const_cast < epicsSingleton < TYPE > * > ( this ); + return pConstCastAway->getReference (); +} + +#endif // epicsSingleton_h + diff --git a/modules/libcom/src/cxxTemplates/epicsSingletonMutex.cpp b/modules/libcom/src/cxxTemplates/epicsSingletonMutex.cpp new file mode 100644 index 000000000..8cd69d969 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/epicsSingletonMutex.cpp @@ -0,0 +1,60 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff O. Hill + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" +#include "epicsSingleton.h" + +#ifndef SIZE_MAX +# define SIZE_MAX UINT_MAX +#endif + +static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT ); +static epicsMutex * pEPICSSigletonMutex = 0; + +extern "C" void SingletonMutexOnce ( void * /* pParm */ ) +{ + // This class exists for the purpose of avoiding file scope + // object chicken and egg problems. Therefore, pEPICSSigletonMutex + // is never destroyed. + pEPICSSigletonMutex = newEpicsMutex; +} + +void SingletonUntyped :: incrRefCount ( PBuild pBuild ) +{ + epicsThreadOnce ( & epicsSigletonOnceFlag, SingletonMutexOnce, 0 ); + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + assert ( _refCount < SIZE_MAX ); + if ( _refCount == 0 ) { + _pInstance = ( * pBuild ) (); + } + _refCount++; +} + +void SingletonUntyped :: decrRefCount ( PDestroy pDestroy ) +{ + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + assert ( _refCount > 0 ); + _refCount--; + if ( _refCount == 0 ) { + ( *pDestroy ) ( _pInstance ); + _pInstance = 0; + } +} diff --git a/modules/libcom/src/cxxTemplates/resourceLib.cpp b/modules/libcom/src/cxxTemplates/resourceLib.cpp new file mode 100644 index 000000000..f3a9a9993 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/resourceLib.cpp @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#include "resourceLib.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class intId < unsigned, 8u, sizeof(unsigned)*CHAR_BIT >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif diff --git a/modules/libcom/src/cxxTemplates/resourceLib.h b/modules/libcom/src/cxxTemplates/resourceLib.h new file mode 100644 index 000000000..7b61e238d --- /dev/null +++ b/modules/libcom/src/cxxTemplates/resourceLib.h @@ -0,0 +1,1170 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * General hash table templates for fast indexing of resources + * of any base resource type and any resource identifier type. Fast + * indexing is implemented with a hash lookup. The identifier type + * implements the hash algorithm (or derives from one of the supplied + * identifier types which provide a hashing routine). The table expands + * dynamically depending on load, and without introducing non-determanistic + * latency. + * + * Unsigned integer and string identifier classes are supplied here. + * + * Authors Jeffrey O. Hill + * Marty Kraimer (string hash algorithm) + * influenced by papers by Peter K. Pearson and Per-Ake Larson + * + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef INCresourceLibh +#define INCresourceLibh + +#include +#include + +#include +#include +#include +#include +#ifndef assert // allow use of epicsAssert.h +#include +#endif + +#include "tsSLList.h" +#include "epicsString.h" +#include "shareLib.h" +typedef size_t resTableIndex; + +template < class T, class ID > class resTableIter; +template < class T, class ID > class resTableIterConst; + +// +// class resTable +// +// This class stores resource entries of type T which can be efficiently +// located with a hash key of type ID. +// +// NOTES: +// 1) class T must derive from class ID and also from class tsSLNode +// +// 2) If the "resTable::show (unsigned level)" member function is called then +// class T must also implement a "show (unsigned level)" member function which +// dumps increasing diagnostics information with increasing "level" to +// standard out. +// +// 3) Classes of type ID must implement the following member functions: +// +// // equivalence test +// bool operator == (const ID &); +// +// // ID to hash index convert (see examples below) +// resTableIndex hash (unsigned nBitsHashIndex) const; +// +// 4) Storage for identifier of type ID must persist until the item of type +// T is deleted from the resTable +// +template +class resTable { +public: + resTable (); + virtual ~resTable(); + // Call " void T::show (unsigned level)" for each entry + void show ( unsigned level ) const; + void verify () const; + int add ( T & res ); // returns -1 (id exists in table), 0 (success) + T * remove ( const ID &idIn ); // remove entry + void removeAll ( tsSLList & destination ); // remove all entries + T * lookup ( const ID &idIn ) const; // locate entry + // Call (pT->*pCB) () for each entry but expect poor performance + // with sparcely populated tables + void traverse ( void (T::*pCB)() ); + void traverseConst ( void (T::*pCB)() const ) const; + unsigned numEntriesInstalled () const; + void setTableSize ( const unsigned newTableSize ); + // iterate through all entries but expect poor performance + // with sparcely populated tables + typedef resTableIter < T, ID > iterator; + typedef resTableIterConst < T, ID > iteratorConst; + iterator firstIter (); + iteratorConst firstIter () const; +private: + tsSLList < T > * pTable; + unsigned nextSplitIndex; + unsigned hashIxMask; + unsigned hashIxSplitMask; + unsigned nBitsHashIxSplitMask; + unsigned logBaseTwoTableSize; + unsigned nInUse; + resTableIndex hash ( const ID & idIn ) const; + T * find ( tsSLList & list, const ID & idIn ) const; + void splitBucket (); + unsigned tableSize () const; + bool setTableSizePrivate ( unsigned logBaseTwoTableSize ); + resTable ( const resTable & ); + resTable & operator = ( const resTable & ); + static unsigned resTableBitMask ( const unsigned nBits ); + friend class resTableIter < T, ID >; + friend class resTableIterConst < T, ID >; +}; + +// +// class resTableIter +// +// an iterator for the resource table class +// +template < class T, class ID > +class resTableIter { +public: + resTableIter (); + bool valid () const; + bool operator == ( const resTableIter < T,ID > & rhs ) const; + bool operator != ( const resTableIter < T,ID > & rhs ) const; + resTableIter < T, ID > & operator = ( const resTableIter < T, ID > & ); + T & operator * () const; + T * operator -> () const; + resTableIter < T, ID > & operator ++ (); + resTableIter < T, ID > operator ++ ( int ); + T * pointer (); +private: + tsSLIter < T > iter; + unsigned index; + resTable < T,ID > * pResTable; + resTableIter ( resTable < T,ID > & tableIn ); + void findNextEntry (); + friend class resTable < T, ID >; +}; + +// +// class resTableIterConst +// +// an iterator for a const resource table class +// +template < class T, class ID > +class resTableIterConst { +public: + resTableIterConst (); + bool valid () const; + bool operator == ( const resTableIterConst < T,ID > & rhs ) const; + bool operator != ( const resTableIterConst < T,ID > & rhs ) const; + resTableIterConst < T, ID > & operator = ( const resTableIterConst < T, ID > & ); + const T & operator * () const; + const T * operator -> () const; + resTableIterConst < T, ID > & operator ++ (); + resTableIterConst < T, ID > operator ++ ( int ); + const T * pointer () const; +private: + tsSLIterConst < T > iter; + unsigned index; + const resTable < T,ID > * pResTable; + resTableIterConst ( const resTable < T,ID > & tableIn ); + void findNextEntry (); + friend class resTable < T, ID >; +}; + +// +// Some ID classes that work with the above template +// + +// +// class intId +// +// signed or unsigned integer identifier (class T must be +// a signed or unsigned integer type) +// +// this class works as type ID in resTable +// +// 1<. +// Set this parameter to zero if unsure of the correct minimum +// hash table size. +// +// MAX_ID_WIDTH specifies the maximum number of ls bits in an +// integer identifier which might be set at any time. +// +// MIN_INDEX_WIDTH and MAX_ID_WIDTH are specified here at +// compile time so that the hash index can be produced +// efficiently. Hash indexes are produced more efficiently +// when (MAX_ID_WIDTH - MIN_INDEX_WIDTH) is minimized. +// +template +class intId { +public: + intId (const T &idIn); + bool operator == (const intId &idIn) const; + resTableIndex hash () const; + const T getId() const; +protected: + T id; +}; + +// +// class chronIntIdResTable +// +// a specialized resTable which uses unsigned integer keys which are +// allocated in chronological sequence +// +// NOTE: ITEM must public inherit from chronIntIdRes +// +class chronIntId : public intId +{ +public: + chronIntId ( const unsigned &idIn ); +}; + +template +class chronIntIdResTable : public resTable { +public: + chronIntIdResTable (); + virtual ~chronIntIdResTable (); + void idAssignAdd ( ITEM & item ); +private: + unsigned allocId; + chronIntIdResTable ( const chronIntIdResTable & ); + chronIntIdResTable & operator = ( const chronIntIdResTable & ); +}; + +// +// class chronIntIdRes +// +// resource with unsigned chronological identifier +// +template +class chronIntIdRes : public chronIntId, public tsSLNode { +public: + chronIntIdRes (); +private: + void setId (unsigned newId); + chronIntIdRes (const chronIntIdRes & ); + friend class chronIntIdResTable; +}; + +// +// class stringId +// +// character string identifier +// +class epicsShareClass stringId { +public: + enum allocationType {copyString, refString}; + stringId (const char * idIn, allocationType typeIn=copyString); + virtual ~stringId(); + resTableIndex hash () const; + bool operator == (const stringId &idIn) const; + const char * resourceName() const; // return the pointer to the string + void show (unsigned level) const; +private: + stringId & operator = ( const stringId & ); + stringId ( const stringId &); + const char * pStr; + const allocationType allocType; +}; + +///////////////////////////////////////////////// +// resTable member functions +///////////////////////////////////////////////// + +// +// resTable::resTable () +// +template +inline resTable::resTable () : + pTable ( 0 ), nextSplitIndex ( 0 ), hashIxMask ( 0 ), + hashIxSplitMask ( 0 ), nBitsHashIxSplitMask ( 0 ), + logBaseTwoTableSize ( 0 ), nInUse ( 0 ) {} + +template +inline unsigned resTable::resTableBitMask ( const unsigned nBits ) +{ + return ( 1 << nBits ) - 1; +} + +// +// resTable::remove () +// +// remove a res from the resTable +// +template +T * resTable::remove ( const ID & idIn ) +{ + if ( this->pTable ) { + // search list for idIn and remove the first match + tsSLList & list = this->pTable [ this->hash(idIn) ]; + tsSLIter pItem = list.firstIter (); + T *pPrev = 0; + while ( pItem.valid () ) { + const ID & idOfItem = *pItem; + if ( idOfItem == idIn ) { + if ( pPrev ) { + list.remove ( *pPrev ); + } + else { + list.get (); + } + this->nInUse--; + break; + } + pPrev = pItem.pointer (); + pItem++; + } + return pItem.pointer (); + } + else { + return 0; + } +} + +template +void resTable::removeAll ( tsSLList & destination ) +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + while ( T * pItem = this->pTable[i].get() ) { + destination.add ( *pItem ); + } + } + this->nInUse = 0; +} + +// +// resTable::lookup () +// +template +inline T * resTable::lookup ( const ID & idIn ) const +{ + if ( this->pTable ) { + tsSLList & list = this->pTable [ this->hash ( idIn ) ]; + return this->find ( list, idIn ); + } + else { + return 0; + } +} + +// +// resTable::hash () +// +template +inline resTableIndex resTable::hash ( const ID & idIn ) const +{ + resTableIndex h = idIn.hash (); + resTableIndex h0 = h & this->hashIxMask; + if ( h0 >= this->nextSplitIndex ) { + return h0; + } + return h & this->hashIxSplitMask; +} + +// +// resTable::show +// +template +void resTable::show ( unsigned level ) const +{ + const unsigned N = this->tableSize (); + + printf ( "Hash table with %u buckets and %u items of type %s installed\n", + N, this->nInUse, typeid(T).name() ); + + if ( level >= 1u && N ) { + + if ( level >= 2u ) { + tsSLList * pList = this->pTable; + while ( pList < & this->pTable[N] ) { + tsSLIter pItem = pList->firstIter (); + while ( pItem.valid () ) { + tsSLIter pNext = pItem; + pNext++; + pItem.pointer()->show ( level - 2u ); + pItem = pNext; + } + pList++; + } + } + + double X = 0.0; + double XX = 0.0; + unsigned maxEntries = 0u; + unsigned empty = 0; + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + unsigned count = 0; + while ( pItem.valid () ) { + if ( level >= 3u ) { + pItem->show ( level ); + } + count++; + pItem++; + } + if ( count > 0u ) { + X += count; + XX += count * count; + if ( count > maxEntries ) { + maxEntries = count; + } + } else + empty++; + } + + double mean = X / N; + double stdDev = sqrt( XX / N - mean * mean ); + printf ( + "entries per bucket: mean = %f std dev = %f max = %u\n", + mean, stdDev, maxEntries ); + printf("%u empty buckets\n", empty); + if ( X != this->nInUse ) { + printf ("this->nInUse didnt match items counted which was %f????\n", X ); + } + } +} + +// self test +template +void resTable::verify () const +{ + const unsigned N = this->tableSize (); + + if ( this->pTable ) { + assert ( this->nextSplitIndex <= this->hashIxMask + 1 ); + assert ( this->hashIxMask ); + assert ( this->hashIxMask == ( this->hashIxSplitMask >> 1 ) ); + assert ( this->hashIxSplitMask ); + assert ( this->nBitsHashIxSplitMask ); + assert ( resTableBitMask ( this->nBitsHashIxSplitMask ) + == this->hashIxSplitMask ); + assert ( this->logBaseTwoTableSize ); + assert ( this->nBitsHashIxSplitMask <= this->logBaseTwoTableSize ); + } + else { + assert ( this->nextSplitIndex == 0 ); + assert ( this->hashIxMask == 0 ); + assert ( this->hashIxSplitMask == 0 ); + assert ( this->nBitsHashIxSplitMask == 0 ); + assert ( this->logBaseTwoTableSize == 0 ); + } + + unsigned total = 0u; + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + unsigned count = 0; + while ( pItem.valid () ) { + resTableIndex index = this->hash ( *pItem ); + assert ( index == i ); + count++; + pItem++; + } + total += count; + } + assert ( total == this->nInUse ); +} + + +// +// resTable::traverse +// +template +void resTable::traverse ( void (T::*pCB)() ) +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + while ( pItem.valid () ) { + tsSLIter pNext = pItem; + pNext++; + ( pItem.pointer ()->*pCB ) (); + pItem = pNext; + } + } +} + +// +// resTable::traverseConst +// +template +void resTable::traverseConst ( void (T::*pCB)() const ) const +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + const tsSLList < T > & table = this->pTable[i]; + tsSLIterConst pItem = table.firstIter (); + while ( pItem.valid () ) { + tsSLIterConst pNext = pItem; + pNext++; + ( pItem.pointer ()->*pCB ) (); + pItem = pNext; + } + } +} + +template +inline unsigned resTable::numEntriesInstalled () const +{ + return this->nInUse; +} + +template +inline unsigned resTable::tableSize () const +{ + if ( this->pTable ) { + return ( this->hashIxMask + 1 ) + this->nextSplitIndex; + } + else { + return 0; + } +} + +// it will be more efficent to call this once prior to installing +// the first entry +template +void resTable::setTableSize ( const unsigned newTableSize ) +{ + if ( newTableSize == 0u ) { + return; + } + + // + // count the number of bits in newTableSize and round up + // to the next power of two + // + unsigned newMask = newTableSize - 1; + unsigned nbits; + for ( nbits = 0; nbits < sizeof (newTableSize) * CHAR_BIT; nbits++ ) { + unsigned nBitsMask = resTableBitMask ( nbits ); + if ( ( newMask & ~nBitsMask ) == 0){ + break; + } + } + setTableSizePrivate ( nbits ); +} + +template +bool resTable::setTableSizePrivate ( unsigned logBaseTwoTableSizeIn ) +{ + // dont shrink + if ( this->logBaseTwoTableSize >= logBaseTwoTableSizeIn ) { + return true; + } + + // dont allow ridiculously small tables + if ( logBaseTwoTableSizeIn < 4 ) { + logBaseTwoTableSizeIn = 4; + } + + const unsigned newTableSize = 1 << logBaseTwoTableSizeIn; +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + const unsigned oldTableSize = this->pTable ? 1 << this->logBaseTwoTableSize : 0; +# endif + const unsigned oldTableOccupiedSize = this->tableSize (); + + tsSLList * pNewTable; + try { + pNewTable = ( tsSLList * ) + ::operator new ( newTableSize * sizeof ( tsSLList ) ); + } + catch ( ... ){ + if ( ! this->pTable ) { + throw; + } + return false; + } + + // run the constructors using placement new + unsigned i; + for ( i = 0u; i < oldTableOccupiedSize; i++ ) { + new ( &pNewTable[i] ) tsSLList ( this->pTable[i] ); + } + for ( i = oldTableOccupiedSize; i < newTableSize; i++ ) { + new ( &pNewTable[i] ) tsSLList; + } + // Run the destructors explicitly. Currently this destructor is a noop. + // The Tornado II compiler and RedHat 6.2 will not compile ~tsSLList() but + // since its a NOOP we can find an ugly workaround +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + for ( i = 0; i < oldTableSize; i++ ) { + this->pTable[i].~tsSLList(); + } +# endif + + if ( ! this->pTable ) { + this->hashIxSplitMask = resTableBitMask ( logBaseTwoTableSizeIn ); + this->nBitsHashIxSplitMask = logBaseTwoTableSizeIn; + this->hashIxMask = this->hashIxSplitMask >> 1; + this->nextSplitIndex = 0; + } + + operator delete ( this->pTable ); + this->pTable = pNewTable; + this->logBaseTwoTableSize = logBaseTwoTableSizeIn; + + return true; +} + +template +void resTable::splitBucket () +{ + // double the hash table when necessary + // (this results in only a memcpy overhead, but + // no hashing or entry redistribution) + if ( this->nextSplitIndex > this->hashIxMask ) { + bool success = this->setTableSizePrivate ( this->nBitsHashIxSplitMask + 1 ); + if ( ! success ) { + return; + } + this->nBitsHashIxSplitMask += 1; + this->hashIxSplitMask = resTableBitMask ( this->nBitsHashIxSplitMask ); + this->hashIxMask = this->hashIxSplitMask >> 1; + this->nextSplitIndex = 0; + } + + // rehash only the items in the split bucket + tsSLList tmp ( this->pTable[ this->nextSplitIndex ] ); + this->nextSplitIndex++; + T *pItem = tmp.get(); + while ( pItem ) { + resTableIndex index = this->hash ( *pItem ); + this->pTable[index].add ( *pItem ); + pItem = tmp.get(); + } +} + +// +// add a res to the resTable +// +template +int resTable::add ( T &res ) +{ + if ( ! this->pTable ) { + this->setTableSizePrivate ( 10 ); + } + else if ( this->nInUse >= this->tableSize() ) { + this->splitBucket (); + tsSLList &list = this->pTable[this->hash(res)]; + if ( this->find ( list, res ) != 0 ) { + return -1; + } + } + tsSLList &list = this->pTable[this->hash(res)]; + if ( this->find ( list, res ) != 0 ) { + return -1; + } + list.add ( res ); + this->nInUse++; + return 0; +} + +// +// find +// searches from where the iterator points to the +// end of the list for idIn +// +// iterator points to the item found upon return +// (or NULL if nothing matching was found) +// +template +T * resTable::find ( tsSLList &list, const ID &idIn ) const +{ + tsSLIter pItem = list.firstIter (); + while ( pItem.valid () ) { + const ID & idOfItem = *pItem; + if ( idOfItem == idIn ) { + break; + } + pItem++; + } + return pItem.pointer (); +} + +// +// ~resTable::resTable() +// +template +resTable::~resTable() +{ + operator delete ( this->pTable ); +} + +// +// resTable::resTable ( const resTable & ) +// private - not to be used - implemented to eliminate warnings +// +template +inline resTable::resTable ( const resTable & ) +{ +} + +// +// resTable::resTable & operator = ( const resTable & ) +// private - not to be used - implemented to eliminate warnings +// +template +inline resTable & resTable::operator = ( const resTable & ) +{ + return *this; +} + +template +inline resTableIterConst < T, ID > resTable::firstIter () const +{ + return resTableIterConst < T, ID > ( *this ); +} + +template +inline resTableIter < T, ID > resTable::firstIter () +{ + return resTableIter < T, ID > ( *this ); +} + +////////////////////////////////////////////// +// resTableIter member functions +////////////////////////////////////////////// + +template < class T, class ID > +inline resTableIter::resTableIter ( resTable < T,ID > & tableIn ) : + index ( 0 ), pResTable ( & tableIn ) +{ + this->findNextEntry (); +} + +template < class T, class ID > +inline resTableIter::resTableIter () : + iter ( tsSLList::invalidIter() ), + index ( 0 ), pResTable ( 0 ) +{ +} + +template < class T, class ID > +inline void resTableIter::findNextEntry () +{ + if ( this->pResTable ) { + while ( this->index < this->pResTable->tableSize() ) { + this->iter = this->pResTable->pTable[this->index++].firstIter (); + if ( this->iter.valid () ) { + break; + } + } + } +} + +template < class T, class ID > +inline bool resTableIter::valid () const +{ + return this->iter.valid (); +} + +template < class T, class ID > +inline bool resTableIter::operator == + ( const resTableIter < T,ID > & rhs ) const +{ + return ( this->pResTable == rhs.pResTable + && this->index == rhs.index + && this->iter == rhs.iter ); +} + +template < class T, class ID > +inline bool resTableIter::operator != + ( const resTableIter < T,ID > & rhs ) const +{ + return ! this->operator == ( rhs ); +} + +template < class T, class ID > +inline resTableIter < T, ID > & resTableIter::operator = + ( const resTableIter < T, ID > & rhs ) +{ + this->pResTable = rhs.pResTable; + this->index = rhs.index; + this->iter = rhs.iter; + return *this; +} + +template < class T, class ID > +inline T & resTableIter::operator * () const +{ + return this->iter.operator * (); +} + +template < class T, class ID > +inline T * resTableIter::operator -> () const +{ + return this->iter.operator -> (); +} + +template < class T, class ID > +inline resTableIter & resTableIter::operator ++ () +{ + this->iter++; + if ( ! this->iter.valid() ) { + this->findNextEntry (); + } + return *this; +} + +template < class T, class ID > +inline resTableIter resTableIter::operator ++ ( int ) +{ + resTableIter tmp = *this; + this->operator ++ (); + return tmp; +} + +template < class T, class ID > +inline T * resTableIter::pointer () +{ + return this->iter.pointer (); +} + +////////////////////////////////////////////// +// resTableIterConst member functions +////////////////////////////////////////////// + +template < class T, class ID > +inline resTableIterConst::resTableIterConst ( const resTable < T,ID > & tableIn ) : + index ( 0 ), pResTable ( & tableIn ) +{ + this->findNextEntry (); +} + +template < class T, class ID > +inline resTableIterConst::resTableIterConst () : + iter ( tsSLList::invalidIter() ), + index ( 0 ), pResTable ( 0 ) +{ +} + +template < class T, class ID > +inline void resTableIterConst::findNextEntry () +{ + if ( this->pResTable ) { + while ( this->index < this->pResTable->tableSize() ) { + const tsSLList * pList = & this->pResTable->pTable[this->index++]; + this->iter = pList->firstIter (); + if ( this->iter.valid () ) { + break; + } + } + } +} + +template < class T, class ID > +inline bool resTableIterConst::valid () const +{ + return this->iter.valid (); +} + +template < class T, class ID > +inline bool resTableIterConst::operator == + ( const resTableIterConst < T,ID > & rhs ) const +{ + return ( this->pResTable == rhs.pResTable + && this->index == rhs.index + && this->iter == rhs.iter ); +} + +template < class T, class ID > +inline bool resTableIterConst::operator != + ( const resTableIterConst < T,ID > & rhs ) const +{ + return ! this->operator == ( rhs ); +} + +template < class T, class ID > +inline resTableIterConst < T, ID > & resTableIterConst::operator = + ( const resTableIterConst < T, ID > & rhs ) +{ + this->pResTable = rhs.pResTable; + this->index = rhs.index; + this->iter = rhs.iter; + return *this; +} + +template < class T, class ID > +inline const T & resTableIterConst::operator * () const +{ + return this->iter.operator * (); +} + +template < class T, class ID > +inline const T * resTableIterConst::operator -> () const +{ + return this->iter.operator -> (); +} + +template < class T, class ID > +inline resTableIterConst & resTableIterConst::operator ++ () +{ + this->iter++; + if ( ! this->iter.valid() ) { + this->findNextEntry (); + } + return *this; +} + +template < class T, class ID > +inline resTableIterConst resTableIterConst::operator ++ ( int ) +{ + resTableIterConst tmp = *this; + this->operator ++ (); + return tmp; +} + +template < class T, class ID > +inline const T * resTableIterConst::pointer () const +{ + return this->iter.pointer (); +} + +////////////////////////////////////////////// +// chronIntIdResTable member functions +////////////////////////////////////////////// +inline chronIntId::chronIntId ( const unsigned &idIn ) : + intId ( idIn ) {} + +// +// chronIntIdResTable::chronIntIdResTable() +// +template +inline chronIntIdResTable::chronIntIdResTable () : + resTable (), allocId(1u) {} + +template +inline chronIntIdResTable::chronIntIdResTable ( const chronIntIdResTable & ) : + resTable (), allocId(1u) {} + +template +inline chronIntIdResTable & chronIntIdResTable:: + operator = ( const chronIntIdResTable & ) +{ + return *this; +} + +// +// chronIntIdResTable::~chronIntIdResTable() +// (not inline because it is virtual) +// +template +chronIntIdResTable::~chronIntIdResTable() {} + +// +// chronIntIdResTable::add() +// +// NOTE: This detects (and avoids) the case where +// the PV id wraps around and we attempt to have two +// resources with the same id. +// +template +inline void chronIntIdResTable::idAssignAdd (ITEM &item) +{ + int status; + do { + item.chronIntIdRes::setId (allocId++); + status = this->resTable::add (item); + } + while (status); +} + +///////////////////////////////////////////////// +// chronIntIdRes member functions +///////////////////////////////////////////////// + +// +// chronIntIdRes::chronIntIdRes +// +template +inline chronIntIdRes::chronIntIdRes () : chronIntId (UINT_MAX) {} + +// +// id::setId () +// +// workaround for bug in DEC compiler +// +template +inline void chronIntIdRes::setId (unsigned newId) +{ + this->id = newId; +} + +///////////////////////////////////////////////// +// intId member functions +///////////////////////////////////////////////// + +// +// intId::intId +// +// (if this is inline SUN PRO botches the template instantiation) +template +intId::intId (const T &idIn) + : id (idIn) {} + +// +// intId::operator == () +// +template +inline bool intId::operator == + (const intId &idIn) const +{ + return this->id == idIn.id; +} + +// +// intId::getId () +// +template +inline const T intId::getId () const +{ + return this->id; +} + +// +// integerHash() +// +// converts any integer into a hash table index +// +template < class T > +inline resTableIndex integerHash ( unsigned MIN_INDEX_WIDTH, + unsigned MAX_ID_WIDTH, const T &id ) +{ + resTableIndex hashid = static_cast ( id ); + + // + // the intent here is to gurantee that all components of the + // integer contribute even if the resTableIndex returned might + // index a small table. + // + // On most compilers the optimizer will unroll this loop so this + // is actually a very small inline function + // + // Experiments using the microsoft compiler show that this isnt + // slower than switching on the architecture size and unrolling the + // loop explicitly (that solution has resulted in portability + // problems in the past). + // + unsigned width = MAX_ID_WIDTH; + do { + width >>= 1u; + hashid ^= hashid>>width; + } while (width>MIN_INDEX_WIDTH); + + // + // the result here is always masked to the + // proper size after it is returned to the "resTable" class + // + return hashid; +} + + +// +// intId::hash() +// +template +inline resTableIndex intId::hash () const +{ + return integerHash ( MIN_INDEX_WIDTH, MAX_ID_WIDTH, this->id ); +} + +//////////////////////////////////////////////////// +// stringId member functions +//////////////////////////////////////////////////// + +// +// stringId::operator == () +// +inline bool stringId::operator == + (const stringId &idIn) const +{ + if (this->pStr!=NULL && idIn.pStr!=NULL) { + return strcmp(this->pStr,idIn.pStr)==0; + } + return false; // not equal +} + +// +// stringId::resourceName () +// +inline const char * stringId::resourceName () const +{ + return this->pStr; +} + +#ifdef instantiateRecourceLib + +// +// stringId::stringId() +// +stringId::stringId (const char * idIn, allocationType typeIn) : + allocType (typeIn) +{ + if (typeIn==copyString) { + unsigned nChars = strlen (idIn) + 1u; + this->pStr = new char [nChars]; + memcpy ( (void *) this->pStr, idIn, nChars ); + } + else { + this->pStr = idIn; + } +} + +// +// stringId::show () +// +void stringId::show (unsigned level) const +{ + if (level>2u) { + printf ("resource id = %s\n", this->pStr); + } +} + +// +// stringId::~stringId() +// +// +// this needs to be instantiated only once (normally in libCom) +// +stringId::~stringId() +{ + if (this->allocType==copyString) { + if (this->pStr!=NULL) { + // + // the microsoft and solaris compilers will + // not allow a pointer to "const char" + // to be deleted + // + // the HP-UX compiler gives us a warning on + // each cast away of const, but in this case + // it cant be avoided. + // + // The DEC compiler complains that const isnt + // really significant in a cast if it is present. + // + // I hope that deleting a pointer to "char" + // is the same as deleting a pointer to + // "const char" on all compilers + // + delete [] const_cast(this->pStr); + } + } +} + +// +// stringId::hash() +// +resTableIndex stringId::hash() const +{ + if (!this->pStr) { + return 0u; + } + return epicsStrHash(this->pStr, 0); +} + +#endif // if instantiateRecourceLib is defined + +#endif // INCresourceLibh + diff --git a/modules/libcom/src/cxxTemplates/test/Makefile b/modules/libcom/src/cxxTemplates/test/Makefile new file mode 100644 index 000000000..61d807c65 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/Makefile @@ -0,0 +1,35 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP=../../../.. + +include $(TOP)/configure/CONFIG + +resourceLibTest_SRCS += resourceLibTest.cc +TESTPROD_HOST += resourceLibTest + +tsDLListBench_SRCS += tsDLListBench.cc +TESTPROD_HOST += tsDLListBench + +tsDLListTest_SRCS += tsDLListTest.cc +TESTPROD_HOST += tsDLListTest + +tsSLListBench_SRCS += tsSLListBench.cc +TESTPROD_HOST += tsSLListBench + +tsSLListTest_SRCS += tsSLListTest.cc +TESTPROD_HOST += tsSLListTest + +minmaxTest_SRCS += minmaxTest.cc +TESTPROD_HOST += minmaxTest + +PROD_LIBS = Com + +include $(TOP)/configure/RULES + diff --git a/modules/libcom/src/cxxTemplates/test/minmaxTest.cc b/modules/libcom/src/cxxTemplates/test/minmaxTest.cc new file mode 100644 index 000000000..b5cf5b110 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/minmaxTest.cc @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "tsMinMax.h" + +int main () +{ + float f1 = 3.3f; + float f2 = 3.4f; + float f3; + + f3 = tsMin(f1,f2); + assert(f3==f1); + + f3 = tsMax(f1,f2); + assert(f3==f2); + + int i1 = 3; + int i2 = 4; + int i3; + + i3 = tsMin(i1,i2); + assert(i3==i1); + + i3 = tsMax(i1,i2); + assert(i3==i2); + + return 0; +} + diff --git a/modules/libcom/src/cxxTemplates/test/resourceLibTest.cc b/modules/libcom/src/cxxTemplates/test/resourceLibTest.cc new file mode 100644 index 000000000..1a6a2c1f9 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/resourceLibTest.cc @@ -0,0 +1,309 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define instantiateRecourceLib +#include "resourceLib.h" + +#if defined(__GNUC__) && ( __GNUC__<2 || (__GNUC__==2 && __GNUC__<8) ) +typedef intId testIntId; +#else +typedef intId testIntId; +#endif + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +static void empty() +{ +} + +class albert : public testIntId, public tsSLNode { +public: + albert (resTable< albert, testIntId > &atIn, unsigned idIn) : + testIntId(idIn), at(atIn) + { + verify (at.add (*this)==0); + } + void show (unsigned /* level */) + { + } + void destroy() + { + at.remove(*this); + delete this; + } +private: + resTable< albert, testIntId > &at; +}; + +class fred : public testIntId, public tsSLNode { +public: + fred (const char *pNameIn, unsigned idIn) : + testIntId(idIn), pName(pNameIn) {} + void show (unsigned) + { + printf("fred %s\n", pName); + } + void destroy() + { + // always on stack so noop + } +private: + const char * const pName; +}; + +class jane : public stringId, public tsSLNode { +public: + jane (const char *pNameIn) : stringId (pNameIn) {} + + void testTraverse(); + + void destroy() + { + // always on stack so noop + } +}; + +// +// jane::testTraverse() +// +void jane::testTraverse() +{ + printf("Traverse Test\n"); + this->show(10); +} + +int main() +{ + unsigned i; + clock_t start, finish; + double duration; + const unsigned LOOPS = 500000; + resTable < fred, testIntId > intTbl; + resTable < jane, stringId> strTbl; + fred fred0("fred0",0); + fred fred1("fred1",0x1000a432); + fred fred2("fred2",0x0000a432); + fred fred3("fred3",1); + fred fred4("fred4",2); + fred fred5("fred5",3); + fred fred6("fred6",4); + fred fred7("fred7",5); + fred fred8("fred8",6); + fred fred9("fred9",7); + jane jane1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + jane jane2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); + fred *pFred; + jane *pJane; + testIntId intId0 (0); + testIntId intId1 (0x1000a432); + testIntId intId2 (0x0000a432); + testIntId intId3 (1); + testIntId intId4 (2); + testIntId intId5 (3); + testIntId intId6 (4); + testIntId intId7 (5); + testIntId intId8 (6); + testIntId intId9 (7); + + stringId strId1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + strTbl.verify (); + stringId strId2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); + strTbl.verify (); + + intTbl.setTableSize ( 100000 ); + + verify (intTbl.add(fred0)==0); + intTbl.verify (); + verify (intTbl.add(fred1)==0); + intTbl.verify (); + verify (intTbl.add(fred2)==0); + intTbl.verify (); + verify (intTbl.add(fred3)==0); + intTbl.verify (); + verify (intTbl.add(fred4)==0); + intTbl.verify (); + + intTbl.setTableSize ( 200000 ); + + verify (intTbl.add(fred5)==0); + intTbl.verify (); + verify (intTbl.add(fred6)==0); + intTbl.verify (); + verify (intTbl.add(fred7)==0); + intTbl.verify (); + verify (intTbl.add(fred8)==0); + intTbl.verify (); + verify (intTbl.add(fred9)==0); + intTbl.verify (); + + start = clock(); + for (i=0; i alTbl; + + for (i=0; i alTblIter ( alTbl.firstIter() ); + albert *pa; + i=0; + while ( ( pa = alTblIter.pointer() ) ) { + i++; + alTblIter++; + } + verify ( i == elementCount ); + alTbl.verify (); + + for ( i = 0; i < elementCount; i++ ) { + verify ( pAlbert[i] == alTbl.lookup( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 0; i < elementCount; i += 2 ) { + verify ( pAlbert[i] == alTbl.remove ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 0; i < elementCount; i += 2 ) { + verify ( 0 == alTbl.lookup ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 1; i < elementCount; i += 2 ) { + verify ( pAlbert[i] == alTbl.lookup ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + return 0; +} + + + diff --git a/modules/libcom/src/cxxTemplates/test/tsDLListBench.cc b/modules/libcom/src/cxxTemplates/test/tsDLListBench.cc new file mode 100644 index 000000000..577a3829d --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/tsDLListBench.cc @@ -0,0 +1,71 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "tsDLList.h" +#include +#include +#include + +class fred : public tsDLNode { +public: + fred() : count(0) {} + void inc () {count++;} +private: + unsigned count; +}; + +class jane : public fred, public tsDLNode { +public: + jane() {} +private: +}; + +#define LOOPCOUNT 100000 + +int main () +{ + tsDLList list; + tsDLIter iter = list.firstIter(); + fred *pFred; + unsigned i; + clock_t clk; + clock_t diff; + double delay; + + for (i=0; iinc(); + iter++; + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + pFred = new fred(); + clk = clock(); + for (i=0; iinc(); + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + return 0; +} + diff --git a/modules/libcom/src/cxxTemplates/test/tsDLListTest.cc b/modules/libcom/src/cxxTemplates/test/tsDLListTest.cc new file mode 100644 index 000000000..02fe0dcd9 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/tsDLListTest.cc @@ -0,0 +1,131 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + + + +#include "tsDLList.h" +#include +#include + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +class fred : public tsDLNode { +public: + fred(const char * const pNameIn) : pName(pNameIn){} + void show () {printf("%s\n", pName);} +private: + const char * const pName; +}; + +class jane : public fred, public tsDLNode { +public: + jane(const char * const pNameIn) : fred(pNameIn){} +private: +}; + +int main () +{ + unsigned i; + tsDLList list; + fred *pFred; + fred *pFredII; + fred *pFredBack; + tsDLList janeList; + tsDLIter janeFwdIter = janeList.firstIter(); + tsDLIter janeBwdIter = janeList.lastIter(); + jane *pJane; + + pFred = new fred ("A"); + pFredII = new fred ("B"); + + list.add (*pFred); + list.add (*pFredII); + tsDLIter iter = list.firstIter(); + verify (iter.pointer() == pFred); + iter++; + verify (iter.pointer() == pFredII); + list.remove(*pFred); + list.add(*pFred); + pFredBack = list.get(); + verify (pFredBack == pFredII); + pFredBack = list.get(); + verify (pFredBack == pFred); + verify (list.count() == 0u); + list.add(*pFred); + list.add(*pFredII); + list.add(* new fred("C")); + list.add(* new fred("D")); + + iter = list.firstIter(); + while ( iter.valid() ) { + iter->show(); + iter++; + } + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + verify ( janeList.find ( *pJane ) == -1 ); + janeList.add(*pJane); + verify ( janeList.find ( *pJane ) == 1 ); + + while ( janeFwdIter.valid() ) { + janeFwdIter->show(); + janeFwdIter++; + } + + while ( janeBwdIter.valid() ) { + janeBwdIter->show(); + janeBwdIter--; + } + + iter = list.firstIter(); + while ( iter.valid() ) { + iter->show(); + iter++; + } + + tsDLIter < jane > bdIter = janeList.firstIter (); + i = 0; + while ( bdIter.valid () ) { + i++; + bdIter++; + } + verify ( i == janeList.count () ); + + iter = list.firstIter(); + while ( iter.pointer() ) { + list.remove( * iter.pointer() ); + iter++; + } + verify (list.count()==0); + + janeFwdIter = janeList.firstIter(); + while ( janeFwdIter.valid() ) { + janeList.remove( * janeFwdIter.pointer() ); + janeFwdIter++; + } + verify (janeList.count()==0); + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + janeList.add(*pJane); + janeBwdIter = janeList.lastIter(); + while ( janeBwdIter.valid() ) { + janeList.remove( * janeBwdIter.pointer() ); + janeBwdIter--; + } + verify (janeList.count()==0); + + return 0; +} + diff --git a/modules/libcom/src/cxxTemplates/test/tsSLListBench.cc b/modules/libcom/src/cxxTemplates/test/tsSLListBench.cc new file mode 100644 index 000000000..0b786dfd3 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/tsSLListBench.cc @@ -0,0 +1,85 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + + + +#include "tsSLList.h" +#include +#include +#include + +/* + * gnuc does not provide this under sunos4 + */ +#if !defined(CLOCKS_PER_SEC) && defined(SUNOS4) +# define CLOCKS_PER_SEC 1000000 +#endif + +class fred : public tsSLNode { +public: + fred() : count(0) {} + void inc () {count++;} +private: + unsigned count; +}; + +class jane : public fred, public tsSLNode { +public: + jane() {} +private: +}; + +#define LOOPCOUNT 100000 + +int main () +{ + tsSLList list; + fred *pFred; + unsigned i; + clock_t clk; + clock_t diff; + double delay; + + for (i=0; i iter ( list.firstIter () ); + while ( iter.valid () ) { + iter->inc (); + iter++; + } + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + pFred = new fred(); + clk = clock(); + { + tsSLIter iter ( list.firstIter () ); + for ( i=0; iinc(); + } + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + return 0; +} + diff --git a/modules/libcom/src/cxxTemplates/test/tsSLListTest.cc b/modules/libcom/src/cxxTemplates/test/tsSLListTest.cc new file mode 100644 index 000000000..32f96359d --- /dev/null +++ b/modules/libcom/src/cxxTemplates/test/tsSLListTest.cc @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + + + +#include "tsSLList.h" +#include +#include + +class fred : public tsSLNode { +public: + fred(const char * const pNameIn) : pName(pNameIn){} + void show () {printf("%s\n", pName);} +private: + const char * const pName; +}; + +class jane : public fred, public tsSLNode { +public: + jane(const char * const pNameIn) : fred(pNameIn){} +private: +}; + +int main () +{ + tsSLList list; + fred *pFred; + fred *pFredII; + fred *pFredBack; + tsSLList janeList; + jane *pJane; + + pFred = new fred("A"); + pFredII = new fred("B"); + + list.add(*pFred); + list.add(*pFredII); + { + tsSLIter iter1 = list.firstIter (); + tsSLIter iter2 = iter1; + tsSLIter iter3 = iter1; + assert ( iter1 == iter3++ ); + assert ( iter3 == ++iter2 ); + list.remove ( *pFredII ); // removes pFred + } + list.add ( *pFred ); + pFredBack = list.get(); + assert (pFredBack == pFred); + pFredBack = list.get(); + assert (pFredBack == pFredII); + list.add(*pFredII); + list.add(*pFred); + while ( list.get () ); + pFredBack = list.get(); + assert (pFredBack == 0); + list.add(*pFred); + list.add(*pFredII); + list.add(* new fred("C")); + list.add(* new fred("D")); + + { + tsSLIter < fred > iter = list.firstIter(); + while ( iter.valid () ) { + iter->show(); + ++iter; + } + } + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + janeList.add(*pJane); + + { + tsSLIter < jane > iter = janeList.firstIter (); + while ( iter.valid () ) { + iter->show(); + ++iter; + } + } + + { + tsSLIter < fred > iter = list.firstIter (); + while ( iter.valid () ) { + iter->show (); + iter++; + } + } + + while ( list.get () ); + + { + tsSLIter < fred > iter = list.firstIter (); + assert ( ! iter.valid () ); + } + + return 0; +} + diff --git a/modules/libcom/src/cxxTemplates/tsDLList.h b/modules/libcom/src/cxxTemplates/tsDLList.h new file mode 100644 index 000000000..a315ecaab --- /dev/null +++ b/modules/libcom/src/cxxTemplates/tsDLList.h @@ -0,0 +1,684 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * type safe doubly linked list templates + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tsDLListH_include +#define tsDLListH_include + +template class tsDLList; +template class tsDLIterConst; +template class tsDLIter; + +// +// class tsDLNode +// +// a node in a doubly linked list containing entries of type T +// ( class T must publicly derive from class tsDLNode ) +// +template < class T > +class tsDLNode { +public: + tsDLNode (); + tsDLNode ( const tsDLNode & ); + const tsDLNode & operator = ( const tsDLNode & ); +private: + T * pNext; + T * pPrev; + friend class tsDLList; + friend class tsDLIter; + friend class tsDLIterConst; +}; + +// +// class tsDLList +// +// a doubly linked list containing entries of type T +// ( class T must publicly derive from class tsDLNode ) +// +template < class T > +class tsDLList { +public: + tsDLList (); // create empty list + unsigned count () const; // number of items on list + void add ( T & item ); // add item to end of list + void add ( tsDLList & addList ); // add to end of list - addList left empty + void push ( T & item ); // add item to beginning of list + void push ( tsDLList & pushList ); // add to beg of list - pushList left empty + void remove ( T & item ); // remove item from list + void removeAll ( tsDLList & destination ); + T * get (); // removes first item on list + T * pop (); // same as get () + void insertAfter ( T & item, T & itemBefore ); // insert item immediately after itemBefore + void insertBefore ( T & item, T & itemAfter ); // insert item immediately before itemAfter + int find ( const T & item ) const; // returns -1 if not present, node number if present + T * first ( void ) const; // ptr to first item on list + T * last ( void ) const; // ptr to last item on list + tsDLIterConst firstIter () const; + tsDLIter firstIter (); + tsDLIterConst lastIter () const; + tsDLIter lastIter (); + static tsDLIterConst invalidConstIter (); + static tsDLIter invalidIter (); +private: + T * pFirst; + T * pLast; + unsigned itemCount; + void clear (); + tsDLList ( const tsDLList & ); // not allowed + const tsDLList & operator = ( const tsDLList & ); // not allowed +}; + +// +// class tsDLIterConst +// +// bi-directional iterator for a const doubly linked list +// +template +class tsDLIterConst { +public: + tsDLIterConst (); + bool valid () const; + bool operator == ( const tsDLIterConst & rhs ) const; + bool operator != ( const tsDLIterConst & rhs ) const; + tsDLIterConst & operator = ( const tsDLIterConst & ); + const T & operator * () const; + const T * operator -> () const; + tsDLIterConst & operator ++ (); + tsDLIterConst operator ++ (int); + tsDLIterConst & operator -- (); + tsDLIterConst operator -- (int); + const T * pointer () const; +private: + const T * pEntry; + tsDLIterConst ( const T * pInitialEntry ); + friend class tsDLList ; +}; + +// +// class tsDLIter +// +// bi-directional iterator for a doubly linked list +// +template +class tsDLIter { +public: + tsDLIter (); + bool valid () const; + bool operator == ( const tsDLIter & rhs ) const; + bool operator != ( const tsDLIter & rhs ) const; + tsDLIter & operator = ( const tsDLIter & ); + T & operator * () const; + T * operator -> () const; + tsDLIter & operator ++ (); + tsDLIter operator ++ ( int ); + tsDLIter & operator -- (); + tsDLIter operator -- ( int ); + T * pointer () const; +private: + T * pEntry; + tsDLIter ( T *pInitialEntry ); + friend class tsDLList ; +}; + +/////////////////////////////////// +// tsDLNode member functions +/////////////////////////////////// + +// +// tsDLNode::tsDLNode () +// +template +inline tsDLNode::tsDLNode() : pNext(0), pPrev(0) {} + +template +inline tsDLNode::tsDLNode ( const tsDLNode & ) : + pNext (0), pPrev(0) {} + +// +// tsDLNode::operator = () +// +// when someone tries to copy another node into a node +// do _not_ change the node pointers +// +template +inline const tsDLNode & tsDLNode::operator = ( const tsDLNode & ) +{ + return * this; +} + +////////////////////////////////////// +// tsDLList member functions +////////////////////////////////////// + +// +// tsDLList::tsDLList () +// +template +inline tsDLList::tsDLList () +{ + this->clear (); +} + +// +// tsDLList::count () +// +// (returns the number of items on the list) +// +template +inline unsigned tsDLList::count () const +{ + return this->itemCount; +} + +// +// tsDLList::first () +// +template +inline T * tsDLList::first (void) const +{ + return this->pFirst; +} + +// +// tsDLList::last () +// +template +inline T *tsDLList::last (void) const +{ + return this->pLast; +} + +// +// tsDLList::clear () +// +template +inline void tsDLList::clear () +{ + this->pFirst = 0; + this->pLast = 0; + this->itemCount = 0u; +} + +// +// tsDLList::remove () +// +template +inline void tsDLList::remove ( T &item ) +{ + tsDLNode &theNode = item; + + if ( this->pLast == &item ) { + this->pLast = theNode.pPrev; + } + else { + tsDLNode &nextNode = *theNode.pNext; + nextNode.pPrev = theNode.pPrev; + } + + if ( this->pFirst == &item ) { + this->pFirst = theNode.pNext; + } + else { + tsDLNode &prevNode = *theNode.pPrev; + prevNode.pNext = theNode.pNext; + } + + this->itemCount--; +} + +// +// tsDLList::removeAll () +// +template +inline void tsDLList::removeAll ( + tsDLList & destination ) +{ + destination.pFirst = this->pFirst; + destination.pLast = this->pLast; + destination.itemCount = this->itemCount; + this->pFirst = 0; + this->pLast = 0; + this->itemCount = 0; +} + +// +// tsDLList::get () +// +template +inline T * tsDLList::get() +{ + T *pItem = this->pFirst; + + if ( pItem ) { + this->remove ( *pItem ); + } + + return pItem; +} + +// +// tsDLList::pop () +// +// (returns the first item on the list) +template +inline T * tsDLList::pop () +{ + return this->get (); +} + +// +// tsDLList::add () +// +// adds addList to the end of the list +// (and removes all items from addList) +// +template +inline void tsDLList::add ( tsDLList &addList ) +{ + if ( addList.itemCount != 0u ) { + if ( this->itemCount == 0u ) { + this->pFirst = addList.pFirst; + } + else { + tsDLNode *pLastNode = this->pLast; + tsDLNode *pAddListFirstNode = addList.pFirst; + pLastNode->pNext = addList.pFirst; + pAddListFirstNode->pPrev = addList.pLast; + } + this->pLast = addList.pLast; + this->itemCount += addList.itemCount; + addList.clear(); + } +} + +// +// tsDLList::add () +// +// add an item to the end of the list +// +template +inline void tsDLList::add (T &item) +{ + tsDLNode &theNode = item; + + theNode.pNext = 0; + theNode.pPrev = this->pLast; + + if ( this->itemCount ) { + tsDLNode &lastNode = *this->pLast; + lastNode.pNext = &item; + } + else { + this->pFirst = &item; + } + + this->pLast = &item; + + this->itemCount++; +} + +// +// tsDLList::insertAfter () +// +// place item in the list immediately after itemBefore +// +template +inline void tsDLList::insertAfter (T &item, T &itemBefore) +{ + tsDLNode &nodeItem = item; + tsDLNode &nodeBefore = itemBefore; + + nodeItem.pPrev = &itemBefore; + nodeItem.pNext = nodeBefore.pNext; + nodeBefore.pNext = &item; + + if (nodeItem.pNext) { + tsDLNode *pNextNode = nodeItem.pNext; + pNextNode->pPrev = &item; + } + else { + this->pLast = &item; + } + + this->itemCount++; +} + +// +// tsDLList::insertBefore () +// +// place item in the list immediately before itemAfter +// +template +inline void tsDLList::insertBefore (T &item, T &itemAfter) +{ + tsDLNode &node = item; + tsDLNode &nodeAfter = itemAfter; + + node.pNext = &itemAfter; + node.pPrev = nodeAfter.pPrev; + nodeAfter.pPrev = &item; + + if (node.pPrev) { + tsDLNode &prevNode = *node.pPrev; + prevNode.pNext = &item; + } + else { + this->pFirst = &item; + } + + this->itemCount++; +} + +// +// tsDLList::push () +// +// adds pushList to the beginning of the list +// (and removes all items from pushList) +// +template +inline void tsDLList::push ( tsDLList & pushList ) +{ + if ( pushList.itemCount != 0u ) { + if ( this->itemCount ) { + tsDLNode * pFirstNode = this->pFirst; + tsDLNode * pAddListLastNode = pushList.pLast; + pFirstNode->pPrev = pushList.pLast; + pAddListLastNode->pNext = this->pFirst; + } + else { + this->pLast = pushList.pLast; + } + this->pFirst = pushList.pFirst; + this->itemCount += pushList.itemCount; + pushList.clear(); + } +} + +// +// tsDLList::push () +// +// add an item at the beginning of the list +// +template +inline void tsDLList::push (T &item) +{ + tsDLNode &theNode = item; + theNode.pPrev = 0; + theNode.pNext = this->pFirst; + + if ( this->itemCount ) { + tsDLNode *pFirstNode = this->pFirst; + pFirstNode->pPrev = & item; + } + else { + this->pLast = & item; + } + + this->pFirst = &item; + + this->itemCount++; +} + +// +// tsDLList::find () +// returns -1 if the item isnt on the list +// and the node number (beginning with zero if +// it is) +// +template < class T > +int tsDLList < T > :: find ( const T &item ) const +{ + tsDLIterConst < T > thisItem ( &item ); + tsDLIterConst < T > iter ( this->first () ); + int itemNo = 0; + + while ( iter.valid () ) { + if ( iter == thisItem ) { + return itemNo; + } + itemNo++; + iter++; + } + return -1; +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: firstIter () const +{ + return tsDLIterConst < T > ( this->pFirst ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: firstIter () +{ + return tsDLIter < T > ( this->pFirst ); +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: lastIter () const +{ + return tsDLIterConst < T > ( this->pLast ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: lastIter () +{ + return tsDLIter < T > ( this->pLast ); +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: invalidConstIter () +{ + return tsDLIterConst < T > ( 0 ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: invalidIter () +{ + return tsDLIter < T > ( 0 ); +} + +////////////////////////////////////////// +// tsDLIterConst member functions +////////////////////////////////////////// +template +inline tsDLIterConst::tsDLIterConst ( const T *pInitialEntry ) : + pEntry ( pInitialEntry ) {} + +template +inline tsDLIterConst::tsDLIterConst () : + pEntry ( 0 ) {} + +template +inline bool tsDLIterConst::valid () const +{ + return this->pEntry != 0; +} + +template +inline bool tsDLIterConst::operator == (const tsDLIterConst &rhs) const +{ + return this->pEntry == rhs.pEntry; +} + +template +inline bool tsDLIterConst::operator != (const tsDLIterConst &rhs) const +{ + return this->pEntry != rhs.pEntry; +} + +template +inline tsDLIterConst & tsDLIterConst::operator = ( const tsDLIterConst & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template +inline const T & tsDLIterConst::operator * () const +{ + return *this->pEntry; +} + +template +inline const T * tsDLIterConst::operator -> () const +{ + return this->pEntry; +} + +// +// prefix ++ +// +template +inline tsDLIterConst & tsDLIterConst::operator ++ () +{ + const tsDLNode &node = *this->pEntry; + this->pEntry = node.pNext; + return *this; +} + +// +// postfix ++ +// +template +inline tsDLIterConst tsDLIterConst::operator ++ (int) +{ + const tsDLIterConst tmp = *this; + const tsDLNode &node = *this->pEntry; + this->pEntry = node.pNext; + return tmp; +} + +// +// prefix -- +// +template +inline tsDLIterConst & tsDLIterConst::operator -- () +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return *this; +} + +// +// postfix -- +// +template +inline tsDLIterConst tsDLIterConst::operator -- (int) +{ + const tsDLIterConst tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return tmp; +} + +template +inline const T * tsDLIterConst::pointer () const +{ + return this->pEntry; +} + +////////////////////////////////////////// +// tsDLIter member functions +////////////////////////////////////////// + +template +inline tsDLIter::tsDLIter ( T * pInitialEntry ) : + pEntry ( pInitialEntry ) {} + +template +inline tsDLIter::tsDLIter () : + pEntry ( 0 ) {} + +template +inline bool tsDLIter::valid () const +{ + return this->pEntry != 0; +} + +template +inline bool tsDLIter::operator == (const tsDLIter &rhs) const +{ + return this->pEntry == rhs.pEntry; +} + +template +inline bool tsDLIter::operator != (const tsDLIter &rhs) const +{ + return this->pEntry != rhs.pEntry; +} + +template +inline tsDLIter & tsDLIter::operator = ( const tsDLIter & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template +inline T & tsDLIter::operator * () const +{ + return *this->pEntry; +} + +template +inline T * tsDLIter::operator -> () const +{ + return this->pEntry; +} + +template +inline tsDLIter & tsDLIter::operator ++ () // prefix ++ +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pNext; + return *this; +} + +template +inline tsDLIter tsDLIter::operator ++ (int) // postfix ++ +{ + const tsDLIter tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pNext; + return tmp; +} + +template +inline tsDLIter & tsDLIter::operator -- () // prefix -- +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return *this; +} + +template +inline tsDLIter tsDLIter::operator -- (int) // postfix -- +{ + const tsDLIter tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return tmp; +} + +template +inline T * tsDLIter::pointer () const +{ + return this->pEntry; +} + +#endif // tsDLListH_include + diff --git a/modules/libcom/src/cxxTemplates/tsFreeList.h b/modules/libcom/src/cxxTemplates/tsFreeList.h new file mode 100644 index 000000000..55787ac6b --- /dev/null +++ b/modules/libcom/src/cxxTemplates/tsFreeList.h @@ -0,0 +1,191 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef tsFreeList_h +#define tsFreeList_h + +/* + * Author: Jeff Hill + */ + +// +// TODO: this should allow free list chaining so that a free +// list (in a different lock domain) is used to provide the +// tsFreeListChunk. +// + +// +// To allow your class to be allocated off of a free list +// using the new operator: +// +// 1) add the following static, private free list data members +// to your class +// +// static tsFreeList < class classXYZ > freeList; +// +// 2) add the following member functions to your class +// +// inline void * classXYZ::operator new ( size_t size ) +// { +// return freeList.allocate ( size ); +// } +// +// inline void classXYZ::operator delete ( void *pCadaver, size_t size ) +// { +// freeList.release ( pCadaver, size ); +// } +// +// NOTES: +// +// 1) A NOOP mutex class may be specified if mutual exclusion isnt required +// +// 2) If you wish to force use of the new operator, then declare your class's +// destructor as a protected member function. +// +// 3) Setting N to zero causes the free list to be bypassed +// + +#ifdef EPICS_FREELIST_DEBUG +# define tsFreeListDebugBypass 1 +# define tsFreeListMemSetNew(P,SIZE) memset ( (P), 0xaa, (SIZE) ) +# define tsFreeListMemSetDelete(P,SIZE) memset ( (P), 0xdd, (SIZE) ) +#else +# define tsFreeListDebugBypass 0 +# define tsFreeListMemSetNew(P,SIZE) +# define tsFreeListMemSetDelete(P,SIZE) +#endif + +#include +#include "string.h" + +#include "compilerDependencies.h" +#include "epicsMutex.h" +#include "epicsGuard.h" + +// ms visual studio 6.0 and before incorrectly +// warn about a missing delete operator if only the +// newly preferred delete operator with a size argument +// is present +#if defined ( _MSC_VER ) && _MSC_VER <= 1200 +# pragma warning ( disable : 4291 ) +#endif + +template < class T > union tsFreeListItem; +template < class T, unsigned N> struct tsFreeListChunk; + +template < class T, unsigned N = 0x400, + class MUTEX = epicsMutex > +class tsFreeList { +public: + tsFreeList (); + ~tsFreeList (); + void * allocate ( size_t size ); + void release ( void * p ); + void release ( void * p, size_t size ); +private: + MUTEX mutex; + tsFreeListItem < T > * pFreeList; + tsFreeListChunk < T, N > * pChunkList; + void * allocateFromNewChunk (); +}; + +template < class T > +union tsFreeListItem { +public: + char pad [ sizeof ( T ) ]; + tsFreeListItem < T > * pNext; +}; + +template < class T, unsigned N = 0x400 > +struct tsFreeListChunk { + tsFreeListItem < T > items [N]; + tsFreeListChunk < T, N > * pNext; +}; + +template < class T, unsigned N, class MUTEX > +inline tsFreeList < T, N, MUTEX > :: tsFreeList () : + pFreeList ( 0 ), pChunkList ( 0 ) {} + +template < class T, unsigned N, class MUTEX > +tsFreeList < T, N, MUTEX > :: ~tsFreeList () +{ + while ( tsFreeListChunk < T, N > *pChunk = this->pChunkList ) { + this->pChunkList = this->pChunkList->pNext; + delete pChunk; + } +} + +template < class T, unsigned N, class MUTEX > +void * tsFreeList < T, N, MUTEX >::allocate ( size_t size ) +{ + if ( size != sizeof ( T ) || N == 0u || tsFreeListDebugBypass ) { + void * p = ::operator new ( size ); + tsFreeListMemSetNew ( p, size ); + return p; + } + + epicsGuard < MUTEX > guard ( this->mutex ); + + tsFreeListItem < T > * p = this->pFreeList; + if ( p ) { + this->pFreeList = p->pNext; + return static_cast < void * > ( p ); + } + return this->allocateFromNewChunk (); +} + +template < class T, unsigned N, class MUTEX > +void * tsFreeList < T, N, MUTEX >::allocateFromNewChunk () +{ + tsFreeListChunk < T, N > * pChunk = + new tsFreeListChunk < T, N >; + + for ( unsigned i=1u; i < N-1; i++ ) { + pChunk->items[i].pNext = &pChunk->items[i+1]; + } + pChunk->items[N-1].pNext = 0; + if ( N > 1 ) { + this->pFreeList = &pChunk->items[1u]; + } + pChunk->pNext = this->pChunkList; + this->pChunkList = pChunk; + + return static_cast ( &pChunk->items[0] ); +} + +template < class T, unsigned N, class MUTEX > +void tsFreeList < T, N, MUTEX >::release ( void * pCadaver, size_t size ) +{ + if ( size != sizeof ( T ) ) { + tsFreeListMemSetDelete ( pCadaver, size ); + ::operator delete ( pCadaver ); + } + else { + this->release ( pCadaver ); + } +} + +template < class T, unsigned N, class MUTEX > +void tsFreeList < T, N, MUTEX >::release ( void * pCadaver ) +{ + if ( N == 0u || tsFreeListDebugBypass ) { + tsFreeListMemSetDelete ( pCadaver, sizeof ( T ) ); + ::operator delete ( pCadaver ); + } + else if ( pCadaver ) { + epicsGuard < MUTEX > guard ( this->mutex ); + tsFreeListItem < T > * p = + static_cast < tsFreeListItem < T > * > ( pCadaver ); + p->pNext = this->pFreeList; + this->pFreeList = p; + } +} + +#endif // tsFreeList_h diff --git a/modules/libcom/src/cxxTemplates/tsMinMax.h b/modules/libcom/src/cxxTemplates/tsMinMax.h new file mode 100644 index 000000000..5adac3ef2 --- /dev/null +++ b/modules/libcom/src/cxxTemplates/tsMinMax.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// simple type safe inline template functions to replace +// the min() and max() macros +// + +#ifndef tsMinMaxh +#define tsMinMaxh + +template +inline const T & tsMax ( const T & a, const T & b ) +{ + return ( a > b ) ? a : b; +} + +template +inline const T & tsMin ( const T & a, const T & b ) +{ + return ( a < b ) ? a : b; +} + +#endif // tsMinMaxh diff --git a/modules/libcom/src/cxxTemplates/tsSLList.h b/modules/libcom/src/cxxTemplates/tsSLList.h new file mode 100644 index 000000000..ce29a6f5c --- /dev/null +++ b/modules/libcom/src/cxxTemplates/tsSLList.h @@ -0,0 +1,443 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * type safe singly linked list templates + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tsSLListh +#define tsSLListh + +#ifndef assert // allow use of epicsAssert.h +#include +#endif + +// +// the hp compiler complains about parameterized friend +// class that has not been declared without this? +// +template < class T > class tsSLList; +template < class T > class tsSLIter; +template < class T > class tsSLIterConst; + +// +// tsSLNode +// NOTE: class T must derive from tsSLNode +// +template +class tsSLNode { +public: + tsSLNode (); + tsSLNode < T > & operator = ( const tsSLNode < T > & ); +private: + void removeNextItem (); // removes the item after this node + T *pNext; + tsSLNode ( const tsSLNode < T > & ); + friend class tsSLList < T >; + friend class tsSLIter < T >; + friend class tsSLIterConst < T >; +}; + + +// +// tsSLList +// NOTE: class T must derive from tsSLNode +// +template < class T > +class tsSLList : public tsSLNode < T > { +public: + tsSLList (); // creates an empty list + tsSLList ( tsSLList & ); + void insert ( T &item, tsSLNode < T > &itemBefore ); // insert after item before + void add ( T &item ); // add to the beginning of the list + T * get (); // remove from the beginning of the list + T * pop (); // same as get + void push ( T &item ); // same as add + T * first () const; + void remove ( T &itemBefore ); + tsSLIterConst firstIter () const; + tsSLIter firstIter (); + static tsSLIterConst invalidConstIter (); + static tsSLIter invalidIter (); +private: + const tsSLList < T > & operator = ( const tsSLList < T > & ); +}; + +// +// tsSLIterConst +// +template < class T > +class tsSLIterConst { +public: + tsSLIterConst (); + bool valid () const; + bool operator == (const tsSLIterConst &rhs) const; + bool operator != (const tsSLIterConst &rhs) const; + tsSLIterConst & operator = (const tsSLIterConst &); + const T & operator * () const; + const T * operator -> () const; + tsSLIterConst & operator ++ (); + tsSLIterConst operator ++ (int); + const T * pointer () const; +protected: + const T * pEntry; + tsSLIterConst ( const T *pInitialEntry ); + friend class tsSLList < T >; +}; + +// +// tsSLIter +// +template < class T > +class tsSLIter { +public: + tsSLIter (); + bool valid () const; + bool operator == (const tsSLIter &rhs) const; + bool operator != (const tsSLIter &rhs) const; + tsSLIter & operator = (const tsSLIter &); + T & operator * () const; + T * operator -> () const; + tsSLIter & operator ++ (); + tsSLIter operator ++ (int); + T * pointer () const; +private: + T *pEntry; + tsSLIter ( T *pInitialEntry ); + friend class tsSLList < T >; +}; + +////////////////////////////////////////// +// +// tsSLNode inline member functions +// +////////////////////////////////////////// + +// +// tsSLNode::tsSLNode () +// +template < class T > +inline tsSLNode < T > :: tsSLNode () : pNext ( 0 ) {} + +// +// tsSLNode::tsSLNode ( const tsSLNode < T > & ) +// private - not to be used - implemented to eliminate warnings +// +template < class T > +inline tsSLNode < T > :: tsSLNode ( const tsSLNode < T > & ) +{ +} + +// +// tsSLNode::operator = +// +// when someone copies into a class deriving from this +// do _not_ change the node pointers +// +template < class T > +inline tsSLNode < T > & tsSLNode < T >::operator = + ( const tsSLNode < T > & ) +{ + return *this; +} + +// +// removeNextItem () +// +// removes the item after this node +// +template +inline void tsSLNode::removeNextItem () +{ + T *pItem = this->pNext; + if ( pItem ) { + tsSLNode < T > *pNode = pItem; + this->pNext = pNode->pNext; + } +} + +////////////////////////////////////////// +// +// tsSLList inline member functions +// +////////////////////////////////////////// + +// +// tsSLList::tsSLList() +// create an empty list +// +template < class T > +inline tsSLList < T > :: tsSLList () +{ +} + +// +// tsSLList::tsSLList( tsSLList & ) +// +template < class T > +inline tsSLList < T > :: tsSLList ( tsSLList &listIn ) +{ + this->pNext = listIn.pNext; + listIn.pNext = 0; +} + +// +// tsSLList::insert() +// (itemBefore might be the list header object and therefore +// will not always be of type T) +// +template < class T > +inline void tsSLList < T > :: insert ( T &item, tsSLNode < T > &itemBefore ) +{ + tsSLNode < T > &node = item; + node.pNext = itemBefore.pNext; + itemBefore.pNext = &item; +} + +// +// tsSLList::add () +// +template < class T > +inline void tsSLList < T > :: add ( T &item ) +{ + this->insert ( item, *this ); +} + +// +// tsSLList::get () +// +template < class T > +inline T * tsSLList < T > :: get () +{ + tsSLNode < T > *pThisNode = this; + T *pItem = pThisNode->pNext; + pThisNode->removeNextItem (); + return pItem; +} + +// +// tsSLList::pop () +// +template < class T > +inline T * tsSLList < T > :: pop () +{ + return this->get (); +} + +// +// tsSLList::push () +// +template +inline void tsSLList < T > :: push ( T &item ) +{ + this->add (item); +} + +template +inline T * tsSLList < T > :: first () const +{ + const tsSLNode < T > *pThisNode = this; + return pThisNode->pNext; +} + +template +inline void tsSLList < T > :: remove ( T &itemBefore ) +{ + tsSLNode < T > *pBeforeNode = &itemBefore; + tsSLNode < T > *pAfterNode = pBeforeNode->pNext; + pBeforeNode->pNext = pAfterNode->pNext; +} + +template +inline tsSLIterConst tsSLList < T > :: firstIter () const +{ + const tsSLNode < T > *pThisNode = this; + return tsSLIterConst ( pThisNode->pNext ); +} + +template +inline tsSLIter tsSLList < T > :: firstIter () +{ + tsSLNode < T > *pThisNode = this; + return tsSLIter ( pThisNode->pNext ); +} + +template +inline tsSLIterConst tsSLList < T > :: invalidConstIter () +{ + return tsSLIterConst ( 0 ); +} + +template +inline tsSLIter tsSLList < T > :: invalidIter () +{ + return tsSLIter ( 0 ); +} + +////////////////////////////////////////// +// +// tsSLIterConst inline member functions +// +////////////////////////////////////////// + +template < class T > +inline tsSLIterConst::tsSLIterConst ( const T *pInitialEntry ) : + pEntry ( pInitialEntry ) +{ +} + +template < class T > +inline tsSLIterConst::tsSLIterConst () : + pEntry ( 0 ) +{ +} + +template < class T > +inline bool tsSLIterConst::valid () const +{ + return this->pEntry != 0; +} + +template < class T > +inline bool tsSLIterConst::operator == ( const tsSLIterConst &rhs ) const +{ + return this->pEntry == rhs.pConstEntry; +} + +template < class T > +inline bool tsSLIterConst::operator != (const tsSLIterConst &rhs) const +{ + return this->pEntry != rhs.pConstEntry; +} + +template < class T > +inline tsSLIterConst & tsSLIterConst::operator = ( const tsSLIterConst & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template < class T > +inline const T & tsSLIterConst::operator * () const +{ + return *this->pEntry; +} + +template < class T > +inline const T * tsSLIterConst::operator -> () const +{ + return this->pEntry; +} + +template < class T > +inline tsSLIterConst & tsSLIterConst::operator ++ () // prefix ++ +{ + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return *this; +} + +template < class T > +inline tsSLIterConst tsSLIterConst::operator ++ ( int ) // postfix ++ +{ + const tsSLIterConst tmp = *this; + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return tmp; +} + +template +inline const T * tsSLIterConst < T > :: pointer () const +{ + return this->pEntry; +} + +////////////////////////////////////////// +// +// tsSLIter inline member functions +// +////////////////////////////////////////// + +template < class T > +inline tsSLIter::tsSLIter ( T *pInitialEntry ) : + pEntry ( pInitialEntry ) +{ +} + +template < class T > +inline tsSLIter::tsSLIter () : + pEntry ( 0 ) +{ +} + +template < class T > +inline bool tsSLIter::valid () const +{ + return this->pEntry != 0; +} + +template < class T > +inline bool tsSLIter::operator == ( const tsSLIter &rhs ) const +{ + return this->pEntry == rhs.pEntry; +} + +template < class T > +inline bool tsSLIter::operator != ( const tsSLIter &rhs ) const +{ + return this->pEntry != rhs.pEntry; +} + +template < class T > +inline tsSLIter & tsSLIter::operator = ( const tsSLIter & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template < class T > +inline T & tsSLIter::operator * () const +{ + return *this->pEntry; +} + +template < class T > +inline T * tsSLIter::operator -> () const +{ + return this->pEntry; +} + +template < class T > +inline tsSLIter & tsSLIter::operator ++ () // prefix ++ +{ + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return *this; +} + +template < class T > +inline tsSLIter tsSLIter::operator ++ ( int ) // postfix ++ +{ + const tsSLIter tmp = *this; + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return tmp; +} + +template +inline T * tsSLIter < T > :: pointer () const +{ + return this->pEntry; +} + +#endif // tsSLListh diff --git a/modules/libcom/src/dbmf/Makefile b/modules/libcom/src/dbmf/Makefile new file mode 100644 index 000000000..2a190eac6 --- /dev/null +++ b/modules/libcom/src/dbmf/Makefile @@ -0,0 +1,13 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/dbmf +INC += dbmf.h +Com_SRCS += dbmf.c + diff --git a/modules/libcom/src/dbmf/dbmf.c b/modules/libcom/src/dbmf/dbmf.c new file mode 100644 index 000000000..e04a7bedf --- /dev/null +++ b/modules/libcom/src/dbmf/dbmf.c @@ -0,0 +1,299 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski and Marty Kraimer + * Date: 4/97 + * + * Intended for applications that create and free requently + * + */ + +#include +#include +#include +#include + +#include "valgrind/valgrind.h" + +#ifndef NVALGRIND +/* buffer around allocations to detect out of bounds access */ +#define REDZONE sizeof(double) +#else +#define REDZONE 0 +#endif + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsMutex.h" +#include "ellLib.h" +#include "dbmf.h" +/* +#define DBMF_FREELIST_DEBUG 1 +*/ +#ifndef DBMF_FREELIST_DEBUG + +/*Default values for dblfInit */ +#define DBMF_SIZE 64 +#define DBMF_INITIAL_ITEMS 10 + +typedef struct chunkNode {/*control block for each set of chunkItems*/ + ELLNODE node; + void *pchunk; + int nNotFree; +}chunkNode; + +typedef struct itemHeader{ + void *pnextFree; + chunkNode *pchunkNode; +}itemHeader; + +typedef struct dbmfPrivate { + ELLLIST chunkList; + epicsMutexId lock; + size_t size; + size_t allocSize; + int chunkItems; + size_t chunkSize; + int nAlloc; + int nFree; + int nGtSize; + void *freeList; +} dbmfPrivate; +dbmfPrivate dbmfPvt; +static dbmfPrivate *pdbmfPvt = NULL; +int dbmfDebug=0; + +int dbmfInit(size_t size, int chunkItems) +{ + if(pdbmfPvt) { + printf("dbmfInit: Already initialized\n"); + return(-1); + } + pdbmfPvt = &dbmfPvt; + ellInit(&pdbmfPvt->chunkList); + pdbmfPvt->lock = epicsMutexMustCreate(); + /*allign to at least a double*/ + pdbmfPvt->size = size + size%sizeof(double); + /* layout is + * | itemHeader | REDZONE | size | REDZONE | + */ + pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader) + 2*REDZONE; + pdbmfPvt->chunkItems = chunkItems; + pdbmfPvt->chunkSize = pdbmfPvt->allocSize * pdbmfPvt->chunkItems; + pdbmfPvt->nAlloc = 0; + pdbmfPvt->nFree = 0; + pdbmfPvt->nGtSize = 0; + pdbmfPvt->freeList = NULL; + VALGRIND_CREATE_MEMPOOL(pdbmfPvt, REDZONE, 0); + return(0); +} + + +void* dbmfMalloc(size_t size) +{ + void **pnextFree; + void **pfreeList; + char *pmem = NULL; + chunkNode *pchunkNode; + itemHeader *pitemHeader; + + if(!pdbmfPvt) dbmfInit(DBMF_SIZE,DBMF_INITIAL_ITEMS); + epicsMutexMustLock(pdbmfPvt->lock); + pfreeList = &pdbmfPvt->freeList; + if(*pfreeList == NULL) { + int i; + size_t nbytesTotal; + + if(dbmfDebug) printf("dbmfMalloc allocating new storage\n"); + nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode); + pmem = (char *)malloc(nbytesTotal); + if(!pmem) { + epicsMutexUnlock(pdbmfPvt->lock); + cantProceed("dbmfMalloc malloc failed\n"); + return(NULL); + } + pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize); + pchunkNode->pchunk = pmem; + pchunkNode->nNotFree=0; + ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node); + for(i=0; ichunkItems; i++) { + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = pchunkNode; + pnextFree = &pitemHeader->pnextFree; + *pnextFree = *pfreeList; *pfreeList = (void *)pmem; + pdbmfPvt->nFree++; + pmem += pdbmfPvt->allocSize; + } + } + if(size<=pdbmfPvt->size) { + pnextFree = *pfreeList; *pfreeList = *pnextFree; + pmem = (void *)pnextFree; + pdbmfPvt->nAlloc++; pdbmfPvt->nFree--; + pitemHeader = (itemHeader *)pnextFree; + pitemHeader->pchunkNode->nNotFree += 1; + } else { + pmem = malloc(sizeof(itemHeader) + 2*REDZONE + size); + if(!pmem) { + epicsMutexUnlock(pdbmfPvt->lock); + cantProceed("dbmfMalloc malloc failed\n"); + return(NULL); + } + pdbmfPvt->nAlloc++; + pdbmfPvt->nGtSize++; + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = NULL; /* not part of free list */ + if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", + (unsigned long)size,pmem); + } + epicsMutexUnlock(pdbmfPvt->lock); + pmem += sizeof(itemHeader) + REDZONE; + VALGRIND_MEMPOOL_ALLOC(pdbmfPvt, pmem, size); + return((void *)pmem); +} + +char * dbmfStrdup(const char *str) +{ + size_t len = strlen(str); + char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */ + + return strcpy(buf, str); +} + +char * dbmfStrndup(const char *str, size_t len) +{ + char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */ + + buf[len] = '\0'; + return strncpy(buf, str, len); +} + +void dbmfFree(void* mem) +{ + char *pmem = (char *)mem; + chunkNode *pchunkNode; + itemHeader *pitemHeader; + + if(!mem) return; + if(!pdbmfPvt) { + printf("dbmfFree called but dbmfInit never called\n"); + return; + } + VALGRIND_MEMPOOL_FREE(pdbmfPvt, mem); + pmem -= sizeof(itemHeader) + REDZONE; + epicsMutexMustLock(pdbmfPvt->lock); + pitemHeader = (itemHeader *)pmem; + if(!pitemHeader->pchunkNode) { + if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem); + free((void *)pmem); pdbmfPvt->nAlloc--; + }else { + void **pfreeList = &pdbmfPvt->freeList; + void **pnextFree = &pitemHeader->pnextFree; + + pchunkNode = pitemHeader->pchunkNode; + pchunkNode->nNotFree--; + *pnextFree = *pfreeList; *pfreeList = pnextFree; + pdbmfPvt->nAlloc--; pdbmfPvt->nFree++; + } + epicsMutexUnlock(pdbmfPvt->lock); +} + +int dbmfShow(int level) +{ + if(pdbmfPvt==NULL) { + printf("Never initialized\n"); + return(0); + } + printf("size %lu allocSize %lu chunkItems %d ", + (unsigned long)pdbmfPvt->size, + (unsigned long)pdbmfPvt->allocSize,pdbmfPvt->chunkItems); + printf("nAlloc %d nFree %d nChunks %d nGtSize %d\n", + pdbmfPvt->nAlloc,pdbmfPvt->nFree, + ellCount(&pdbmfPvt->chunkList),pdbmfPvt->nGtSize); + if(level>0) { + chunkNode *pchunkNode; + + pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); + while(pchunkNode) { + printf("pchunkNode %p nNotFree %d\n", + (void*)pchunkNode,pchunkNode->nNotFree); + pchunkNode = (chunkNode *)ellNext(&pchunkNode->node); + } + } + if(level>1) { + void **pnextFree;; + + epicsMutexMustLock(pdbmfPvt->lock); + pnextFree = (void**)pdbmfPvt->freeList; + while(pnextFree) { + printf("%p\n",*pnextFree); + pnextFree = (void**)*pnextFree; + } + epicsMutexUnlock(pdbmfPvt->lock); + } + return(0); +} + +void dbmfFreeChunks(void) +{ + chunkNode *pchunkNode; + chunkNode *pnext;; + + if(!pdbmfPvt) { + printf("dbmfFreeChunks called but dbmfInit never called\n"); + return; + } + epicsMutexMustLock(pdbmfPvt->lock); + if(pdbmfPvt->nFree + != (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) { + printf("dbmfFinish: not all free\n"); + epicsMutexUnlock(pdbmfPvt->lock); + return; + } + pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); + while(pchunkNode) { + pnext = (chunkNode *)ellNext(&pchunkNode->node); + ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node); + free(pchunkNode->pchunk); + pchunkNode = pnext; + } + pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL; + epicsMutexUnlock(pdbmfPvt->lock); +} + +#else /* DBMF_FREELIST_DEBUG */ + +int dbmfInit(size_t size, int chunkItems) +{ return 0; } + +void* dbmfMalloc(size_t size) +{ return malloc(size); } + +char * dbmfStrdup(const char *str) +{ return strdup((char*)str); } + +void dbmfFree(void* mem) +{ free(mem); } + +int dbmfShow(int level) +{ return 0; } + +void dbmfFreeChunks(void) {} + +#endif /* DBMF_FREELIST_DEBUG */ + +char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs) +{ + size_t len = strlen(lhs) + strlen(mid) + strlen(rhs) + 1; + char *buf = dbmfMalloc(len); + strcpy(buf, lhs); + strcat(buf, mid); + strcat(buf, rhs); + return buf; +} + diff --git a/modules/libcom/src/dbmf/dbmf.h b/modules/libcom/src/dbmf/dbmf.h new file mode 100644 index 000000000..2c8a28f2c --- /dev/null +++ b/modules/libcom/src/dbmf/dbmf.h @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski and Marty Kraimer + * Date: 4/97 + * + * A library to manage storage that is allocated and then freed. + */ +#ifndef DBMF_H +#define DBMF_H + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int dbmfInit(size_t size, int chunkItems); +epicsShareFunc void * dbmfMalloc(size_t bytes); +epicsShareFunc char * dbmfStrdup(const char *str); +epicsShareFunc char * dbmfStrndup(const char *str, size_t len); +epicsShareFunc char * dbmfStrcat3(const char *lhs, const char *mid, + const char *rhs); +epicsShareFunc void dbmfFree(void *bytes); +epicsShareFunc void dbmfFreeChunks(void); +epicsShareFunc int dbmfShow(int level); + +/* Rules: + * 1) Size is always made a multiple of 8. + * 2) if dbmfInit is not called before one of the other routines then it + * is automatically called with size=64 and chunkItems=10 + * 3) These routines should only be used to allocate storage that will + * shortly thereafter be freed. + * 4) dbmfFreeChunks can only free chunks that contain only free items +*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/ellLib/Makefile b/modules/libcom/src/ellLib/Makefile new file mode 100644 index 000000000..9a5cb3e67 --- /dev/null +++ b/modules/libcom/src/ellLib/Makefile @@ -0,0 +1,13 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/ellLib +INC += ellLib.h +Com_SRCS += ellLib.c +Com_SRCS += ellSort.c diff --git a/modules/libcom/src/ellLib/ellLib.c b/modules/libcom/src/ellLib/ellLib.c new file mode 100644 index 000000000..c539a843f --- /dev/null +++ b/modules/libcom/src/ellLib/ellLib.c @@ -0,0 +1,331 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: John Winans (ANL) + * Date: 07-02-92 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "ellLib.h" + + /**************************************************************************** + * + * This function adds a node to the end of a linked list. + * + *****************************************************************************/ +void ellAdd (ELLLIST *pList, ELLNODE *pNode) +{ + pNode->next = NULL; + pNode->previous = pList->node.previous; + + if (pList->count) + pList->node.previous->next = pNode; + else + pList->node.next = pNode; + + pList->node.previous = pNode; + pList->count++; + + return; +} + /**************************************************************************** + * + * This function concatinates the second linked list to the end of the first + * list. The second list is left empty. Either list (or both) lists may + * be empty at the begining of the operation. + * + *****************************************************************************/ +void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList) +{ + if (pAddList->count == 0) + return; /* Add list is empty, nothing to add. */ + + if (pDstList->count == 0) { + /* Destination list is empty... just transfer the add list over. */ + pDstList->node.next = pAddList->node.next; + pDstList->node.previous = pAddList->node.previous; + pDstList->count = pAddList->count; + } else { + /* Destination list not empty... append the add list. */ + pDstList->node.previous->next = pAddList->node.next; + pAddList->node.next->previous = pDstList->node.previous; + pDstList->node.previous = pAddList->node.previous; + pDstList->count += pAddList->count; + } + + pAddList->count = 0; + pAddList->node.next = NULL; + pAddList->node.previous = NULL; + + return; +} + /**************************************************************************** + * + * This function deletes a specific node from a specified list; + * + *****************************************************************************/ +void ellDelete (ELLLIST *pList, ELLNODE *pNode) +{ + if (pList->node.previous == pNode) + pList->node.previous = pNode->previous; + else + pNode->next->previous = pNode->previous; + + if (pList->node.next == pNode) + pList->node.next = pNode->next; + else + pNode->previous->next = pNode->next; + + pList->count--; + + return; +} + /**************************************************************************** + * + * This function extracts a sublist that starts with pStartNode and ends with + * pEndNode from pSrcList and places it in pDstList. + * + * WRS is unclear as to what happens when pDstList is non-empty at the start + * of the operation. We will place the extracted list at the END of pDstList + * when it is non-empty. + * + *****************************************************************************/ +void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList) +{ + ELLNODE *pnode; + int count; + + /* Cut the list out of the source list (update count later) */ + if (pStartNode->previous != NULL) + pStartNode->previous->next = pEndNode->next; + else + pSrcList->node.next = pEndNode->next; + + if (pEndNode->next != NULL) { + pEndNode->next->previous = pStartNode->previous; + pEndNode->next = NULL; + } + else + pSrcList->node.previous = pStartNode->previous; + + /* Place the sublist into the destination list */ + pStartNode->previous = pDstList->node.previous; + if (pDstList->count) + pDstList->node.previous->next = pStartNode; + else + pDstList->node.next = pStartNode; + + pDstList->node.previous = pEndNode; + + /* Adjust the counts */ + pnode = pStartNode; + count = 1; + while(pnode != pEndNode) + { + pnode = pnode->next; + count++; + } + pSrcList->count -= count; + pDstList->count += count; + + return; +} + /**************************************************************************** + * + * This function returns the first node in the specified list. The node is + * removed from the list. If the list is empty, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellGet (ELLLIST *pList) +{ + ELLNODE *pnode = pList->node.next; + + if (pnode != NULL) + ellDelete(pList, pnode); + + return pnode; +} +/**************************************************************************** +* +* This function returns the last node in the specified list. The node is +* removed from the list. If the list is empty, NULL will be returned. +* +*****************************************************************************/ +ELLNODE * ellPop (ELLLIST *pList) +{ + ELLNODE *pnode = pList->node.previous; + + if (pnode != NULL) + ellDelete(pList, pnode); + + return pnode; +} + /**************************************************************************** + * + * This function inserts the specified node pNode after pPrev in the list + * plist. If pPrev is NULL, then pNode will be inserted at the head of the + * list. + * + *****************************************************************************/ +void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode) +{ + if (pPrev != NULL) { + pNode->previous = pPrev; + pNode->next = pPrev->next; + pPrev->next = pNode; + } else { + pNode->previous = NULL; + pNode->next = plist->node.next; + plist->node.next = pNode; + } + + if (pNode->next == NULL) + plist->node.previous = pNode; + else + pNode->next->previous = pNode; + + plist->count++; + + return; +} + /**************************************************************************** + * + * This function returns the nodeNum'th element in pList. If there is no + * nodeNum'th node in the list, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellNth (ELLLIST *pList, int nodeNum) +{ + ELLNODE *pnode; + + if ((nodeNum < 1) || (pList->count == 0)) + return NULL; + + if (nodeNum > pList->count/2) { + if (nodeNum > pList->count) + return NULL; + + pnode = pList->node.previous; + nodeNum = pList->count - nodeNum; + while(nodeNum) { + pnode = pnode->previous; + nodeNum--; + } + return pnode; + } + + pnode = pList->node.next; + while (--nodeNum > 0) + pnode = pnode->next; + + return pnode; +} + /**************************************************************************** + * + * This function returns the node, nStep nodes forward from pNode. If there is + * no node that many steps from pNode, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellNStep (ELLNODE *pNode, int nStep) +{ + if (nStep > 0) { + while ((pNode != NULL) && nStep) { + pNode = pNode->next; + nStep--; + } + } else { + while ((pNode != NULL) && nStep) { + pNode = pNode->previous; + nStep++; + } + } + return pNode; +} + /**************************************************************************** + * + * This function returns the node number of pNode within pList. If the node is + * not in pList, -1 is returned. Note that the first node is 1. + * + *****************************************************************************/ +int ellFind (ELLLIST *pList, ELLNODE *pNode) +{ + ELLNODE *got = pList->node.next; + int count = 1; + + while ((got != pNode) && (got != NULL)) { + got = got->next; + count++; + } + if (got == NULL) + return -1; + + return count; +} + /**************************************************************************** + * + * This function frees the nodes in a list. It makes the list into an empty + * list, and calls freeFunc() for all the nodes that are (were) in that list. + * + * NOTE: the nodes in the list are free()'d on the assumption that the node + * structures were malloc()'d one-at-a-time and that the ELLNODE structure is + * the first member of the parent structure. + * + *****************************************************************************/ +void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc) +{ + ELLNODE *nnode = pList->node.next; + ELLNODE *pnode; + + while (nnode != NULL) + { + pnode = nnode; + nnode = nnode->next; + freeFunc(pnode); + } + pList->node.next = NULL; + pList->node.previous = NULL; + pList->count = 0; +} + + /**************************************************************************** + * + * This function verifies that the list is consistent. + * joh 12-12-97 + * + *****************************************************************************/ +void ellVerify (ELLLIST *pList) +{ + ELLNODE *pNode; + ELLNODE *pNext; + int count = 0; + + assert (pList); + + pNode = ellFirst(pList); + if (pNode) { + assert (ellPrevious(pNode) == NULL); + while (1) { + count++; + pNext = ellNext(pNode); + if (pNext) { + assert (ellPrevious(pNext) == pNode); + } else { + break; + } + pNode = pNext; + } + assert (ellNext(pNode) == NULL); + } + + assert (pNode == ellLast(pList)); + assert (count == ellCount(pList)); +} diff --git a/modules/libcom/src/ellLib/ellLib.h b/modules/libcom/src/ellLib/ellLib.h new file mode 100644 index 000000000..52b58d169 --- /dev/null +++ b/modules/libcom/src/ellLib/ellLib.h @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: John Winans (ANL) + * Andrew Johnson + */ +#ifndef INC_ellLib_H +#define INC_ellLib_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ELLNODE { + struct ELLNODE *next; + struct ELLNODE *previous; +} ELLNODE; + +#define ELLNODE_INIT {NULL, NULL} + +typedef struct ELLLIST { + ELLNODE node; + int count; +} ELLLIST; + +#define ELLLIST_INIT {ELLNODE_INIT, 0} + +typedef void (*FREEFUNC)(void *); + +#define ellInit(PLIST) {\ + (PLIST)->node.next = (PLIST)->node.previous = NULL;\ + (PLIST)->count = 0;\ +} +#define ellCount(PLIST) ((PLIST)->count) +#define ellFirst(PLIST) ((PLIST)->node.next) +#define ellLast(PLIST) ((PLIST)->node.previous) +#define ellNext(PNODE) ((PNODE)->next) +#define ellPrevious(PNODE) ((PNODE)->previous) +#define ellFree(PLIST) ellFree2(PLIST, free) + +epicsShareFunc void ellAdd (ELLLIST *pList, ELLNODE *pNode); +epicsShareFunc void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList); +epicsShareFunc void ellDelete (ELLLIST *pList, ELLNODE *pNode); +epicsShareFunc void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList); +epicsShareFunc ELLNODE * ellGet (ELLLIST *pList); +epicsShareFunc ELLNODE * ellPop (ELLLIST *pList); +epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode); +epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum); +epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep); +epicsShareFunc int ellFind (ELLLIST *pList, ELLNODE *pNode); +typedef int (*pListCmp)(const ELLNODE* A, const ELLNODE* B); +epicsShareFunc void ellSortStable(ELLLIST *pList, pListCmp); +epicsShareFunc void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc); +epicsShareFunc void ellVerify (ELLLIST *pList); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_ellLib_H */ diff --git a/modules/libcom/src/ellLib/ellSort.c b/modules/libcom/src/ellLib/ellSort.c new file mode 100644 index 000000000..2f8f2748b --- /dev/null +++ b/modules/libcom/src/ellLib/ellSort.c @@ -0,0 +1,83 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Assoc., as Operator of Argonne +* National Laboratory. +* Copyright (c) 2016 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Use of mergesort algorithm based on analysis by + * http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html + */ +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "ellLib.h" + +static void ellMoveN(ELLLIST* pTo, ELLLIST* pFrom, int count ) +{ + for(;count && ellCount(pFrom); count--) { + ELLNODE *node = ellGet(pFrom); + ellAdd(pTo, node); + } +} + +/* Stable (MergeSort) to given list. + * The comparison function cmp(A,B) is expected + * to return -1 for AB. + */ +void ellSortStable(ELLLIST *pList, pListCmp cmp) +{ + ELLLIST INP, P, Q; + size_t insize = 1; /* initial sub-list size */ + if(ellCount(pList)<=1) + return; + + ellInit(&INP); + ellInit(&P); + ellInit(&Q); + + /* Process is to iteratively sort + * a sequence of sub-lists of size 'insize' + */ + + while(insize < ellCount(pList)) { + + assert(ellCount(&INP)==0); + + /* shift previous results to inputs */ + ellConcat(&INP, pList); + + while(ellCount(&INP)) + { + ELLNODE *p, *q; + + /* Pull out the next pair of sub-lists */ + ellMoveN(&Q, &INP, insize); + ellMoveN(&P, &INP, insize); + + /* merge these sub-lists */ + while((p=ellFirst(&P)) && (q=ellFirst(&Q))) + { + if((*cmp)(p,q) < 0) { + ellAdd(pList, ellGet(&P)); + } else { + ellAdd(pList, ellGet(&Q)); + } + } + + /* concatenate any remaining to result */ + if(ellFirst(&P)) + ellConcat(pList, &P); + else if(ellFirst(&Q)) + ellConcat(pList, &Q); + + assert(!ellFirst(&P) && !ellFirst(&Q)); + } + + insize *= 2; + } + +} diff --git a/modules/libcom/src/env/Makefile b/modules/libcom/src/env/Makefile new file mode 100644 index 000000000..804426bc0 --- /dev/null +++ b/modules/libcom/src/env/Makefile @@ -0,0 +1,22 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/env + +vpath %.pl $(USR_VPATH) $(SRC_DIRS) + +PERL_SCRIPTS += bldEnvData.pl +PERL_SCRIPTS += libcomModuleDirs.pm + +INC += envDefs.h + +Com_SRCS += envSubr.c +Com_SRCS += envData.c + +CLEANS += envData.c diff --git a/modules/libcom/src/env/RULES b/modules/libcom/src/env/RULES new file mode 100644 index 000000000..02df6aa7a --- /dev/null +++ b/modules/libcom/src/env/RULES @@ -0,0 +1,15 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +envData.c: $(LIBCOM)/env/envDefs.h \ + $(INSTALL_HOST_BIN)/bldEnvData.pl $(INSTALL_HOST_BIN)/libcomModuleDirs.pm \ + $(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV \ + $(wildcard $(CONFIG)/os/CONFIG_SITE_ENV.$(T_A)) + $(PERL) $(INSTALL_HOST_BIN)/bldEnvData.pl $(QUIET_FLAG) -t $(T_A) \ + -c $(CMPLR_CLASS) -s $(OS_CLASS) $(CONFIG) diff --git a/modules/libcom/src/env/bldEnvData.pl b/modules/libcom/src/env/bldEnvData.pl new file mode 100644 index 000000000..e3c21b772 --- /dev/null +++ b/modules/libcom/src/env/bldEnvData.pl @@ -0,0 +1,141 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# Author: Kay-Uwe Kasemir +# Date: 1-30-97 + +use strict; + +use FindBin qw($Bin); +use lib ($Bin, "$Bin/../../lib/perl"); +use libcomModuleDirs; +no lib $Bin; + +use Getopt::Std; +use File::Basename; +use EPICS::Path; +use EPICS::Release; +use Text::Wrap; + +my $tool = basename($0); + +our ($opt_h, $opt_q, $opt_t, $opt_s, $opt_c); +our $opt_o = 'envData.c'; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; +$Text::Wrap::columns = 75; + +HELP_MESSAGE() unless getopts('ho:qt:s:c:') && @ARGV == 1; +HELP_MESSAGE() if $opt_h; + +my $config = AbsPath(shift); +my $env_defs = AbsPath('../env/envDefs.h'); + +# Parse the ENV_PARAM declarations in envDefs.h +# to get the param names we are interested in +# +open SRC, '<', $env_defs + or die "$tool: Cannot open $env_defs: $!\n"; + +my @vars; +while () { + if (m/epicsShareExtern\s+const\s+ENV_PARAM\s+([A-Za-z_]\w*)\s*;/) { + push @vars, $1; + } +} +close SRC; + +# A list of configure/CONFIG_* files to read +# +my @configs = ("$config/CONFIG_ENV", "$config/CONFIG_SITE_ENV"); + +if ($opt_t) { + my $config_arch_env = "$config/os/CONFIG_SITE_ENV.$opt_t"; + push @configs, $config_arch_env + if -f $config_arch_env; +} + +my @sources = ($env_defs, @configs); + +# Get values from the config files +# +my (%values, @dummy); +readRelease($_, \%values, \@dummy) foreach @configs; +expandRelease(\%values); + +# Get values from the command-line +# +$values{EPICS_BUILD_COMPILER_CLASS} = $opt_c if $opt_c; +$values{EPICS_BUILD_OS_CLASS} = $opt_s if $opt_s; +$values{EPICS_BUILD_TARGET_ARCH} = $opt_t if $opt_t; + +# Warn about vars with no configured value +# +my @undefs = grep {!exists $values{$_}} @vars; +warn "$tool: No value given for $_\n" foreach @undefs; + +print "Generating $opt_o\n" unless $opt_q; + +# Start creating the output +# +open OUT, '>', $opt_o + or die "$tool: Cannot create $opt_o: $!\n"; + +my $sources = join "\n", map {" * $_"} @sources; + +print OUT << "END"; +/* Generated file $opt_o + * + * Created from +$sources + */ + +#include +#define epicsExportSharedSymbols +#include "envDefs.h" + +END + +# Define a default value for each named parameter +# +foreach my $var (@vars) { + my $default = $values{$var}; + if (defined $default) { + $default =~ s/^"//; + $default =~ s/"$//; + } + else { + $default = ''; + } + + print OUT "epicsShareDef const ENV_PARAM $var =\n", + " {\"$var\", \"$default\"};\n"; +} + +# Also provide a list of all defined parameters +# +print OUT "\n", + "epicsShareDef const ENV_PARAM* env_param_list[] = {\n", + wrap(' ', ' ', join(', ', map("&$_", @vars), 'NULL')), + "\n};\n"; +close OUT; + +sub HELP_MESSAGE { + print STDERR "Usage: $tool [options] configure\n", + " -h Help: Print this message\n", + " -q Quiet: Only print errors\n", + " -o file Output filename, default is $opt_o\n", + " -t arch Target architecture \$(T_A) name\n", + " -s os Operating system \$(OS_CLASS)\n", + " -c comp Compiler class \$(CMPLR_CLASS)\n", + "\n"; + + exit 1; +} diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h new file mode 100644 index 000000000..13df94733 --- /dev/null +++ b/modules/libcom/src/env/envDefs.h @@ -0,0 +1,109 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Roger A. Cole + * Date: 07-20-91 + * + */ +/**************************************************************************** +* TITLE envDefs.h - definitions for environment get/set routines +* +* DESCRIPTION +* This file defines the environment parameters for EPICS. These +* ENV_PARAM's are created in envData.c by bldEnvData for +* use by EPICS programs running under UNIX and VxWorks. +* +* User programs can define their own environment parameters for their +* own use--the only caveat is that such parameters aren't automatically +* setup by EPICS. +* +*****************************************************************************/ + +#ifndef envDefsH +#define envDefsH + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +typedef struct envParam { + char *name; /* text name of the parameter */ + char *pdflt; +} ENV_PARAM; + +/* + * bldEnvData.pl looks for "epicsShareExtern const ENV_PARAM ;" + */ +epicsShareExtern const ENV_PARAM EPICS_CA_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CA_CONN_TMO; +epicsShareExtern const ENV_PARAM EPICS_CA_AUTO_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CA_REPEATER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_SERVER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_MAX_ARRAY_BYTES; +epicsShareExtern const ENV_PARAM EPICS_CA_AUTO_ARRAY_BYTES; +epicsShareExtern const ENV_PARAM EPICS_CA_MAX_SEARCH_PERIOD; +epicsShareExtern const ENV_PARAM EPICS_CA_NAME_SERVERS; +epicsShareExtern const ENV_PARAM EPICS_CA_MCAST_TTL; +epicsShareExtern const ENV_PARAM EPICS_CAS_INTF_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_IGNORE_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_AUTO_BEACON_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_SERVER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_BEACON_PERIOD; /* deprecated */ +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PERIOD; +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT; +epicsShareExtern const ENV_PARAM EPICS_BUILD_COMPILER_CLASS; +epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; +epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; +epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; +epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_IGNORE_SERVERS; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_NAME; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND; +epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT; +epicsShareExtern const ENV_PARAM EPICS_AR_PORT; +epicsShareExtern const ENV_PARAM IOCSH_PS1; +epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE; +epicsShareExtern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; + +epicsShareExtern const ENV_PARAM *env_param_list[]; + +struct in_addr; + +epicsShareFunc char * epicsShareAPI + envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf); +epicsShareFunc const char * epicsShareAPI + envGetConfigParamPtr(const ENV_PARAM *pParam); +epicsShareFunc long epicsShareAPI + envPrtConfigParam(const ENV_PARAM *pParam); +epicsShareFunc long epicsShareAPI + envGetInetAddrConfigParam(const ENV_PARAM *pParam, struct in_addr *pAddr); +epicsShareFunc long epicsShareAPI + envGetDoubleConfigParam(const ENV_PARAM *pParam, double *pDouble); +epicsShareFunc long epicsShareAPI + envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong); +epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam + (const ENV_PARAM *pEnv, unsigned short defaultPort); +epicsShareFunc long epicsShareAPI + envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool); +epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void); +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value); +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /*envDefsH*/ + diff --git a/modules/libcom/src/env/envSubr.c b/modules/libcom/src/env/envSubr.c new file mode 100644 index 000000000..283b13290 --- /dev/null +++ b/modules/libcom/src/env/envSubr.c @@ -0,0 +1,426 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Roger A. Cole + * Date: 07-20-91 + */ +/*+/mod*********************************************************************** +* TITLE envSubr.c - routines to get and set EPICS environment parameters +* +* DESCRIPTION +* These routines are oriented for use with EPICS environment +* parameters under UNIX and VxWorks. They may be used for other +* purposes, as well. +* +* Many EPICS environment parameters are predefined in envDefs.h. +* +* QUICK REFERENCE +* #include "envDefs.h" +* ENV_PARAM param; +* char *envGetConfigParamPtr( pParam ) +* char *envGetConfigParam( pParam, bufDim, pBuf ) +* long envGetLongConfigParam( pParam, pLong ) +* long envGetDoubleConfigParam( pParam, pDouble ) +* long envGetInetAddrConfigParam( pParam, pAddr ) +* long envPrtConfigParam( pParam ) +* +* SEE ALSO +* $epics/share/bin/envSetupParams, envDefs.h +* +*-***************************************************************************/ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdlib.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "errMdef.h" +#include "errlog.h" +#include "envDefs.h" +#include "epicsAssert.h" +#include "osiSock.h" + + +/*+/subr********************************************************************** +* NAME envGetConfigParamPtr - returns a pointer to the configuration +* parameter value string +* +* DESCRIPTION +* Returns a pointer to a configuration parameter value. +* If the configuration parameter isn't found in the environment, +* then a pointer to the default value for the parameter is copied. +* If no parameter is found and there is no default, then +* NULL is returned. +* +* RETURNS +* pointer to the environment variable value string, or +* NULL if no parameter value and no default value was found +* +* EXAMPLES +* 1. Get the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* const char *pStr; +* +* pStr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); +* if (pStr) { +* printf("NTP time server is: %s\n", pStr); +* } +* +*-*/ +const char * epicsShareAPI envGetConfigParamPtr( +const ENV_PARAM *pParam /* I pointer to config param structure */ +) +{ + const char *pEnv; /* pointer to environment string */ + + pEnv = getenv(pParam->name); + + if (pEnv == NULL) { + pEnv = pParam->pdflt; + } + + if (pEnv) { + if (pEnv[0u] == '\0') { + pEnv = NULL; + } + } + + return pEnv; +} + + +/*+/subr********************************************************************** +* NAME envGetConfigParam - get value of a configuration parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it +* into the caller's buffer. If the configuration parameter +* isn't found in the environment, then the default value for +* the parameter is copied. If no parameter is found and there +* is no default, then '\0' is copied and NULL is returned. +* +* RETURNS +* pointer to callers buffer, or +* NULL if no parameter value or default value was found +* +* EXAMPLES +* 1. Get the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* char temp[80]; +* +* printf("NTP time server is: %s\n", +* envGetConfigParam(&EPICS_TS_NTP_INET, sizeof(temp), temp)); +* +* 2. Get the value for the DISPLAY environment parameter under UNIX. +* +* #include "envDefs.h" +* char temp[80]; +* ENV_PARAM display={"DISPLAY",""} +* +* if (envGetConfigParam(&display, sizeof(temp), temp) == NULL) +* printf("DISPLAY isn't defined\n"); +* else +* printf("DISPLAY is %s\n", temp); +* +*-*/ +char * epicsShareAPI envGetConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +int bufDim, /* I dimension of parameter buffer */ +char *pBuf /* I pointer to parameter buffer */ +) +{ + const char *pEnv; /* pointer to environment string */ + + pEnv = envGetConfigParamPtr(pParam); + if (!pEnv) { + return NULL; + } + + strncpy(pBuf, pEnv, bufDim-1); + pBuf[bufDim-1] = '\0'; + + return pBuf; +} + +/*+/subr********************************************************************** +* NAME envGetDoubleConfigParam - get value of a double configuration parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it into the +* caller's real (double) buffer. If the configuration parameter isn't +* found in the environment, then the default value for the parameter +* is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the caller's buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value for the real environment parameter EPICS_THRESHOLD. +* +* #include "envDefs.h" +* double threshold; +* long status; +* +* status = envGetDoubleConfigParam(&EPICS_THRESHOLD, &threshold); +* if (status == 0) { +* printf("the threshold is: %lf\n", threshold); +* } +* else { +* printf("%s could not be found or was not a real number\n", +* EPICS_THRESHOLD.name); +* } +* +*-*/ +long epicsShareAPI envGetDoubleConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +double *pDouble /* O pointer to place to store value */ +) +{ + char text[128]; + char *ptext; + int count; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext != NULL) { + count = epicsScanDouble(text, pDouble); + if (count == 1) { + return 0; + } + (void)fprintf(stderr,"Unable to find a real number in %s=%s\n", + pParam->name, text); + } + + return -1; +} + +/*+/subr********************************************************************** +* NAME envGetInetAddrConfigParam - get value of an inet addr config parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it into +* the caller's (struct in_addr) buffer. If the configuration parameter +* isn't found in the environment, then the default value for +* the parameter is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the callers buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value for the inet address environment parameter EPICS_INET. +* +* #include "envDefs.h" +* struct in_addr addr; +* long status; +* +* status = envGetInetAddrConfigParam(&EPICS_INET, &addr); +* if (status == 0) { +* printf("the s_addr is: %x\n", addr.s_addr); +* } +* else { +* printf("%s could not be found or was not an inet address\n", +* EPICS_INET.name); +* } +* +*-*/ +long epicsShareAPI envGetInetAddrConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +struct in_addr *pAddr /* O pointer to struct to receive inet addr */ +) +{ + char text[128]; + char *ptext; + long status; + struct sockaddr_in sin; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext) { + status = aToIPAddr (text, 0u, &sin); + if (status == 0) { + *pAddr = sin.sin_addr; + return 0; + } + (void)fprintf(stderr,"Unable to find an IP address or valid host name in %s=%s\n", + pParam->name, text); + } + return -1; +} + +/*+/subr********************************************************************** +* NAME envGetLongConfigParam - get value of an integer config parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it +* into the caller's integer (long) buffer. If the configuration +* parameter isn't found in the environment, then the default value for +* the parameter is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the callers buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value as a long for the integer environment parameter +* EPICS_NUMBER_OF_ITEMS. +* +* #include "envDefs.h" +* long count; +* long status; +* +* status = envGetLongConfigParam(&EPICS_NUMBER_OF_ITEMS, &count); +* if (status == 0) { +* printf("and the count is: %d\n", count); +* } +* else { +* printf("%s could not be found or was not an integer\n", +* EPICS_NUMBER_OF_ITEMS.name); +* } +* +*-*/ +long epicsShareAPI envGetLongConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +long *pLong /* O pointer to place to store value */ +) +{ + char text[128]; + char *ptext; + int count; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext) { + count = sscanf(text, "%ld", pLong); + if (count == 1) + return 0; + (void)fprintf(stderr,"Unable to find an integer in %s=%s\n", + pParam->name, text); + } + return -1; +} + + +long epicsShareAPI +envGetBoolConfigParam(const ENV_PARAM *pParam, int *pBool) +{ + char text[20]; + + if(!envGetConfigParam(pParam, sizeof(text), text)) + return -1; + *pBool = epicsStrCaseCmp(text, "yes")==0; + return 0; +} + +/*+/subr********************************************************************** +* NAME envPrtConfigParam - print value of a configuration parameter +* +* DESCRIPTION +* Prints the value of a configuration parameter. +* +* RETURNS +* 0 +* +* EXAMPLE +* 1. Print the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* +* envPrtConfigParam(&EPICS_TS_NTP_INET); +* +*-*/ +long epicsShareAPI envPrtConfigParam( +const ENV_PARAM *pParam) /* pointer to config param structure */ +{ + const char *pVal; + + pVal = envGetConfigParamPtr(pParam); + if (pVal == NULL) + fprintf(stdout, "%s is undefined\n", pParam->name); + else + fprintf(stdout,"%s: %s\n", pParam->name, pVal); + return 0; +} + +/*+/subr********************************************************************** +* NAME epicsPrtEnvParams - print value of all configuration parameters +* +* DESCRIPTION +* Prints all configuration parameters and their current value. +* +* RETURNS +* 0 +* +* EXAMPLE +* 1. Print the value for all EPICS-defined environment parameters. +* +* #include "envDefs.h" +* +* epicsPrtEnvParams(); +* +*-*/ +long epicsShareAPI +epicsPrtEnvParams(void) +{ + const ENV_PARAM **ppParam = env_param_list; + + while (*ppParam != NULL) + envPrtConfigParam(*(ppParam++)); + + return 0; +} + +/* + * envGetInetPortConfigParam () + */ +epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam + (const ENV_PARAM *pEnv, unsigned short defaultPort) +{ + long longStatus; + long epicsParam; + + longStatus = envGetLongConfigParam (pEnv, &epicsParam); + if (longStatus!=0) { + epicsParam = (long) defaultPort; + errlogPrintf ("EPICS Environment \"%s\" integer fetch failed\n", pEnv->name); + errlogPrintf ("setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + if (epicsParam<=IPPORT_USERRESERVED || epicsParam>USHRT_MAX) { + errlogPrintf ("EPICS Environment \"%s\" out of range\n", pEnv->name); + /* + * Quit if the port is wrong due coding error + */ + assert (epicsParam != (long) defaultPort); + epicsParam = (long) defaultPort; + errlogPrintf ("Setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + /* + * ok to clip to unsigned short here because we checked the range + */ + return (unsigned short) epicsParam; +} + diff --git a/modules/libcom/src/error/Makefile b/modules/libcom/src/error/Makefile new file mode 100644 index 000000000..c79752cc3 --- /dev/null +++ b/modules/libcom/src/error/Makefile @@ -0,0 +1,30 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/error + +INC += epicsPrint.h +INC += errMdef.h +INC += errSymTbl.h +INC += errlog.h + +Com_SRCS += errlog.c +Com_SRCS += errSymLib.c +Com_SRCS += errSymTbl.c + +# For bldErrSymTbl +# +ERR_S_FILES += $(LIBCOM)/osi/devLib.h +ERR_S_FILES += $(LIBCOM)/osi/epicsTime.h +ERR_S_FILES += $(LIBCOM)/as/asLib.h +ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h +ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h +ERR_S_FILES += $(LIBCOM)/error/errMdef.h + +CLEANS += errSymTbl.c diff --git a/modules/libcom/src/error/RULES b/modules/libcom/src/error/RULES new file mode 100644 index 000000000..dc2926ff3 --- /dev/null +++ b/modules/libcom/src/error/RULES @@ -0,0 +1,11 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +errSymTbl.c: $(ERR_S_FILES) $(LIBCOM)/error/makeStatTbl.pl + $(PERL) $(LIBCOM)/error/makeStatTbl.pl $(ERR_S_FILES) diff --git a/modules/libcom/src/error/epicsPrint.h b/modules/libcom/src/error/epicsPrint.h new file mode 100644 index 000000000..db889794a --- /dev/null +++ b/modules/libcom/src/error/epicsPrint.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsPrint.h */ + +/*This is now obsolete. Replaced by errlog.h */ +#ifndef INCepicsPrintH +#define INCepicsPrintH + +#include "errlog.h" + +#endif /*INCepicsPrintH*/ diff --git a/modules/libcom/src/error/errMdef.h b/modules/libcom/src/error/errMdef.h new file mode 100644 index 000000000..4f68f7693 --- /dev/null +++ b/modules/libcom/src/error/errMdef.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Error Handling definitions */ +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INC_errMdef_H +#define INC_errMdef_H + +#define RTN_SUCCESS(STATUS) ((STATUS)==0) + +/* Module numbers start above 500 for compatibility with vxWorks errnoLib */ + +/* FIXME: M_xxx values could be declared as integer variables and set + * at runtime from registration routines; the S_xxx definitions would + * still work with that change, with careful initialization. + */ + +/* libCom */ +#define M_asLib (501 << 16) /* Access Security */ +#define M_bucket (502 << 16) /* Bucket Hash */ +#define M_devLib (503 << 16) /* Hardware RegisterAccess */ +#define M_stdlib (504 << 16) /* EPICS Standard library */ +#define M_pool (505 << 16) /* Thread pool */ +#define M_time (506 << 16) /* epicsTime */ + +/* ioc */ +#define M_dbAccess (511 << 16) /* Database Access Routines */ +#define M_dbLib (512 << 16) /* Static Database Access */ +#define M_drvSup (513 << 16) /* Driver Support */ +#define M_devSup (514 << 16) /* Device Support */ +#define M_recSup (515 << 16) /* Record Support */ + +/* cas */ +#define M_cas (521 << 16) /* CA server */ +#define M_gddFuncTbl (522 << 16) /* gdd jump table */ +#define M_casApp (523 << 16) /* CA server application */ + +#endif /*INC_errMdef_H*/ diff --git a/modules/libcom/src/error/errSymLib.c b/modules/libcom/src/error/errSymLib.c new file mode 100644 index 000000000..b220f2bc0 --- /dev/null +++ b/modules/libcom/src/error/errSymLib.c @@ -0,0 +1,282 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * errSymLib.c + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsAssert.h" +#include "epicsStdio.h" +#include "errMdef.h" +#include "errSymTbl.h" +#include "ellLib.h" +#include "errlog.h" + +#define NHASH 256 + +static epicsUInt16 errhash(long errNum); + +typedef struct errnumnode { + ELLNODE node; + long errNum; + struct errnumnode *hashnode; + const char *message; + long pad; +} ERRNUMNODE; + +static ELLLIST errnumlist = ELLLIST_INIT; +static ERRNUMNODE **hashtable; +static int initialized = 0; +extern ERRSYMTAB_ID errSymTbl; + +/**************************************************************** + * ERRSYMBLD + * + * Create the normal ell LIST of sorted error messages nodes + * Followed by linked hash lists - that link together those + * ell nodes that have a common hash number. + * + ***************************************************************/ +int errSymBld(void) +{ + ERRSYMBOL *errArray = errSymTbl->symbols; + ERRNUMNODE *perrNumNode = NULL; + ERRNUMNODE *pNextNode = NULL; + ERRNUMNODE **phashnode = NULL; + int i; + int modnum; + + if (initialized) + return(0); + + hashtable = (ERRNUMNODE**)callocMustSucceed + (NHASH, sizeof(ERRNUMNODE*),"errSymBld"); + for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) { + modnum = errArray->errNum >> 16; + if (modnum < 501) { + fprintf(stderr, "errSymBld: ERROR - Module number in errSymTbl < 501 was Module=%lx Name=%s\n", + errArray->errNum, errArray->name); + continue; + } + if ((errSymbolAdd(errArray->errNum, errArray->name)) < 0) { + fprintf(stderr, "errSymBld: ERROR - errSymbolAdd() failed \n"); + continue; + } + } + perrNumNode = (ERRNUMNODE *) ellFirst(&errnumlist); + while (perrNumNode) { + /* hash each perrNumNode->errNum */ + epicsUInt16 hashInd = errhash(perrNumNode->errNum); + + phashnode = (ERRNUMNODE**)&hashtable[hashInd]; + pNextNode = (ERRNUMNODE*) *phashnode; + /* search for last node (NULL) of hashnode linked list */ + while (pNextNode) { + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + *phashnode = perrNumNode; + perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode); + } + initialized = 1; + return(0); +} + +/**************************************************************** + * HASH + * returns the hash index of errNum +****************************************************************/ +static epicsUInt16 errhash(long errNum) +{ + epicsUInt16 modnum; + epicsUInt16 errnum; + + modnum = (unsigned short) (errNum >> 16); + errnum = (unsigned short) (errNum & 0xffff); + return (((modnum - 500) * 20) + errnum) % NHASH; +} + +/**************************************************************** + * ERRSYMBOLADD + * adds symbols to the master errnumlist as compiled from errSymTbl.c + ***************************************************************/ +int errSymbolAdd(long errNum, const char *name) +{ + ERRNUMNODE *pNew = (ERRNUMNODE*) callocMustSucceed(1, + sizeof(ERRNUMNODE), "errSymbolAdd"); + + pNew->errNum = errNum; + pNew->message = name; + ellAdd(&errnumlist, (ELLNODE*)pNew); + return 0; +} + +/**************************************************************** + * errRawCopy + ***************************************************************/ +static void errRawCopy(long statusToDecode, char *pBuf, size_t bufLength) +{ + epicsUInt16 modnum = (statusToDecode >>= 16) & 0xffff; + epicsUInt16 errnum = statusToDecode & 0xffff; + + assert(bufLength > 20); + + if (modnum == 0) { + epicsSnprintf(pBuf, bufLength, "Error #%u", errnum); + } + else { + epicsSnprintf(pBuf, bufLength, "Error (%u,%u)", modnum, errnum); + } +} + +static +const char* errSymLookupInternal(long status) +{ + unsigned modNum; + ERRNUMNODE *pNextNode; + ERRNUMNODE **phashnode = NULL; + + if (!initialized) + errSymBld(); + + modNum = (unsigned) status; + modNum >>= 16; + modNum &= 0xffff; + if (modNum <= 500) { + const char * pStr = strerror ((int) status); + if (pStr) { + return pStr; + } + } + else { + unsigned hashInd = errhash(status); + phashnode = (ERRNUMNODE**)&hashtable[hashInd]; + pNextNode = *phashnode; + while (pNextNode) { + if (pNextNode->errNum==status){ + return pNextNode->message; + } + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + } + return NULL; +} + +const char* errSymMsg(long status) +{ + const char* msg = errSymLookupInternal(status); + return msg ? msg : ""; +} + +/**************************************************************** + * errSymLookup + ***************************************************************/ +void errSymLookup(long status, char * pBuf, size_t bufLength) +{ + const char* msg = errSymLookupInternal(status); + if(msg) { + strncpy(pBuf, msg, bufLength); + pBuf[bufLength-1] = '\0'; + return; + } + errRawCopy(status, pBuf, bufLength); +} + +/**************************************************************** + * errSymDump + ***************************************************************/ +void errSymDump(void) +{ + int i; + int msgcount = 0; + + if (!initialized) errSymBld(); + + msgcount = 0; + printf("errSymDump: number of hash slots = %d\n", NHASH); + for (i = 0; i < NHASH; i++) { + ERRNUMNODE **phashnode = &hashtable[i]; + ERRNUMNODE *pNextNode = *phashnode; + int count = 0; + + while (pNextNode) { + int modnum = pNextNode->errNum >> 16; + int errnum = pNextNode->errNum & 0xffff; + + if (!count++) { + printf("HASHNODE = %d\n", i); + } + printf("\tmod %d num %d \"%s\"\n", + modnum , errnum , pNextNode->message); + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + msgcount += count; + } + printf("\nerrSymDump: total number of error messages = %d\n", msgcount); +} + + +/**************************************************************** + * errSymTestPrint + ***************************************************************/ +void errSymTestPrint(long errNum) +{ + char message[256]; + epicsUInt16 modnum; + epicsUInt16 errnum; + + if (!initialized) errSymBld(); + + message[0] = '\0'; + modnum = (epicsUInt16) (errNum >> 16); + errnum = (epicsUInt16) (errNum & 0xffff); + if (modnum < 501) { + fprintf(stderr, "Usage: errSymTestPrint(long errNum) \n"); + fprintf(stderr, "errSymTestPrint: module number < 501 \n"); + return; + } + errSymLookup(errNum, message, sizeof(message)); + if ( message[0] == '\0' ) return; + printf("module %hu number %hu message=\"%s\"\n", + modnum, errnum, message); + return; +} + +/**************************************************************** + * ERRSYMTEST +****************************************************************/ +void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum, + epicsUInt16 endErrNum) +{ + long errNum; + epicsUInt16 errnum; + + if (!initialized) errSymBld(); + if (modnum < 501) + return; + + /* print range of error messages */ + for (errnum = begErrNum; errnum <= endErrNum; errnum++) { + errNum = modnum << 16; + errNum |= (errnum & 0xffff); + errSymTestPrint(errNum); + } +} diff --git a/modules/libcom/src/error/errSymTbl.h b/modules/libcom/src/error/errSymTbl.h new file mode 100644 index 000000000..7fc25f733 --- /dev/null +++ b/modules/libcom/src/error/errSymTbl.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_errSymTbl_H +#define INC_errSymTbl_H + +#include + +#include "shareLib.h" +#include "epicsTypes.h" + +/* ERRSYMBOL - entry in symbol table */ +typedef struct { + long errNum; /* errMessage symbol number */ + const char *name; /* pointer to symbol name */ +} ERRSYMBOL; + +/* ERRSYMTAB - symbol table */ +typedef struct { + int nsymbols; /* current number of symbols in table */ + ERRSYMBOL *symbols; /* ptr to array of symbol entries */ +} ERRSYMTAB; + +typedef ERRSYMTAB *ERRSYMTAB_ID; + + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void errSymLookup(long status, char *pBuf, size_t bufLength); +epicsShareFunc const char* errSymMsg(long status); +epicsShareFunc void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum, + epicsUInt16 endErrNum); +epicsShareFunc void errSymTestPrint(long errNum); +epicsShareFunc int errSymBld(void); +epicsShareFunc int errSymbolAdd(long errNum, const char *name); +epicsShareFunc void errSymDump(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_errSymTbl_H */ diff --git a/modules/libcom/src/error/errlog.c b/modules/libcom/src/error/errlog.c new file mode 100644 index 000000000..8445a5a7e --- /dev/null +++ b/modules/libcom/src/error/errlog.c @@ -0,0 +1,695 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Original Author: Marty Kraimer + * Date: 07JAN1998 + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define ERRLOG_INIT +#include "adjustment.h" +#include "dbDefs.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsInterrupt.h" +#include "errMdef.h" +#include "errSymTbl.h" +#include "ellLib.h" +#include "errlog.h" +#include "epicsStdio.h" +#include "epicsExit.h" + + +#define BUFFER_SIZE 1280 +#define MAX_MESSAGE_SIZE 256 + +/*Declare storage for errVerbose */ +epicsShareDef int errVerbose = 0; + +static void errlogExitHandler(void *); +static void errlogThread(void); + +static char *msgbufGetFree(int noConsoleMessage); +static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */ +static char *msgbufGetSend(int *noConsoleMessage); +static void msgbufFreeSend(void); + +typedef struct listenerNode{ + ELLNODE node; + errlogListener listener; + void *pPrivate; +} listenerNode; + +/*each message consists of a msgNode immediately followed by the message */ +typedef struct msgNode { + ELLNODE node; + char *message; + int length; + int noConsoleMessage; +} msgNode; + +static struct { + epicsEventId waitForWork; /*errlogThread waits for this*/ + epicsMutexId msgQueueLock; + epicsMutexId listenerLock; + epicsEventId waitForFlush; /*errlogFlush waits for this*/ + epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/ + epicsMutexId flushLock; + epicsEventId waitForExit; /*errlogExitHandler waits for this*/ + int atExit; /*TRUE when errlogExitHandler is active*/ + ELLLIST listenerList; + ELLLIST msgQueue; + msgNode *pnextSend; + int errlogInitFailed; + int buffersize; + int maxMsgSize; + int msgNeeded; + int sevToLog; + int toConsole; + FILE *console; + int missedMessages; + char *pbuffer; +} pvtData; + + +/* + * vsnprintf with truncation message + */ +static int tvsnPrint(char *str, size_t size, const char *format, va_list ap) +{ + static const char tmsg[] = "<>\n"; + int nchar = epicsVsnprintf(str, size, format ? format : "", ap); + + if (nchar >= size) { + if (size > sizeof tmsg) + strcpy(str + size - sizeof tmsg, tmsg); + nchar = size - 1; + } + return nchar; +} + +int errlogPrintf(const char *pFormat, ...) +{ + va_list pvar; + char *pbuffer; + int nchar; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogPrintf called from interrupt level\n"); + return 0; + } + + errlogInit(0); + isOkToBlock = epicsThreadIsOkToBlock(); + + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + va_start(pvar, pFormat); + nchar = vfprintf(console, pFormat, pvar); + va_end (pvar); + fflush(console); + } + + if (pvtData.atExit) + return nchar; + + pbuffer = msgbufGetFree(isOkToBlock); + if (!pbuffer) + return 0; + + va_start(pvar, pFormat); + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + va_end(pvar); + msgbufSetSize(nchar); + return nchar; +} + +int errlogVprintf(const char *pFormat,va_list pvar) +{ + int nchar; + char *pbuffer; + int isOkToBlock; + FILE *console; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogVprintf called from interrupt level\n"); + return 0; + } + + errlogInit(0); + if (pvtData.atExit) + return 0; + isOkToBlock = epicsThreadIsOkToBlock(); + + pbuffer = msgbufGetFree(isOkToBlock); + if (!pbuffer) { + console = pvtData.console ? pvtData.console : stderr; + vfprintf(console, pFormat, pvar); + fflush(console); + return 0; + } + + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + console = pvtData.console ? pvtData.console : stderr; + fprintf(console, "%s", pbuffer); + fflush(console); + } + msgbufSetSize(nchar); + return nchar; +} + +int errlogMessage(const char *message) +{ + errlogPrintf("%s", message); + return 0; +} + +int errlogPrintfNoConsole(const char *pFormat, ...) +{ + va_list pvar; + int nchar; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogPrintfNoConsole called from interrupt level\n"); + return 0; + } + + errlogInit(0); + va_start(pvar, pFormat); + nchar = errlogVprintfNoConsole(pFormat, pvar); + va_end(pvar); + return nchar; +} + +int errlogVprintfNoConsole(const char *pFormat, va_list pvar) +{ + int nchar; + char *pbuffer; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogVprintfNoConsole called from interrupt level\n"); + return 0; + } + + errlogInit(0); + if (pvtData.atExit) + return 0; + + pbuffer = msgbufGetFree(1); + if (!pbuffer) + return 0; + + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + msgbufSetSize(nchar); + return nchar; +} + + +int errlogSevPrintf(errlogSevEnum severity, const char *pFormat, ...) +{ + va_list pvar; + int nchar; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogSevPrintf called from interrupt level\n"); + return 0; + } + + errlogInit(0); + if (pvtData.sevToLog > severity) + return 0; + + isOkToBlock = epicsThreadIsOkToBlock(); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + fprintf(console, "sevr=%s ", errlogGetSevEnumString(severity)); + va_start(pvar, pFormat); + vfprintf(console, pFormat, pvar); + va_end(pvar); + fflush(console); + } + + va_start(pvar, pFormat); + nchar = errlogSevVprintf(severity, pFormat, pvar); + va_end(pvar); + return nchar; +} + +int errlogSevVprintf(errlogSevEnum severity, const char *pFormat, va_list pvar) +{ + char *pnext; + int nchar; + int totalChar = 0; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogSevVprintf called from interrupt level\n"); + return 0; + } + + errlogInit(0); + if (pvtData.atExit) + return 0; + + isOkToBlock = epicsThreadIsOkToBlock(); + pnext = msgbufGetFree(isOkToBlock); + if (!pnext) + return 0; + + nchar = sprintf(pnext, "sevr=%s ", errlogGetSevEnumString(severity)); + pnext += nchar; totalChar += nchar; + nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pFormat, pvar); + pnext += nchar; totalChar += nchar; + if (pnext[-1] != '\n') { + strcpy(pnext,"\n"); + totalChar++; + } + msgbufSetSize(totalChar); + return nchar; +} + + +const char * errlogGetSevEnumString(errlogSevEnum severity) +{ + errlogInit(0); + if (severity > 3) + return "unknown"; + return errlogSevEnumString[severity]; +} + +void errlogSetSevToLog(errlogSevEnum severity) +{ + errlogInit(0); + pvtData.sevToLog = severity; +} + +errlogSevEnum errlogGetSevToLog(void) +{ + errlogInit(0); + return pvtData.sevToLog; +} + +void errlogAddListener(errlogListener listener, void *pPrivate) +{ + listenerNode *plistenerNode; + + errlogInit(0); + if (pvtData.atExit) + return; + + plistenerNode = callocMustSucceed(1,sizeof(listenerNode), + "errlogAddListener"); + epicsMutexMustLock(pvtData.listenerLock); + plistenerNode->listener = listener; + plistenerNode->pPrivate = pPrivate; + ellAdd(&pvtData.listenerList,&plistenerNode->node); + epicsMutexUnlock(pvtData.listenerLock); +} + +int errlogRemoveListeners(errlogListener listener, void *pPrivate) +{ + listenerNode *plistenerNode; + int count = 0; + + errlogInit(0); + if (!pvtData.atExit) + epicsMutexMustLock(pvtData.listenerLock); + + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); + while (plistenerNode) { + listenerNode *pnext = (listenerNode *)ellNext(&plistenerNode->node); + + if (plistenerNode->listener == listener && + plistenerNode->pPrivate == pPrivate) { + ellDelete(&pvtData.listenerList, &plistenerNode->node); + free(plistenerNode); + ++count; + } + plistenerNode = pnext; + } + + if (!pvtData.atExit) + epicsMutexUnlock(pvtData.listenerLock); + + if (count == 0) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + fprintf(console, + "errlogRemoveListeners: No listeners found\n"); + } + return count; +} + +int eltc(int yesno) +{ + errlogInit(0); + errlogFlush(); + pvtData.toConsole = yesno; + return 0; +} + +int errlogSetConsole(FILE *stream) +{ + errlogInit(0); + pvtData.console = stream; + return 0; +} + +void errPrintf(long status, const char *pFileName, int lineno, + const char *pformat, ...) +{ + va_list pvar; + char *pnext; + int nchar; + int totalChar=0; + int isOkToBlock; + char name[256]; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage("errPrintf called from interrupt level\n"); + return; + } + + errlogInit(0); + isOkToBlock = epicsThreadIsOkToBlock(); + if (status == 0) + status = errno; + + if (status > 0) { + errSymLookup(status, name, sizeof(name)); + } + + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + if (pFileName) + fprintf(console, "filename=\"%s\" line number=%d\n", + pFileName, lineno); + if (status > 0) + fprintf(console, "%s ", name); + + va_start(pvar, pformat); + vfprintf(console, pformat, pvar); + va_end(pvar); + fputc('\n', console); + fflush(console); + } + + if (pvtData.atExit) + return; + + pnext = msgbufGetFree(isOkToBlock); + if (!pnext) + return; + + if (pFileName) { + nchar = sprintf(pnext,"filename=\"%s\" line number=%d\n", + pFileName, lineno); + pnext += nchar; totalChar += nchar; + } + + if (status > 0) { + nchar = sprintf(pnext,"%s ",name); + pnext += nchar; totalChar += nchar; + } + va_start(pvar, pformat); + nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pformat, pvar); + va_end(pvar); + if (nchar>0) { + pnext += nchar; + totalChar += nchar; + } + strcpy(pnext, "\n"); + totalChar++ ; /*include the \n */ + msgbufSetSize(totalChar); +} + + +static void errlogExitHandler(void *pvt) +{ + pvtData.atExit = 1; + epicsEventSignal(pvtData.waitForWork); + epicsEventMustWait(pvtData.waitForExit); +} + +struct initArgs { + int bufsize; + int maxMsgSize; +}; + +static void errlogInitPvt(void *arg) +{ + struct initArgs *pconfig = (struct initArgs *) arg; + epicsThreadId tid; + + pvtData.errlogInitFailed = TRUE; + pvtData.buffersize = pconfig->bufsize; + pvtData.maxMsgSize = pconfig->maxMsgSize; + pvtData.msgNeeded = adjustToWorstCaseAlignment(pvtData.maxMsgSize + + sizeof(msgNode)); + ellInit(&pvtData.listenerList); + ellInit(&pvtData.msgQueue); + pvtData.toConsole = TRUE; + pvtData.console = NULL; + pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty); + pvtData.listenerLock = epicsMutexMustCreate(); + pvtData.msgQueueLock = epicsMutexMustCreate(); + pvtData.waitForFlush = epicsEventMustCreate(epicsEventEmpty); + pvtData.flush = epicsEventMustCreate(epicsEventEmpty); + pvtData.flushLock = epicsMutexMustCreate(); + pvtData.waitForExit = epicsEventMustCreate(epicsEventEmpty); + pvtData.pbuffer = callocMustSucceed(1, pvtData.buffersize, + "errlogInitPvt"); + + errSymBld(); /* Better not to do this lazily... */ + + tid = epicsThreadCreate("errlog", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + (EPICSTHREADFUNC)errlogThread, 0); + if (tid) { + pvtData.errlogInitFailed = FALSE; + } +} + + +int errlogInit2(int bufsize, int maxMsgSize) +{ + static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; + struct initArgs config; + + if (pvtData.atExit) + return 0; + + if (bufsize < BUFFER_SIZE) + bufsize = BUFFER_SIZE; + config.bufsize = bufsize; + + if (maxMsgSize < MAX_MESSAGE_SIZE) + maxMsgSize = MAX_MESSAGE_SIZE; + config.maxMsgSize = maxMsgSize; + + epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, &config); + if (pvtData.errlogInitFailed) { + fprintf(stderr,"errlogInit failed\n"); + exit(1); + } + return 0; +} + +int errlogInit(int bufsize) +{ + return errlogInit2(bufsize, MAX_MESSAGE_SIZE); +} + +void errlogFlush(void) +{ + int count; + + errlogInit(0); + if (pvtData.atExit) + return; + + /*If nothing in queue dont wake up errlogThread*/ + epicsMutexMustLock(pvtData.msgQueueLock); + count = ellCount(&pvtData.msgQueue); + epicsMutexUnlock(pvtData.msgQueueLock); + if (count <= 0) + return; + + /*must let errlogThread empty queue*/ + epicsMutexMustLock(pvtData.flushLock); + epicsEventSignal(pvtData.flush); + epicsEventSignal(pvtData.waitForWork); + epicsEventMustWait(pvtData.waitForFlush); + epicsMutexUnlock(pvtData.flushLock); +} + +static void errlogThread(void) +{ + listenerNode *plistenerNode; + int noConsoleMessage; + char *pmessage; + + epicsAtExit(errlogExitHandler,0); + while (TRUE) { + epicsEventMustWait(pvtData.waitForWork); + while ((pmessage = msgbufGetSend(&noConsoleMessage))) { + epicsMutexMustLock(pvtData.listenerLock); + if (pvtData.toConsole && !noConsoleMessage) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + fprintf(console, "%s", pmessage); + fflush(console); + } + + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); + while (plistenerNode) { + (*plistenerNode->listener)(plistenerNode->pPrivate, pmessage); + plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); + } + + epicsMutexUnlock(pvtData.listenerLock); + msgbufFreeSend(); + } + + if (pvtData.atExit) + break; + if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) + continue; + + epicsThreadSleep(.2); /*just wait an extra .2 seconds*/ + epicsEventSignal(pvtData.waitForFlush); + } + epicsEventSignal(pvtData.waitForExit); +} + + +static msgNode * msgbufGetNode(void) +{ + char *pbuffer = pvtData.pbuffer; + char *pnextFree; + msgNode *pnextSend; + + if (ellCount(&pvtData.msgQueue) == 0 ) { + pnextFree = pbuffer; /* Reset if empty */ + } + else { + msgNode *pfirst = (msgNode *)ellFirst(&pvtData.msgQueue); + msgNode *plast = (msgNode *)ellLast(&pvtData.msgQueue); + char *plimit = pbuffer + pvtData.buffersize; + + pnextFree = plast->message + adjustToWorstCaseAlignment(plast->length); + if (pfirst > plast) { + plimit = (char *)pfirst; + } + else if (pnextFree + pvtData.msgNeeded > plimit) { + pnextFree = pbuffer; /* Hit end, wrap to start */ + plimit = (char *)pfirst; + } + if (pnextFree + pvtData.msgNeeded > plimit) { + return 0; /* No room */ + } + } + + pnextSend = (msgNode *)pnextFree; + pnextSend->message = pnextFree + sizeof(msgNode); + pnextSend->length = 0; + return pnextSend; +} + +static char * msgbufGetFree(int noConsoleMessage) +{ + msgNode *pnextSend; + + if (epicsMutexLock(pvtData.msgQueueLock) != epicsMutexLockOK) + return 0; + + if ((ellCount(&pvtData.msgQueue) == 0) && pvtData.missedMessages) { + int nchar; + + pnextSend = msgbufGetNode(); + nchar = sprintf(pnextSend->message, + "errlog: %d messages were discarded\n", pvtData.missedMessages); + pnextSend->length = nchar + 1; + pvtData.missedMessages = 0; + ellAdd(&pvtData.msgQueue, &pnextSend->node); + } + + pvtData.pnextSend = pnextSend = msgbufGetNode(); + if (pnextSend) { + pnextSend->noConsoleMessage = noConsoleMessage; + pnextSend->length = 0; + return pnextSend->message; /* NB: msgQueueLock is still locked */ + } + + ++pvtData.missedMessages; + epicsMutexUnlock(pvtData.msgQueueLock); + return 0; +} + +static void msgbufSetSize(int size) +{ + msgNode *pnextSend = pvtData.pnextSend; + + pnextSend->length = size+1; + ellAdd(&pvtData.msgQueue, &pnextSend->node); + epicsMutexUnlock(pvtData.msgQueueLock); + epicsEventSignal(pvtData.waitForWork); +} + + +static char * msgbufGetSend(int *noConsoleMessage) +{ + msgNode *pnextSend; + + epicsMutexMustLock(pvtData.msgQueueLock); + pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); + epicsMutexUnlock(pvtData.msgQueueLock); + if (!pnextSend) + return 0; + + *noConsoleMessage = pnextSend->noConsoleMessage; + return pnextSend->message; +} + +static void msgbufFreeSend(void) +{ + msgNode *pnextSend; + + epicsMutexMustLock(pvtData.msgQueueLock); + pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); + if (!pnextSend) { + FILE *console = pvtData.console ? pvtData.console : stderr; + + fprintf(console, "errlog: msgbufFreeSend logic error\n"); + epicsThreadSuspendSelf(); + } + ellDelete(&pvtData.msgQueue, &pnextSend->node); + epicsMutexUnlock(pvtData.msgQueueLock); +} diff --git a/modules/libcom/src/error/errlog.h b/modules/libcom/src/error/errlog.h new file mode 100644 index 000000000..e8a4fb557 --- /dev/null +++ b/modules/libcom/src/error/errlog.h @@ -0,0 +1,91 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_errlog_H +#define INC_errlog_H + +#include +#include +#include + +#include "shareLib.h" +#include "compilerDependencies.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*errlogListener)(void *pPrivate, const char *message); + +typedef enum { + errlogInfo, + errlogMinor, + errlogMajor, + errlogFatal +} errlogSevEnum; + +epicsShareExtern int errVerbose; + + +#ifdef ERRLOG_INIT + epicsShareDef const char *errlogSevEnumString[] = { + "info", + "minor", + "major", + "fatal" + }; +#else + epicsShareExtern const char * errlogSevEnumString[]; +#endif + +/* errMessage is a macro so it can get the file and line number */ +#define errMessage(S, PM) \ + errPrintf(S, __FILE__, __LINE__, "%s", PM) +/* epicsPrintf and epicsVprintf are old names for errlog routines*/ +#define epicsPrintf errlogPrintf +#define epicsVprintf errlogVprintf + +epicsShareFunc int errlogPrintf(const char *pformat, ...) + EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int errlogVprintf(const char *pformat, va_list pvar); +epicsShareFunc int errlogSevPrintf(const errlogSevEnum severity, + const char *pformat, ...) EPICS_PRINTF_STYLE(2,3); +epicsShareFunc int errlogSevVprintf(const errlogSevEnum severity, + const char *pformat, va_list pvar); +epicsShareFunc int errlogMessage(const char *message); + +epicsShareFunc const char * errlogGetSevEnumString(errlogSevEnum severity); +epicsShareFunc void errlogSetSevToLog(errlogSevEnum severity); +epicsShareFunc errlogSevEnum errlogGetSevToLog(void); + +epicsShareFunc void errlogAddListener(errlogListener listener, void *pPrivate); +epicsShareFunc int errlogRemoveListeners(errlogListener listener, + void *pPrivate); + +epicsShareFunc int eltc(int yesno); +epicsShareFunc int errlogSetConsole(FILE *stream); + +epicsShareFunc int errlogInit(int bufsize); +epicsShareFunc int errlogInit2(int bufsize, int maxMsgSize); +epicsShareFunc void errlogFlush(void); + +epicsShareFunc void errPrintf(long status, const char *pFileName, int lineno, + const char *pformat, ...) EPICS_PRINTF_STYLE(4,5); + +epicsShareFunc int errlogPrintfNoConsole(const char *pformat, ...) + EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int errlogVprintfNoConsole(const char *pformat,va_list pvar); + +epicsShareFunc void errSymLookup(long status, char *pBuf, size_t bufLength); + +#ifdef __cplusplus +} +#endif + +#endif /*INC_errlog_H*/ diff --git a/modules/libcom/src/error/makeStatTbl.pl b/modules/libcom/src/error/makeStatTbl.pl new file mode 100644 index 000000000..e9ca10eda --- /dev/null +++ b/modules/libcom/src/error/makeStatTbl.pl @@ -0,0 +1,116 @@ +#!/usr/bin/env perl + +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2014 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# makeStatTbl.pl - Create Error Symbol Table +# +# Original Author: Kay-Uwe Kasemir, 1-31-97 +# +# SYNOPSIS +# perl makeStatTbl.pl files.h... +# +# DESCRIPTION +# This tool creates a symbol table (ERRSYMTAB) structure which contains the +# names and values of all the status codes defined in the .h files named in +# its input arguments. The status codes must be prefixed with "S_" +# in order to be included in this table. +# Module numbers definitions prefixed with "M_" are also read from the input +# files and included in the output. +# +# This tool's primary use is for creating an error status table used +# by errPrint, and errSymLookup. +# +# FILES +# errMdef.h Module number file for each h directory +# errSymTbl.c Source file generated by tool in the cwd +# +# SEE ALSO: errnoLib(1), symLib(1) + +use strict; +use Getopt::Std; + +my $tool = 'makeStatTbl.pl'; + +our ($opt_h); +our $opt_o = 'errSymTbl.c'; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +&HELP_MESSAGE unless getopts('ho:') && @ARGV; +&HELP_MESSAGE if $opt_h; + +my (@syms, %vals, %msgs); + +# Extract names, values and comments from all S_ and M_ symbol definitions +while (<>) { + chomp; + next unless m/^ \s* \# \s* define \s+ ([SM]_[A-Za-z0-9_]+) + \s+ (.*?) \s* \/ \* \s* (.*?) \s* \* \/ \s* $/x; + push @syms, $1; + $vals{$1} = $2; + $msgs{$1} = $3; +} + +open my $out, '>', $opt_o or + die "Can't create $opt_o: $!\n"; + +print $out <<"END"; +/* Generated file $opt_o */ + +#include "errMdef.h" +#include "errSymTbl.h" +#include "dbDefs.h" + +END + +my @mods = grep {/^M_/} @syms; +my @errs = grep {/^S_/} @syms; + +foreach my $mod (@mods) { + my $val = $vals{$mod}; + my $msg = $msgs{$mod}; + print $out + "#ifndef $mod\n", + "#define $mod $val /* $msg */\n", + "#endif\n"; +} + +print $out + "\n", + "static ERRSYMBOL symbols[] = {\n"; + +foreach my $err (@errs) { + my $msg = escape($msgs{$err}); + my $val = $vals{$err}; + print $out + " { $val, \"$msg\"},\n"; +} + +print $out <<"END"; +}; + +static ERRSYMTAB symTbl = { + NELEMENTS(symbols), symbols +}; + +ERRSYMTAB_ID errSymTbl = &symTbl; + +END + +sub HELP_MESSAGE { + print STDERR "Usage: $tool [-o file.c] files.h ...\n"; + exit 2; +} + +sub escape { + $_ = shift; + s/"/\\"/g; + return $_; +} diff --git a/modules/libcom/src/fdmgr/Makefile b/modules/libcom/src/fdmgr/Makefile new file mode 100644 index 000000000..34b1a3dbc --- /dev/null +++ b/modules/libcom/src/fdmgr/Makefile @@ -0,0 +1,14 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/fdmgr +INC += fdManager.h +INC += fdmgr.h +Com_SRCS += fdmgr.cpp +Com_SRCS += fdManager.cpp diff --git a/modules/libcom/src/fdmgr/fdManager.cpp b/modules/libcom/src/fdmgr/fdManager.cpp new file mode 100644 index 000000000..00f4d4a87 --- /dev/null +++ b/modules/libcom/src/fdmgr/fdManager.cpp @@ -0,0 +1,359 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// File descriptor management C++ class library +// (for multiplexing IO in a single threaded environment) +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// +// NOTES: +// 1) This library is not thread safe +// + +#include + +#define instantiateRecourceLib +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsThread.h" +#include "fdManager.h" +#include "locationException.h" + +using std :: max; + +epicsShareDef fdManager fileDescriptorManager; + +const unsigned mSecPerSec = 1000u; +const unsigned uSecPerSec = 1000u * mSecPerSec; + +// +// fdManager::fdManager() +// +// hopefully its a reasonable guess that select() and epicsThreadSleep() +// will have the same sleep quantum +// +epicsShareFunc fdManager::fdManager () : + sleepQuantum ( epicsThreadSleepQuantum () ), + fdSetsPtr ( new fd_set [fdrNEnums] ), + pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ), + pCBReg ( 0 ) +{ + int status = osiSockAttach (); + assert (status); + + for ( size_t i = 0u; i < fdrNEnums; i++ ) { + FD_ZERO ( &fdSetsPtr[i] ); + } +} + +// +// fdManager::~fdManager() +// +epicsShareFunc fdManager::~fdManager() +{ + fdReg *pReg; + + while ( (pReg = this->regList.get()) ) { + pReg->state = fdReg::limbo; + pReg->destroy(); + } + while ( (pReg = this->activeList.get()) ) { + pReg->state = fdReg::limbo; + pReg->destroy(); + } + delete this->pTimerQueue; + delete [] this->fdSetsPtr; + osiSockRelease(); +} + +// +// fdManager::process() +// +epicsShareFunc void fdManager::process (double delay) +{ + this->lazyInitTimerQueue (); + + // + // no recursion + // + if (this->processInProg) { + return; + } + this->processInProg = true; + + // + // One shot at expired timers prior to going into + // select. This allows zero delay timers to arm + // fd writes. We will never process the timer queue + // more than once here so that fd activity get serviced + // in a reasonable length of time. + // + double minDelay = this->pTimerQueue->process(epicsTime::getCurrent()); + + if ( minDelay >= delay ) { + minDelay = delay; + } + + bool ioPending = false; + tsDLIter < fdReg > iter = this->regList.firstIter (); + while ( iter.valid () ) { + FD_SET(iter->getFD(), &this->fdSetsPtr[iter->getType()]); + ioPending = true; + ++iter; + } + + if ( ioPending ) { + struct timeval tv; + tv.tv_sec = static_cast ( minDelay ); + tv.tv_usec = static_cast ( (minDelay-tv.tv_sec) * uSecPerSec ); + + fd_set * pReadSet = & this->fdSetsPtr[fdrRead]; + fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite]; + fd_set * pExceptSet = & this->fdSetsPtr[fdrException]; + int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv); + + this->pTimerQueue->process(epicsTime::getCurrent()); + + if ( status > 0 ) { + + // + // Look for activity + // + iter=this->regList.firstIter (); + while ( iter.valid () && status > 0 ) { + tsDLIter < fdReg > tmp = iter; + tmp++; + if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) { + FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]); + this->regList.remove(*iter); + this->activeList.add(*iter); + iter->state = fdReg::active; + status--; + } + iter = tmp; + } + + // + // I am careful to prevent problems if they access the + // above list while in a "callBack()" routine + // + fdReg * pReg; + while ( (pReg = this->activeList.get()) ) { + pReg->state = fdReg::limbo; + + // + // Tag current fdReg so that we + // can detect if it was deleted + // during the call back + // + this->pCBReg = pReg; + pReg->callBack(); + if (this->pCBReg != NULL) { + // + // check only after we see that it is non-null so + // that we dont trigger bounds-checker dangling pointer + // error + // + assert (this->pCBReg==pReg); + this->pCBReg = 0; + if (pReg->onceOnly) { + pReg->destroy(); + } + else { + this->regList.add(*pReg); + pReg->state = fdReg::pending; + } + } + } + } + else if ( status < 0 ) { + int errnoCpy = SOCKERRNO; + + // dont depend on flags being properly set if + // an error is retuned from select + for ( size_t i = 0u; i < fdrNEnums; i++ ) { + FD_ZERO ( &fdSetsPtr[i] ); + } + + // + // print a message if its an unexpected error + // + if ( errnoCpy != SOCK_EINTR ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, + "fdManager: select failed because \"%s\"\n", + sockErrBuf ); + } + } + } + else { + /* + * recover from subtle differences between + * windows sockets and UNIX sockets implementation + * of select() + */ + epicsThreadSleep(minDelay); + this->pTimerQueue->process(epicsTime::getCurrent()); + } + this->processInProg = false; + return; +} + +// +// fdReg::destroy() +// (default destroy method) +// +void fdReg::destroy() +{ + delete this; +} + +// +// fdReg::~fdReg() +// +fdReg::~fdReg() +{ + this->manager.removeReg(*this); +} + +// +// fdReg::show() +// +void fdReg::show(unsigned level) const +{ + printf ("fdReg at %p\n", (void *) this); + if (level>1u) { + printf ("\tstate = %d, onceOnly = %d\n", + this->state, this->onceOnly); + } + this->fdRegId::show(level); +} + +// +// fdRegId::show() +// +void fdRegId::show ( unsigned level ) const +{ + printf ( "fdRegId at %p\n", + static_cast ( this ) ); + if ( level > 1u ) { + printf ( "\tfd = %d, type = %d\n", + this->fd, this->type ); + } +} + +// +// fdManager::installReg () +// +void fdManager::installReg (fdReg ®) +{ + this->maxFD = max ( this->maxFD, reg.getFD()+1 ); + // Most applications will find that its important to push here to + // the front of the list so that transient writes get executed + // first allowing incoming read protocol to find that outgoing + // buffer space is newly available. + this->regList.push ( reg ); + reg.state = fdReg::pending; + + int status = this->fdTbl.add ( reg ); + if ( status != 0 ) { + throwWithLocation ( fdInterestSubscriptionAlreadyExits () ); + } +} + +// +// fdManager::removeReg () +// +void fdManager::removeReg (fdReg ®In) +{ + fdReg *pItemFound; + + pItemFound = this->fdTbl.remove (regIn); + if (pItemFound!=®In) { + fprintf(stderr, + "fdManager::removeReg() bad fd registration object\n"); + return; + } + + // + // signal fdManager that the fdReg was deleted + // during the call back + // + if (this->pCBReg == ®In) { + this->pCBReg = 0; + } + + switch (regIn.state) { + case fdReg::active: + this->activeList.remove (regIn); + break; + case fdReg::pending: + this->regList.remove (regIn); + break; + case fdReg::limbo: + break; + default: + // + // here if memory corrupted + // + assert(0); + } + regIn.state = fdReg::limbo; + + FD_CLR(regIn.getFD(), &this->fdSetsPtr[regIn.getType()]); +} + +// +// fdManager::reschedule () +// NOOP - this only runs single threaded, and therefore they can only +// add a new timer from places that will always end up in a reschedule +// +void fdManager::reschedule () +{ +} + +double fdManager::quantum () +{ + return this->sleepQuantum; +} + +// +// lookUpFD() +// +epicsShareFunc fdReg *fdManager::lookUpFD (const SOCKET fd, const fdRegType type) +{ + if (fd<0) { + return NULL; + } + fdRegId id (fd,type); + return this->fdTbl.lookup(id); +} + +// +// fdReg::fdReg() +// +fdReg::fdReg (const SOCKET fdIn, const fdRegType typIn, + const bool onceOnlyIn, fdManager &managerIn) : + fdRegId (fdIn,typIn), state (limbo), + onceOnly (onceOnlyIn), manager (managerIn) +{ + if (!FD_IN_FDSET(fdIn)) { + fprintf (stderr, "%s: fd > FD_SETSIZE ignored\n", + __FILE__); + return; + } + this->manager.installReg (*this); +} + +template class resTable; diff --git a/modules/libcom/src/fdmgr/fdManager.h b/modules/libcom/src/fdmgr/fdManager.h new file mode 100644 index 000000000..59a2f355f --- /dev/null +++ b/modules/libcom/src/fdmgr/fdManager.h @@ -0,0 +1,204 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * File descriptor management C++ class library + * (for multiplexing IO in a single threaded environment) + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef fdManagerH_included +#define fdManagerH_included + +#include "shareLib.h" // reset share lib defines +#include "tsDLList.h" +#include "resourceLib.h" +#include "epicsTime.h" +#include "osiSock.h" +#include "epicsTimer.h" + +enum fdRegType {fdrRead, fdrWrite, fdrException, fdrNEnums}; + +// +// fdRegId +// +// file descriptor interest id +// +class epicsShareClass fdRegId +{ +public: + + fdRegId (const SOCKET fdIn, const fdRegType typeIn) : + fd(fdIn), type(typeIn) {} + + SOCKET getFD () const + { + return this->fd; + } + + fdRegType getType () const + { + return this->type; + } + + bool operator == (const fdRegId &idIn) const + { + return this->fd == idIn.fd && this->type==idIn.type; + } + + resTableIndex hash () const; + + virtual void show (unsigned level) const; + + virtual ~fdRegId() {} +private: + SOCKET fd; + fdRegType type; +}; + +// +// fdManager +// +// file descriptor manager +// +class fdManager : public epicsTimerQueueNotify { +public: + // + // exceptions + // + class fdInterestSubscriptionAlreadyExits {}; + + epicsShareFunc fdManager (); + epicsShareFunc virtual ~fdManager (); + epicsShareFunc void process ( double delay ); // delay parameter is in seconds + + // returns NULL if the fd is unknown + epicsShareFunc class fdReg *lookUpFD (const SOCKET fd, const fdRegType type); + + epicsTimer & createTimer (); + +private: + tsDLList < fdReg > regList; + tsDLList < fdReg > activeList; + resTable < fdReg, fdRegId > fdTbl; + const double sleepQuantum; + fd_set * fdSetsPtr; + epicsTimerQueuePassive * pTimerQueue; + SOCKET maxFD; + bool processInProg; + // + // Set to fdreg when in call back + // and nill otherwise + // + fdReg * pCBReg; + void reschedule (); + double quantum (); + void installReg (fdReg ®); + void removeReg (fdReg ®); + void lazyInitTimerQueue (); + fdManager ( const fdManager & ); + fdManager & operator = ( const fdManager & ); + friend class fdReg; +}; + +// +// default file descriptor manager +// +epicsShareExtern fdManager fileDescriptorManager; + +// +// fdReg +// +// file descriptor registration +// +class epicsShareClass fdReg : + public fdRegId, public tsDLNode, public tsSLNode { + friend class fdManager; + +public: + + fdReg (const SOCKET fdIn, const fdRegType type, + const bool onceOnly=false, fdManager &manager = fileDescriptorManager); + virtual ~fdReg (); + + virtual void show (unsigned level) const; + + // + // Called by the file descriptor manager: + // 1) If the fdManager is deleted and there are still + // fdReg objects attached + // 2) Immediately after calling "callBack()" if + // the constructor specified "onceOnly" + // + // fdReg::destroy() does a "delete this" + // + virtual void destroy (); + +private: + enum state {active, pending, limbo}; + + // + // called when there is activity on the fd + // NOTES + // 1) the fdManager will call this only once during the + // lifetime of a fdReg object if the constructor + // specified "onceOnly" + // + virtual void callBack ()=0; + + unsigned char state; // state enums go here + unsigned char onceOnly; + fdManager &manager; + + fdReg ( const fdReg & ); + fdReg & operator = ( const fdReg & ); +}; + +// +// fdRegId::hash() +// +inline resTableIndex fdRegId::hash () const +{ + const unsigned fdManagerHashTableMinIndexBits = 8; + const unsigned fdManagerHashTableMaxIndexBits = sizeof(SOCKET)*CHAR_BIT; + resTableIndex hashid; + + hashid = integerHash ( fdManagerHashTableMinIndexBits, + fdManagerHashTableMaxIndexBits, this->fd ); + + // + // also evenly distribute based on the type of fdRegType + // + hashid ^= this->type; + + // + // the result here is always masked to the + // proper size after it is returned to the resource class + // + return hashid; +} + +inline void fdManager::lazyInitTimerQueue () +{ + if ( ! this->pTimerQueue ) { + this->pTimerQueue = & epicsTimerQueuePassive::create ( *this ); + } +} + +inline epicsTimer & fdManager::createTimer () +{ + this->lazyInitTimerQueue (); + return this->pTimerQueue->createTimer (); +} + +#endif // fdManagerH_included + diff --git a/modules/libcom/src/fdmgr/fdmgr.cpp b/modules/libcom/src/fdmgr/fdmgr.cpp new file mode 100644 index 000000000..342bae862 --- /dev/null +++ b/modules/libcom/src/fdmgr/fdmgr.cpp @@ -0,0 +1,336 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// File descriptor management C++ class library +// (for multiplexing IO in a single threaded environment) +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// +// NOTES: +// 1) the routines in this file provide backward compatibility with the original +// "C" based file descriptor manager API +// 2) This library is _not_ thread safe +// + +#include +#define epicsExportSharedSymbols +#include "locationException.h" +#include "epicsAssert.h" +#include "fdManager.h" +#include "fdmgr.h" + +static const fdRegType fdiToFdRegType[] = {fdrRead, fdrWrite, fdrException}; +static const unsigned fdiToFdRegTypeNElements = sizeof (fdiToFdRegType) / sizeof (fdiToFdRegType[0]); +const unsigned mSecPerSec = 1000u; +const unsigned uSecPerSec = 1000u * mSecPerSec; + +class fdRegForOldFdmgr : public fdReg { +public: + // + // exceptions + // + class noFunctionSpecified {}; + class doubleDelete {}; + + epicsShareFunc fdRegForOldFdmgr (const SOCKET fdIn, const fdRegType type, + const bool onceOnly, fdManager &manager, pCallBackFDMgr pFunc, void *pParam); + epicsShareFunc ~fdRegForOldFdmgr (); + +private: + pCallBackFDMgr pFunc; + void *pParam; + epicsShareFunc virtual void callBack (); + fdRegForOldFdmgr ( const fdRegForOldFdmgr & ); + fdRegForOldFdmgr & operator = ( const fdRegForOldFdmgr & ); +}; + +class oldFdmgr; + +// +// timerForOldFdmgr +// +class timerForOldFdmgr : public epicsTimerNotify, public chronIntIdRes { +public: + epicsShareFunc timerForOldFdmgr (oldFdmgr &fdmgr, double delay, pCallBackFDMgr pFunc, void *pParam); + epicsShareFunc virtual ~timerForOldFdmgr (); + + // + // exceptions + // + class noFunctionSpecified {}; + class doubleDelete {}; +private: + epicsTimer &timer; + oldFdmgr &fdmgr; + pCallBackFDMgr pFunc; + void *pParam; + unsigned id; + epicsShareFunc expireStatus expire ( const epicsTime & currentTime ); + timerForOldFdmgr ( const timerForOldFdmgr & ); + timerForOldFdmgr & operator = ( const timerForOldFdmgr & ); +}; + +class oldFdmgr : public fdManager { + friend class timerForOldFdmgr; + friend epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id); + +public: + oldFdmgr (); + +private: + chronIntIdResTable resTbl; + oldFdmgr ( const oldFdmgr & ); + oldFdmgr & operator = ( const oldFdmgr & ); +}; + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class chronIntIdResTable ; +template class resTable; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +epicsShareFunc fdRegForOldFdmgr::fdRegForOldFdmgr + (const SOCKET fdIn, const fdRegType typeIn, + const bool onceOnlyIn, fdManager &managerIn, + pCallBackFDMgr pFuncIn, void *pParamIn) : + fdReg (fdIn, typeIn, onceOnlyIn, managerIn), + pFunc (pFuncIn), pParam (pParamIn) +{ + if (pFuncIn==NULL) { + throwWithLocation ( noFunctionSpecified () ); + } +} + +epicsShareFunc fdRegForOldFdmgr::~fdRegForOldFdmgr () +{ + if (this->pFunc==NULL) { + throwWithLocation ( doubleDelete () ); + } +} + +epicsShareFunc void fdRegForOldFdmgr::callBack () +{ + (*this->pFunc) (this->pParam); +} + +timerForOldFdmgr::timerForOldFdmgr ( oldFdmgr &fdmgrIn, + double delayIn, pCallBackFDMgr pFuncIn, void * pParamIn ) : + timer ( fdmgrIn.createTimer() ), + fdmgr ( fdmgrIn ), pFunc ( pFuncIn ), pParam( pParamIn ) +{ + if ( pFuncIn == NULL ) { + throwWithLocation ( noFunctionSpecified () ); + } + this->fdmgr.resTbl.idAssignAdd (*this); + this->timer.start ( *this, delayIn ); +} + +timerForOldFdmgr::~timerForOldFdmgr () +{ + this->fdmgr.resTbl.remove ( this->getId() ); + this->timer.destroy (); +} + +epicsTimerNotify::expireStatus timerForOldFdmgr::expire ( const epicsTime & ) +{ + (*this->pFunc) (this->pParam); + return noRestart; +} + +oldFdmgr::oldFdmgr () {} + +extern "C" epicsShareFunc fdctx * epicsShareAPI fdmgr_init (void) +{ + oldFdmgr *pfdm; + + try { + pfdm = new oldFdmgr(); + } + catch (...) + { + pfdm = NULL; + } + + return (fdctx *) pfdm; +} + +extern "C" epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout ( + fdctx *pfdctx, struct timeval *ptimeout, pCallBackFDMgr pFunc, void *pParam) +{ + double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); + oldFdmgr *pfdm = static_cast (pfdctx); + timerForOldFdmgr *pTimer; + unsigned id = fdmgrNoAlarm; + + if (!pfdm) { + return fdmgrNoAlarm; + } + + while (true) { + try { + pTimer = new timerForOldFdmgr + (*pfdm, delay, pFunc, pParam); + } + catch (...) + { + pTimer = NULL; + } + if (pTimer) { + id = pTimer->getId (); + if (id!=fdmgrNoAlarm) { + break; + } + else { + delete pTimer; + } + } + else { + break; + } + } + + return id; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + timerForOldFdmgr *pTimer; + + try { + pTimer = pfdm->resTbl.remove (id); + } + catch (...) + { + pTimer = NULL; + } + + if (pTimer==NULL) { + return -1; + } + delete pTimer; + return 0; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_callback ( + fdctx *pfdctx, SOCKET fd, enum fdi_type fdi, pCallBackFDMgr pFunc, void *pParam) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + fdRegForOldFdmgr *pfdrbc; + bool onceOnly = (fdi==fdi_write); + unsigned fdiType; + + if (pfdm==NULL) { + return -1; + } + + if (pFunc==NULL) { + return -1; + } + + if (fdi<0) { + return -1; + } + + fdiType = (unsigned) fdi; + + if (fdiType>=fdiToFdRegTypeNElements) { + return -1; + } + + try { + pfdrbc = new fdRegForOldFdmgr (fd, fdiToFdRegType[fdiType], onceOnly, *pfdm, pFunc, pParam); + } + catch (...) + { + pfdrbc = NULL; + } + + if (pfdrbc==NULL) { + return -1; + } + else { + return 0; + } +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_callback ( + fdctx *pfdctx, SOCKET fd, enum fdi_type fdi) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + fdReg *pFDR; + + if (pfdm==NULL) { + return -1; + } + + try { + pFDR = pfdm->lookUpFD (fd, fdiToFdRegType[fdi]); + } + catch (...) + { + pFDR = NULL; + } + + if (pFDR==NULL) { + return -1; + } + else { + delete pFDR; + return 0; + } +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_pend_event (fdctx *pfdctx, struct timeval *ptimeout) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); + + try { + pfdm->process (delay); + } + catch (...) { + return -1; + } + + return 0; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_delete (fdctx *pfdctx) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + delete pfdm; + return 0; +} + +/* + * depricated interface + */ +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_fd (fdctx *pfdctx, SOCKET fd) +{ + return fdmgr_clear_callback(pfdctx, fd, fdi_read); +} + +/* + * depricated interface + */ +extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_fd ( + fdctx *pfdctx, SOCKET fd, void (*pfunc)(void *pParam), void *param) +{ + return fdmgr_add_callback (pfdctx, fd, fdi_read, pfunc, param); +} diff --git a/modules/libcom/src/fdmgr/fdmgr.h b/modules/libcom/src/fdmgr/fdmgr.h new file mode 100644 index 000000000..a227335e7 --- /dev/null +++ b/modules/libcom/src/fdmgr/fdmgr.h @@ -0,0 +1,169 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* fdmgr.h + * + * Header file associated with a file descriptor manager + * for use with the UNIX system call select + * + * Author Jeffrey O. Hill + * hill@atdiv.lanl.gov + * 505 665 1831 + */ + +#ifndef includeFdmgrH +#define includeFdmgrH + +#include "ellLib.h" +#include "bucketLib.h" +#include "osiSock.h" +#include "epicsThread.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum fdi_type {fdi_read, fdi_write, fdi_excp}; +enum alarm_list_type {alt_invalid, alt_alarm, alt_expired, alt_free}; + +typedef void fdctx; +typedef void (*pCallBackFDMgr)(void *); + +/* + * C "typedef" name "alarm" was changed to "fdmgrAlarm" to avoid collisions + * with other libraries. Next the identifier was changed again to + * an unsigned integer type "fdmgrAlarmId". + * + * This "#define" is for codes that used to use a pointer to the old typedef + * "alarm" or "fdmgrAlarm" types to identify an alarm. + * + * ie the following code will allow compilation against + * all versions: + * + * #if defined (NEW_FDMGR_ALARMID) + * fdmgrAlarmId XXXX + * #elif defined (NEW_FDMGR_ALARM) + * fdmgrAlarm *XXXX; + * #else + * alarm *XXXX; + * #endif + * + * XXXX = fdmgrAlarmId fdmgr_add_timeout() + */ +typedef unsigned fdmgrAlarmId; +#define NEW_FDMGR_ALARMID + +/* + * + * Initialize a file descriptor manager session + * + */ +epicsShareFunc fdctx * epicsShareAPI fdmgr_init(void); + +/* + * Specify a function to be called with a specified parameter + * after a specified delay relative to the current time + * + * Returns fdmgrNoAlarm (zero) if alarm cant be created + */ +#define fdmgrNoAlarm 0 +epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +struct timeval *ptimeout, /* relative delay from current time */ +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param /* first parameter passed to the func */ +); + +/* + * Clear a timeout which has not executed its function (handler) + * yet. + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_timeout( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +fdmgrAlarmId id /* alarm to delete */ +); + +/* + * + * Specify a function (handler) to be called with a specified parameter + * when a file descriptor becomes active. The parameter fdi (file + * descriptor interest) specifies the type of activity (IO) we wish + * to be informed of: read, write, or exception. For more + * info on this see the man pages for the UNIX system call select(). + * + * read and exception callbacks are permanent( ie the application's + * function (handler) continues to be called each time the + * file descriptor becomes active until fdmgr_add_callback() + * is called). + * + * write callbacks are called only once after each call to + * fdmgr_add_callback() + * + */ +epicsShareFunc int epicsShareAPI fdmgr_add_callback( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, /* file descriptor */ +enum fdi_type fdi, /* file descriptor interest type */ +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param /* first parameter passed to the func */ +); + +/* + * + * Clear nterest in a type of file descriptor activity (IO). + * + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_callback( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, /* file descriptor */ +enum fdi_type fdi /* file descriptor interest type */ +); + +/* + * + * Wait a specified delay relative from the current time for file + * descriptor activity (IO) or timeouts (timer expiration). Application + * specified functions (handlers) will not be called unless the + * application waits in this function or polls it frequently + * enough. + * + */ +epicsShareFunc int epicsShareAPI fdmgr_pend_event( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +struct timeval *ptimeout +); + + +/* + * obsolete interface + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_fd( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd +); + +/* + * obsolete interface + */ +epicsShareFunc int epicsShareAPI fdmgr_add_fd( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param +); + +epicsShareFunc int epicsShareAPI fdmgr_delete(fdctx *pfdctx); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef includeFdmgrH (last line in this file) */ + diff --git a/modules/libcom/src/flex/COPYING b/modules/libcom/src/flex/COPYING new file mode 100644 index 000000000..9b01361ca --- /dev/null +++ b/modules/libcom/src/flex/COPYING @@ -0,0 +1,38 @@ +Flex carries the copyright used for BSD software, slightly modified +because it originated at the Lawrence Berkeley (not Livermore!) Laboratory, +which operates under a contract with the Department of Energy: + + Copyright (c) 1990 The Regents of the University of California. + All rights reserved. + + This code is derived from software contributed to Berkeley by + Vern Paxson. + + The United States Government has rights in this work pursuant + to contract no. DE-AC03-76SF00098 between the United States + Department of Energy and the University of California. + + Redistribution and use in source and binary forms are permitted + provided that: (1) source distributions retain this entire + copyright notice and comment, and (2) distributions including + binaries display the following acknowledgement: ``This product + includes software developed by the University of California, + Berkeley and its contributors'' in the documentation or other + materials provided with the distribution and in all advertising + materials mentioning features or use of this software. Neither the + name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. + +This basically says "do whatever you please with this software except +remove this notice or take advantage of the University's (or the flex +authors') name". + +Note that the "flex.skel" scanner skeleton carries no copyright notice. +You are free to do whatever you please with scanners generated using flex; +for them, you are not even bound by the above copyright. diff --git a/modules/libcom/src/flex/Changes b/modules/libcom/src/flex/Changes new file mode 100644 index 000000000..0111a1f21 --- /dev/null +++ b/modules/libcom/src/flex/Changes @@ -0,0 +1,345 @@ +Changes between 2.3 Patch #7 (28Mar91) and 2.3 Patch #6: + + - Fixed out-of-bounds array access that caused bad tables + to be produced on machines where the bad reference happened + to yield a 1. This caused problems installing or running + flex on some Suns, in particular. + + +Changes between 2.3 Patch #6 (29Aug90) and 2.3 Patch #5: + + - Fixed a serious bug in yymore() which basically made it + completely broken. Thanks goes to Jean Christophe of + the Nethack development team for finding the problem + and passing along the fix. + + +Changes between 2.3 Patch #5 (16Aug90) and 2.3 Patch #4: + + - An up-to-date version of initscan.c so "make test" will + work after applying the previous patches + + +Changes between 2.3 Patch #4 (14Aug90) and 2.3 Patch #3: + + - Fixed bug in hexadecimal escapes which allowed only digits, + not letters, in escapes + - Fixed bug in previous "Changes" file! + + +Changes between 2.3 Patch #3 (03Aug90) and 2.3 Patch #2: + + - Correction to patch #2 for gcc compilation; thanks goes to + Paul Eggert for catching this. + + +Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release: + + - Fixed (hopefully) headaches involving declaring malloc() + and free() for gcc, which defines __STDC__ but (often) doesn't + come with the standard include files such as . + Reordered #ifdef maze in the scanner skeleton in the hope of + getting the declarations right for cfront and g++, too. + + - Note that this patch supercedes patch #1 for release 2.3, + which was never announced but was available briefly for + anonymous ftp. + + +Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release: + + User-visible: + + - A lone <> rule (that is, one which is not qualified with + a list of start conditions) now specifies the EOF action for + *all* start conditions which haven't already had <> actions + given. To specify an end-of-file action for just the initial + state, use <>. + + - -d debug output is now contigent on the global yy_flex_debug + being set to a non-zero value, which it is by default. + + - A new macro, YY_USER_INIT, is provided for the user to specify + initialization action to be taken on the first call to the + scanner. This action is done before the scanner does its + own initialization. + + - yy_new_buffer() has been added as an alias for yy_create_buffer() + + - Comments beginning with '#' and extending to the end of the line + now work, but have been deprecated (in anticipation of making + flex recognize #line directives). + + - The funky restrictions on when semi-colons could follow the + YY_NEW_FILE and yyless macros have been removed. They now + behave identically to functions. + + - A bug in the sample redefinition of YY_INPUT in the documentation + has been corrected. + + - A bug in the sample simple tokener in the documentation has + been corrected. + + - The documentation on the incompatibilities between flex and + lex has been reordered so that the discussion of yylineno + and input() come first, as it's anticipated that these will + be the most common source of headaches. + + + Things which didn't used to be documented but now are: + + - flex interprets "^foo|bar" differently from lex. flex interprets + it as "match either a 'foo' or a 'bar', providing it comes at the + beginning of a line", whereas lex interprets it as "match either + a 'foo' at the beginning of a line, or a 'bar' anywhere". + + - flex initializes the global "yyin" on the first call to the + scanner, while lex initializes it at compile-time. + + - yy_switch_to_buffer() can be used in the yywrap() macro/routine. + + - flex scanners do not use stdio for their input, and hence when + writing an interactive scanner one must explictly call fflush() + after writing out a prompt. + + - flex scanner can be made reentrant (after a fashion) by using + "yyrestart( yyin );". This is useful for interactive scanners + which have interrupt handlers that long-jump out of the scanner. + + - a defense of why yylineno is not supported is included, along + with a suggestion on how to convert scanners which rely on it. + + + Other changes: + + - Prototypes and proper declarations of void routines have + been added to the flex source code, courtesy of Kevin B. Kenny. + + - Routines dealing with memory allocation now use void* pointers + instead of char* - see Makefile for porting implications. + + - Error-checking is now done when flex closes a file. + + - Various lint tweaks were added to reduce the number of gripes. + + - Makefile has been further parameterized to aid in porting. + + - Support for SCO Unix added. + + - Flex now sports the latest & greatest UC copyright notice + (which is only slightly different from the previous one). + + - A note has been added to flexdoc.1 mentioning work in progress + on modifying flex to generate straight C code rather than a + table-driven automaton, with an email address of whom to contact + if you are working along similar lines. + + +Changes between 2.2 Patch #3 (30Mar90) and 2.2 Patch #2: + + - fixed bug which caused -I scanners to bomb + + +Changes between 2.2 Patch #2 (27Mar90) and 2.2 Patch #1: + + - fixed bug writing past end of input buffer in yyunput() + - fixed bug detecting NUL's at the end of a buffer + + +Changes between 2.2 Patch #1 (23Mar90) and 2.2 (alpha) release: + + - Makefile fixes: definition of MAKE variable for systems + which don't have it; installation of flexdoc.1 along with + flex.1; fixed two bugs which could cause "bigtest" to fail. + + - flex.skel fix for compiling with g++. + + - README and flexdoc.1 no longer list an out-of-date BITNET address + for contacting me. + + - minor typos and formatting changes to flex.1 and flexdoc.1. + + +Changes between 2.2 (alpha) release of March '90 and previous release: + + User-visible: + + - Full user documentation now available. + + - Support for 8-bit scanners. + + - Scanners now accept NUL's. + + - A facility has been added for dealing with multiple + input buffers. + + - Two manual entries now. One which fully describes flex + (rather than just its differences from lex), and the + other for quick(er) reference. + + - A number of changes to bring flex closer into compliance + with the latest POSIX lex draft: + + %t support + flex now accepts multiple input files and concatenates + them together to form its input + previous -c (compress) flag renamed -C + do-nothing -c and -n flags added + Any indented code or code within %{}'s in section 2 is + now copied to the output + + - yyleng is now a bona fide global integer. + + - -d debug information now gives the line number of the + matched rule instead of which number rule it was from + the beginning of the file. + + - -v output now includes a summary of the flags used to generate + the scanner. + + - unput() and yyrestart() are now globally callable. + + - yyrestart() no longer closes the previous value of yyin. + + - C++ support; generated scanners can be compiled with C++ compiler. + + - Primitive -lfl library added, containing default main() + which calls yylex(). A number of routines currently living + in the scanner skeleton will probably migrate to here + in the future (in particular, yywrap() will probably cease + to be a macro and instead be a function in the -lfl library). + + - Hexadecimal (\x) escape sequences added. + + - Support for MS-DOS, VMS, and Turbo-C integrated. + + - The %used/%unused operators have been deprecated. They + may go away soon. + + + Other changes: + + - Makefile enhanced for easier testing and installation. + - The parser has been tweaked to detect some erroneous + constructions which previously were missed. + - Scanner input buffer overflow is now detected. + - Bugs with missing "const" declarations fixed. + - Out-of-date Minix/Atari patches provided. + - Scanners no longer require printf() unless FLEX_DEBUG is being used. + - A subtle input() bug has been fixed. + - Line numbers for "continued action" rules (those following + the special '|' action) are now correct. + - unput() bug fixed; had been causing problems porting flex to VMS. + - yymore() handling rewritten to fix bug with interaction + between yymore() and trailing context. + - EOF in actions now generates an error message. + - Bug involving -CFe and generating equivalence classes fixed. + - Bug which made -CF be treated as -Cf fixed. + - Support for SysV tmpnam() added. + - Unused #define's for scanner no longer generated. + - Error messages which are associated with a particular input + line are now all identified with their input line in standard + format. + - % directives which are valid to lex but not to flex are + now ignored instead of generating warnings. + - -DSYS_V flag can now also be specified -DUSG for System V + compilation. + + +Changes between 2.1 beta-test release of June '89 and previous release: + + User-visible: + + - -p flag generates a performance report to stderr. The report + consists of comments regarding features of the scanner rules + which result in slower scanners. + + - -b flag generates backtracking information to lex.backtrack. + This is a list of scanner states which require backtracking + and the characters on which they do so. By adding rules + one can remove backtracking states. If all backtracking states + are eliminated, the generated scanner will run faster. + Backtracking is not yet documented in the manual entry. + + - Variable trailing context now works, i.e., one can have + rules like "(foo)*/[ \t]*bletch". Some trailing context + patterns still cannot be properly matched and generate + error messages. These are patterns where the ending of the + first part of the rule matches the beginning of the second + part, such as "zx*/xy*", where the 'x*' matches the 'x' at + the beginning of the trailing context. Lex won't get these + patterns right either. + + - Faster scanners. + + - End-of-file rules. The special rule "<>" indicates + actions which are to be taken when an end-of-file is + encountered and yywrap() returns non-zero (i.e., indicates + no further files to process). See manual entry for example. + + - The -r (reject used) flag is gone. flex now scans the input + for occurrences of the string "REJECT" to determine if the + action is needed. It tries to be intelligent about this but + can be fooled. One can force the presence or absence of + REJECT by adding a line in the first section of the form + "%used REJECT" or "%unused REJECT". + + - yymore() has been implemented. Similarly to REJECT, flex + detects the use of yymore(), which can be overridden using + "%used" or "%unused". + + - Patterns like "x{0,3}" now work (i.e., with lower-limit == 0). + + - Removed '\^x' for ctrl-x misfeature. + + - Added '\a' and '\v' escape sequences. + + - \ now works for octal escape sequences; previously + \0 was required. + + - Better error reporting; line numbers are associated with rules. + + - yyleng is a macro; it cannot be accessed outside of the + scanner source file. + + - yytext and yyleng should not be modified within a flex action. + + - Generated scanners #define the name FLEX_SCANNER. + + - Rules are internally separated by YY_BREAK in lex.yy.c rather + than break, to allow redefinition. + + - The macro YY_USER_ACTION can be redefined to provide an action + which is always executed prior to the matched rule's action. + + - yyrestart() is a new action which can be used to restart + the scanner after it has seen an end-of-file (a "real" one, + that is, one for which yywrap() returned non-zero). It takes + a FILE* argument indicating a new file to scan and sets + things up so that a subsequent call to yylex() will start + scanning that file. + + - Internal scanner names all preceded by "yy_" + + - lex.yy.c is deleted if errors are encountered during processing. + + - Comments may be put in the first section of the input by preceding + them with '#'. + + + + Other changes: + + - Some portability-related bugs fixed, in particular for machines + with unsigned characters or sizeof( int* ) != sizeof( int ). + Also, tweaks for VMS and Microsoft C (MS-DOS), and identifiers all + trimmed to be 31 or fewer characters. Shortened file names + for dinosaur OS's. Checks for allocating > 64K memory + on 16 bit'ers. Amiga tweaks. Compiles using gcc on a Sun-3. + - Compressed and fast scanner skeletons merged. + - Skeleton header files done away with. + - Generated scanner uses prototypes and "const" for __STDC__. + - -DSV flag is now -DSYS_V for System V compilation. + - Removed all references to FTL language. + - Software now covered by BSD Copyright. + - flex will replace lex in subsequent BSD releases. diff --git a/modules/libcom/src/flex/EPICS_READ_THIS b/modules/libcom/src/flex/EPICS_READ_THIS new file mode 100644 index 000000000..a1d04b97d --- /dev/null +++ b/modules/libcom/src/flex/EPICS_READ_THIS @@ -0,0 +1,32 @@ +This is a version of the BSD flex that has had its skeleton file munged in +order to force it to build lex programs that have all their functions and +variables defined as static. + +The file flex.skel.static is simply a copy of flex.skel that has been altered +to make all the components into static variables. + +In order to be able to actually use the lex files produced by this flavor of +flex, you must #include them into your C programs. Otherwise they will +be uncallable (all functions are static). This is typical of lex programs +that are used by yacc programs anyway. + +The scan.c file is actually the output of scan.l.DISTRIB when run through +itself, using the regular flex.skel skeleton with the -i option. + +To regenerate scan.c, make sure you have a build of a working e_flex binary +somewhere, then in this directory (not an O. build directory): + +% mv scan.l.DISTRIB scan.l +% /path/to/e_flex -Sflex.skel -8 -i scan.l +% mv lex.yy.c scan.c +% make + +Then use the new binary to make sure it can build itself: + +% O./e_flex -Sflex.skel -8 -i scan.l +% mv lex.yy.c scan.c +% make + +If that succeeds, don't forget to rename scan.l back again: + +% mv scan.l scan.l.DISTRIB diff --git a/modules/libcom/src/flex/Flex.doc b/modules/libcom/src/flex/Flex.doc new file mode 100644 index 000000000..3a37128e1 --- /dev/null +++ b/modules/libcom/src/flex/Flex.doc @@ -0,0 +1,1792 @@ + 26 May 1990 FLEX(1) + + NAME + flex - fast lexical analyzer generator + + SYNOPSIS + flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...] + + DESCRIPTION + flex is a tool for generating scanners: programs which recognized lexi- + cal patterns in text. flex reads the given input files, or its standard + input if no file names are given, for a description of a scanner to gen- + erate. The description is in the form of pairs of regular expressions + and C code, called rules. flex generates as output a C source file, + lex.yy.c, which defines a routine yylex(). This file is compiled and + linked with the -lfl library to produce an executable. When the execut- + able is run, it analyzes its input for occurrences of the regular + expressions. Whenever it finds one, it executes the corresponding C + code. + + SOME SIMPLE EXAMPLES + + First some simple examples to get the flavor of how one uses flex. The + following flex input specifies a scanner which whenever it encounters + the string "username" will replace it with the user's login name: + + %% + username printf( "%s", getlogin() ); + + By default, any text not matched by a flex scanner is copied to the out- + put, so the net effect of this scanner is to copy its input file to its + output with each occurrence of "username" expanded. In this input, + there is just one rule. "username" is the pattern and the "printf" is + the action. The "%%" marks the beginning of the rules. + + Here's another simple example: + + int num_lines = 0, num_chars = 0; + + %% + \n ++num_lines; ++num_chars; + . ++num_chars; + + %% + main() + { + yylex(); + printf( "# of lines = %d, # of chars = %d\n", + num_lines, num_chars ); + } + + This scanner counts the number of characters and the number of lines in + its input (it produces no output other than the final report on the + counts). The first line declares two globals, "num_lines" and + "num_chars", which are accessible both inside yylex() and in the main() + + Version 2.3 1 + + FLEX(1) 26 May 1990 + + routine declared after the second "%%". There are two rules, one which + matches a newline ("\n") and increments both the line count and the + character count, and one which matches any character other than a new- + line (indicated by the "." regular expression). + + A somewhat more complicated example: + + /* scanner for a toy Pascal-like language */ + + %{ + /* need this for the call to atof() below */ + #include + %} + + DIGIT [0-9] + ID [a-z][a-z0-9]* + + %% + + {DIGIT}+ { + printf( "An integer: %s (%d)\n", yytext, + atoi( yytext ) ); + } + + {DIGIT}+"."{DIGIT}* { + printf( "A float: %s (%g)\n", yytext, + atof( yytext ) ); + } + + if|then|begin|end|procedure|function { + printf( "A keyword: %s\n", yytext ); + } + + {ID} printf( "An identifier: %s\n", yytext ); + + "+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext ); + + "{"[^}\n]*"}" /* eat up one-line comments */ + + [ \t\n]+ /* eat up whitespace */ + + . printf( "Unrecognized character: %s\n", yytext ); + + %% + + main( argc, argv ) + int argc; + char **argv; + { + ++argv, --argc; /* skip over program name */ + if ( argc > 0 ) + yyin = fopen( argv[0], "r" ); + else + yyin = stdin; + + 2 Version 2.3 + + 26 May 1990 FLEX(1) + + yylex(); + } + + This is the beginnings of a simple scanner for a language like Pascal. + It identifies different types of tokens and reports on what it has seen. + + The details of this example will be explained in the following sections. + + FORMAT OF THE INPUT FILE + The flex input file consists of three sections, separated by a line with + just %% in it: + + definitions + %% + rules + %% + user code + + The definitions section contains declarations of simple name definitions + to simplify the scanner specification, and declarations of start condi- + tions, which are explained in a later section. + + Name definitions have the form: + + name definition + + The "name" is a word beginning with a letter or an underscore ('_') fol- + lowed by zero or more letters, digits, '_', or '-' (dash). The defini- + tion is taken to begin at the first non-white-space character following + the name and continuing to the end of the line. The definition can sub- + sequently be referred to using "{name}", which will expand to "(defini- + tion)". For example, + + DIGIT [0-9] + ID [a-z][a-z0-9]* + + defines "DIGIT" to be a regular expression which matches a single digit, + and "ID" to be a regular expression which matches a letter followed by + zero-or-more letters-or-digits. A subsequent reference to + + {DIGIT}+"."{DIGIT}* + + is identical to + + ([0-9])+"."([0-9])* + + and matches one-or-more digits followed by a '.' followed by zero-or- + more digits. + + The rules section of the flex input contains a series of rules of the + form: + + pattern action + + Version 2.3 3 + + FLEX(1) 26 May 1990 + + where the pattern must be unindented and the action must begin on the + same line. + + See below for a further description of patterns and actions. + + Finally, the user code section is simply copied to lex.yy.c verbatim. + It is used for companion routines which call or are called by the + scanner. The presence of this section is optional; if it is missing, + the second %% in the input file may be skipped, too. + + In the definitions and rules sections, any indented text or text + enclosed in %{ and %} is copied verbatim to the output (with the %{}'s + removed). The %{}'s must appear unindented on lines by themselves. + + In the rules section, any indented or %{} text appearing before the + first rule may be used to declare variables which are local to the scan- + ning routine and (after the declarations) code which is to be executed + whenever the scanning routine is entered. Other indented or %{} text in + the rule section is still copied to the output, but its meaning is not + well-defined and it may well cause compile-time errors (this feature is + present for POSIX compliance; see below for other such features). + + In the definitions section, an unindented comment (i.e., a line begin- + ning with "/*") is also copied verbatim to the output up to the next + "*/". Also, any line in the definitions section beginning with '#' is + ignored, though this style of comment is deprecated and may go away in + the future. + + PATTERNS + The patterns in the input are written using an extended set of regular + expressions. These are: + + x match the character 'x' + . any character except newline + [xyz] a "character class"; in this case, the pattern + matches either an 'x', a 'y', or a 'z' + [abj-oZ] a "character class" with a range in it; matches + an 'a', a 'b', any letter from 'j' through 'o', + or a 'Z' + [^A-Z] a "negated character class", i.e., any character + but those in the class. In this case, any + character EXCEPT an uppercase letter. + [^A-Z\n] any character EXCEPT an uppercase letter or + a newline + r* zero or more r's, where r is any regular expression + r+ one or more r's + r? zero or one r's (that is, "an optional r") + r{2,5} anywhere from two to five r's + r{2,} two or more r's + r{4} exactly 4 r's + {name} the expansion of the "name" definition + (see above) + "[xyz]\"foo" + + 4 Version 2.3 + + 26 May 1990 FLEX(1) + + the literal string: [xyz]"foo + \X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v', + then the ANSI-C interpretation of \x. + Otherwise, a literal 'X' (used to escape + operators such as '*') + \123 the character with octal value 123 + \x2a the character with hexadecimal value 2a + (r) match an r; parentheses are used to override + precedence (see below) + + rs the regular expression r followed by the + regular expression s; called "concatenation" + + r|s either an r or an s + + r/s an r but only if it is followed by an s. The + s is not part of the matched text. This type + of pattern is called as "trailing context". + ^r an r, but only at the beginning of a line + r$ an r, but only at the end of a line. Equivalent + to "r/\n". + + r an r, but only in start condition s (see + below for discussion of start conditions) + r + same, but in any of start conditions s1, + s2, or s3 + + <> an end-of-file + <> + an end-of-file when in start condition s1 or s2 + + The regular expressions listed above are grouped according to pre- + cedence, from highest precedence at the top to lowest at the bottom. + Those grouped together have equal precedence. For example, + + foo|bar* + + is the same as + + (foo)|(ba(r*)) + + since the '*' operator has higher precedence than concatenation, and + concatenation higher than alternation ('|'). This pattern therefore + matches either the string "foo" or the string "ba" followed by zero-or- + more r's. To match "foo" or zero-or-more "bar"'s, use: + + foo|(bar)* + + Version 2.3 5 + + FLEX(1) 26 May 1990 + + and to match zero-or-more "foo"'s-or-"bar"'s: + + (foo|bar)* + + Some notes on patterns: + + - A negated character class such as the example "[^A-Z]" above will + match a newline unless "\n" (or an equivalent escape sequence) is + one of the characters explicitly present in the negated character + class (e.g., "[^A-Z\n]"). This is unlike how many other regular + expression tools treat negated character classes, but unfortunately + the inconsistency is historically entrenched. Matching newlines + means that a pattern like [^"]* can match an entire input (over- + flowing the scanner's input buffer) unless there's another quote in + the input. + + - A rule can have at most one instance of trailing context (the '/' + operator or the '$' operator). The start condition, '^', and + "<>" patterns can only occur at the beginning of a pattern, + and, as well as with '/' and '$', cannot be grouped inside + parentheses. A '^' which does not occur at the beginning of a rule + or a '$' which does not occur at the end of a rule loses its spe- + cial properties and is treated as a normal character. + + The following are illegal: + + foo/bar$ + foobar + + Note that the first of these, can be written "foo/bar\n". + + The following will result in '$' or '^' being treated as a normal + character: + + foo|(bar$) + foo|^bar + + If what's wanted is a "foo" or a bar-followed-by-a-newline, the + following could be used (the special '|' action is explained + below): + + foo | + bar$ /* action goes here */ + + A similar trick will work for matching a foo or a bar-at-the- + beginning-of-a-line. + + HOW THE INPUT IS MATCHED + When the generated scanner is run, it analyzes its input looking for + strings which match any of its patterns. If it finds more than one + match, it takes the one matching the most text (for trailing context + rules, this includes the length of the trailing part, even though it + will then be returned to the input). If it finds two or more matches of + + 6 Version 2.3 + + 26 May 1990 FLEX(1) + + the same length, the rule listed first in the flex input file is chosen. + + Once the match is determined, the text corresponding to the match + (called the token) is made available in the global character pointer + yytext, and its length in the global integer yyleng. The action + corresponding to the matched pattern is then executed (a more detailed + description of actions follows), and then the remaining input is scanned + for another match. + + If no match is found, then the default rule is executed: the next char- + acter in the input is considered matched and copied to the standard out- + put. Thus, the simplest legal flex input is: + + %% + + which generates a scanner that simply copies its input (one character at + a time) to its output. + + ACTIONS + Each pattern in a rule has a corresponding action, which can be any + arbitrary C statement. The pattern ends at the first non-escaped whi- + tespace character; the remainder of the line is its action. If the + action is empty, then when the pattern is matched the input token is + simply discarded. For example, here is the specification for a program + which deletes all occurrences of "zap me" from its input: + + %% + "zap me" + + (It will copy all other characters in the input to the output since they + will be matched by the default rule.) + + Here is a program which compresses multiple blanks and tabs down to a + single blank, and throws away whitespace found at the end of a line: + + %% + [ \t]+ putchar( ' ' ); + [ \t]+$ /* ignore this token */ + + If the action contains a '{', then the action spans till the balancing + '}' is found, and the action may cross multiple lines. flex knows about + C strings and comments and won't be fooled by braces found within them, + but also allows actions to begin with %{ and will consider the action to + be all the text up to the next %} (regardless of ordinary braces inside + the action). + + An action consisting solely of a vertical bar ('|') means "same as the + action for the next rule." See below for an illustration. + + Actions can include arbitrary C code, including return statements to + return a value to whatever routine called yylex(). Each time yylex() is + called it continues processing tokens from where it last left off until + it either reaches the end of the file or executes a return. Once it + + Version 2.3 7 + + FLEX(1) 26 May 1990 + + reaches an end-of-file, however, then any subsequent call to yylex() + will simply immediately return, unless yyrestart() is first called (see + below). + + Actions are not allowed to modify yytext or yyleng. + + There are a number of special directives which can be included within an + action: + + - ECHO copies yytext to the scanner's output. + + - BEGIN followed by the name of a start condition places the scanner + in the corresponding start condition (see below). + + - REJECT directs the scanner to proceed on to the "second best" rule + which matched the input (or a prefix of the input). The rule is + chosen as described above in "How the Input is Matched", and yytext + and yyleng set up appropriately. It may either be one which + matched as much text as the originally chosen rule but came later + in the flex input file, or one which matched less text. For exam- + ple, the following will both count the words in the input and call + the routine special() whenever "frob" is seen: + + int word_count = 0; + %% + + frob special(); REJECT; + [^ \t\n]+ ++word_count; + + Without the REJECT, any "frob"'s in the input would not be counted + as words, since the scanner normally executes only one action per + token. Multiple REJECT's are allowed, each one finding the next + best choice to the currently active rule. For example, when the + following scanner scans the token "abcd", it will write "abcdab- + caba" to the output: + + %% + a | + ab | + abc | + abcd ECHO; REJECT; + .|\n /* eat up any unmatched character */ + + (The first three rules share the fourth's action since they use the + special '|' action.) REJECT is a particularly expensive feature in + terms scanner performance; if it is used in any of the scanner's + actions it will slow down all of the scanner's matching. Further- + more, REJECT cannot be used with the -f or -F options (see below). + + Note also that unlike the other special actions, REJECT is a + branch; code immediately following it in the action will not be + executed. + + - yymore() tells the scanner that the next time it matches a rule, + + 8 Version 2.3 + + 26 May 1990 FLEX(1) + + the corresponding token should be appended onto the current value + of yytext rather than replacing it. For example, given the input + "mega-kludge" the following will write "mega-mega-kludge" to the + output: + + %% + mega- ECHO; yymore(); + kludge ECHO; + + First "mega-" is matched and echoed to the output. Then "kludge" + is matched, but the previous "mega-" is still hanging around at the + beginning of yytext so the ECHO for the "kludge" rule will actually + write "mega-kludge". The presence of yymore() in the scanner's + action entails a minor performance penalty in the scanner's match- + ing speed. + + - yyless(n) returns all but the first n characters of the current + token back to the input stream, where they will be rescanned when + the scanner looks for the next match. yytext and yyleng are + adjusted appropriately (e.g., yyleng will now be equal to n ). For + example, on the input "foobar" the following will write out + "foobarbar": + + %% + foobar ECHO; yyless(3); + [a-z]+ ECHO; + + An argument of 0 to yyless will cause the entire current input + string to be scanned again. Unless you've changed how the scanner + will subsequently process its input (using BEGIN, for example), + this will result in an endless loop. + + - unput(c) puts the character c back onto the input stream. It will + be the next character scanned. The following action will take the + current token and cause it to be rescanned enclosed in parentheses. + + { + int i; + unput( ')' ); + for ( i = yyleng - 1; i >= 0; --i ) + unput( yytext[i] ); + unput( '(' ); + } + + Note that since each unput() puts the given character back at the + beginning of the input stream, pushing back strings must be done + back-to-front. + + - input() reads the next character from the input stream. For exam- + ple, the following is one way to eat up C comments: + + %% + "/*" { + register int c; + + Version 2.3 9 + + FLEX(1) 26 May 1990 + + for ( ; ; ) + { + while ( (c = input()) != '*' && + c != EOF ) + ; /* eat up text of comment */ + + if ( c == '*' ) + { + while ( (c = input()) == '*' ) + ; + if ( c == '/' ) + break; /* found the end */ + } + + if ( c == EOF ) + { + error( "EOF in comment" ); + break; + } + } + } + + (Note that if the scanner is compiled using C++, then input() is + instead referred to as yyinput(), in order to avoid a name clash + with the C++ stream by the name of input.) + + - yyterminate() can be used in lieu of a return statement in an + action. It terminates the scanner and returns a 0 to the scanner's + caller, indicating "all done". Subsequent calls to the scanner + will immediately return unless preceded by a call to yyrestart() + (see below). By default, yyterminate() is also called when an + end-of-file is encountered. It is a macro and may be redefined. + + THE GENERATED SCANNER + The output of flex is the file lex.yy.c, which contains the scanning + routine yylex(), a number of tables used by it for matching tokens, and + a number of auxiliary routines and macros. By default, yylex() is + declared as follows: + + int yylex() + { + ... various definitions and the actions in here ... + } + + (If your environment supports function prototypes, then it will be "int + yylex( void )".) This definition may be changed by redefining the + "YY_DECL" macro. For example, you could use: + + #undef YY_DECL + #define YY_DECL float lexscan( a, b ) float a, b; + + to give the scanning routine the name lexscan, returning a float, and + taking two floats as arguments. Note that if you give arguments to the + + 10 Version 2.3 + + 26 May 1990 FLEX(1) + + scanning routine using a K&R-style/non-prototyped function declaration, + you must terminate the definition with a semi-colon (;). + + Whenever yylex() is called, it scans tokens from the global input file + yyin (which defaults to stdin). It continues until it either reaches an + end-of-file (at which point it returns the value 0) or one of its + actions executes a return statement. In the former case, when called + again the scanner will immediately return unless yyrestart() is called + to point yyin at the new input file. ( yyrestart() takes one argument, + a FILE * pointer.) In the latter case (i.e., when an action executes a + return), the scanner may then be called again and it will resume scan- + ning where it left off. + + By default (and for purposes of efficiency), the scanner uses block- + reads rather than simple getc() calls to read characters from yyin. The + nature of how it gets its input can be controlled by redefining the + YY_INPUT macro. YY_INPUT's calling sequence is + "YY_INPUT(buf,result,max_size)". Its action is to place up to max_size + characters in the character array buf and return in the integer variable + result either the number of characters read or the constant YY_NULL (0 + on Unix systems) to indicate EOF. The default YY_INPUT reads from the + global file-pointer "yyin". + + A sample redefinition of YY_INPUT (in the definitions section of the + input file): + + %{ + #undef YY_INPUT + #define YY_INPUT(buf,result,max_size) \ + { \ + int c = getchar(); \ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ + } + %} + + This definition will change the input processing to occur one character + at a time. + + You also can add in things like keeping track of the input line number + this way; but don't expect your scanner to go very fast. + + When the scanner receives an end-of-file indication from YY_INPUT, it + then checks the yywrap() function. If yywrap() returns false (zero), + then it is assumed that the function has gone ahead and set up yyin to + point to another input file, and scanning continues. If it returns true + (non-zero), then the scanner terminates, returning 0 to its caller. + + The default yywrap() always returns 1. Presently, to redefine it you + must first "#undef yywrap", as it is currently implemented as a macro. + As indicated by the hedging in the previous sentence, it may be changed + to a true function in the near future. + + The scanner writes its ECHO output to the yyout global (default, + stdout), which may be redefined by the user simply by assigning it to + + Version 2.3 11 + + FLEX(1) 26 May 1990 + + some other FILE pointer. + + START CONDITIONS + flex provides a mechanism for conditionally activating rules. Any rule + whose pattern is prefixed with "" will only be active when the + scanner is in the start condition named "sc". For example, + + [^"]* { /* eat up the string body ... */ + ... + } + + will be active only when the scanner is in the "STRING" start condition, + and + + \. { /* handle an escape ... */ + ... + } + + will be active only when the current start condition is either "INI- + TIAL", "STRING", or "QUOTE". + + Start conditions are declared in the definitions (first) section of the + input using unindented lines beginning with either %s or %x followed by + a list of names. The former declares inclusive start conditions, the + latter exclusive start conditions. A start condition is activated using + the BEGIN action. Until the next BEGIN action is executed, rules with + the given start condition will be active and rules with other start con- + ditions will be inactive. If the start condition is inclusive, then + rules with no start conditions at all will also be active. If it is + exclusive, then only rules qualified with the start condition will be + active. A set of rules contingent on the same exclusive start condition + describe a scanner which is independent of any of the other rules in the + flex input. Because of this, exclusive start conditions make it easy to + specify "mini-scanners" which scan portions of the input that are syn- + tactically different from the rest (e.g., comments). + + If the distinction between inclusive and exclusive start conditions is + still a little vague, here's a simple example illustrating the connec- + tion between the two. The set of rules: + + %s example + %% + foo /* do something */ + + is equivalent to + + %x example + %% + foo /* do something */ + + The default rule (to ECHO any unmatched character) remains active in + start conditions. + + 12 Version 2.3 + + 26 May 1990 FLEX(1) + + BEGIN(0) returns to the original state where only the rules with no + start conditions are active. This state can also be referred to as the + start-condition "INITIAL", so BEGIN(INITIAL) is equivalent to BEGIN(0). + (The parentheses around the start condition name are not required but + are considered good style.) + + BEGIN actions can also be given as indented code at the beginning of the + rules section. For example, the following will cause the scanner to + enter the "SPECIAL" start condition whenever yylex() is called and the + global variable enter_special is true: + + int enter_special; + + %x SPECIAL + %% + if ( enter_special ) + BEGIN(SPECIAL); + + blahblahblah + ...more rules follow... + + To illustrate the uses of start conditions, here is a scanner which pro- + vides two different interpretations of a string like "123.456". By + default it will treat it as as three tokens, the integer "123", a dot + ('.'), and the integer "456". But if the string is preceded earlier in + the line by the string "expect-floats" it will treat it as a single + token, the floating-point number 123.456: + + %{ + #include + %} + %s expect + + %% + expect-floats BEGIN(expect); + + [0-9]+"."[0-9]+ { + printf( "found a float, = %f\n", + atof( yytext ) ); + } + \n { + /* that's the end of the line, so + * we need another "expect-number" + * before we'll recognize any more + * numbers + */ + BEGIN(INITIAL); + } + + [0-9]+ { + printf( "found an integer, = %d\n", + atoi( yytext ) ); + } + + Version 2.3 13 + + FLEX(1) 26 May 1990 + + "." printf( "found a dot\n" ); + + Here is a scanner which recognizes (and discards) C comments while main- + taining a count of the current input line. + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* /* eat anything that's not a '*' */ + "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ + \n ++line_num; + "*"+"/" BEGIN(INITIAL); + + Note that start-conditions names are really integer values and can be + stored as such. Thus, the above could be extended in the following + fashion: + + %x comment foo + %% + int line_num = 1; + int comment_caller; + + "/*" { + comment_caller = INITIAL; + BEGIN(comment); + } + + ... + + "/*" { + comment_caller = foo; + BEGIN(comment); + } + + [^*\n]* /* eat anything that's not a '*' */ + "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ + \n ++line_num; + "*"+"/" BEGIN(comment_caller); + + One can then implement a "stack" of start conditions using an array of + integers. (It is likely that such stacks will become a full-fledged + flex feature in the future.) Note, though, that start conditions do not + have their own name-space; %s's and %x's declare names in the same + fashion as #define's. + + MULTIPLE INPUT BUFFERS + Some scanners (such as those which support "include" files) require + reading from several input streams. As flex scanners do a large amount + of buffering, one cannot control where the next input will be read from + by simply writing a YY_INPUT which is sensitive to the scanning context. + + 14 Version 2.3 + + 26 May 1990 FLEX(1) + + YY_INPUT is only called when the scanner reaches the end of its buffer, + which may be a long time after scanning a statement such as an "include" + which requires switching the input source. + + To negotiate these sorts of problems, flex provides a mechanism for + creating and switching between multiple input buffers. An input buffer + is created by using: + + YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + + which takes a FILE pointer and a size and creates a buffer associated + with the given file and large enough to hold size characters (when in + doubt, use YY_BUF_SIZE for the size). It returns a YY_BUFFER_STATE han- + dle, which may then be passed to other routines: + + void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + + switches the scanner's input buffer so subsequent tokens will come from + new_buffer. Note that yy_switch_to_buffer() may be used by yywrap() to + sets things up for continued scanning, instead of opening a new file and + pointing yyin at it. + + void yy_delete_buffer( YY_BUFFER_STATE buffer ) + + is used to reclaim the storage associated with a buffer. + + yy_new_buffer() is an alias for yy_create_buffer(), provided for compa- + tibility with the C++ use of new and delete for creating and destroying + dynamic objects. + + Finally, the YY_CURRENT_BUFFER macro returns a YY_BUFFER_STATE handle to + the current buffer. + + Here is an example of using these features for writing a scanner which + expands include files (the <> feature is discussed below): + + /* the "incl" state is used for picking up the name + * of an include file + */ + %x incl + + %{ + #define MAX_INCLUDE_DEPTH 10 + YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; + int include_stack_ptr = 0; + %} + + %% + include BEGIN(incl); + + [a-z]+ ECHO; + [^a-z\n]*\n? ECHO; + + [ \t]* /* eat the whitespace */ + + Version 2.3 15 + + FLEX(1) 26 May 1990 + + [^ \t\n]+ { /* got the include file name */ + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) + { + fprintf( stderr, "Includes nested too deeply" ); + exit( 1 ); + } + + include_stack[include_stack_ptr++] = + YY_CURRENT_BUFFER; + + yyin = fopen( yytext, "r" ); + + if ( ! yyin ) + error( ... ); + + yy_switch_to_buffer( + yy_create_buffer( yyin, YY_BUF_SIZE ) ); + + BEGIN(INITIAL); + } + + <> { + if ( --include_stack_ptr < 0 ) + { + yyterminate(); + } + + else + yy_switch_to_buffer( + include_stack[include_stack_ptr] ); + } + + END-OF-FILE RULES + The special rule "<>" indicates actions which are to be taken when + an end-of-file is encountered and yywrap() returns non-zero (i.e., indi- + cates no further files to process). The action must finish by doing one + of four things: + + - the special YY_NEW_FILE action, if yyin has been pointed at a new + file to process; + + - a return statement; + + - the special yyterminate() action; + + - or, switching to a new buffer using yy_switch_to_buffer() as shown + in the example above. + + <> rules may not be used with other patterns; they may only be + qualified with a list of start conditions. If an unqualified <> + rule is given, it applies to all start conditions which do not already + have <> actions. To specify an <> rule for only the initial + start condition, use + + 16 Version 2.3 + + 26 May 1990 FLEX(1) + + <> + + These rules are useful for catching things like unclosed comments. An + example: + + %x quote + %% + + ...other rules for dealing with quotes... + + <> { + error( "unterminated quote" ); + yyterminate(); + } + <> { + if ( *++filelist ) + { + yyin = fopen( *filelist, "r" ); + YY_NEW_FILE; + } + else + yyterminate(); + } + + MISCELLANEOUS MACROS + The macro YY_USER_ACTION can be redefined to provide an action which is + always executed prior to the matched rule's action. For example, it + could be #define'd to call a routine to convert yytext to lower-case. + + The macro YY_USER_INIT may be redefined to provide an action which is + always executed before the first scan (and before the scanner's internal + initializations are done). For example, it could be used to call a rou- + tine to read in a data table or open a logging file. + + In the generated scanner, the actions are all gathered in one large + switch statement and separated using YY_BREAK, which may be redefined. + By default, it is simply a "break", to separate each rule's action from + the following rule's. Redefining YY_BREAK allows, for example, C++ + users to #define YY_BREAK to do nothing (while being very careful that + every rule ends with a "break" or a "return"!) to avoid suffering from + unreachable statement warnings where because a rule's action ends with + "return", the YY_BREAK is inaccessible. + + INTERFACING WITH YACC + One of the main uses of flex is as a companion to the yacc parser- + generator. yacc parsers expect to call a routine named yylex() to find + the next input token. The routine is supposed to return the type of the + next token as well as putting any associated value in the global yylval. + To use flex with yacc, one specifies the -d option to yacc to instruct + it to generate the file y.tab.h containing definitions of all the + %tokens appearing in the yacc input. This file is then included in the + + Version 2.3 17 + + FLEX(1) 26 May 1990 + + flex scanner. For example, if one of the tokens is "TOK_NUMBER", part + of the scanner might look like: + + %{ + #include "y.tab.h" + %} + + %% + + [0-9]+ yylval = atoi( yytext ); return TOK_NUMBER; + + TRANSLATION TABLE + In the name of POSIX compliance, flex supports a translation table for + mapping input characters into groups. The table is specified in the + first section, and its format looks like: + + %t + 1 abcd + 2 ABCDEFGHIJKLMNOPQRSTUVWXYZ + 52 0123456789 + 6 \t\ \n + %t + + This example specifies that the characters 'a', 'b', 'c', and 'd' are to + all be lumped into group #1, upper-case letters in group #2, digits in + group #52, tabs, blanks, and newlines into group #6, and no other char- + acters will appear in the patterns. The group numbers are actually + disregarded by flex; %t serves, though, to lump characters together. + Given the above table, for example, the pattern "a(AA)*5" is equivalent + to "d(ZQ)*0". They both say, "match any character in group #1, followed + by zero-or-more pairs of characters from group #2, followed by a charac- + ter from group #52." Thus %t provides a crude way for introducing + equivalence classes into the scanner specification. + + Note that the -i option (see below) coupled with the equivalence classes + which flex automatically generates take care of virtually all the + instances when one might consider using %t. But what the hell, it's + there if you want it. + + OPTIONS + flex has the following options: + + -b Generate backtracking information to lex.backtrack. This is a list + of scanner states which require backtracking and the input charac- + ters on which they do so. By adding rules one can remove back- + tracking states. If all backtracking states are eliminated and -f + or -F is used, the generated scanner will run faster (see the -p + flag). Only users who wish to squeeze every last cycle out of + their scanners need worry about this option. (See the section on + PERFORMANCE CONSIDERATIONS below.) + + -c is a do-nothing, deprecated option included for POSIX compliance. + + 18 Version 2.3 + + 26 May 1990 FLEX(1) + + NOTE: in previous releases of flex -c specified table-compression + options. This functionality is now given by the -C flag. To ease + the the impact of this change, when flex encounters -c, it + currently issues a warning message and assumes that -C was desired + instead. In the future this "promotion" of -c to -C will go away + in the name of full POSIX compliance (unless the POSIX meaning is + removed first). + + -d makes the generated scanner run in debug mode. Whenever a pattern + is recognized and the global yy_flex_debug is non-zero (which is + the default), the scanner will write to stderr a line of the form: + + --accepting rule at line 53 ("the matched text") + + The line number refers to the location of the rule in the file + defining the scanner (i.e., the file that was fed to flex). Mes- + sages are also generated when the scanner backtracks, accepts the + default rule, reaches the end of its input buffer (or encounters a + NUL; at this point, the two look the same as far as the scanner's + concerned), or reaches an end-of-file. + + -f specifies (take your pick) full table or fast scanner. No table + compression is done. The result is large but fast. This option is + equivalent to -Cf (see below). + + -i instructs flex to generate a case-insensitive scanner. The case of + letters given in the flex input patterns will be ignored, and + tokens in the input will be matched regardless of case. The + matched text given in yytext will have the preserved case (i.e., it + will not be folded). + + -n is another do-nothing, deprecated option included only for POSIX + compliance. + + -p generates a performance report to stderr. The report consists of + comments regarding features of the flex input file which will cause + a loss of performance in the resulting scanner. Note that the use + of REJECT and variable trailing context (see the BUGS section in + flex(1)) entails a substantial performance penalty; use of + yymore(), the ^ operator, and the -I flag entail minor performance + penalties. + + -s causes the default rule (that unmatched scanner input is echoed to + stdout) to be suppressed. If the scanner encounters input that + does not match any of its rules, it aborts with an error. This + option is useful for finding holes in a scanner's rule set. + + -t instructs flex to write the scanner it generates to standard output + instead of lex.yy.c. + + -v specifies that flex should write to stderr a summary of statistics + regarding the scanner it generates. Most of the statistics are + meaningless to the casual flex user, but the first line identifies + the version of flex, which is useful for figuring out where you + + Version 2.3 19 + + FLEX(1) 26 May 1990 + + stand with respect to patches and new releases, and the next two + lines give the date when the scanner was created and a summary of + the flags which were in effect. + + -F specifies that the fast scanner table representation should be + used. This representation is about as fast as the full table + representation (-f), and for some sets of patterns will be consid- + erably smaller (and for others, larger). In general, if the pat- + tern set contains both "keywords" and a catch-all, "identifier" + rule, such as in the set: + + "case" return TOK_CASE; + "switch" return TOK_SWITCH; + ... + "default" return TOK_DEFAULT; + [a-z]+ return TOK_ID; + + then you're better off using the full table representation. If + only the "identifier" rule is present and you then use a hash table + or some such to detect the keywords, you're better off using -F. + + This option is equivalent to -CF (see below). + + -I instructs flex to generate an interactive scanner. Normally, + scanners generated by flex always look ahead one character before + deciding that a rule has been matched. At the cost of some scan- + ning overhead, flex will generate a scanner which only looks ahead + when needed. Such scanners are called interactive because if you + want to write a scanner for an interactive system such as a command + shell, you will probably want the user's input to be terminated + with a newline, and without -I the user will have to type a charac- + ter in addition to the newline in order to have the newline recog- + nized. This leads to dreadful interactive performance. + + If all this seems to confusing, here's the general rule: if a human + will be typing in input to your scanner, use -I, otherwise don't; + if you don't care about squeezing the utmost performance from your + scanner and you don't want to make any assumptions about the input + to your scanner, use -I. + + + Note, -I cannot be used in conjunction with full or fast tables, + i.e., the -f, -F, -Cf, or -CF flags. + + -L instructs flex not to generate #line directives. Without this + option, flex peppers the generated scanner with #line directives so + error messages in the actions will be correctly located with + respect to the original flex input file, and not to the fairly + meaningless line numbers of lex.yy.c. (Unfortunately flex does not + presently generate the necessary directives to "retarget" the line + numbers for those parts of lex.yy.c which it generated. So if + there is an error in the generated code, a meaningless line number + is reported.) + + -T makes flex run in trace mode. It will generate a lot of messages + + 20 Version 2.3 + + 26 May 1990 FLEX(1) + + to stdout concerning the form of the input and the resultant non- + deterministic and deterministic finite automata. This option is + mostly for use in maintaining flex. + + -8 instructs flex to generate an 8-bit scanner, i.e., one which can + recognize 8-bit characters. On some sites, flex is installed with + this option as the default. On others, the default is 7-bit char- + acters. To see which is the case, check the verbose (-v) output + for "equivalence classes created". If the denominator of the + number shown is 128, then by default flex is generating 7-bit char- + acters. If it is 256, then the default is 8-bit characters and the + -8 flag is not required (but may be a good idea to keep the scanner + specification portable). Feeding a 7-bit scanner 8-bit characters + will result in infinite loops, bus errors, or other such fireworks, + so when in doubt, use the flag. Note that if equivalence classes + are used, 8-bit scanners take only slightly more table space than + 7-bit scanners (128 bytes, to be exact); if equivalence classes are + not used, however, then the tables may grow up to twice their 7-bit + size. + + -C[efmF] + controls the degree of table compression. + + -Ce directs flex to construct equivalence classes, i.e., sets of + characters which have identical lexical properties (for example, if + the only appearance of digits in the flex input is in the character + class "[0-9]" then the digits '0', '1', ..., '9' will all be put in + the same equivalence class). Equivalence classes usually give + dramatic reductions in the final table/object file sizes (typically + a factor of 2-5) and are pretty cheap performance-wise (one array + look-up per character scanned). + + -Cf specifies that the full scanner tables should be generated - + flex should not compress the tables by taking advantages of similar + transition functions for different states. + + -CF specifies that the alternate fast scanner representation + (described above under the -F flag) should be used. + + -Cm directs flex to construct meta-equivalence classes, which are + sets of equivalence classes (or characters, if equivalence classes + are not being used) that are commonly used together. Meta- + equivalence classes are often a big win when using compressed + tables, but they have a moderate performance impact (one or two + "if" tests and one array look-up per character scanned). + + A lone -C specifies that the scanner tables should be compressed + but neither equivalence classes nor meta-equivalence classes should + be used. + + The options -Cf or -CF and -Cm do not make sense together - there + is no opportunity for meta-equivalence classes if the table is not + being compressed. Otherwise the options may be freely mixed. + + Version 2.3 21 + + FLEX(1) 26 May 1990 + + The default setting is -Cem, which specifies that flex should gen- + erate equivalence classes and meta-equivalence classes. This set- + ting provides the highest degree of table compression. You can + trade off faster-executing scanners at the cost of larger tables + with the following generally being true: + + slowest & smallest + -Cem + -Cm + -Ce + -C + -C{f,F}e + -C{f,F} + fastest & largest + + Note that scanners with the smallest tables are usually generated + and compiled the quickest, so during development you will usually + want to use the default, maximal compression. + + -Cfe is often a good compromise between speed and size for produc- + tion scanners. + + -C options are not cumulative; whenever the flag is encountered, + the previous -C settings are forgotten. + + -Sskeleton_file + overrides the default skeleton file from which flex constructs its + scanners. You'll never need this option unless you are doing flex + maintenance or development. + + PERFORMANCE CONSIDERATIONS + The main design goal of flex is that it generate high-performance + scanners. It has been optimized for dealing well with large sets of + rules. Aside from the effects of table compression on scanner speed + outlined above, there are a number of options/actions which degrade per- + formance. These are, from most expensive to least: + + REJECT + + pattern sets that require backtracking + arbitrary trailing context + + '^' beginning-of-line operator + yymore() + + with the first three all being quite expensive and the last two being + quite cheap. + + REJECT should be avoided at all costs when performance is important. It + is a particularly expensive option. + + Getting rid of backtracking is messy and often may be an enormous amount + of work for a complicated scanner. In principal, one begins by using + the -b flag to generate a lex.backtrack file. For example, on the input + + 22 Version 2.3 + + 26 May 1990 FLEX(1) + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + the file looks like: + + State #6 is non-accepting - + associated rule line numbers: + 2 3 + out-transitions: [ o ] + jam-transitions: EOF [ \001-n p-\177 ] + + State #8 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ a ] + jam-transitions: EOF [ \001-` b-\177 ] + + State #9 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ r ] + jam-transitions: EOF [ \001-q s-\177 ] + + Compressed tables always backtrack. + + The first few lines tell us that there's a scanner state in which it can + make a transition on an 'o' but not on any other character, and that in + that state the currently scanned text does not match any rule. The + state occurs when trying to match the rules found at lines 2 and 3 in + the input file. If the scanner is in that state and then reads some- + thing other than an 'o', it will have to backtrack to find a rule which + is matched. With a bit of headscratching one can see that this must be + the state it's in when it has seen "fo". When this has happened, if + anything other than another 'o' is seen, the scanner will have to back + up to simply match the 'f' (by the default rule). + + The comment regarding State #8 indicates there's a problem when "foob" + has been scanned. Indeed, on any character other than a 'b', the + scanner will have to back up to accept "foo". Similarly, the comment + for State #9 concerns when "fooba" has been scanned. + + The final comment reminds us that there's no point going to all the + trouble of removing backtracking from the rules unless we're using -f or + -F, since there's no performance gain doing so with compressed scanners. + + The way to remove the backtracking is to add "error" rules: + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + fooba | + + Version 2.3 23 + + FLEX(1) 26 May 1990 + + foob | + fo { + /* false alarm, not really a keyword */ + return TOK_ID; + } + + Eliminating backtracking among a list of keywords can also be done using + a "catch-all" rule: + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + [a-z]+ return TOK_ID; + + This is usually the best solution when appropriate. + + Backtracking messages tend to cascade. With a complicated set of rules + it's not uncommon to get hundreds of messages. If one can decipher + them, though, it often only takes a dozen or so rules to eliminate the + backtracking (though it's easy to make a mistake and have an error rule + accidentally match a valid token. A possible future flex feature will + be to automatically add rules to eliminate backtracking). + + Variable trailing context (where both the leading and trailing parts do + not have a fixed length) entails almost the same performance loss as + REJECT (i.e., substantial). So when possible a rule like: + + %% + mouse|rat/(cat|dog) run(); + + is better written: + + %% + mouse/cat|dog run(); + rat/cat|dog run(); + + or as + + %% + mouse|rat/cat run(); + mouse|rat/dog run(); + + Note that here the special '|' action does not provide any savings, and + can even make things worse (see BUGS in flex(1)). + + Another area where the user can increase a scanner's performance (and + one that's easier to implement) arises from the fact that the longer the + tokens matched, the faster the scanner will run. This is because with + long tokens the processing of most input characters takes place in the + (short) inner scanning loop, and does not often have to go through the + additional work of setting up the scanning environment (e.g., yytext) + for the action. Recall the scanner for C comments: + + 24 Version 2.3 + + 26 May 1990 FLEX(1) + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* + "*"+[^*/\n]* + \n ++line_num; + "*"+"/" BEGIN(INITIAL); + + This could be sped up by writing it as: + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* + [^*\n]*\n ++line_num; + "*"+[^*/\n]* + "*"+[^*/\n]*\n ++line_num; + "*"+"/" BEGIN(INITIAL); + + Now instead of each newline requiring the processing of another action, + recognizing the newlines is "distributed" over the other rules to keep + the matched text as long as possible. Note that adding rules does not + slow down the scanner! The speed of the scanner is independent of the + number of rules or (modulo the considerations given at the beginning of + this section) how complicated the rules are with regard to operators + such as '*' and '|'. + + A final example in speeding up a scanner: suppose you want to scan + through a file containing identifiers and keywords, one per line and + with no other extraneous characters, and recognize all the keywords. A + natural first approach is: + + %% + asm | + auto | + break | + ... etc ... + volatile | + while /* it's a keyword */ + + .|\n /* it's not a keyword */ + + To eliminate the back-tracking, introduce a catch-all rule: + + %% + asm | + auto | + + Version 2.3 25 + + FLEX(1) 26 May 1990 + + break | + ... etc ... + volatile | + while /* it's a keyword */ + + [a-z]+ | + .|\n /* it's not a keyword */ + + Now, if it's guaranteed that there's exactly one word per line, then we + can reduce the total number of matches by a half by merging in the + recognition of newlines with that of the other tokens: + + %% + asm\n | + auto\n | + break\n | + ... etc ... + volatile\n | + while\n /* it's a keyword */ + + [a-z]+\n | + .|\n /* it's not a keyword */ + + One has to be careful here, as we have now reintroduced backtracking + into the scanner. In particular, while we know that there will never be + any characters in the input stream other than letters or newlines, flex + can't figure this out, and it will plan for possibly needing backtrack- + ing when it has scanned a token like "auto" and then the next character + is something other than a newline or a letter. Previously it would then + just match the "auto" rule and be done, but now it has no "auto" rule, + only a "auto\n" rule. To eliminate the possibility of backtracking, we + could either duplicate all rules but without final newlines, or, since + we never expect to encounter such an input and therefore don't how it's + classified, we can introduce one more catch-all rule, this one which + doesn't include a newline: + + %% + asm\n | + auto\n | + break\n | + ... etc ... + volatile\n | + while\n /* it's a keyword */ + + [a-z]+\n | + [a-z]+ | + .|\n /* it's not a keyword */ + + Compiled with -Cf, this is about as fast as one can get a flex scanner + to go for this particular problem. + + A final note: flex is slow when matching NUL's, particularly when a + token contains multiple NUL's. It's best to write rules which match + short amounts of text if it's anticipated that the text will often + + 26 Version 2.3 + + 26 May 1990 FLEX(1) + + include NUL's. + + INCOMPATIBILITIES WITH LEX AND POSIX + flex is a rewrite of the Unix lex tool (the two implementations do not + share any code, though), with some extensions and incompatibilities, + both of which are of concern to those who wish to write scanners accept- + able to either implementation. At present, the POSIX lex draft is very + close to the original lex implementation, so some of these incompatibil- + ities are also in conflict with the POSIX draft. But the intent is that + except as noted below, flex as it presently stands will ultimately be + POSIX conformant (i.e., that those areas of conflict with the POSIX + draft will be resolved in flex's favor). Please bear in mind that all + the comments which follow are with regard to the POSIX draft standard of + Summer 1989, and not the final document (or subsequent drafts); they are + included so flex users can be aware of the standardization issues and + those areas where flex may in the near future undergo changes incompati- + ble with its current definition. + + flex is fully compatible with lex with the following exceptions: + + - The undocumented lex scanner internal variable yylineno is not sup- + ported. It is difficult to support this option efficiently, since + it requires examining every character scanned and reexamining the + characters when the scanner backs up. Things get more complicated + when the end of buffer or file is reached or a NUL is scanned + (since the scan must then be restarted with the proper line number + count), or the user uses the yyless(), unput(), or REJECT actions, + or the multiple input buffer functions. + + The fix is to add rules which, upon seeing a newline, increment + yylineno. This is usually an easy process, though it can be a drag + if some of the patterns can match multiple newlines along with + other characters. + + yylineno is not part of the POSIX draft. + + - The input() routine is not redefinable, though it may be called to + read characters following whatever has been matched by a rule. If + input() encounters an end-of-file the normal yywrap() processing is + done. A ``real'' end-of-file is returned by input() as EOF. + + Input is instead controlled by redefining the YY_INPUT macro. + + The flex restriction that input() cannot be redefined is in accor- + dance with the POSIX draft, but YY_INPUT has not yet been accepted + into the draft (and probably won't; it looks like the draft will + simply not specify any way of controlling the scanner's input other + than by making an initial assignment to yyin). + + - flex scanners do not use stdio for input. Because of this, when + writing an interactive scanner one must explicitly call fflush() on + the stream associated with the terminal after writing out a prompt. + With lex such writes are automatically flushed since lex scanners + use getchar() for their input. Also, when writing interactive + + Version 2.3 27 + + FLEX(1) 26 May 1990 + + scanners with flex, the -I flag must be used. + + - flex scanners are not as reentrant as lex scanners. In particular, + if you have an interactive scanner and an interrupt handler which + long-jumps out of the scanner, and the scanner is subsequently + called again, you may get the following message: + + fatal flex scanner internal error--end of buffer missed + + To reenter the scanner, first use + + yyrestart( yyin ); + + - output() is not supported. Output from the ECHO macro is done to + the file-pointer yyout (default stdout). + + The POSIX draft mentions that an output() routine exists but + currently gives no details as to what it does. + + - lex does not support exclusive start conditions (%x), though they + are in the current POSIX draft. + + - When definitions are expanded, flex encloses them in parentheses. + With lex, the following: + + NAME [A-Z][A-Z0-9]* + %% + foo{NAME}? printf( "Found it\n" ); + %% + + will not match the string "foo" because when the macro is expanded + the rule is equivalent to "foo[A-Z][A-Z0-9]*?" and the precedence + is such that the '?' is associated with "[A-Z0-9]*". With flex, + the rule will be expanded to "foo([A-Z][A-Z0-9]*)?" and so the + string "foo" will match. Note that because of this, the ^, $, , + /, and <> operators cannot be used in a flex definition. + + The POSIX draft interpretation is the same as flex's. + + - To specify a character class which matches anything but a left + bracket (']'), in lex one can use "[^]]" but with flex one must use + "[^\]]". The latter works with lex, too. + + - The lex %r (generate a Ratfor scanner) option is not supported. It + is not part of the POSIX draft. + + - If you are providing your own yywrap() routine, you must include a + "#undef yywrap" in the definitions section (section 1). Note that + the "#undef" will have to be enclosed in %{}'s. + + The POSIX draft specifies that yywrap() is a function and this is + very unlikely to change; so flex users are warned that yywrap() is + likely to be changed to a function in the near future. + + 28 Version 2.3 + + 26 May 1990 FLEX(1) + + - After a call to unput(), yytext and yyleng are undefined until the + next token is matched. This is not the case with lex or the + present POSIX draft. + + - The precedence of the {} (numeric range) operator is different. + lex interprets "abc{1,3}" as "match one, two, or three occurrences + of 'abc'", whereas flex interprets it as "match 'ab' followed by + one, two, or three occurrences of 'c'". The latter is in agreement + with the current POSIX draft. + + - The precedence of the ^ operator is different. lex interprets + "^foo|bar" as "match either 'foo' at the beginning of a line, or + 'bar' anywhere", whereas flex interprets it as "match either 'foo' + or 'bar' if they come at the beginning of a line". The latter is + in agreement with the current POSIX draft. + + - To refer to yytext outside of the scanner source file, the correct + definition with flex is "extern char *yytext" rather than "extern + char yytext[]". This is contrary to the current POSIX draft but a + point on which flex will not be changing, as the array representa- + tion entails a serious performance penalty. It is hoped that the + POSIX draft will be emended to support the flex variety of declara- + tion (as this is a fairly painless change to require of lex users). + + - yyin is initialized by lex to be stdin; flex, on the other hand, + initializes yyin to NULL and then assigns it to stdin the first + time the scanner is called, providing yyin has not already been + assigned to a non-NULL value. The difference is subtle, but the + net effect is that with flex scanners, yyin does not have a valid + value until the scanner has been called. + + - The special table-size declarations such as %a supported by lex are + not required by flex scanners; flex ignores them. + + - The name FLEX_SCANNER is #define'd so scanners may be written for + use with either flex or lex. + + The following flex features are not included in lex or the POSIX draft + standard: + + yyterminate() + <> + YY_DECL + #line directives + %{}'s around actions + yyrestart() + comments beginning with '#' (deprecated) + multiple actions on a line + + This last feature refers to the fact that with flex you can put multiple + actions on the same line, separated with semi-colons, while with lex, + the following + + foo handle_foo(); ++num_foos_seen; + + Version 2.3 29 + + FLEX(1) 26 May 1990 + + is (rather surprisingly) truncated to + + foo handle_foo(); + + flex does not truncate the action. Actions that are not enclosed in + braces are simply terminated at the end of the line. + + DIAGNOSTICS + reject_used_but_not_detected undefined or yymore_used_but_not_detected + undefined - These errors can occur at compile time. They indicate that + the scanner uses REJECT or yymore() but that flex failed to notice the + fact, meaning that flex scanned the first two sections looking for + occurrences of these actions and failed to find any, but somehow you + snuck some in (via a #include file, for example). Make an explicit + reference to the action in your flex input file. (Note that previously + flex supported a %used/%unused mechanism for dealing with this problem; + this feature is still supported but now deprecated, and will go away + soon unless the author hears from people who can argue compellingly that + they need it.) + + flex scanner jammed - a scanner compiled with -s has encountered an + input string which wasn't matched by any of its rules. + + flex input buffer overflowed - a scanner rule matched a string long + enough to overflow the scanner's internal input buffer (16K bytes by + default - controlled by YY_BUF_SIZE in "flex.skel". Note that to rede- + fine this macro, you must first #undefine it). + + scanner requires -8 flag - Your scanner specification includes recogniz- + ing 8-bit characters and you did not specify the -8 flag (and your site + has not installed flex with -8 as the default). + + fatal flex scanner internal error--end of buffer missed - This can occur + in an scanner which is reentered after a long-jump has jumped out (or + over) the scanner's activation frame. Before reentering the scanner, + use: + + yyrestart( yyin ); + + too many %t classes! - You managed to put every single character into + its own %t class. flex requires that at least one of the classes share + characters. + + DEFICIENCIES / BUGS + See flex(1). + + SEE ALSO + + flex(1), lex(1), yacc(1), sed(1), awk(1). + + M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator + + 30 Version 2.3 + + 26 May 1990 FLEX(1) + + AUTHOR + Vern Paxson, with the help of many ideas and much inspiration from Van + Jacobson. Original version by Jef Poskanzer. The fast table represen- + tation is a partial implementation of a design done by Van Jacobson. + The implementation was done by Kevin Gong and Vern Paxson. + + Thanks to the many flex beta-testers, feedbackers, and contributors, + especially Casey Leedom, benson@odi.com, Keith Bostic, Frederic Brehm, + Nick Christopher, Jason Coughlin, Scott David Daniels, Leo Eskin, Chris + Faylor, Eric Goldman, Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny, + Ronald Lamprecht, Greg Lee, Craig Leres, Mohamed el Lozy, Jim Meyering, + Marc Nozell, Esmond Pitt, Jef Poskanzer, Jim Roskind, Dave Tallman, + Frank Whaley, Ken Yap, and those whose names have slipped my marginal + mail-archiving skills but whose contributions are appreciated all the + same. + + Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob Mulcahy, Rich + Salz, and Richard Stallman for help with various distribution headaches. + + Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to + Benson Margulies and Fred Burke for C++ support; to Ove Ewerlid for the + basics of support for NUL's; and to Eric Hughes for the basics of sup- + port for multiple buffers. + + Work is being done on extending flex to generate scanners in which the + state machine is directly represented in C code rather than tables. + These scanners may well be substantially faster than those generated + using -f or -F. If you are working in this area and are interested in + comparing notes and seeing whether redundant work can be avoided, con- + tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE). + + This work was primarily done when I was at the Real Time Systems Group + at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all + there for the support I received. + + Send comments to: + + Vern Paxson + Computer Science Department + 4126 Upson Hall + Cornell University + Ithaca, NY 14853-7501 + + vern@cs.cornell.edu + decvax!cornell!vern + + Version 2.3 31 + +99 diff --git a/modules/libcom/src/flex/Makefile b/modules/libcom/src/flex/Makefile new file mode 100644 index 000000000..20cce5d30 --- /dev/null +++ b/modules/libcom/src/flex/Makefile @@ -0,0 +1,36 @@ +#************************************************************************* +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/flex + +parse_YACCOPT = -l -d + +SKELETON_FILE = flex.skel.static + +parse_CPPFLAGS = -DDEFAULT_SKELETON_FILE=$(SKELETON_FILE) + +INC += flex.skel.static + +# flex.c is included in parse.c +e_flex_SRCS += ccl.c +e_flex_SRCS += dfa.c +e_flex_SRCS += ecs.c +e_flex_SRCS += gen.c +e_flex_SRCS += misc.c +e_flex_SRCS += nfa.c +e_flex_SRCS += sym.c +e_flex_SRCS += tblcmp.c +e_flex_SRCS += parse.c +e_flex_OBJS += epicsTempFile$(OBJ) + +PROD_HOST += e_flex + +CLEANS += parse.c parse.h diff --git a/modules/libcom/src/flex/README b/modules/libcom/src/flex/README new file mode 100644 index 000000000..210290892 --- /dev/null +++ b/modules/libcom/src/flex/README @@ -0,0 +1,76 @@ +This is release 2.3 of flex - a full release. + +The flex distribution consists of the following files: + + README This message + + Makefile + flexdef.h + parse.y + scan.l + ccl.c + dfa.c + ecs.c flex sources + gen.c + main.c + misc.c + nfa.c + sym.c + tblcmp.c + yylex.c + + libmain.c flex library (-lfl) source + + initscan.c pre-flex'd version of scan.l + + flex.skel skeleton for generated scanners + + flexdoc.1 full user documentation + flex.1 reference documentation + + Changes Differences between this release and the previous one + + COPYING flex's copyright + + MISC/ a directory containing miscellaneous porting-related + notes (for Atari, MS-DOS, Turbo-C, and VMS) + + +Decide where you want to keep flex.skel (suggestion: /usr/local/lib), +but don't move it there yet. Edit "Makefile" and change the definition +of SKELETON_FILE to reflect the full pathname of flex.skel. + +Read the "Porting considerations" note in the Makefile and make +the necessary changes. + +To make flex for the first time, use: + + make first_flex + +which uses the pre-generated copy of the flex scanner (the scanner +itself is written using flex). + +Assuming it builds successfully, you can test it using + + make test + +The "diff" should not show any differences. + +If you're feeling adventurous, issue "make bigtest" and be prepared +to wait a while. + +Install flex using: + + make install + + +Please send problems and feedback to: + + vern@cs.cornell.edu + decvax!cornell!vern + + Vern Paxson + CS Department + 4126 Upson Hall + Cornell University + Ithaca, NY 14853-7501 diff --git a/modules/libcom/src/flex/RULES b/modules/libcom/src/flex/RULES new file mode 100644 index 000000000..22fe5283f --- /dev/null +++ b/modules/libcom/src/flex/RULES @@ -0,0 +1,13 @@ +#************************************************************************* +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +# Ensure that the lexer is built before it is needed +parse.c: $(INSTALL_HOST_BIN)/antelope$(HOSTEXE) diff --git a/modules/libcom/src/flex/ccl.c b/modules/libcom/src/flex/ccl.c new file mode 100644 index 000000000..36841d9b1 --- /dev/null +++ b/modules/libcom/src/flex/ccl.c @@ -0,0 +1,169 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ccl - routines for character classes */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + +/* ccladd - add a single character to a ccl + * + * synopsis + * int cclp; + * int ch; + * ccladd( cclp, ch ); + */ + +void ccladd(int cclp, int ch) +{ + int ind, len, newpos, i; + + len = ccllen[cclp]; + ind = cclmap[cclp]; + + /* check to see if the character is already in the ccl */ + + for ( i = 0; i < len; ++i ) + if ( ccltbl[ind + i] == ch ) + return; + + newpos = ind + len; + + if ( newpos >= current_max_ccl_tbl_size ) + { + current_max_ccl_tbl_size += MAX_CCL_TBL_SIZE_INCREMENT; + + ++num_reallocs; + + ccltbl = reallocate_character_array( ccltbl, current_max_ccl_tbl_size ); + } + + ccllen[cclp] = len + 1; + ccltbl[newpos] = ch; + } + + +/* cclinit - make an empty ccl + * + * synopsis + * int cclinit(); + * new_ccl = cclinit(); + */ + +int cclinit(void) +{ + if ( ++lastccl >= current_maxccls ) + { + current_maxccls += MAX_CCLS_INCREMENT; + + ++num_reallocs; + + cclmap = reallocate_integer_array( cclmap, current_maxccls ); + ccllen = reallocate_integer_array( ccllen, current_maxccls ); + cclng = reallocate_integer_array( cclng, current_maxccls ); + } + + if ( lastccl == 1 ) + /* we're making the first ccl */ + cclmap[lastccl] = 0; + + else + /* the new pointer is just past the end of the last ccl. Since + * the cclmap points to the \first/ character of a ccl, adding the + * length of the ccl to the cclmap pointer will produce a cursor + * to the first free space + */ + cclmap[lastccl] = cclmap[lastccl - 1] + ccllen[lastccl - 1]; + + ccllen[lastccl] = 0; + cclng[lastccl] = 0; /* ccl's start out life un-negated */ + + return ( lastccl ); + } + + +/* cclnegate - negate a ccl + * + * synopsis + * int cclp; + * cclnegate( ccl ); + */ + +void cclnegate(int cclp) +{ + cclng[cclp] = 1; + } + + +/* list_character_set - list the members of a set of characters in CCL form + * + * synopsis + * int cset[CSIZE]; + * FILE *file; + * list_character_set( cset ); + * + * writes to the given file a character-class representation of those + * characters present in the given set. A character is present if it + * has a non-zero value in the set array. + */ + +void list_character_set(FILE *file, int cset[]) +{ + int i; + char *readable_form(); + + putc( '[', file ); + + for ( i = 0; i < csize; ++i ) + { + if ( cset[i] ) + { + int start_char = i; + + putc( ' ', file ); + + fputs( readable_form( i ), file ); + + while ( ++i < csize && cset[i] ) + ; + + if ( i - 1 > start_char ) + /* this was a run */ + fprintf( file, "-%s", readable_form( i - 1 ) ); + + putc( ' ', file ); + } + } + + putc( ']', file ); + } diff --git a/modules/libcom/src/flex/dfa.c b/modules/libcom/src/flex/dfa.c new file mode 100644 index 000000000..559a12fd1 --- /dev/null +++ b/modules/libcom/src/flex/dfa.c @@ -0,0 +1,1056 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dfa - DFA construction routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +void dump_associated_rules (FILE*, int); +void dump_transitions (FILE*, int[]); +void sympartition (int[], int, int[], int[]); +int symfollowset (int[], int, int, int[]); + + +/* check_for_backtracking - check a DFA state for backtracking + * + * synopsis + * int ds, state[numecs]; + * check_for_backtracking( ds, state ); + * + * ds is the number of the state to check and state[] is its out-transitions, + * indexed by equivalence class, and state_rules[] is the set of rules + * associated with this state + */ + +void check_for_backtracking(int ds, int state[]) +{ + if ( (reject && ! dfaacc[ds].dfaacc_set) || ! dfaacc[ds].dfaacc_state ) + { /* state is non-accepting */ + ++num_backtracking; + + if ( backtrack_report ) + { + fprintf( backtrack_file, "State #%d is non-accepting -\n", ds ); + + /* identify the state */ + dump_associated_rules( backtrack_file, ds ); + + /* now identify it further using the out- and jam-transitions */ + dump_transitions( backtrack_file, state ); + + putc( '\n', backtrack_file ); + } + } + } + + +/* check_trailing_context - check to see if NFA state set constitutes + * "dangerous" trailing context + * + * synopsis + * int nfa_states[num_states+1], num_states; + * int accset[nacc+1], nacc; + * check_trailing_context( nfa_states, num_states, accset, nacc ); + * + * NOTES + * Trailing context is "dangerous" if both the head and the trailing + * part are of variable size \and/ there's a DFA state which contains + * both an accepting state for the head part of the rule and NFA states + * which occur after the beginning of the trailing context. + * When such a rule is matched, it's impossible to tell if having been + * in the DFA state indicates the beginning of the trailing context + * or further-along scanning of the pattern. In these cases, a warning + * message is issued. + * + * nfa_states[1 .. num_states] is the list of NFA states in the DFA. + * accset[1 .. nacc] is the list of accepting numbers for the DFA state. + */ + +void check_trailing_context(int *nfa_states, int num_states, int *accset, int nacc) +{ + int i, j; + + for ( i = 1; i <= num_states; ++i ) + { + int ns = nfa_states[i]; + int type = state_type[ns]; + int ar = assoc_rule[ns]; + + if ( type == STATE_NORMAL || rule_type[ar] != RULE_VARIABLE ) + { /* do nothing */ + } + + else if ( type == STATE_TRAILING_CONTEXT ) + { + /* potential trouble. Scan set of accepting numbers for + * the one marking the end of the "head". We assume that + * this looping will be fairly cheap since it's rare that + * an accepting number set is large. + */ + for ( j = 1; j <= nacc; ++j ) + if ( accset[j] & YY_TRAILING_HEAD_MASK ) + { + fprintf( stderr, + "%s: Dangerous trailing context in rule at line %d\n", + program_name, rule_linenum[ar] ); + return; + } + } + } + } + + +/* dump_associated_rules - list the rules associated with a DFA state + * + * synopisis + * int ds; + * FILE *file; + * dump_associated_rules( file, ds ); + * + * goes through the set of NFA states associated with the DFA and + * extracts the first MAX_ASSOC_RULES unique rules, sorts them, + * and writes a report to the given file + */ + +void dump_associated_rules(FILE *file, int ds) +{ + int i, j; + int num_associated_rules = 0; + int rule_set[MAX_ASSOC_RULES + 1]; + int *dset = dss[ds]; + int size = dfasiz[ds]; + + for ( i = 1; i <= size; ++i ) + { + int rule_num = rule_linenum[assoc_rule[dset[i]]]; + + for ( j = 1; j <= num_associated_rules; ++j ) + if ( rule_num == rule_set[j] ) + break; + + if ( j > num_associated_rules ) + { /* new rule */ + if ( num_associated_rules < MAX_ASSOC_RULES ) + rule_set[++num_associated_rules] = rule_num; + } + } + + bubble( rule_set, num_associated_rules ); + + fprintf( file, " associated rule line numbers:" ); + + for ( i = 1; i <= num_associated_rules; ++i ) + { + if ( i % 8 == 1 ) + putc( '\n', file ); + + fprintf( file, "\t%d", rule_set[i] ); + } + + putc( '\n', file ); + } + + +/* dump_transitions - list the transitions associated with a DFA state + * + * synopisis + * int state[numecs]; + * FILE *file; + * dump_transitions( file, state ); + * + * goes through the set of out-transitions and lists them in human-readable + * form (i.e., not as equivalence classes); also lists jam transitions + * (i.e., all those which are not out-transitions, plus EOF). The dump + * is done to the given file. + */ + +void dump_transitions(FILE *file, int state[]) +{ + int i, ec; + int out_char_set[CSIZE]; + + for ( i = 0; i < csize; ++i ) + { + ec = abs( ecgroup[i] ); + out_char_set[i] = state[ec]; + } + + fprintf( file, " out-transitions: " ); + + list_character_set( file, out_char_set ); + + /* now invert the members of the set to get the jam transitions */ + for ( i = 0; i < csize; ++i ) + out_char_set[i] = ! out_char_set[i]; + + fprintf( file, "\n jam-transitions: EOF " ); + + list_character_set( file, out_char_set ); + + putc( '\n', file ); + } + + +/* epsclosure - construct the epsilon closure of a set of ndfa states + * + * synopsis + * int t[current_max_dfa_size], numstates, accset[num_rules + 1], nacc; + * int hashval; + * int *epsclosure(); + * t = epsclosure( t, &numstates, accset, &nacc, &hashval ); + * + * NOTES + * the epsilon closure is the set of all states reachable by an arbitrary + * number of epsilon transitions which themselves do not have epsilon + * transitions going out, unioned with the set of states which have non-null + * accepting numbers. t is an array of size numstates of nfa state numbers. + * Upon return, t holds the epsilon closure and numstates is updated. accset + * holds a list of the accepting numbers, and the size of accset is given + * by nacc. t may be subjected to reallocation if it is not large enough + * to hold the epsilon closure. + * + * hashval is the hash value for the dfa corresponding to the state set + */ + +int *epsclosure(int *t, int *ns_addr, int accset[], int *nacc_addr, int *hv_addr) +{ + int stkpos, ns, tsp; + int numstates = *ns_addr, nacc, hashval, transsym, nfaccnum; + int stkend, nstate; + static int did_stk_init = false, *stk; + +#define MARK_STATE(state) \ + trans1[state] = trans1[state] - MARKER_DIFFERENCE; + +#define IS_MARKED(state) (trans1[state] < 0) + +#define UNMARK_STATE(state) \ + trans1[state] = trans1[state] + MARKER_DIFFERENCE; + +#define CHECK_ACCEPT(state) \ + { \ + nfaccnum = accptnum[state]; \ + if ( nfaccnum != NIL ) \ + accset[++nacc] = nfaccnum; \ + } + +#define DO_REALLOCATION \ + { \ + current_max_dfa_size += MAX_DFA_SIZE_INCREMENT; \ + ++num_reallocs; \ + t = reallocate_integer_array( t, current_max_dfa_size ); \ + stk = reallocate_integer_array( stk, current_max_dfa_size ); \ + } \ + +#define PUT_ON_STACK(state) \ + { \ + if ( ++stkend >= current_max_dfa_size ) \ + DO_REALLOCATION \ + stk[stkend] = state; \ + MARK_STATE(state) \ + } + +#define ADD_STATE(state) \ + { \ + if ( ++numstates >= current_max_dfa_size ) \ + DO_REALLOCATION \ + t[numstates] = state; \ + hashval = hashval + state; \ + } + +#define STACK_STATE(state) \ + { \ + PUT_ON_STACK(state) \ + CHECK_ACCEPT(state) \ + if ( nfaccnum != NIL || transchar[state] != SYM_EPSILON ) \ + ADD_STATE(state) \ + } + + if ( ! did_stk_init ) + { + stk = allocate_integer_array( current_max_dfa_size ); + did_stk_init = true; + } + + nacc = stkend = hashval = 0; + + for ( nstate = 1; nstate <= numstates; ++nstate ) + { + ns = t[nstate]; + + /* the state could be marked if we've already pushed it onto + * the stack + */ + if ( ! IS_MARKED(ns) ) + PUT_ON_STACK(ns) + + CHECK_ACCEPT(ns) + hashval = hashval + ns; + } + + for ( stkpos = 1; stkpos <= stkend; ++stkpos ) + { + ns = stk[stkpos]; + transsym = transchar[ns]; + + if ( transsym == SYM_EPSILON ) + { + tsp = trans1[ns] + MARKER_DIFFERENCE; + + if ( tsp != NO_TRANSITION ) + { + if ( ! IS_MARKED(tsp) ) + STACK_STATE(tsp) + + tsp = trans2[ns]; + + if ( tsp != NO_TRANSITION ) + if ( ! IS_MARKED(tsp) ) + STACK_STATE(tsp) + } + } + } + + /* clear out "visit" markers */ + + for ( stkpos = 1; stkpos <= stkend; ++stkpos ) + { + if ( IS_MARKED(stk[stkpos]) ) + { + UNMARK_STATE(stk[stkpos]) + } + else + flexfatal( "consistency check failed in epsclosure()" ); + } + + *ns_addr = numstates; + *hv_addr = hashval; + *nacc_addr = nacc; + + return ( t ); + } + + +/* increase_max_dfas - increase the maximum number of DFAs */ + +void increase_max_dfas(void) +{ + current_max_dfas += MAX_DFAS_INCREMENT; + + ++num_reallocs; + + base = reallocate_integer_array( base, current_max_dfas ); + def = reallocate_integer_array( def, current_max_dfas ); + dfasiz = reallocate_integer_array( dfasiz, current_max_dfas ); + accsiz = reallocate_integer_array( accsiz, current_max_dfas ); + dhash = reallocate_integer_array( dhash, current_max_dfas ); + dss = reallocate_int_ptr_array( dss, current_max_dfas ); + dfaacc = reallocate_dfaacc_union( dfaacc, current_max_dfas ); + + if ( nultrans ) + nultrans = reallocate_integer_array( nultrans, current_max_dfas ); + } + + +/* ntod - convert an ndfa to a dfa + * + * synopsis + * ntod(); + * + * creates the dfa corresponding to the ndfa we've constructed. the + * dfa starts out in state #1. + */ + +void ntod(void) +{ + int *accset, ds, nacc, newds; + int sym, hashval, numstates, dsize; + int num_full_table_rows = 0; /* used only for -f */ + int *nset, *dset; + int targptr, totaltrans, i, comstate, comfreq, targ; + int *epsclosure(int *t, int *ns_addr, int *accset, int *nacc_addr, int *hv_addr); + int snstods(int *sns, int numstates, int *accset, int nacc, int hashval, int *newds_addr); + int symlist[CSIZE + 1]; + int num_start_states; + int todo_head, todo_next; + + /* note that the following are indexed by *equivalence classes* + * and not by characters. Since equivalence classes are indexed + * beginning with 1, even if the scanner accepts NUL's, this + * means that (since every character is potentially in its own + * equivalence class) these arrays must have room for indices + * from 1 to CSIZE, so their size must be CSIZE + 1. + */ + int duplist[CSIZE + 1], state[CSIZE + 1]; + int targfreq[CSIZE + 1], targstate[CSIZE + 1]; + + /* this is so find_table_space(...) will know where to start looking in + * chk/nxt for unused records for space to put in the state + */ + if ( fullspd ) + firstfree = 0; + + accset = allocate_integer_array( num_rules + 1 ); + nset = allocate_integer_array( current_max_dfa_size ); + + /* the "todo" queue is represented by the head, which is the DFA + * state currently being processed, and the "next", which is the + * next DFA state number available (not in use). We depend on the + * fact that snstods() returns DFA's \in increasing order/, and thus + * need only know the bounds of the dfas to be processed. + */ + todo_head = todo_next = 0; + + for ( i = 0; i <= csize; ++i ) + { + duplist[i] = NIL; + symlist[i] = false; + } + + for ( i = 0; i <= num_rules; ++i ) + accset[i] = NIL; + + if ( trace ) + { + dumpnfa( scset[1] ); + fputs( "\n\nDFA Dump:\n\n", stderr ); + } + + inittbl(); + + /* check to see whether we should build a separate table for transitions + * on NUL characters. We don't do this for full-speed (-F) scanners, + * since for them we don't have a simple state number lying around with + * which to index the table. We also don't bother doing it for scanners + * unless (1) NUL is in its own equivalence class (indicated by a + * positive value of ecgroup[NUL]), (2) NUL's equilvalence class is + * the last equivalence class, and (3) the number of equivalence classes + * is the same as the number of characters. This latter case comes about + * when useecs is false or when its true but every character still + * manages to land in its own class (unlikely, but it's cheap to check + * for). If all these things are true then the character code needed + * to represent NUL's equivalence class for indexing the tables is + * going to take one more bit than the number of characters, and therefore + * we won't be assured of being able to fit it into a YY_CHAR variable. + * This rules out storing the transitions in a compressed table, since + * the code for interpreting them uses a YY_CHAR variable (perhaps it + * should just use an integer, though; this is worth pondering ... ###). + * + * Finally, for full tables, we want the number of entries in the + * table to be a power of two so the array references go fast (it + * will just take a shift to compute the major index). If encoding + * NUL's transitions in the table will spoil this, we give it its + * own table (note that this will be the case if we're not using + * equivalence classes). + */ + + /* note that the test for ecgroup[0] == numecs below accomplishes + * both (1) and (2) above + */ + if ( ! fullspd && ecgroup[0] == numecs ) + { /* NUL is alone in its equivalence class, which is the last one */ + int use_NUL_table = (numecs == csize); + + if ( fulltbl && ! use_NUL_table ) + { /* we still may want to use the table if numecs is a power of 2 */ + int power_of_two; + + for ( power_of_two = 1; power_of_two <= csize; power_of_two *= 2 ) + if ( numecs == power_of_two ) + { + use_NUL_table = true; + break; + } + } + + if ( use_NUL_table ) + nultrans = allocate_integer_array( current_max_dfas ); + /* from now on, nultrans != nil indicates that we're + * saving null transitions for later, separate encoding + */ + } + + + if ( fullspd ) + { + for ( i = 0; i <= numecs; ++i ) + state[i] = 0; + place_state( state, 0, 0 ); + } + + else if ( fulltbl ) + { + if ( nultrans ) + /* we won't be including NUL's transitions in the table, + * so build it for entries from 0 .. numecs - 1 + */ + num_full_table_rows = numecs; + + else + /* take into account the fact that we'll be including + * the NUL entries in the transition table. Build it + * from 0 .. numecs. + */ + num_full_table_rows = numecs + 1; + + /* declare it "short" because it's a real long-shot that that + * won't be large enough. + */ + printf( "static short int yy_nxt[][%d] =\n {\n", + /* '}' so vi doesn't get too confused */ + num_full_table_rows ); + + /* generate 0 entries for state #0 */ + for ( i = 0; i < num_full_table_rows; ++i ) + mk2data( 0 ); + + /* force ',' and dataflush() next call to mk2data */ + datapos = NUMDATAITEMS; + + /* force extra blank line next dataflush() */ + dataline = NUMDATALINES; + } + + /* create the first states */ + + num_start_states = lastsc * 2; + + for ( i = 1; i <= num_start_states; ++i ) + { + numstates = 1; + + /* for each start condition, make one state for the case when + * we're at the beginning of the line (the '%' operator) and + * one for the case when we're not + */ + if ( i % 2 == 1 ) + nset[numstates] = scset[(i / 2) + 1]; + else + nset[numstates] = mkbranch( scbol[i / 2], scset[i / 2] ); + + nset = epsclosure( nset, &numstates, accset, &nacc, &hashval ); + + if ( snstods( nset, numstates, accset, nacc, hashval, &ds ) ) + { + numas += nacc; + totnst += numstates; + ++todo_next; + + if ( variable_trailing_context_rules && nacc > 0 ) + check_trailing_context( nset, numstates, accset, nacc ); + } + } + + if ( ! fullspd ) + { + if ( ! snstods( nset, 0, accset, 0, 0, &end_of_buffer_state ) ) + flexfatal( "could not create unique end-of-buffer state" ); + + ++numas; + ++num_start_states; + ++todo_next; + } + + while ( todo_head < todo_next ) + { + targptr = 0; + totaltrans = 0; + + for ( i = 1; i <= numecs; ++i ) + state[i] = 0; + + ds = ++todo_head; + + dset = dss[ds]; + dsize = dfasiz[ds]; + + if ( trace ) + fprintf( stderr, "state # %d:\n", ds ); + + sympartition( dset, dsize, symlist, duplist ); + + for ( sym = 1; sym <= numecs; ++sym ) + { + if ( symlist[sym] ) + { + symlist[sym] = 0; + + if ( duplist[sym] == NIL ) + { /* symbol has unique out-transitions */ + numstates = symfollowset( dset, dsize, sym, nset ); + nset = epsclosure( nset, &numstates, accset, + &nacc, &hashval ); + + if ( snstods( nset, numstates, accset, + nacc, hashval, &newds ) ) + { + totnst = totnst + numstates; + ++todo_next; + numas += nacc; + + if ( variable_trailing_context_rules && nacc > 0 ) + check_trailing_context( nset, numstates, + accset, nacc ); + } + + state[sym] = newds; + + if ( trace ) + fprintf( stderr, "\t%d\t%d\n", sym, newds ); + + targfreq[++targptr] = 1; + targstate[targptr] = newds; + ++numuniq; + } + + else + { + /* sym's equivalence class has the same transitions + * as duplist(sym)'s equivalence class + */ + targ = state[duplist[sym]]; + state[sym] = targ; + + if ( trace ) + fprintf( stderr, "\t%d\t%d\n", sym, targ ); + + /* update frequency count for destination state */ + + i = 0; + while ( targstate[++i] != targ ) + ; + + ++targfreq[i]; + ++numdup; + } + + ++totaltrans; + duplist[sym] = NIL; + } + } + + numsnpairs = numsnpairs + totaltrans; + + if ( caseins && ! useecs ) + { + int j; + + for ( i = 'A', j = 'a'; i <= 'Z'; ++i, ++j ) + state[i] = state[j]; + } + + if ( ds > num_start_states ) + check_for_backtracking( ds, state ); + + if ( nultrans ) + { + nultrans[ds] = state[NUL_ec]; + state[NUL_ec] = 0; /* remove transition */ + } + + if ( fulltbl ) + { + /* supply array's 0-element */ + if ( ds == end_of_buffer_state ) + mk2data( -end_of_buffer_state ); + else + mk2data( end_of_buffer_state ); + + for ( i = 1; i < num_full_table_rows; ++i ) + /* jams are marked by negative of state number */ + mk2data( state[i] ? state[i] : -ds ); + + /* force ',' and dataflush() next call to mk2data */ + datapos = NUMDATAITEMS; + + /* force extra blank line next dataflush() */ + dataline = NUMDATALINES; + } + + else if ( fullspd ) + place_state( state, ds, totaltrans ); + + else if ( ds == end_of_buffer_state ) + /* special case this state to make sure it does what it's + * supposed to, i.e., jam on end-of-buffer + */ + stack1( ds, 0, 0, JAMSTATE ); + + else /* normal, compressed state */ + { + /* determine which destination state is the most common, and + * how many transitions to it there are + */ + + comfreq = 0; + comstate = 0; + + for ( i = 1; i <= targptr; ++i ) + if ( targfreq[i] > comfreq ) + { + comfreq = targfreq[i]; + comstate = targstate[i]; + } + + bldtbl( state, ds, totaltrans, comstate, comfreq ); + } + } + + if ( fulltbl ) + dataend(); + + else if ( ! fullspd ) + { + cmptmps(); /* create compressed template entries */ + + /* create tables for all the states with only one out-transition */ + while ( onesp > 0 ) + { + mk1tbl( onestate[onesp], onesym[onesp], onenext[onesp], + onedef[onesp] ); + --onesp; + } + + mkdeftbl(); + } + } + + +/* snstods - converts a set of ndfa states into a dfa state + * + * synopsis + * int sns[numstates], numstates, newds, accset[num_rules + 1], nacc, hashval; + * int snstods(); + * is_new_state = snstods( sns, numstates, accset, nacc, hashval, &newds ); + * + * on return, the dfa state number is in newds. + */ + +int snstods(int sns[], int numstates, int accset[], int nacc, int hashval, int *newds_addr) +{ + int didsort = 0; + int i, j; + int newds, *oldsns; + + for ( i = 1; i <= lastdfa; ++i ) + if ( hashval == dhash[i] ) + { + if ( numstates == dfasiz[i] ) + { + oldsns = dss[i]; + + if ( ! didsort ) + { + /* we sort the states in sns so we can compare it to + * oldsns quickly. we use bubble because there probably + * aren't very many states + */ + bubble( sns, numstates ); + didsort = 1; + } + + for ( j = 1; j <= numstates; ++j ) + if ( sns[j] != oldsns[j] ) + break; + + if ( j > numstates ) + { + ++dfaeql; + *newds_addr = i; + return ( 0 ); + } + + ++hshcol; + } + + else + ++hshsave; + } + + /* make a new dfa */ + + if ( ++lastdfa >= current_max_dfas ) + increase_max_dfas(); + + newds = lastdfa; + + dss[newds] = (int *) malloc( (unsigned) ((numstates + 1) * sizeof( int )) ); + + if ( ! dss[newds] ) + flexfatal( "dynamic memory failure in snstods()" ); + + /* if we haven't already sorted the states in sns, we do so now, so that + * future comparisons with it can be made quickly + */ + + if ( ! didsort ) + bubble( sns, numstates ); + + for ( i = 1; i <= numstates; ++i ) + dss[newds][i] = sns[i]; + + dfasiz[newds] = numstates; + dhash[newds] = hashval; + + if ( nacc == 0 ) + { + if ( reject ) + dfaacc[newds].dfaacc_set = (int *) 0; + else + dfaacc[newds].dfaacc_state = 0; + + accsiz[newds] = 0; + } + + else if ( reject ) + { + /* we sort the accepting set in increasing order so the disambiguating + * rule that the first rule listed is considered match in the event of + * ties will work. We use a bubble sort since the list is probably + * quite small. + */ + + bubble( accset, nacc ); + + dfaacc[newds].dfaacc_set = + (int *) malloc( (unsigned) ((nacc + 1) * sizeof( int )) ); + + if ( ! dfaacc[newds].dfaacc_set ) + flexfatal( "dynamic memory failure in snstods()" ); + + /* save the accepting set for later */ + for ( i = 1; i <= nacc; ++i ) + dfaacc[newds].dfaacc_set[i] = accset[i]; + + accsiz[newds] = nacc; + } + + else + { /* find lowest numbered rule so the disambiguating rule will work */ + j = num_rules + 1; + + for ( i = 1; i <= nacc; ++i ) + if ( accset[i] < j ) + j = accset[i]; + + dfaacc[newds].dfaacc_state = j; + } + + *newds_addr = newds; + + return ( 1 ); + } + + +/* symfollowset - follow the symbol transitions one step + * + * synopsis + * int ds[current_max_dfa_size], dsize, transsym; + * int nset[current_max_dfa_size], numstates; + * numstates = symfollowset( ds, dsize, transsym, nset ); + */ + +int symfollowset(int ds[], int dsize, int transsym, int nset[]) +{ + int ns, tsp, sym, i, j, lenccl, ch, numstates; + int ccllist; + + numstates = 0; + + for ( i = 1; i <= dsize; ++i ) + { /* for each nfa state ns in the state set of ds */ + ns = ds[i]; + sym = transchar[ns]; + tsp = trans1[ns]; + + if ( sym < 0 ) + { /* it's a character class */ + sym = -sym; + ccllist = cclmap[sym]; + lenccl = ccllen[sym]; + + if ( cclng[sym] ) + { + for ( j = 0; j < lenccl; ++j ) + { /* loop through negated character class */ + ch = ccltbl[ccllist + j]; + + if ( ch == 0 ) + ch = NUL_ec; + + if ( ch > transsym ) + break; /* transsym isn't in negated ccl */ + + else if ( ch == transsym ) + /* next 2 */ goto bottom; + } + + /* didn't find transsym in ccl */ + nset[++numstates] = tsp; + } + + else + for ( j = 0; j < lenccl; ++j ) + { + ch = ccltbl[ccllist + j]; + + if ( ch == 0 ) + ch = NUL_ec; + + if ( ch > transsym ) + break; + + else if ( ch == transsym ) + { + nset[++numstates] = tsp; + break; + } + } + } + + else if ( sym >= 'A' && sym <= 'Z' && caseins ) + flexfatal( "consistency check failed in symfollowset" ); + + else if ( sym == SYM_EPSILON ) + { /* do nothing */ + } + + else if ( abs( ecgroup[sym] ) == transsym ) + nset[++numstates] = tsp; + +bottom: + ; + } + + return ( numstates ); + } + + +/* sympartition - partition characters with same out-transitions + * + * synopsis + * integer ds[current_max_dfa_size], numstates, duplist[numecs]; + * symlist[numecs]; + * sympartition( ds, numstates, symlist, duplist ); + */ + +void sympartition(int ds[], int numstates, int symlist[], int duplist[]) +{ + int tch, i, j, k, ns, dupfwd[CSIZE + 1], lenccl, cclp, ich; + + /* partitioning is done by creating equivalence classes for those + * characters which have out-transitions from the given state. Thus + * we are really creating equivalence classes of equivalence classes. + */ + + for ( i = 1; i <= numecs; ++i ) + { /* initialize equivalence class list */ + duplist[i] = i - 1; + dupfwd[i] = i + 1; + } + + duplist[1] = NIL; + dupfwd[numecs] = NIL; + + for ( i = 1; i <= numstates; ++i ) + { + ns = ds[i]; + tch = transchar[ns]; + + if ( tch != SYM_EPSILON ) + { + if ( tch < -lastccl || tch > csize ) + { + if ( tch > csize && tch <= CSIZE ) + flexerror( "scanner requires -8 flag" ); + + else + flexfatal( + "bad transition character detected in sympartition()" ); + } + + if ( tch >= 0 ) + { /* character transition */ + /* abs() needed for fake %t ec's */ + int ec = abs( ecgroup[tch] ); + + mkechar( ec, dupfwd, duplist ); + symlist[ec] = 1; + } + + else + { /* character class */ + tch = -tch; + + lenccl = ccllen[tch]; + cclp = cclmap[tch]; + mkeccl( ccltbl + cclp, lenccl, dupfwd, duplist, numecs, + NUL_ec ); + + if ( cclng[tch] ) + { + j = 0; + + for ( k = 0; k < lenccl; ++k ) + { + ich = ccltbl[cclp + k]; + + if ( ich == 0 ) + ich = NUL_ec; + + for ( ++j; j < ich; ++j ) + symlist[j] = 1; + } + + for ( ++j; j <= numecs; ++j ) + symlist[j] = 1; + } + + else + for ( k = 0; k < lenccl; ++k ) + { + ich = ccltbl[cclp + k]; + + if ( ich == 0 ) + ich = NUL_ec; + + symlist[ich] = 1; + } + } + } + } + } diff --git a/modules/libcom/src/flex/ecs.c b/modules/libcom/src/flex/ecs.c new file mode 100644 index 000000000..416ea601a --- /dev/null +++ b/modules/libcom/src/flex/ecs.c @@ -0,0 +1,342 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ecs - equivalence class routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + +/* ccl2ecl - convert character classes to set of equivalence classes + * + * synopsis + * ccl2ecl(); + */ + +void ccl2ecl(void) +{ + int i, ich, newlen, cclp, ccls, cclmec; + + for ( i = 1; i <= lastccl; ++i ) + { + /* we loop through each character class, and for each character + * in the class, add the character's equivalence class to the + * new "character" class we are creating. Thus when we are all + * done, character classes will really consist of collections + * of equivalence classes + */ + + newlen = 0; + cclp = cclmap[i]; + + for ( ccls = 0; ccls < ccllen[i]; ++ccls ) + { + ich = ccltbl[cclp + ccls]; + cclmec = ecgroup[ich]; + + if ( xlation && cclmec < 0 ) + { + /* special hack--if we're doing %t tables then it's + * possible that no representative of this character's + * equivalence class is in the ccl. So waiting till + * we see the representative would be disastrous. Instead, + * we add this character's equivalence class anyway, if it's + * not already present. + */ + int j; + + /* this loop makes this whole process n^2; but we don't + * really care about %t performance anyway + */ + for ( j = 0; j < newlen; ++j ) + if ( ccltbl[cclp + j] == -cclmec ) + break; + + if ( j >= newlen ) + { /* no representative yet, add this one in */ + ccltbl[cclp + newlen] = -cclmec; + ++newlen; + } + } + + else if ( cclmec > 0 ) + { + ccltbl[cclp + newlen] = cclmec; + ++newlen; + } + } + + ccllen[i] = newlen; + } + } + + +/* cre8ecs - associate equivalence class numbers with class members + * + * synopsis + * int cre8ecs(); + * number of classes = cre8ecs( fwd, bck, num ); + * + * fwd is the forward linked-list of equivalence class members. bck + * is the backward linked-list, and num is the number of class members. + * + * Returned is the number of classes. + */ + +int cre8ecs(int fwd[], int bck[], int num) +{ + int i, j, numcl; + + numcl = 0; + + /* create equivalence class numbers. From now on, abs( bck(x) ) + * is the equivalence class number for object x. If bck(x) + * is positive, then x is the representative of its equivalence + * class. + */ + for ( i = 1; i <= num; ++i ) + if ( bck[i] == NIL ) + { + bck[i] = ++numcl; + for ( j = fwd[i]; j != NIL; j = fwd[j] ) + bck[j] = -numcl; + } + + return ( numcl ); + } + + +/* ecs_from_xlation - associate equivalence class numbers using %t table + * + * synopsis + * numecs = ecs_from_xlation( ecmap ); + * + * Upon return, ecmap will map each character code to its equivalence + * class. The mapping will be positive if the character is the representative + * of its class, negative otherwise. + * + * Returns the number of equivalence classes used. + */ + +int ecs_from_xlation(int ecmap[]) +{ + int i; + int nul_is_alone = false; + int did_default_xlation_class = false; + + if ( xlation[0] != 0 ) + { + /* if NUL shares its translation with other characters, choose one + * of the other characters as the representative for the equivalence + * class. This allows a cheap test later to see whether we can + * do away with NUL's equivalence class. + */ + for ( i = 1; i < csize; ++i ) + if ( xlation[i] == -xlation[0] ) + { + xlation[i] = xlation[0]; + ecmap[0] = -xlation[0]; + break; + } + + if ( i >= csize ) + /* didn't find a companion character--remember this fact */ + nul_is_alone = true; + } + + for ( i = 1; i < csize; ++i ) + if ( xlation[i] == 0 ) + { + if ( did_default_xlation_class ) + ecmap[i] = -num_xlations; + + else + { + /* make an equivalence class for those characters not + * specified in the %t table + */ + ++num_xlations; + ecmap[i] = num_xlations; + did_default_xlation_class = true; + } + } + + else + ecmap[i] = xlation[i]; + + if ( nul_is_alone ) + /* force NUL's equivalence class to be the last one */ + { + ++num_xlations; + ecmap[0] = num_xlations; + + /* there's actually a bug here: if someone is fanatic enough to + * put every character in its own translation class, then right + * now we just promoted NUL's equivalence class to be csize + 1; + * we can handle NUL's class number being == csize (by instead + * putting it in its own table), but we can't handle some *other* + * character having to be put in its own table, too. So in + * this case we bail out. + */ + if ( num_xlations > csize ) + flexfatal( "too many %t classes!" ); + } + + return num_xlations; + } + + +/* mkeccl - update equivalence classes based on character class xtions + * + * synopsis + * Char ccls[]; + * int lenccl, fwd[llsiz], bck[llsiz], llsiz, NUL_mapping; + * mkeccl( ccls, lenccl, fwd, bck, llsiz, NUL_mapping ); + * + * where ccls contains the elements of the character class, lenccl is the + * number of elements in the ccl, fwd is the forward link-list of equivalent + * characters, bck is the backward link-list, and llsiz size of the link-list + * + * NUL_mapping is the value which NUL (0) should be mapped to. + */ + +void mkeccl(unsigned char ccls[], int lenccl, int fwd[], int bck[], int llsiz, int NUL_mapping) +{ + int cclp, oldec, newec; + int cclm, i, j; + static unsigned char cclflags[CSIZE]; /* initialized to all '\0' */ + + /* note that it doesn't matter whether or not the character class is + * negated. The same results will be obtained in either case. + */ + + cclp = 0; + + while ( cclp < lenccl ) + { + cclm = ccls[cclp]; + + if ( NUL_mapping && cclm == 0 ) + cclm = NUL_mapping; + + oldec = bck[cclm]; + newec = cclm; + + j = cclp + 1; + + for ( i = fwd[cclm]; i != NIL && i <= llsiz; i = fwd[i] ) + { /* look for the symbol in the character class */ + for ( ; j < lenccl; ++j ) + { + int ccl_char; + + if ( NUL_mapping && ccls[j] == 0 ) + ccl_char = NUL_mapping; + else + ccl_char = ccls[j]; + + if ( ccl_char > i ) + break; + + if ( ccl_char == i && ! cclflags[j] ) + { + /* we found an old companion of cclm in the ccl. + * link it into the new equivalence class and flag it as + * having been processed + */ + + bck[i] = newec; + fwd[newec] = i; + newec = i; + cclflags[j] = 1; /* set flag so we don't reprocess */ + + /* get next equivalence class member */ + /* continue 2 */ + goto next_pt; + } + } + + /* symbol isn't in character class. Put it in the old equivalence + * class + */ + + bck[i] = oldec; + + if ( oldec != NIL ) + fwd[oldec] = i; + + oldec = i; +next_pt: + ; + } + + if ( bck[cclm] != NIL || oldec != bck[cclm] ) + { + bck[cclm] = NIL; + fwd[oldec] = NIL; + } + + fwd[newec] = NIL; + + /* find next ccl member to process */ + + for ( ++cclp; cclflags[cclp] && cclp < lenccl; ++cclp ) + { + /* reset "doesn't need processing" flag */ + cclflags[cclp] = 0; + } + } + } + + +/* mkechar - create equivalence class for single character + * + * synopsis + * int tch, fwd[], bck[]; + * mkechar( tch, fwd, bck ); + */ + +void mkechar(int tch, int fwd[], int bck[]) +{ + /* if until now the character has been a proper subset of + * an equivalence class, break it away to create a new ec + */ + + if ( fwd[tch] != NIL ) + bck[fwd[tch]] = bck[tch]; + + if ( bck[tch] != NIL ) + fwd[bck[tch]] = fwd[tch]; + + fwd[tch] = NIL; + bck[tch] = NIL; + } diff --git a/modules/libcom/src/flex/flex.c b/modules/libcom/src/flex/flex.c new file mode 100644 index 000000000..196f4e4cc --- /dev/null +++ b/modules/libcom/src/flex/flex.c @@ -0,0 +1,748 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* flex - tool to generate fast lexical analyzers */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define ENQUOTE(path) #path + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + + +#define epicsExportSharedSymbols +#include "epicsTempFile.h" +#undef epicsExportSharedSymbols + +static char flex_version[] = "2.3"; + + +/* declare functions that have forward references */ + +void flexinit (int, char**); +void readin (void); +void set_up_initial_allocations (void); + + +/* these globals are all defined and commented in flexdef.h */ +int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; +int interactive, caseins, useecs, fulltbl, usemecs; +int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; +int yymore_used, reject, real_reject, continued_action; +int yymore_really_used, reject_really_used; +int datapos, dataline, linenum; +FILE *skelfile = NULL; +char *infilename = NULL; +int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; +int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; +int current_mns, num_rules, current_max_rules, lastnfa; +int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; +int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; +int current_state_type; +int variable_trailing_context_rules; +int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; +int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; +int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs, tecfwd[CSIZE + 1]; +int tecbck[CSIZE + 1]; +int *xlation = (int *) 0; +int num_xlations; +int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; +char **scname; +int current_max_dfa_size, current_max_xpairs; +int current_max_template_xpairs, current_max_dfas; +int lastdfa, *nxt, *chk, *tnxt; +int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; +union dfaacc_union *dfaacc; +int *accsiz, *dhash, numas; +int numsnpairs, jambase, jamstate; +int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; +int current_max_ccl_tbl_size; +Char *ccltbl; +char *starttime, *endtime, nmstr[MAXLINE]; +int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; +int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; +int num_backtracking, bol_needed; +FILE *temp_action_file; +FILE *backtrack_file; +int end_of_buffer_state; +char action_file_name[256>L_tmpnam?256:L_tmpnam]; +char **input_files; +int num_input_files; +char *program_name; + +#ifndef SHORT_FILE_NAMES +static char *outfile = "lex.yy.c"; +#else +static char *outfile = "lexyy.c"; +#endif +static int outfile_created = 0; +static int use_stdout; +static char *skelname = NULL; + + +int main(int argc, char *argv[]) +{ + flexinit( argc, argv ); + + readin(); + + if ( syntaxerror ) + flexend( 1 ); + + if ( yymore_really_used == REALLY_USED ) + yymore_used = true; + else if ( yymore_really_used == REALLY_NOT_USED ) + yymore_used = false; + + if ( reject_really_used == REALLY_USED ) + reject = true; + else if ( reject_really_used == REALLY_NOT_USED ) + reject = false; + + if ( performance_report ) + { + if ( interactive ) + fprintf( stderr, + "-I (interactive) entails a minor performance penalty\n" ); + + if ( yymore_used ) + fprintf( stderr, "yymore() entails a minor performance penalty\n" ); + + if ( reject ) + fprintf( stderr, "REJECT entails a large performance penalty\n" ); + + if ( variable_trailing_context_rules ) + fprintf( stderr, +"Variable trailing context rules entail a large performance penalty\n" ); + } + + if ( reject ) + real_reject = true; + + if ( variable_trailing_context_rules ) + reject = true; + + if ( (fulltbl || fullspd) && reject ) + { + if ( real_reject ) + flexerror( "REJECT cannot be used with -f or -F" ); + else + flexerror( + "variable trailing context rules cannot be used with -f or -F" ); + } + + ntod(); + + /* generate the C state transition tables from the DFA */ + make_tables(); + + /* note, flexend does not return. It exits with its argument as status. */ + + flexend( 0 ); + + /*NOTREACHED*/ +} + + +/* flexend - terminate flex + * + * synopsis + * int status; + * flexend( status ); + * + * status is exit status. + * + * note + * This routine does not return. + */ + +void flexend(int status) +{ + int tblsiz; + char *flex_gettime(); + + if ( skelfile != NULL ) + { + if ( ferror( skelfile ) ) + flexfatal( "error occurred when writing skeleton file" ); + + else if ( fclose( skelfile ) ) + flexfatal( "error occurred when closing skeleton file" ); + } + + if ( temp_action_file ) + { + if ( ferror( temp_action_file ) ) + flexfatal( "error occurred when writing temporary action file" ); + + else if ( fclose( temp_action_file ) ) + flexfatal( "error occurred when closing temporary action file" ); + + else if ( unlink( action_file_name ) ) + flexfatal( "error occurred when deleting temporary action file" ); + } + + if ( status != 0 && outfile_created ) + { + if ( ferror( stdout ) ) + flexfatal( "error occurred when writing output file" ); + + else if ( fclose( stdout ) ) + flexfatal( "error occurred when closing output file" ); + + else if ( unlink( outfile ) ) + flexfatal( "error occurred when deleting output file" ); + } + + if ( backtrack_report && backtrack_file ) + { + if ( num_backtracking == 0 ) + fprintf( backtrack_file, "No backtracking.\n" ); + else if ( fullspd || fulltbl ) + fprintf( backtrack_file, + "%d backtracking (non-accepting) states.\n", + num_backtracking ); + else + fprintf( backtrack_file, "Compressed tables always backtrack.\n" ); + + if ( ferror( backtrack_file ) ) + flexfatal( "error occurred when writing backtracking file" ); + + else if ( fclose( backtrack_file ) ) + flexfatal( "error occurred when closing backtracking file" ); + } + + if ( printstats ) + { + endtime = flex_gettime(); + + fprintf( stderr, "%s version %s usage statistics:\n", program_name, + flex_version ); + fprintf( stderr, " started at %s, finished at %s\n", + starttime, endtime ); + + fprintf( stderr, " scanner options: -" ); + + if ( backtrack_report ) + putc( 'b', stderr ); + if ( ddebug ) + putc( 'd', stderr ); + if ( interactive ) + putc( 'I', stderr ); + if ( caseins ) + putc( 'i', stderr ); + if ( ! gen_line_dirs ) + putc( 'L', stderr ); + if ( performance_report ) + putc( 'p', stderr ); + if ( spprdflt ) + putc( 's', stderr ); + if ( use_stdout ) + putc( 't', stderr ); + if ( trace ) + putc( 'T', stderr ); + if ( printstats ) + putc( 'v', stderr ); /* always true! */ + if ( csize == 256 ) + putc( '8', stderr ); + + fprintf( stderr, " -C" ); + + if ( fulltbl ) + putc( 'f', stderr ); + if ( fullspd ) + putc( 'F', stderr ); + if ( useecs ) + putc( 'e', stderr ); + if ( usemecs ) + putc( 'm', stderr ); + + if ( strcmp( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ) ) + fprintf( stderr, " -S%s", skelname ); + + putc( '\n', stderr ); + + fprintf( stderr, " %d/%d NFA states\n", lastnfa, current_mns ); + fprintf( stderr, " %d/%d DFA states (%d words)\n", lastdfa, + current_max_dfas, totnst ); + fprintf( stderr, + " %d rules\n", num_rules - 1 /* - 1 for def. rule */ ); + + if ( num_backtracking == 0 ) + fprintf( stderr, " No backtracking\n" ); + else if ( fullspd || fulltbl ) + fprintf( stderr, " %d backtracking (non-accepting) states\n", + num_backtracking ); + else + fprintf( stderr, " compressed tables always backtrack\n" ); + + if ( bol_needed ) + fprintf( stderr, " Beginning-of-line patterns used\n" ); + + fprintf( stderr, " %d/%d start conditions\n", lastsc, + current_max_scs ); + fprintf( stderr, " %d epsilon states, %d double epsilon states\n", + numeps, eps2 ); + + if ( lastccl == 0 ) + fprintf( stderr, " no character classes\n" ); + else + fprintf( stderr, + " %d/%d character classes needed %d/%d words of storage, %d reused\n", + lastccl, current_maxccls, + cclmap[lastccl] + ccllen[lastccl], + current_max_ccl_tbl_size, cclreuse ); + + fprintf( stderr, " %d state/nextstate pairs created\n", numsnpairs ); + fprintf( stderr, " %d/%d unique/duplicate transitions\n", + numuniq, numdup ); + + if ( fulltbl ) + { + tblsiz = lastdfa * numecs; + fprintf( stderr, " %d table entries\n", tblsiz ); + } + + else + { + tblsiz = 2 * (lastdfa + numtemps) + 2 * tblend; + + fprintf( stderr, " %d/%d base-def entries created\n", + lastdfa + numtemps, current_max_dfas ); + fprintf( stderr, " %d/%d (peak %d) nxt-chk entries created\n", + tblend, current_max_xpairs, peakpairs ); + fprintf( stderr, + " %d/%d (peak %d) template nxt-chk entries created\n", + numtemps * nummecs, current_max_template_xpairs, + numtemps * numecs ); + fprintf( stderr, " %d empty table entries\n", nummt ); + fprintf( stderr, " %d protos created\n", numprots ); + fprintf( stderr, " %d templates created, %d uses\n", + numtemps, tmpuses ); + } + + if ( useecs ) + { + tblsiz = tblsiz + csize; + fprintf( stderr, " %d/%d equivalence classes created\n", + numecs, csize ); + } + + if ( usemecs ) + { + tblsiz = tblsiz + numecs; + fprintf( stderr, " %d/%d meta-equivalence classes created\n", + nummecs, csize ); + } + + fprintf( stderr, " %d (%d saved) hash collisions, %d DFAs equal\n", + hshcol, hshsave, dfaeql ); + fprintf( stderr, " %d sets of reallocations needed\n", num_reallocs ); + fprintf( stderr, " %d total table entries needed\n", tblsiz ); + } + + exit( status ); +} + + +/* flexinit - initialize flex + * + * synopsis + * int argc; + * char **argv; + * flexinit( argc, argv ); + */ + +void flexinit(int argc, char **argv) +{ + int i, sawcmpflag; + char *arg, *flex_gettime(), *mktemp(); + + printstats = syntaxerror = trace = spprdflt = interactive = caseins = false; + backtrack_report = performance_report = ddebug = fulltbl = fullspd = false; + yymore_used = continued_action = reject = false; + yymore_really_used = reject_really_used = false; + gen_line_dirs = usemecs = useecs = true; + + sawcmpflag = false; + use_stdout = false; + + csize = DEFAULT_CSIZE; + + program_name = argv[0]; + + /* read flags */ + for ( --argc, ++argv; argc ; --argc, ++argv ) + { + if ( argv[0][0] != '-' || argv[0][1] == '\0' ) + break; + + arg = argv[0]; + + for ( i = 1; arg[i] != '\0'; ++i ) + switch ( arg[i] ) + { + case 'b': + backtrack_report = true; + break; + + case 'c': + fprintf( stderr, + "%s: Assuming use of deprecated -c flag is really intended to be -C\n", + program_name ); + + /* fall through */ + + case 'C': + if ( i != 1 ) + flexerror( "-C flag must be given separately" ); + + if ( ! sawcmpflag ) + { + useecs = false; + usemecs = false; + fulltbl = false; + sawcmpflag = true; + } + + for ( ++i; arg[i] != '\0'; ++i ) + switch ( arg[i] ) + { + case 'e': + useecs = true; + break; + + case 'F': + fullspd = true; + break; + + case 'f': + fulltbl = true; + break; + + case 'm': + usemecs = true; + break; + + default: + lerrif( "unknown -C option '%c'", + (int) arg[i] ); + break; + } + + goto get_next_arg; + + case 'd': + ddebug = true; + break; + + case 'f': + useecs = usemecs = false; + fulltbl = true; + break; + + case 'F': + useecs = usemecs = false; + fullspd = true; + break; + + case 'I': + interactive = true; + break; + + case 'i': + caseins = true; + break; + + case 'L': + gen_line_dirs = false; + break; + + case 'n': + /* stupid do-nothing deprecated option */ + break; + + case 'p': + performance_report = true; + break; + + case 'S': + if ( i != 1 ) + flexerror( "-S flag must be given separately" ); + + skelname = arg + i + 1; + goto get_next_arg; + + case 's': + spprdflt = true; + break; + + case 't': + use_stdout = true; + break; + + case 'T': + trace = true; + break; + + case 'v': + printstats = true; + break; + + case '8': + csize = CSIZE; + break; + + default: + lerrif( "unknown flag '%c'", (int) arg[i] ); + break; + } + +get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */ + ; + } + + if ( (fulltbl || fullspd) && usemecs ) + flexerror( "full table and -Cm don't make sense together" ); + + if ( (fulltbl || fullspd) && interactive ) + flexerror( "full table and -I are (currently) incompatible" ); + + if ( fulltbl && fullspd ) + flexerror( "full table and -F are mutually exclusive" ); + + if ( ! skelname ) + { + static char skeleton_name_storage[400]; + + skelname = skeleton_name_storage; + (void) strcpy( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ); + } + + if ( ! use_stdout ) + { + FILE *prev_stdout = freopen( outfile, "w", stdout ); + + if ( prev_stdout == NULL ) + lerrsf( "could not create %s", outfile ); + + outfile_created = 1; + } + + num_input_files = argc; + input_files = argv; + set_input_file( num_input_files > 0 ? input_files[0] : NULL ); + + if ( backtrack_report ) + { +#ifndef SHORT_FILE_NAMES + backtrack_file = fopen( "lex.backtrack", "w" ); +#else + backtrack_file = fopen( "lex.bck", "w" ); +#endif + + if ( backtrack_file == NULL ) + flexerror( "could not create lex.backtrack" ); + } + + else + backtrack_file = NULL; + + + lastccl = 0; + lastsc = 0; + + /* initialize the statistics */ + starttime = flex_gettime(); + + if ( (skelfile = fopen( skelname, "r" )) == NULL ) + lerrsf( "can't open skeleton file %s", skelname ); + + epicsTempName ( action_file_name, sizeof ( action_file_name ) ); + if ( action_file_name[0] == '\0' ) + { + lerrsf( "can't create temporary file name", "" ); + } + + if ( ( temp_action_file = fopen ( action_file_name, "w" ) ) == NULL ) + lerrsf( "can't open temporary action file %s", action_file_name ); + + lastdfa = lastnfa = num_rules = numas = numsnpairs = tmpuses = 0; + numecs = numeps = eps2 = num_reallocs = hshcol = dfaeql = totnst = 0; + numuniq = numdup = hshsave = eofseen = datapos = dataline = 0; + num_backtracking = onesp = numprots = 0; + variable_trailing_context_rules = bol_needed = false; + + linenum = sectnum = 1; + firstprot = NIL; + + /* used in mkprot() so that the first proto goes in slot 1 + * of the proto queue + */ + lastprot = 1; + + if ( useecs ) + { /* set up doubly-linked equivalence classes */ + /* We loop all the way up to csize, since ecgroup[csize] is the + * position used for NUL characters + */ + ecgroup[1] = NIL; + + for ( i = 2; i <= csize; ++i ) + { + ecgroup[i] = i - 1; + nextecm[i - 1] = i; + } + + nextecm[csize] = NIL; + } + + else + { /* put everything in its own equivalence class */ + for ( i = 1; i <= csize; ++i ) + { + ecgroup[i] = i; + nextecm[i] = BAD_SUBSCRIPT; /* to catch errors */ + } + } + + set_up_initial_allocations(); +} + + +/* readin - read in the rules section of the input file(s) + * + * synopsis + * readin(); + */ + +void readin(void) +{ + skelout(); + + if ( ddebug ) + puts( "#define FLEX_DEBUG" ); + + if ( csize == 256 ) + puts( "#define YY_CHAR unsigned char" ); + else + puts( "#define YY_CHAR char" ); + + line_directive_out( stdout ); + + if ( yyparse() ) + { + pinpoint_message( "fatal parse error" ); + flexend( 1 ); + } + + if ( xlation ) + { + numecs = ecs_from_xlation( ecgroup ); + useecs = true; + } + + else if ( useecs ) + numecs = cre8ecs( nextecm, ecgroup, csize ); + + else + numecs = csize; + + /* now map the equivalence class for NUL to its expected place */ + ecgroup[0] = ecgroup[csize]; + NUL_ec = abs( ecgroup[0] ); + + if ( useecs ) + ccl2ecl(); +} + + + +/* set_up_initial_allocations - allocate memory for internal tables */ + +void set_up_initial_allocations(void) +{ + current_mns = INITIAL_MNS; + firstst = allocate_integer_array( current_mns ); + lastst = allocate_integer_array( current_mns ); + finalst = allocate_integer_array( current_mns ); + transchar = allocate_integer_array( current_mns ); + trans1 = allocate_integer_array( current_mns ); + trans2 = allocate_integer_array( current_mns ); + accptnum = allocate_integer_array( current_mns ); + assoc_rule = allocate_integer_array( current_mns ); + state_type = allocate_integer_array( current_mns ); + + current_max_rules = INITIAL_MAX_RULES; + rule_type = allocate_integer_array( current_max_rules ); + rule_linenum = allocate_integer_array( current_max_rules ); + + current_max_scs = INITIAL_MAX_SCS; + scset = allocate_integer_array( current_max_scs ); + scbol = allocate_integer_array( current_max_scs ); + scxclu = allocate_integer_array( current_max_scs ); + sceof = allocate_integer_array( current_max_scs ); + scname = allocate_char_ptr_array( current_max_scs ); + actvsc = allocate_integer_array( current_max_scs ); + + current_maxccls = INITIAL_MAX_CCLS; + cclmap = allocate_integer_array( current_maxccls ); + ccllen = allocate_integer_array( current_maxccls ); + cclng = allocate_integer_array( current_maxccls ); + + current_max_ccl_tbl_size = INITIAL_MAX_CCL_TBL_SIZE; + ccltbl = allocate_character_array( current_max_ccl_tbl_size ); + + current_max_dfa_size = INITIAL_MAX_DFA_SIZE; + + current_max_xpairs = INITIAL_MAX_XPAIRS; + nxt = allocate_integer_array( current_max_xpairs ); + chk = allocate_integer_array( current_max_xpairs ); + + current_max_template_xpairs = INITIAL_MAX_TEMPLATE_XPAIRS; + tnxt = allocate_integer_array( current_max_template_xpairs ); + + current_max_dfas = INITIAL_MAX_DFAS; + base = allocate_integer_array( current_max_dfas ); + def = allocate_integer_array( current_max_dfas ); + dfasiz = allocate_integer_array( current_max_dfas ); + accsiz = allocate_integer_array( current_max_dfas ); + dhash = allocate_integer_array( current_max_dfas ); + dss = allocate_int_ptr_array( current_max_dfas ); + dfaacc = allocate_dfaacc_union( current_max_dfas ); + + nultrans = (int *) 0; +} diff --git a/modules/libcom/src/flex/flex.html b/modules/libcom/src/flex/flex.html new file mode 100644 index 000000000..3b6ae9b80 --- /dev/null +++ b/modules/libcom/src/flex/flex.html @@ -0,0 +1,611 @@ + + +
+
+
+
+

NAME

+     flex - fast lexical analyzer generator
+
+
+
+

SYNOPSIS

+     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]
+
+
+
+

DESCRIPTION

+     flex is a  tool  for  generating  scanners:  programs  which
+     recognized  lexical  patterns in text.  flex reads the given
+     input files, or its standard input  if  no  file  names  are
+     given,  for  a  description  of  a scanner to generate.  The
+     description is in the form of pairs of  regular  expressions
+     and  C  code,  called  rules.  flex  generates as output a C
+     source file, lex.yy.c, which defines a routine yylex(). This
+     file is compiled and linked with the -lfl library to produce
+     an executable.  When the executable is run, it analyzes  its
+     input  for occurrences of the regular expressions.  Whenever
+     it finds one, it executes the corresponding C code.
+
+     For full documentation, see flexdoc(1). This manual entry is
+     intended for use as a quick reference.
+
+
+
+

OPTIONS

+     flex has the following options:
+
+     -b   Generate  backtracking  information  to  lex.backtrack.
+          This  is  a  list of scanner states which require back-
+          tracking and the input characters on which they do  so.
+          By adding rules one can remove backtracking states.  If
+          all backtracking states are eliminated and -f or -F  is
+          used, the generated scanner will run faster.
+
+     -c   is a do-nothing, deprecated option included  for  POSIX
+          compliance.
+
+          NOTE: in previous releases of flex -c specified  table-
+          compression  options.   This functionality is now given
+          by the -C flag.  To ease the the impact of this change,
+          when  flex encounters -c, it currently issues a warning
+          message and assumes that -C was  desired  instead.   In
+          the future this "promotion" of -c to -C will go away in
+          the name of full POSIX  compliance  (unless  the  POSIX
+          meaning is removed first).
+
+     -d   makes the generated scanner run in debug  mode.   When-
+          ever   a   pattern   is   recognized   and  the  global
+          yy_flex_debug is non-zero (which is the  default),  the
+          scanner will write to stderr a line of the form:
+
+              --accepting rule at line 53 ("the matched text")
+
+          The line number refers to the location of the  rule  in
+          the  file defining the scanner (i.e., the file that was
+          fed to flex).  Messages are  also  generated  when  the
+          scanner  backtracks,  accepts the default rule, reaches
+          the end of its input buffer (or encounters a  NUL;  the
+          two  look  the same as far as the scanner's concerned),
+          or reaches an end-of-file.
+
+     -f   specifies (take your pick) full table or fast  scanner.
+          No  table compression is done.  The result is large but
+          fast.  This option is equivalent to -Cf (see below).
+
+     -i   instructs flex to generate a case-insensitive  scanner.
+          The  case  of  letters given in the flex input patterns
+          will be ignored,  and  tokens  in  the  input  will  be
+          matched  regardless of case.  The matched text given in
+          yytext will have the preserved case (i.e., it will  not
+          be folded).
+
+     -n   is another do-nothing, deprecated option included  only
+          for POSIX compliance.
+
+     -p   generates a performance report to stderr.   The  report
+          consists  of  comments  regarding  features of the flex
+          input file which will cause a loss  of  performance  in
+          the resulting scanner.
+
+     -s   causes the default rule (that unmatched  scanner  input
+          is  echoed to stdout) to be suppressed.  If the scanner
+          encounters input that does not match any of its  rules,
+          it aborts with an error.
+
+     -t   instructs flex to write the  scanner  it  generates  to
+          standard output instead of lex.yy.c.
+
+     -v   specifies that flex should write to stderr a summary of
+          statistics regarding the scanner it generates.
+
+     -F   specifies that the fast  scanner  table  representation
+          should  be  used.  This representation is about as fast
+          as the full table representation  (-f),  and  for  some
+          sets  of patterns will be considerably smaller (and for
+          others, larger).  See flexdoc(1) for details.
+
+          This option is equivalent to -CF (see below).
+
+     -I   instructs flex to generate an interactive scanner, that
+          is, a scanner which stops immediately rather than look-
+          ing ahead if it knows that the currently  scanned  text
+          cannot  be  part  of a longer rule's match.  Again, see
+          flexdoc(1) for details.
+
+          Note, -I cannot be used in  conjunction  with  full  or
+          fast tables, i.e., the -f, -F, -Cf, or -CF flags.
+
+     -L   instructs flex not  to  generate  #line  directives  in
+          lex.yy.c. The default is to generate such directives so
+          error messages in the actions will be correctly located
+          with  respect  to the original flex input file, and not
+          to the fairly meaningless line numbers of lex.yy.c.
+
+     -T   makes flex run in trace mode.  It will generate  a  lot
+          of  messages to stdout concerning the form of the input
+          and the resultant non-deterministic  and  deterministic
+          finite  automata.   This  option  is  mostly for use in
+          maintaining flex.
+
+     -8   instructs flex to generate an 8-bit scanner.   On  some
+          sites,  this is the default.  On others, the default is
+          7-bit characters.  To see which is the case, check  the
+          verbose  (-v) output for "equivalence classes created".
+          If the denominator of the number shown is 128, then  by
+          default  flex is generating 7-bit characters.  If it is
+          256, then the default is 8-bit characters.
+
+     -C[efmF]
+          controls the degree of table compression.
+
+          -Ce directs  flex  to  construct  equivalence  classes,
+          i.e.,  sets  of characters which have identical lexical
+          properties.  Equivalence classes usually give  dramatic
+          reductions  in the final table/object file sizes (typi-
+          cally  a  factor  of  2-5)   and   are   pretty   cheap
+          performance-wise   (one  array  look-up  per  character
+          scanned).
+
+          -Cf specifies that the full scanner  tables  should  be
+          generated - flex should not compress the tables by tak-
+          ing advantages of similar transition functions for dif-
+          ferent states.
+
+          -CF specifies that the alternate fast scanner represen-
+          tation (described in flexdoc(1)) should be used.
+
+          -Cm directs flex to construct meta-equivalence classes,
+          which  are  sets of equivalence classes (or characters,
+          if equivalence classes are not  being  used)  that  are
+          commonly  used  together.  Meta-equivalence classes are
+          often a big win when using compressed tables, but  they
+          have  a  moderate  performance  impact (one or two "if"
+          tests and one array look-up per character scanned).
+
+          A lone -C specifies that the scanner tables  should  be
+          compressed  but  neither  equivalence classes nor meta-
+          equivalence classes should be used.
+          The options -Cf or  -CF  and  -Cm  do  not  make  sense
+          together - there is no opportunity for meta-equivalence
+          classes if the table is not being  compressed.   Other-
+          wise the options may be freely mixed.
+
+          The default setting is -Cem, which specifies that  flex
+          should   generate   equivalence   classes   and   meta-
+          equivalence classes.  This setting provides the highest
+          degree   of  table  compression.   You  can  trade  off
+          faster-executing scanners at the cost of larger  tables
+          with the following generally being true:
+
+              slowest & smallest
+                    -Cem
+                    -Cm
+                    -Ce
+                    -C
+                    -C{f,F}e
+                    -C{f,F}
+              fastest & largest
+
+
+          -C options are not cumulative;  whenever  the  flag  is
+          encountered, the previous -C settings are forgotten.
+
+     -Sskeleton_file
+          overrides the default skeleton  file  from  which  flex
+          constructs its scanners.  You'll never need this option
+          unless you are doing flex maintenance or development.
+
+
+
+

SUMMARY OF FLEX REGULAR EXPRESSIONS

+     The patterns in the input are written using an extended  set
+     of regular expressions.  These are:
+
+         x          match the character 'x'
+         .          any character except newline
+         [xyz]      a "character class"; in this case, the pattern
+                      matches either an 'x', a 'y', or a 'z'
+         [abj-oZ]   a "character class" with a range in it; matches
+                      an 'a', a 'b', any letter from 'j' through 'o',
+                      or a 'Z'
+         [^A-Z]     a "negated character class", i.e., any character
+                      but those in the class.  In this case, any
+                      character EXCEPT an uppercase letter.
+         [^A-Z\n]   any character EXCEPT an uppercase letter or
+                      a newline
+         r*         zero or more r's, where r is any regular expression
+         r+         one or more r's
+         r?         zero or one r's (that is, "an optional r")
+         r{2,5}     anywhere from two to five r's
+         r{2,}      two or more r's
+         r{4}       exactly 4 r's
+         {name}     the expansion of the "name" definition
+                    (see above)
+         "[xyz]\"foo"
+                    the literal string: [xyz]"foo
+         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+                      then the ANSI-C interpretation of \x.
+                      Otherwise, a literal 'X' (used to escape
+                      operators such as '*')
+         \123       the character with octal value 123
+         \x2a       the character with hexadecimal value 2a
+         (r)        match an r; parentheses are used to override
+                      precedence (see below)
+
+
+         rs         the regular expression r followed by the
+                      regular expression s; called "concatenation"
+
+
+         r|s        either an r or an s
+
+
+         r/s        an r but only if it is followed by an s.  The
+                      s is not part of the matched text.  This type
+                      of pattern is called as "trailing context".
+         ^r         an r, but only at the beginning of a line
+         r$         an r, but only at the end of a line.  Equivalent
+                      to "r/\n".
+
+
+         <s>r       an r, but only in start condition s (see
+                    below for discussion of start conditions)
+         <s1,s2,s3>r
+                    same, but in any of start conditions s1,
+                    s2, or s3
+
+
+         <<EOF>>    an end-of-file
+         <s1,s2><<EOF>>
+                    an end-of-file when in start condition s1 or s2
+
+     The regular expressions listed above are  grouped  according
+     to  precedence, from highest precedence at the top to lowest
+     at the bottom.   Those  grouped  together  have  equal  pre-
+     cedence.
+
+     Some notes on patterns:
+
+     -    Negated character classes match  newlines  unless  "\n"
+          (or  an equivalent escape sequence) is one of the char-
+          acters explicitly  present  in  the  negated  character
+          class (e.g., "[^A-Z\n]").
+
+     -    A rule can have at most one instance of  trailing  con-
+          text (the '/' operator or the '$' operator).  The start
+          condition, '^', and "<<EOF>>" patterns can  only  occur
+          at the beginning of a pattern, and, as well as with '/'
+          and '$', cannot be  grouped  inside  parentheses.   The
+          following are all illegal:
+
+              foo/bar$
+              foo|(bar$)
+              foo|^bar
+              <sc1>foo<sc2>bar
+
+
+
+
+

SUMMARY OF SPECIAL ACTIONS

+     In addition to arbitrary C code, the following can appear in
+     actions:
+
+     -    ECHO copies yytext to the scanner's output.
+
+     -    BEGIN followed by the name of a start condition  places
+          the scanner in the corresponding start condition.
+
+     -    REJECT directs the scanner to proceed on to the "second
+          best"  rule which matched the input (or a prefix of the
+          input).  yytext and yyleng are  set  up  appropriately.
+          Note that REJECT is a particularly expensive feature in
+          terms scanner performance; if it is used in any of  the
+          scanner's   actions  it  will  slow  down  all  of  the
+          scanner's matching.  Furthermore, REJECT cannot be used
+          with the -f or -F options.
+
+          Note also that unlike the other special actions, REJECT
+          is  a  branch;  code  immediately  following  it in the
+          action will not be executed.
+
+     -    yymore() tells  the  scanner  that  the  next  time  it
+          matches  a  rule,  the  corresponding  token  should be
+          appended onto the current value of yytext  rather  than
+          replacing it.
+
+     -    yyless(n) returns all but the first n characters of the
+          current token back to the input stream, where they will
+          be rescanned when the scanner looks for the next match.
+          yytext  and  yyleng  are  adjusted appropriately (e.g.,
+          yyleng will now be equal to n ).
+
+     -    unput(c) puts the  character  c  back  onto  the  input
+          stream.  It will be the next character scanned.
+
+     -    input() reads the next character from the input  stream
+          (this  routine  is  called  yyinput() if the scanner is
+          compiled using C++).
+
+     -    yyterminate() can be used in lieu of a return statement
+          in  an action.  It terminates the scanner and returns a
+          0 to the scanner's caller, indicating "all done".
+
+          By default, yyterminate() is also called when  an  end-
+          of-file is encountered.  It is a macro and may be rede-
+          fined.
+
+     -    YY_NEW_FILE is an  action  available  only  in  <<EOF>>
+          rules.   It  means "Okay, I've set up a new input file,
+          continue scanning".
+
+     -    yy_create_buffer( file, size ) takes a FILE pointer and
+          an integer size. It returns a YY_BUFFER_STATE handle to
+          a new input buffer  large  enough  to  accomodate  size
+          characters and associated with the given file.  When in
+          doubt, use YY_BUF_SIZE for the size.
+
+     -    yy_switch_to_buffer(   new_buffer   )   switches    the
+          scanner's  processing to scan for tokens from the given
+          buffer, which must be a YY_BUFFER_STATE.
+
+     -    yy_delete_buffer( buffer ) deletes the given buffer.
+
+
+
+

VALUES AVAILABLE TO THE USER

+     -    char *yytext holds the text of the current  token.   It
+          may not be modified.
+
+     -    int yyleng holds the length of the current  token.   It
+          may not be modified.
+
+     -    FILE *yyin is the file  which  by  default  flex  reads
+          from.   It  may  be  redefined  but doing so only makes
+          sense before scanning begins.  Changing it in the  mid-
+          dle of scanning will have unexpected results since flex
+          buffers its input.  Once scanning terminates because an
+          end-of-file   has   been  seen,  void  yyrestart(  FILE
+          *new_file ) may be called to  point  yyin  at  the  new
+          input file.
+
+     -    FILE *yyout is the file to which ECHO actions are done.
+          It can be reassigned by the user.
+
+     -    YY_CURRENT_BUFFER returns a YY_BUFFER_STATE  handle  to
+          the current buffer.
+
+
+
+

MACROS THE USER CAN REDEFINE

+     -    YY_DECL controls how the scanning routine is  declared.
+          By  default, it is "int yylex()", or, if prototypes are
+          being used, "int yylex(void)".  This definition may  be
+          changed  by  redefining the "YY_DECL" macro.  Note that
+          if you give arguments to the scanning routine  using  a
+          K&R-style/non-prototyped function declaration, you must
+          terminate the definition with a semi-colon (;).
+
+     -    The nature of how the scanner gets  its  input  can  be
+          controlled    by   redefining   the   YY_INPUT   macro.
+          YY_INPUT's         calling         sequence          is
+          "YY_INPUT(buf,result,max_size)".    Its  action  is  to
+          place up to max_size characters in the character  array
+          buf  and  return  in the integer variable result either
+          the number of characters read or the  constant  YY_NULL
+          (0  on  Unix  systems)  to  indicate  EOF.  The default
+          YY_INPUT reads from the global file-pointer "yyin".   A
+          sample  redefinition  of  YY_INPUT  (in the definitions
+          section of the input file):
+
+              %{
+              #undef YY_INPUT
+              #define YY_INPUT(buf,result,max_size) \
+                  { \
+                  int c = getchar(); \
+                  result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
+                  }
+              %}
+
+
+     -    When the scanner  receives  an  end-of-file  indication
+          from  YY_INPUT,  it  then checks the yywrap() function.
+          If yywrap() returns false (zero), then  it  is  assumed
+          that  the  function  has  gone ahead and set up yyin to
+          point to another input file,  and  scanning  continues.
+          If  it  returns  true (non-zero), then the scanner ter-
+          minates, returning 0 to its caller.
+
+          The default yywrap() always returns 1.   Presently,  to
+          redefine  it  you  must first "#undef yywrap", as it is
+          currently implemented as a macro.  It  is  likely  that
+          yywrap()  will  soon be defined to be a function rather
+          than a macro.
+
+     -    YY_USER_ACTION can be redefined to  provide  an  action
+          which  is  always  executed prior to the matched rule's
+          action.
+
+     -    The macro YY_USER_INIT may be redefined to  provide  an
+          action which is always executed before the first scan.
+
+     -    In the generated scanner, the actions are all  gathered
+          in  one  large  switch  statement  and  separated using
+          YY_BREAK, which may be redefined.  By  default,  it  is
+          simply  a  "break", to separate each rule's action from
+          the following rule's.
+
+
+
+

FILES

+     flex.skel
+          skeleton scanner.
+
+     lex.yy.c
+          generated scanner (called lexyy.c on some systems).
+
+     lex.backtrack
+          backtracking information for -b flag (called lex.bck on
+          some systems).
+
+     -lfl library with which to link the scanners.
+
+
+
+

SEE ALSO

+     flexdoc(1), lex(1), yacc(1), sed(1), awk(1).
+
+     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator
+
+
+
+

DIAGNOSTICS

+     reject_used_but_not_detected undefined or
+
+     yymore_used_but_not_detected undefined -  These  errors  can
+     occur  at compile time.  They indicate that the scanner uses
+     REJECT or yymore() but that flex failed to notice the  fact,
+     meaning that flex scanned the first two sections looking for
+     occurrences of these actions and failed  to  find  any,  but
+     somehow  you  snuck  some in (via a #include file, for exam-
+     ple).  Make an explicit reference to the action in your flex
+     input   file.    (Note  that  previously  flex  supported  a
+     %used/%unused mechanism for dealing with this problem;  this
+     feature  is  still supported but now deprecated, and will go
+     away soon unless the author hears from people who can  argue
+     compellingly that they need it.)
+
+     flex scanner jammed - a scanner compiled with -s has encoun-
+     tered  an  input  string  which wasn't matched by any of its
+     rules.
+
+     flex input buffer overflowed -  a  scanner  rule  matched  a
+     string  long enough to overflow the scanner's internal input
+     buffer  (16K   bytes   -   controlled   by   YY_BUF_MAX   in
+     "flex.skel").
+
+     scanner  requires  -8  flag  -  Your  scanner  specification
+     includes  recognizing  8-bit  characters  and  you  did  not
+     specify the -8 flag (and your site has  not  installed  flex
+     with -8 as the default).
+
+     fatal flex scanner internal error--end of  buffer  missed  -
+     This  can  occur  in  an  scanner which is reentered after a
+     long-jump has jumped out (or over) the scanner's  activation
+     frame.  Before reentering the scanner, use:
+         yyrestart( yyin );
+
+
+     too many %t classes! - You managed to put every single char-
+     acter  into  its  own %t class.  flex requires that at least
+     one of the classes share characters.
+
+
+
+

AUTHOR

+     Vern Paxson, with the help of many ideas and  much  inspira-
+     tion from Van Jacobson.  Original version by Jef Poskanzer.
+
+     See flexdoc(1) for additional credits  and  the  address  to
+     send comments to.
+
+
+
+

DEFICIENCIES / BUGS

+     Some trailing context patterns cannot  be  properly  matched
+     and  generate  warning  messages  ("Dangerous  trailing con-
+     text").  These are patterns where the ending  of  the  first
+     part  of  the rule matches the beginning of the second part,
+     such as "zx*/xy*", where the 'x*' matches  the  'x'  at  the
+     beginning  of  the  trailing  context.  (Note that the POSIX
+     draft states that the text matched by such patterns is unde-
+     fined.)
+
+     For some trailing context rules, parts  which  are  actually
+     fixed-length  are  not  recognized  as  such, leading to the
+     abovementioned performance loss.  In particular, parts using
+     '|'   or  {n}  (such  as  "foo{3}")  are  always  considered
+     variable-length.
+
+     Combining trailing context with the special '|'  action  can
+     result  in fixed trailing context being turned into the more
+     expensive variable trailing context.  For example, this hap-
+     pens in the following example:
+
+         %%
+         abc      |
+         xyz/def
+
+
+     Use of unput() invalidates yytext and yyleng.
+
+     Use of unput() to push back more text than was  matched  can
+     result  in the pushed-back text matching a beginning-of-line
+     ('^') rule even though it didn't come at  the  beginning  of
+     the line (though this is rare!).
+
+     Pattern-matching  of  NUL's  is  substantially  slower  than
+     matching other characters.
+
+     flex does not generate correct  #line  directives  for  code
+     internal to the scanner; thus, bugs in flex.skel yield bogus
+     line numbers.
+
+     Due to both buffering of input and  read-ahead,  you  cannot
+     intermix  calls to <stdio.h> routines, such as, for example,
+     getchar(), with flex rules and  expect  it  to  work.   Call
+     input() instead.
+
+     The total table entries listed by the -v flag  excludes  the
+     number  of  table  entries needed to determine what rule has
+     been matched.  The number of entries is equal to the  number
+     of  DFA states if the scanner does not use REJECT, and some-
+     what greater than the number of states if it does.
+
+     REJECT cannot be used with the -f or -F options.
+
+     Some of the macros, such as  yywrap(),  may  in  the  future
+     become  functions which live in the -lfl library.  This will
+     doubtless break a lot of  code,  but  may  be  required  for
+     POSIX-compliance.
+
+     The flex internal algorithms need documentation.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Man(1) output converted with +man2html +
+ + diff --git a/modules/libcom/src/flex/flex.skel b/modules/libcom/src/flex/flex.skel new file mode 100644 index 000000000..6b9805b04 --- /dev/null +++ b/modules/libcom/src/flex/flex.skel @@ -0,0 +1,748 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton */ + +#define FLEX_SCANNER + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +#else /* ! __cplusplus */ + +#ifdef __GNUC__ +#include +void *malloc( size_t ); +void free( void* ); +#else +#include +#endif /* __GNUC__ */ + +#endif /* ! __cplusplus */ + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +%% section 1 definitions go here + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ +%% code to fiddle yytext and yyleng for yymore() goes here + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +%% data tables for the DFA go here + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +void yy_load_buffer_state ( void ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + +%% user's declarations go here + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { +%% yymore()-related code goes here + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + +%% code to set up and find next match goes here + +yy_find_action: +%% code to find the action number goes here + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + +%% debug code goes here + + switch ( yy_act ) + { +%% actions go here + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +%% code to do backtracking for compressed tables and set up yy_cp goes here + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + +%% code to get the start state into yy_current_state goes here + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { +%% code to find the next state goes here + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) + { + register int yy_is_jam; +%% code to find the next state, and perhaps do backtracking, goes here + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input(void) +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +void yyrestart( FILE *input_file ) + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } diff --git a/modules/libcom/src/flex/flex.skel.static b/modules/libcom/src/flex/flex.skel.static new file mode 100644 index 000000000..5e8bf2810 --- /dev/null +++ b/modules/libcom/src/flex/flex.skel.static @@ -0,0 +1,754 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton */ + +/* modified by Jim Kowalkowski to have everything declared static */ + +#define FLEX_SCANNER + +#include +#include + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ + +/* #define yyterminate() return ( YY_NULL ) replaced by jbk */ +static int yyterminate_internal( void ); +#define yyterminate() return yyterminate_internal() + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters - jbk added the static to YY_DECL + */ +#define YY_DECL static int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +%% section 1 definitions go here + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ +%% code to fiddle yytext and yyleng for yymore() goes here + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters */ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +/* jbk update +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; +*/ + +static YY_CHAR *yytext; /* jbk added static */ +static int yyleng; /* jbk added static */ + +static FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; /* jbk added static */ + +%% data tables for the DFA go here + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); + +/* jbk added static in front all these */ +static void yyrestart ( FILE *input_file ); +static void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +static void yy_load_buffer_state ( void ); +static YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +static void yy_delete_buffer ( YY_BUFFER_STATE b ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +#ifdef yyneed_input +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + +%% user's declarations go here + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { +%% yymore()-related code goes here + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + +%% code to set up and find next match goes here + +yy_find_action: +%% code to find the action number goes here + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + +%% debug code goes here + + switch ( yy_act ) + { +%% actions go here + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +%% code to do backtracking for compressed tables and set up yy_cp goes here + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer( void ) + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move - YY_MORE_ADJ == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state( void ) + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + +%% code to get the start state into yy_current_state goes here + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { +%% code to find the next state goes here + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) + { + register int yy_is_jam; +%% code to find the next state, and perhaps do backtracking, goes here + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef yyneed_input +#ifdef __cplusplus +static int yyinput( void ) +#else +static int input( void ) +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } +#endif /* yyneed_input */ + + +/* jbk added static in front of func */ +static void yyrestart( FILE *input_file ) + { + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, input_file ); + else + yy_current_buffer = yy_create_buffer( input_file, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + +/* jbk added static in front of func */ +static void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +/* jbk added static in front of func */ +static void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +/* jbk added static in front of func */ +static YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +/* jbk added static in front of func */ +static void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +/* jbk added static in front of func */ +static void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } + +static int yyterminate_internal( void ) +{ + /* jbk fix - buffer created by yy_create_buffer needs to be freed */ + yy_delete_buffer(yy_current_buffer); + yy_current_buffer=NULL; + return YY_NULL; +} + diff --git a/modules/libcom/src/flex/flexdef.h b/modules/libcom/src/flex/flexdef.h new file mode 100644 index 000000000..79cfa9427 --- /dev/null +++ b/modules/libcom/src/flex/flexdef.h @@ -0,0 +1,837 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* flexdef - definitions file for flex */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef INC_flexdef_H +#define INC_flexdef_H + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/* always be prepared to generate an 8-bit scanner */ +#define FLEX_8_BIT_CHARS + +#ifdef FLEX_8_BIT_CHARS +#define CSIZE 256 +#define Char unsigned char +#else +#define Char char +#define CSIZE 128 +#endif + +/* size of input alphabet - should be size of ASCII set */ +#ifndef DEFAULT_CSIZE +#define DEFAULT_CSIZE 128 +#endif + + +/* maximum line length we'll have to deal with */ +#define MAXLINE BUFSIZ + +/* maximum size of file name */ +#define FILENAMESIZE 1024 + +#ifndef min +#define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef max +#define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#define true 1 +#define false 0 + + +#ifndef DEFAULT_SKELETON_FILE +#define DEFAULT_SKELETON_FILE "flex.skel" +#endif + +/* special chk[] values marking the slots taking by end-of-buffer and action + * numbers + */ +#define EOB_POSITION -1 +#define ACTION_POSITION -2 + +/* number of data items per line for -f output */ +#define NUMDATAITEMS 10 + +/* number of lines of data in -f output before inserting a blank line for + * readability. + */ +#define NUMDATALINES 10 + +/* transition_struct_out() definitions */ +#define TRANS_STRUCT_PRINT_LENGTH 15 + +/* returns true if an nfa state has an epsilon out-transition slot + * that can be used. This definition is currently not used. + */ +#define FREE_EPSILON(state) \ + (transchar[state] == SYM_EPSILON && \ + trans2[state] == NO_TRANSITION && \ + finalst[state] != state) + +/* returns true if an nfa state has an epsilon out-transition character + * and both slots are free + */ +#define SUPER_FREE_EPSILON(state) \ + (transchar[state] == SYM_EPSILON && \ + trans1[state] == NO_TRANSITION) \ + +/* maximum number of NFA states that can comprise a DFA state. It's real + * big because if there's a lot of rules, the initial state will have a + * huge epsilon closure. + */ +#define INITIAL_MAX_DFA_SIZE 750 +#define MAX_DFA_SIZE_INCREMENT 750 + + +/* a note on the following masks. They are used to mark accepting numbers + * as being special. As such, they implicitly limit the number of accepting + * numbers (i.e., rules) because if there are too many rules the rule numbers + * will overload the mask bits. Fortunately, this limit is \large/ (0x2000 == + * 8192) so unlikely to actually cause any problems. A check is made in + * new_rule() to ensure that this limit is not reached. + */ + +/* mask to mark a trailing context accepting number */ +#define YY_TRAILING_MASK 0x2000 + +/* mask to mark the accepting number of the "head" of a trailing context rule */ +#define YY_TRAILING_HEAD_MASK 0x4000 + +/* maximum number of rules, as outlined in the above note */ +#define MAX_RULE (YY_TRAILING_MASK - 1) + + +/* NIL must be 0. If not, its special meaning when making equivalence classes + * (it marks the representative of a given e.c.) will be unidentifiable + */ +#define NIL 0 + +#define JAM -1 /* to mark a missing DFA transition */ +#define NO_TRANSITION NIL +#define UNIQUE -1 /* marks a symbol as an e.c. representative */ +#define INFINITY -1 /* for x{5,} constructions */ + +#define INITIAL_MAX_CCLS 100 /* max number of unique character classes */ +#define MAX_CCLS_INCREMENT 100 + +/* size of table holding members of character classes */ +#define INITIAL_MAX_CCL_TBL_SIZE 500 +#define MAX_CCL_TBL_SIZE_INCREMENT 250 + +#define INITIAL_MAX_RULES 100 /* default maximum number of rules */ +#define MAX_RULES_INCREMENT 100 + +#define INITIAL_MNS 2000 /* default maximum number of nfa states */ +#define MNS_INCREMENT 1000 /* amount to bump above by if it's not enough */ + +#define INITIAL_MAX_DFAS 1000 /* default maximum number of dfa states */ +#define MAX_DFAS_INCREMENT 1000 + +#define JAMSTATE -32766 /* marks a reference to the state that always jams */ + +/* enough so that if it's subtracted from an NFA state number, the result + * is guaranteed to be negative + */ +#define MARKER_DIFFERENCE 32000 +#define MAXIMUM_MNS 31999 + +/* maximum number of nxt/chk pairs for non-templates */ +#define INITIAL_MAX_XPAIRS 2000 +#define MAX_XPAIRS_INCREMENT 2000 + +/* maximum number of nxt/chk pairs needed for templates */ +#define INITIAL_MAX_TEMPLATE_XPAIRS 2500 +#define MAX_TEMPLATE_XPAIRS_INCREMENT 2500 + +#define SYM_EPSILON (CSIZE + 1) /* to mark transitions on the symbol epsilon */ + +#define INITIAL_MAX_SCS 40 /* maximum number of start conditions */ +#define MAX_SCS_INCREMENT 40 /* amount to bump by if it's not enough */ + +#define ONE_STACK_SIZE 500 /* stack of states with only one out-transition */ +#define SAME_TRANS -1 /* transition is the same as "default" entry for state */ + +/* the following percentages are used to tune table compression: + + * the percentage the number of out-transitions a state must be of the + * number of equivalence classes in order to be considered for table + * compaction by using protos + */ +#define PROTO_SIZE_PERCENTAGE 15 + +/* the percentage the number of homogeneous out-transitions of a state + * must be of the number of total out-transitions of the state in order + * that the state's transition table is first compared with a potential + * template of the most common out-transition instead of with the first + * proto in the proto queue + */ +#define CHECK_COM_PERCENTAGE 50 + +/* the percentage the number of differences between a state's transition + * table and the proto it was first compared with must be of the total + * number of out-transitions of the state in order to keep the first + * proto as a good match and not search any further + */ +#define FIRST_MATCH_DIFF_PERCENTAGE 10 + +/* the percentage the number of differences between a state's transition + * table and the most similar proto must be of the state's total number + * of out-transitions to use the proto as an acceptable close match + */ +#define ACCEPTABLE_DIFF_PERCENTAGE 50 + +/* the percentage the number of homogeneous out-transitions of a state + * must be of the number of total out-transitions of the state in order + * to consider making a template from the state + */ +#define TEMPLATE_SAME_PERCENTAGE 60 + +/* the percentage the number of differences between a state's transition + * table and the most similar proto must be of the state's total number + * of out-transitions to create a new proto from the state + */ +#define NEW_PROTO_DIFF_PERCENTAGE 20 + +/* the percentage the total number of out-transitions of a state must be + * of the number of equivalence classes in order to consider trying to + * fit the transition table into "holes" inside the nxt/chk table. + */ +#define INTERIOR_FIT_PERCENTAGE 15 + +/* size of region set aside to cache the complete transition table of + * protos on the proto queue to enable quick comparisons + */ +#define PROT_SAVE_SIZE 2000 + +#define MSP 50 /* maximum number of saved protos (protos on the proto queue) */ + +/* maximum number of out-transitions a state can have that we'll rummage + * around through the interior of the internal fast table looking for a + * spot for it + */ +#define MAX_XTIONS_FULL_INTERIOR_FIT 4 + +/* maximum number of rules which will be reported as being associated + * with a DFA state + */ +#define MAX_ASSOC_RULES 100 + +/* number that, if used to subscript an array, has a good chance of producing + * an error; should be small enough to fit into a short + */ +#define BAD_SUBSCRIPT -32767 + +/* absolute value of largest number that can be stored in a short, with a + * bit of slop thrown in for general paranoia. + */ +#define MAX_SHORT 32766 + + +/* Declarations for global variables. */ + +/* variables for symbol tables: + * sctbl - start-condition symbol table + * ndtbl - name-definition symbol table + * ccltab - character class text symbol table + */ + +struct hash_entry + { + struct hash_entry *prev, *next; + char *name; + char *str_val; + int int_val; + } ; + +typedef struct hash_entry *hash_table[]; + +#define NAME_TABLE_HASH_SIZE 101 +#define START_COND_HASH_SIZE 101 +#define CCL_HASH_SIZE 101 + +extern struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; +extern struct hash_entry *sctbl[START_COND_HASH_SIZE]; +extern struct hash_entry *ccltab[CCL_HASH_SIZE]; + + +/* variables for flags: + * printstats - if true (-v), dump statistics + * syntaxerror - true if a syntax error has been found + * eofseen - true if we've seen an eof in the input file + * ddebug - if true (-d), make a "debug" scanner + * trace - if true (-T), trace processing + * spprdflt - if true (-s), suppress the default rule + * interactive - if true (-I), generate an interactive scanner + * caseins - if true (-i), generate a case-insensitive scanner + * useecs - if true (-Ce flag), use equivalence classes + * fulltbl - if true (-Cf flag), don't compress the DFA state table + * usemecs - if true (-Cm flag), use meta-equivalence classes + * fullspd - if true (-F flag), use Jacobson method of table representation + * gen_line_dirs - if true (i.e., no -L flag), generate #line directives + * performance_report - if true (i.e., -p flag), generate a report relating + * to scanner performance + * backtrack_report - if true (i.e., -b flag), generate "lex.backtrack" file + * listing backtracking states + * csize - size of character set for the scanner we're generating; + * 128 for 7-bit chars and 256 for 8-bit + * yymore_used - if true, yymore() is used in input rules + * reject - if true, generate backtracking tables for REJECT macro + * real_reject - if true, scanner really uses REJECT (as opposed to just + * having "reject" set for variable trailing context) + * continued_action - true if this rule's action is to "fall through" to + * the next rule's action (i.e., the '|' action) + * yymore_really_used - has a REALLY_xxx value indicating whether a + * %used or %notused was used with yymore() + * reject_really_used - same for REJECT + */ + +extern int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; +extern int interactive, caseins, useecs, fulltbl, usemecs; +extern int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; +extern int yymore_used, reject, real_reject, continued_action; + +#define REALLY_NOT_DETERMINED 0 +#define REALLY_USED 1 +#define REALLY_NOT_USED 2 +extern int yymore_really_used, reject_really_used; + + +/* variables used in the flex input routines: + * datapos - characters on current output line + * dataline - number of contiguous lines of data in current data + * statement. Used to generate readable -f output + * linenum - current input line number + * skelfile - the skeleton file + * yyin - input file + * temp_action_file - temporary file to hold actions + * backtrack_file - file to summarize backtracking states to + * infilename - name of input file + * action_file_name - name of the temporary file + * input_files - array holding names of input files + * num_input_files - size of input_files array + * program_name - name with which program was invoked + */ + +extern int datapos, dataline, linenum; +extern FILE *skelfile, *yyin, *temp_action_file, *backtrack_file; +extern char *infilename; +extern char action_file_name[]; +extern char **input_files; +extern int num_input_files; +extern char *program_name; + + +/* variables for stack of states having only one out-transition: + * onestate - state number + * onesym - transition symbol + * onenext - target state + * onedef - default base entry + * onesp - stack pointer + */ + +extern int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; +extern int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; + + +/* variables for nfa machine data: + * current_mns - current maximum on number of NFA states + * num_rules - number of the last accepting state; also is number of + * rules created so far + * current_max_rules - current maximum number of rules + * lastnfa - last nfa state number created + * firstst - physically the first state of a fragment + * lastst - last physical state of fragment + * finalst - last logical state of fragment + * transchar - transition character + * trans1 - transition state + * trans2 - 2nd transition state for epsilons + * accptnum - accepting number + * assoc_rule - rule associated with this NFA state (or 0 if none) + * state_type - a STATE_xxx type identifying whether the state is part + * of a normal rule, the leading state in a trailing context + * rule (i.e., the state which marks the transition from + * recognizing the text-to-be-matched to the beginning of + * the trailing context), or a subsequent state in a trailing + * context rule + * rule_type - a RULE_xxx type identifying whether this a a ho-hum + * normal rule or one which has variable head & trailing + * context + * rule_linenum - line number associated with rule + */ + +extern int current_mns, num_rules, current_max_rules, lastnfa; +extern int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; +extern int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; + +/* different types of states; values are useful as masks, as well, for + * routines like check_trailing_context() + */ +#define STATE_NORMAL 0x1 +#define STATE_TRAILING_CONTEXT 0x2 + +/* global holding current type of state we're making */ + +extern int current_state_type; + +/* different types of rules */ +#define RULE_NORMAL 0 +#define RULE_VARIABLE 1 + +/* true if the input rules include a rule with both variable-length head + * and trailing context, false otherwise + */ +extern int variable_trailing_context_rules; + + +/* variables for protos: + * numtemps - number of templates created + * numprots - number of protos created + * protprev - backlink to a more-recently used proto + * protnext - forward link to a less-recently used proto + * prottbl - base/def table entry for proto + * protcomst - common state of proto + * firstprot - number of the most recently used proto + * lastprot - number of the least recently used proto + * protsave contains the entire state array for protos + */ + +extern int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; +extern int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; + + +/* variables for managing equivalence classes: + * numecs - number of equivalence classes + * nextecm - forward link of Equivalence Class members + * ecgroup - class number or backward link of EC members + * nummecs - number of meta-equivalence classes (used to compress + * templates) + * tecfwd - forward link of meta-equivalence classes members + * tecbck - backward link of MEC's + * xlation - maps character codes to their translations, or nil if no %t table + * num_xlations - number of different xlation values + */ + +/* reserve enough room in the equivalence class arrays so that we + * can use the CSIZE'th element to hold equivalence class information + * for the NUL character. Later we'll move this information into + * the 0th element. + */ +extern int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs; + +/* meta-equivalence classes are indexed starting at 1, so it's possible + * that they will require positions from 1 .. CSIZE, i.e., CSIZE + 1 + * slots total (since the arrays are 0-based). nextecm[] and ecgroup[] + * don't require the extra position since they're indexed from 1 .. CSIZE - 1. + */ +extern int tecfwd[CSIZE + 1], tecbck[CSIZE + 1]; + +extern int *xlation; +extern int num_xlations; + + +/* variables for start conditions: + * lastsc - last start condition created + * current_max_scs - current limit on number of start conditions + * scset - set of rules active in start condition + * scbol - set of rules active only at the beginning of line in a s.c. + * scxclu - true if start condition is exclusive + * sceof - true if start condition has EOF rule + * scname - start condition name + * actvsc - stack of active start conditions for the current rule + */ + +extern int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; +extern char **scname; + + +/* variables for dfa machine data: + * current_max_dfa_size - current maximum number of NFA states in DFA + * current_max_xpairs - current maximum number of non-template xtion pairs + * current_max_template_xpairs - current maximum number of template pairs + * current_max_dfas - current maximum number DFA states + * lastdfa - last dfa state number created + * nxt - state to enter upon reading character + * chk - check value to see if "nxt" applies + * tnxt - internal nxt table for templates + * base - offset into "nxt" for given state + * def - where to go if "chk" disallows "nxt" entry + * nultrans - NUL transition for each state + * NUL_ec - equivalence class of the NUL character + * tblend - last "nxt/chk" table entry being used + * firstfree - first empty entry in "nxt/chk" table + * dss - nfa state set for each dfa + * dfasiz - size of nfa state set for each dfa + * dfaacc - accepting set for each dfa state (or accepting number, if + * -r is not given) + * accsiz - size of accepting set for each dfa state + * dhash - dfa state hash value + * numas - number of DFA accepting states created; note that this + * is not necessarily the same value as num_rules, which is the analogous + * value for the NFA + * numsnpairs - number of state/nextstate transition pairs + * jambase - position in base/def where the default jam table starts + * jamstate - state number corresponding to "jam" state + * end_of_buffer_state - end-of-buffer dfa state number + */ + +extern int current_max_dfa_size, current_max_xpairs; +extern int current_max_template_xpairs, current_max_dfas; +extern int lastdfa, lasttemp, *nxt, *chk, *tnxt; +extern int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; +extern union dfaacc_union + { + int *dfaacc_set; + int dfaacc_state; + } *dfaacc; +extern int *accsiz, *dhash, numas; +extern int numsnpairs, jambase, jamstate; +extern int end_of_buffer_state; + +/* variables for ccl information: + * lastccl - ccl index of the last created ccl + * current_maxccls - current limit on the maximum number of unique ccl's + * cclmap - maps a ccl index to its set pointer + * ccllen - gives the length of a ccl + * cclng - true for a given ccl if the ccl is negated + * cclreuse - counts how many times a ccl is re-used + * current_max_ccl_tbl_size - current limit on number of characters needed + * to represent the unique ccl's + * ccltbl - holds the characters in each ccl - indexed by cclmap + */ + +extern int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; +extern int current_max_ccl_tbl_size; +extern Char *ccltbl; + + +/* variables for miscellaneous information: + * starttime - real-time when we started + * endtime - real-time when we ended + * nmstr - last NAME scanned by the scanner + * sectnum - section number currently being parsed + * nummt - number of empty nxt/chk table entries + * hshcol - number of hash collisions detected by snstods + * dfaeql - number of times a newly created dfa was equal to an old one + * numeps - number of epsilon NFA states created + * eps2 - number of epsilon states which have 2 out-transitions + * num_reallocs - number of times it was necessary to realloc() a group + * of arrays + * tmpuses - number of DFA states that chain to templates + * totnst - total number of NFA states used to make DFA states + * peakpairs - peak number of transition pairs we had to store internally + * numuniq - number of unique transitions + * numdup - number of duplicate transitions + * hshsave - number of hash collisions saved by checking number of states + * num_backtracking - number of DFA states requiring back-tracking + * bol_needed - whether scanner needs beginning-of-line recognition + */ + +extern char *starttime, *endtime, nmstr[MAXLINE]; +extern int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; +extern int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; +extern int num_backtracking, bol_needed; + +void *allocate_array(int size, int element_size); +void *reallocate_array(void *array, int size, int element_size); + +#define allocate_integer_array(size) \ + (int *) allocate_array( size, sizeof( int ) ) + +#define reallocate_integer_array(array,size) \ + (int *) reallocate_array( (void *) array, size, sizeof( int ) ) + +#define allocate_int_ptr_array(size) \ + (int **) allocate_array( size, sizeof( int * ) ) + +#define allocate_char_ptr_array(size) \ + (char **) allocate_array( size, sizeof( char * ) ) + +#define allocate_dfaacc_union(size) \ + (union dfaacc_union *) \ + allocate_array( size, sizeof( union dfaacc_union ) ) + +#define reallocate_int_ptr_array(array,size) \ + (int **) reallocate_array( (void *) array, size, sizeof( int * ) ) + +#define reallocate_char_ptr_array(array,size) \ + (char **) reallocate_array( (void *) array, size, sizeof( char * ) ) + +#define reallocate_dfaacc_union(array, size) \ + (union dfaacc_union *) \ + reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) ) + +#define allocate_character_array(size) \ + (Char *) allocate_array( size, sizeof( Char ) ) + +#define reallocate_character_array(array,size) \ + (Char *) reallocate_array( (void *) array, size, sizeof( Char ) ) + +#if 0 /* JRW this might couse truuble... but not for IOC usage */ +/* used to communicate between scanner and parser. The type should really + * be YYSTYPE, but we can't easily get our hands on it. + */ +#ifdef __alpha /* inconsistency with parse.y, line 57... on Alpha */ +extern long yylval; +#else +extern int yylval; +#endif +#endif + + +/* external functions that are cross-referenced among the flex source files */ + + +/* from file ccl.c */ + +extern void ccladd (int, int); /* Add a single character to a ccl */ +extern int cclinit (void); /* make an empty ccl */ +extern void cclnegate (int); /* negate a ccl */ + +/* list the members of a set of characters in CCL form */ +extern void list_character_set (FILE*, int[]); + + +/* from file dfa.c */ + +/* increase the maximum number of dfas */ +extern void increase_max_dfas (void); + +extern void ntod (void); /* convert a ndfa to a dfa */ + + +/* from file ecs.c */ + +/* convert character classes to set of equivalence classes */ +extern void ccl2ecl (void); + +/* associate equivalence class numbers with class members */ +extern int cre8ecs (int[], int[], int); + +/* associate equivalence class numbers using %t table */ +extern int ecs_from_xlation (int[]); + +/* update equivalence classes based on character class transitions */ +extern void mkeccl (Char[], int, int[], int[], int, int); + +/* create equivalence class for single character */ +extern void mkechar (int, int[], int[]); + + +/* from file gen.c */ + +extern void make_tables (void); /* generate transition tables */ + + +/* from file main.c */ + +extern void flexend (int) NORETURN; + + +/* from file misc.c */ + +/* write out the actions from the temporary file to lex.yy.c */ +extern void action_out (void); + +/* true if a string is all lower case */ +extern int all_lower (Char *); + +/* true if a string is all upper case */ +extern int all_upper (Char *); + +/* bubble sort an integer array */ +extern void bubble (int [], int); + +/* shell sort a character array */ +extern void cshell (Char [], int, int); + +extern void dataend (void); /* finish up a block of data declarations */ + +/* report an error message and terminate */ +extern void flexerror (char[]) NORETURN; + +/* report a fatal error message and terminate */ +extern void flexfatal (char[]); + +/* report an error message formatted with one integer argument */ +extern void lerrif (char[], int); + +/* report an error message formatted with one string argument */ +extern void lerrsf (char[], char[]); + +/* spit out a "# line" statement */ +extern void line_directive_out (FILE*); + +/* generate a data statment for a two-dimensional array */ +extern void mk2data (int); + +extern void mkdata (int); /* generate a data statement */ + +/* return the integer represented by a string of digits */ +extern int myctoi (Char []); + +/* write out one section of the skeleton file */ +extern void skelout (void); + +/* output a yy_trans_info structure */ +extern void transition_struct_out (int, int); + + +/* from file nfa.c */ + +/* add an accepting state to a machine */ +extern void add_accept (int, int); + +/* make a given number of copies of a singleton machine */ +extern int copysingl (int, int); + +/* debugging routine to write out an nfa */ +extern void dumpnfa (int); + +/* finish up the processing for a rule */ +extern void finish_rule (int, int, int, int); + +/* connect two machines together */ +extern int link_machines (int, int); + +/* mark each "beginning" state in a machine as being a "normal" (i.e., + * not trailing context associated) state + */ +extern void mark_beginning_as_normal (int); + +/* make a machine that branches to two machines */ +extern int mkbranch (int, int); + +extern int mkclos (int); /* convert a machine into a closure */ +extern int mkopt (int); /* make a machine optional */ + +/* make a machine that matches either one of two machines */ +extern int mkor (int, int); + +/* convert a machine into a positive closure */ +extern int mkposcl (int); + +extern int mkrep (int, int, int); /* make a replicated machine */ + +/* create a state with a transition on a given symbol */ +extern int mkstate (int); + +extern void new_rule (void); /* initialize for a new rule */ + + +/* from file parse.y */ + +/* write out a message formatted with one string, pinpointing its location */ +extern void format_pinpoint_message (char[], char[]); + +/* write out a message, pinpointing its location */ +extern void pinpoint_message (char[]); + +extern void synerr (char []); /* report a syntax error */ +/* extern int yyparse ();*/ /* the YACC parser */ + + +/* from file scan.l */ + +extern int flexscan (); /* the Flex-generated scanner for flex */ + +/* open the given file (if NULL, stdin) for scanning */ +extern void set_input_file (char*); + +extern int yywrap (); /* wrapup a file in the lexical analyzer */ + + +/* from file sym.c */ + +/* save the text of a character class */ +extern void cclinstal (Char [], int); + +/* lookup the number associated with character class */ +extern int ccllookup (Char []); + +extern void ndinstal (char[], Char[]); /* install a name definition */ +extern void scinstal (char[], int); /* make a start condition */ + +/* lookup the number associated with a start condition */ +extern int sclookup (char[]); + + +/* from file tblcmp.c */ + +/* build table entries for dfa state */ +extern void bldtbl (int[], int, int, int, int); + +extern void cmptmps (void); /* compress template table entries */ +extern void inittbl (void); /* initialize transition tables */ +extern void mkdeftbl (void); /* make the default, "jam" table entries */ + +/* create table entries for a state (or state fragment) which has + * only one out-transition */ +extern void mk1tbl (int, int, int, int); + +/* place a state into full speed transition table */ +extern void place_state (int*, int, int); + +/* save states with only one out-transition to be processed later */ +extern void stack1 (int, int, int, int); + + +/* from file yylex.c */ + +extern int yylex (); + + +/* The Unix kernel calls used here */ + +extern int read (int, char*, int); +#ifndef _WIN32 +extern int unlink (char*); +#endif +extern int write (int, char*, int); + + +#endif /* INC_flexdef_H */ + diff --git a/modules/libcom/src/flex/flexdoc.html b/modules/libcom/src/flex/flexdoc.html new file mode 100644 index 000000000..30ed69521 --- /dev/null +++ b/modules/libcom/src/flex/flexdoc.html @@ -0,0 +1,1881 @@ + + +
+
+
+
+

NAME

+     flex - fast lexical analyzer generator
+
+
+
+

SYNOPSIS

+     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]
+
+
+
+

DESCRIPTION

+     flex is a  tool  for  generating  scanners:  programs  which
+     recognized  lexical  patterns in text.  flex reads the given
+     input files, or its standard input  if  no  file  names  are
+     given,  for  a  description  of  a scanner to generate.  The
+     description is in the form of pairs of  regular  expressions
+     and  C  code,  called  rules.  flex  generates as output a C
+     source file, lex.yy.c, which defines a routine yylex(). This
+     file is compiled and linked with the -lfl library to produce
+     an executable.  When the executable is run, it analyzes  its
+     input  for occurrences of the regular expressions.  Whenever
+     it finds one, it executes the corresponding C code.
+
+
+
+

SOME SIMPLE EXAMPLES

+     First some simple examples to get the flavor of how one uses
+     flex.  The  following  flex  input specifies a scanner which
+     whenever it encounters the string "username" will replace it
+     with the user's login name:
+
+         %%
+         username    printf( "%s", getlogin() );
+
+     By default, any text not matched by a flex scanner is copied
+     to  the output, so the net effect of this scanner is to copy
+     its input file to its output with each occurrence of  "user-
+     name"  expanded.   In  this  input,  there is just one rule.
+     "username" is the pattern and the "printf"  is  the  action.
+     The "%%" marks the beginning of the rules.
+
+     Here's another simple example:
+
+             int num_lines = 0, num_chars = 0;
+
+         %%
+         \n    ++num_lines; ++num_chars;
+         .     ++num_chars;
+
+         %%
+         main()
+             {
+             yylex();
+             printf( "# of lines = %d, # of chars = %d\n",
+                     num_lines, num_chars );
+             }
+
+     This scanner counts the number of characters and the  number
+     of  lines in its input (it produces no output other than the
+     final report on the counts).  The first  line  declares  two
+     globals,  "num_lines"  and "num_chars", which are accessible
+     both inside yylex() and in the main() routine declared after
+     the  second  "%%".  There are two rules, one which matches a
+     newline ("\n") and increments both the line  count  and  the
+     character  count,  and one which matches any character other
+     than a newline (indicated by the "." regular expression).
+
+     A somewhat more complicated example:
+
+         /* scanner for a toy Pascal-like language */
+
+         %{
+         /* need this for the call to atof() below */
+         #include <math.h>
+         %}
+
+         DIGIT    [0-9]
+         ID       [a-z][a-z0-9]*
+
+         %%
+
+         {DIGIT}+    {
+                     printf( "An integer: %s (%d)\n", yytext,
+                             atoi( yytext ) );
+                     }
+
+         {DIGIT}+"."{DIGIT}*        {
+                     printf( "A float: %s (%g)\n", yytext,
+                             atof( yytext ) );
+                     }
+
+         if|then|begin|end|procedure|function        {
+                     printf( "A keyword: %s\n", yytext );
+                     }
+
+         {ID}        printf( "An identifier: %s\n", yytext );
+
+         "+"|"-"|"*"|"/"   printf( "An operator: %s\n", yytext );
+
+         "{"[^}\n]*"}"     /* eat up one-line comments */
+
+         [ \t\n]+          /* eat up whitespace */
+
+         .           printf( "Unrecognized character: %s\n", yytext );
+
+         %%
+
+         main( argc, argv )
+         int argc;
+         char **argv;
+             {
+             ++argv, --argc;  /* skip over program name */
+             if ( argc > 0 )
+                     yyin = fopen( argv[0], "r" );
+             else
+                     yyin = stdin;
+
+             yylex();
+             }
+
+     This is the beginnings of a simple scanner  for  a  language
+     like  Pascal.   It  identifies different types of tokens and
+     reports on what it has seen.
+
+     The details of this example will be explained in the follow-
+     ing sections.
+
+
+
+

FORMAT OF THE INPUT FILE

+     The flex input file consists of three sections, separated by
+     a line with just %% in it:
+
+         definitions
+         %%
+         rules
+         %%
+         user code
+
+     The definitions section contains declarations of simple name
+     definitions  to  simplify  the  scanner  specification,  and
+     declarations of start conditions, which are explained  in  a
+     later section.
+
+     Name definitions have the form:
+
+         name definition
+
+     The "name" is a word beginning with a letter  or  an  under-
+     score  ('_')  followed by zero or more letters, digits, '_',
+     or '-' (dash).  The definition is  taken  to  begin  at  the
+     first  non-white-space character following the name and con-
+     tinuing to the end of the line.  The definition  can  subse-
+     quently  be referred to using "{name}", which will expand to
+     "(definition)".  For example,
+
+         DIGIT    [0-9]
+         ID       [a-z][a-z0-9]*
+
+     defines "DIGIT" to be a regular expression which  matches  a
+     single  digit,  and  "ID"  to  be a regular expression which
+     matches a letter followed by zero-or-more letters-or-digits.
+     A subsequent reference to
+
+         {DIGIT}+"."{DIGIT}*
+
+     is identical to
+
+         ([0-9])+"."([0-9])*
+
+     and matches one-or-more digits followed by a '.' followed by
+     zero-or-more digits.
+
+     The rules section of the flex input  contains  a  series  of
+     rules of the form:
+
+         pattern   action
+
+     where the pattern must be unindented  and  the  action  must
+     begin on the same line.
+
+     See below for a further description of patterns and actions.
+
+     Finally, the user code section is simply copied to  lex.yy.c
+     verbatim.   It  is used for companion routines which call or
+     are called by the scanner.  The presence of this section  is
+     optional;  if it is missing, the second %% in the input file
+     may be skipped, too.
+
+     In the definitions and rules sections, any indented text  or
+     text  enclosed in %{ and %} is copied verbatim to the output
+     (with the %{}'s removed).  The %{}'s must appear  unindented
+     on lines by themselves.
+
+     In the rules section, any indented  or  %{}  text  appearing
+     before the first rule may be used to declare variables which
+     are local to the scanning routine and  (after  the  declara-
+     tions)  code  which  is to be executed whenever the scanning
+     routine is entered.  Other indented or %{} text in the  rule
+     section  is  still  copied to the output, but its meaning is
+     not well-defined and it may well cause  compile-time  errors
+     (this feature is present for POSIX compliance; see below for
+     other such features).
+
+     In the definitions section, an unindented comment  (i.e.,  a
+     line  beginning  with  "/*")  is also copied verbatim to the
+     output up to the next "*/".  Also, any line in  the  defini-
+     tions  section  beginning  with  '#' is ignored, though this
+     style of comment is  deprecated  and  may  go  away  in  the
+     future.
+
+
+
+

PATTERNS

+     The patterns in the input are written using an extended  set
+     of regular expressions.  These are:
+
+         x          match the character 'x'
+         .          any character except newline
+         [xyz]      a "character class"; in this case, the pattern
+                      matches either an 'x', a 'y', or a 'z'
+         [abj-oZ]   a "character class" with a range in it; matches
+                      an 'a', a 'b', any letter from 'j' through 'o',
+                      or a 'Z'
+         [^A-Z]     a "negated character class", i.e., any character
+                      but those in the class.  In this case, any
+                      character EXCEPT an uppercase letter.
+         [^A-Z\n]   any character EXCEPT an uppercase letter or
+                      a newline
+         r*         zero or more r's, where r is any regular expression
+         r+         one or more r's
+         r?         zero or one r's (that is, "an optional r")
+         r{2,5}     anywhere from two to five r's
+         r{2,}      two or more r's
+         r{4}       exactly 4 r's
+         {name}     the expansion of the "name" definition
+                    (see above)
+         "[xyz]\"foo"
+                    the literal string: [xyz]"foo
+         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+                      then the ANSI-C interpretation of \x.
+                      Otherwise, a literal 'X' (used to escape
+                      operators such as '*')
+         \123       the character with octal value 123
+         \x2a       the character with hexadecimal value 2a
+         (r)        match an r; parentheses are used to override
+                      precedence (see below)
+
+
+         rs         the regular expression r followed by the
+                      regular expression s; called "concatenation"
+
+
+         r|s        either an r or an s
+
+
+         r/s        an r but only if it is followed by an s.  The
+                      s is not part of the matched text.  This type
+                      of pattern is called as "trailing context".
+         ^r         an r, but only at the beginning of a line
+         r$         an r, but only at the end of a line.  Equivalent
+                      to "r/\n".
+
+
+         <s>r       an r, but only in start condition s (see
+                    below for discussion of start conditions)
+         <s1,s2,s3>r
+                    same, but in any of start conditions s1,
+                    s2, or s3
+
+         <<EOF>>    an end-of-file
+         <s1,s2><<EOF>>
+                    an end-of-file when in start condition s1 or s2
+
+     The regular expressions listed above are  grouped  according
+     to  precedence, from highest precedence at the top to lowest
+     at the bottom.   Those  grouped  together  have  equal  pre-
+     cedence.  For example,
+
+         foo|bar*
+
+     is the same as
+
+         (foo)|(ba(r*))
+
+     since the '*' operator has higher precedence than concatena-
+     tion, and concatenation higher than alternation ('|').  This
+     pattern therefore matches either the  string  "foo"  or  the
+     string "ba" followed by zero-or-more r's.  To match "foo" or
+     zero-or-more "bar"'s, use:
+
+         foo|(bar)*
+
+     and to match zero-or-more "foo"'s-or-"bar"'s:
+
+         (foo|bar)*
+
+
+     Some notes on patterns:
+
+     -    A negated character class such as the example  "[^A-Z]"
+          above   will   match  a  newline  unless  "\n"  (or  an
+          equivalent escape sequence) is one  of  the  characters
+          explicitly  present  in  the  negated  character  class
+          (e.g., "[^A-Z\n]").  This is unlike how many other reg-
+          ular  expression tools treat negated character classes,
+          but unfortunately  the  inconsistency  is  historically
+          entrenched.   Matching  newlines  means  that a pattern
+          like [^"]* can match an entire input  (overflowing  the
+          scanner's input buffer) unless there's another quote in
+          the input.
+
+     -    A rule can have at most one instance of  trailing  con-
+          text (the '/' operator or the '$' operator).  The start
+          condition, '^', and "<<EOF>>" patterns can  only  occur
+          at the beginning of a pattern, and, as well as with '/'
+          and '$', cannot be grouped inside parentheses.   A  '^'
+          which  does  not  occur at the beginning of a rule or a
+          '$' which does not occur at the end of a rule loses its
+          special  properties  and is treated as a normal charac-
+          ter.
+
+          The following are illegal:
+
+              foo/bar$
+              <sc1>foo<sc2>bar
+
+          Note  that  the  first  of  these,   can   be   written
+          "foo/bar\n".
+
+          The following will result in '$' or '^'  being  treated
+          as a normal character:
+
+              foo|(bar$)
+              foo|^bar
+
+          If what's wanted is a  "foo"  or  a  bar-followed-by-a-
+          newline,  the  following could be used (the special '|'
+          action is explained below):
+
+              foo      |
+              bar$     /* action goes here */
+
+          A similar trick will work for matching a foo or a  bar-
+          at-the-beginning-of-a-line.
+
+
+
+

HOW THE INPUT IS MATCHED

+     When the generated scanner is run,  it  analyzes  its  input
+     looking  for strings which match any of its patterns.  If it
+     finds more than one match, it takes  the  one  matching  the
+     most  text  (for  trailing  context rules, this includes the
+     length of the trailing part, even though  it  will  then  be
+     returned  to the input).  If it finds two or more matches of
+     the same length, the rule listed first  in  the  flex  input
+     file is chosen.
+
+     Once the match is determined, the text corresponding to  the
+     match  (called  the  token)  is made available in the global
+     character pointer yytext,  and  its  length  in  the  global
+     integer yyleng. The action corresponding to the matched pat-
+     tern is  then  executed  (a  more  detailed  description  of
+     actions  follows),  and  then the remaining input is scanned
+     for another match.
+
+     If no match is found, then the default rule is executed: the
+     next character in the input is considered matched and copied
+     to the standard output.  Thus, the simplest legal flex input
+     is:
+
+         %%
+
+     which generates a scanner that simply copies its input  (one
+     character at a time) to its output.
+
+
+
+

ACTIONS

+     Each pattern in a rule has a corresponding action, which can
+     be any arbitrary C statement.  The pattern ends at the first
+     non-escaped whitespace character; the remainder of the  line
+     is  its  action.  If the action is empty, then when the pat-
+     tern is matched the input token is  simply  discarded.   For
+     example,  here  is  the  specification  for  a program which
+     deletes all occurrences of "zap me" from its input:
+
+         %%
+         "zap me"
+
+     (It will copy all other characters in the input to the  out-
+     put since they will be matched by the default rule.)
+
+     Here is a program which compresses multiple blanks and  tabs
+     down  to a single blank, and throws away whitespace found at
+     the end of a line:
+
+         %%
+         [ \t]+        putchar( ' ' );
+         [ \t]+$       /* ignore this token */
+
+
+     If the action contains a '{', then the action spans till the
+     balancing  '}'  is  found, and the action may cross multiple
+     lines.  flex knows about C strings and comments and won't be
+     fooled  by braces found within them, but also allows actions
+     to begin with %{ and will consider the action to be all  the
+     text up to the next %} (regardless of ordinary braces inside
+     the action).
+
+     An action consisting solely of a vertical  bar  ('|')  means
+     "same  as  the  action for the next rule."  See below for an
+     illustration.
+
+     Actions can  include  arbitrary  C  code,  including  return
+     statements  to  return  a  value  to whatever routine called
+     yylex(). Each time yylex() is called it continues processing
+     tokens  from  where it last left off until it either reaches
+     the end of the file or executes a return.  Once  it  reaches
+     an end-of-file, however, then any subsequent call to yylex()
+     will simply immediately return, unless yyrestart() is  first
+     called (see below).
+
+     Actions are not allowed to modify yytext or yyleng.
+
+     There are a  number  of  special  directives  which  can  be
+     included within an action:
+
+     -    ECHO copies yytext to the scanner's output.
+
+     -    BEGIN followed by the name of a start condition  places
+          the  scanner  in the corresponding start condition (see
+          below).
+
+     -    REJECT directs the scanner to proceed on to the "second
+          best"  rule which matched the input (or a prefix of the
+          input).  The rule is chosen as described above in  "How
+          the  Input  is  Matched",  and yytext and yyleng set up
+          appropriately.  It may either be one which  matched  as
+          much  text as the originally chosen rule but came later
+          in the flex input file, or one which matched less text.
+          For example, the following will both count the words in
+          the input  and  call  the  routine  special()  whenever
+          "frob" is seen:
+
+                      int word_count = 0;
+              %%
+
+              frob        special(); REJECT;
+              [^ \t\n]+   ++word_count;
+
+          Without the REJECT, any "frob"'s in the input would not
+          be  counted  as  words, since the scanner normally exe-
+          cutes only one action per token.  Multiple REJECT's are
+          allowed,  each  one finding the next best choice to the
+          currently active rule.  For example, when the following
+          scanner  scans the token "abcd", it will write "abcdab-
+          caba" to the output:
+
+              %%
+              a        |
+              ab       |
+              abc      |
+              abcd     ECHO; REJECT;
+              .|\n     /* eat up any unmatched character */
+
+          (The first three rules share the fourth's action  since
+          they use the special '|' action.)  REJECT is a particu-
+          larly expensive feature in terms  scanner  performance;
+          if  it  is used in any of the scanner's actions it will
+          slow down all of the scanner's matching.   Furthermore,
+          REJECT  cannot  be  used with the -f or -F options (see
+          below).
+
+          Note also that unlike the other special actions, REJECT
+          is  a  branch;  code  immediately  following  it in the
+          action will not be executed.
+
+     -    yymore() tells  the  scanner  that  the  next  time  it
+          matches  a  rule,  the  corresponding  token  should be
+          appended onto the current value of yytext  rather  than
+          replacing  it.   For  example,  given  the input "mega-
+          kludge" the following will write "mega-mega-kludge"  to
+          the output:
+
+              %%
+              mega-    ECHO; yymore();
+              kludge   ECHO;
+
+          First "mega-" is matched  and  echoed  to  the  output.
+          Then  "kludge"  is matched, but the previous "mega-" is
+          still hanging around at the beginning of yytext so  the
+          ECHO  for  the "kludge" rule will actually write "mega-
+          kludge".  The presence of  yymore()  in  the  scanner's
+          action  entails  a  minor  performance  penalty  in the
+          scanner's matching speed.
+
+     -    yyless(n) returns all but the first n characters of the
+          current token back to the input stream, where they will
+          be rescanned when the scanner looks for the next match.
+          yytext  and  yyleng  are  adjusted appropriately (e.g.,
+          yyleng will now be equal to n ).  For example,  on  the
+          input  "foobar"  the  following will write out "foobar-
+          bar":
+
+              %%
+              foobar    ECHO; yyless(3);
+              [a-z]+    ECHO;
+
+          An argument of  0  to  yyless  will  cause  the  entire
+          current  input  string  to  be  scanned  again.  Unless
+          you've changed how the scanner will  subsequently  pro-
+          cess  its  input  (using BEGIN, for example), this will
+          result in an endless loop.
+
+     -    unput(c) puts the  character  c  back  onto  the  input
+          stream.   It  will  be the next character scanned.  The
+          following action will take the current token and  cause
+          it to be rescanned enclosed in parentheses.
+
+              {
+              int i;
+              unput( ')' );
+              for ( i = yyleng - 1; i >= 0; --i )
+                  unput( yytext[i] );
+              unput( '(' );
+              }
+
+          Note that since each unput() puts the  given  character
+          back at the beginning of the input stream, pushing back
+          strings must be done back-to-front.
+
+     -    input() reads the next character from the input stream.
+          For  example,  the  following  is  one  way to eat up C
+          comments:
+
+              %%
+              "/*"        {
+                          register int c;
+
+                          for ( ; ; )
+                              {
+                              while ( (c = input()) != '*' &&
+                                      c != EOF )
+                                  ;    /* eat up text of comment */
+
+                              if ( c == '*' )
+                                  {
+                                  while ( (c = input()) == '*' )
+                                      ;
+                                  if ( c == '/' )
+                                      break;    /* found the end */
+                                  }
+
+                              if ( c == EOF )
+                                  {
+                                  error( "EOF in comment" );
+                                  break;
+                                  }
+                              }
+                          }
+
+          (Note that if the scanner is compiled using  C++,  then
+          input()  is  instead referred to as yyinput(), in order
+          to avoid a name clash with the C++ stream by  the  name
+          of input.)
+
+     -    yyterminate() can be used in lieu of a return statement
+          in  an action.  It terminates the scanner and returns a
+          0 to the scanner's caller, indicating "all done".  Sub-
+          sequent  calls  to  the scanner will immediately return
+          unless preceded by a call to yyrestart()  (see  below).
+          By  default,  yyterminate() is also called when an end-
+          of-file is encountered.  It is a macro and may be rede-
+          fined.
+
+
+
+

THE GENERATED SCANNER

+     The output of flex is the file lex.yy.c, which contains  the
+     scanning  routine yylex(), a number of tables used by it for
+     matching tokens, and a number of auxiliary routines and mac-
+     ros.  By default, yylex() is declared as follows:
+
+         int yylex()
+             {
+             ... various definitions and the actions in here ...
+             }
+
+     (If your environment supports function prototypes,  then  it
+     will  be  "int  yylex(  void  )".)   This  definition may be
+     changed by redefining the "YY_DECL" macro.  For example, you
+     could use:
+
+         #undef YY_DECL
+         #define YY_DECL float lexscan( a, b ) float a, b;
+
+     to give the scanning routine the name lexscan,  returning  a
+     float, and taking two floats as arguments.  Note that if you
+     give  arguments  to  the  scanning  routine  using  a   K&R-
+     style/non-prototyped  function  declaration,  you  must ter-
+     minate the definition with a semi-colon (;).
+
+     Whenever yylex() is called, it scans tokens from the  global
+     input  file  yyin  (which  defaults to stdin).  It continues
+     until it either reaches an end-of-file (at  which  point  it
+     returns the value 0) or one of its actions executes a return
+     statement.  In  the  former  case,  when  called  again  the
+     scanner will immediately return unless yyrestart() is called
+     to point yyin at the new input file.   (  yyrestart()  takes
+     one  argument, a FILE * pointer.)  In the latter case (i.e.,
+     when an action executes a return), the scanner may  then  be
+     called again and it will resume scanning where it left off.
+
+     By default (and for purposes  of  efficiency),  the  scanner
+     uses  block-reads  rather  than  simple getc() calls to read
+     characters from yyin. The nature of how it  gets  its  input
+     can   be   controlled  by  redefining  the  YY_INPUT  macro.
+     YY_INPUT's           calling           sequence           is
+     "YY_INPUT(buf,result,max_size)".   Its action is to place up
+     to max_size characters in the character array buf and return
+     in  the integer variable result either the number of charac-
+     ters read or the constant YY_NULL (0  on  Unix  systems)  to
+     indicate  EOF.   The  default YY_INPUT reads from the global
+     file-pointer "yyin".
+
+     A sample redefinition of YY_INPUT (in the  definitions  sec-
+     tion of the input file):
+
+         %{
+         #undef YY_INPUT
+         #define YY_INPUT(buf,result,max_size) \
+             { \
+             int c = getchar(); \
+             result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
+             }
+         %}
+
+     This definition will change the input  processing  to  occur
+     one character at a time.
+
+     You also can add in things like keeping track of  the  input
+     line  number  this  way; but don't expect your scanner to go
+     very fast.
+
+     When the scanner receives  an  end-of-file  indication  from
+     YY_INPUT, it then checks the yywrap() function.  If yywrap()
+     returns false (zero), then it is assumed that  the  function
+     has  gone  ahead  and  set up yyin to point to another input
+     file, and scanning continues.   If  it  returns  true  (non-
+     zero),  then  the  scanner  terminates,  returning  0 to its
+     caller.
+
+     The default yywrap() always returns 1.  Presently, to  rede-
+     fine  it  you must first "#undef yywrap", as it is currently
+     implemented as a macro.  As indicated by the hedging in  the
+     previous  sentence,  it may be changed to a true function in
+     the near future.
+
+     The scanner writes its  ECHO  output  to  the  yyout  global
+     (default, stdout), which may be redefined by the user simply
+     by assigning it to some other FILE pointer.
+
+
+
+

START CONDITIONS

+     flex  provides  a  mechanism  for  conditionally  activating
+     rules.   Any rule whose pattern is prefixed with "<sc>" will
+     only be active when the scanner is in  the  start  condition
+     named "sc".  For example,
+
+         <STRING>[^"]*        { /* eat up the string body ... */
+                     ...
+                     }
+
+     will be active only when the  scanner  is  in  the  "STRING"
+     start condition, and
+
+         <INITIAL,STRING,QUOTE>\.        { /* handle an escape ... */
+                     ...
+                     }
+
+     will be active only when  the  current  start  condition  is
+     either "INITIAL", "STRING", or "QUOTE".
+
+     Start conditions are declared  in  the  definitions  (first)
+     section  of  the input using unindented lines beginning with
+     either %s or %x followed by a list  of  names.   The  former
+     declares  inclusive  start  conditions, the latter exclusive
+     start conditions.  A start condition is activated using  the
+     BEGIN  action.   Until  the  next  BEGIN action is executed,
+     rules with the given start  condition  will  be  active  and
+     rules  with other start conditions will be inactive.  If the
+     start condition is inclusive, then rules with no start  con-
+     ditions  at  all  will  also be active.  If it is exclusive,
+     then only rules qualified with the start condition  will  be
+     active.   A  set  of  rules contingent on the same exclusive
+     start condition describe a scanner which is  independent  of
+     any  of the other rules in the flex input.  Because of this,
+     exclusive start conditions make it easy  to  specify  "mini-
+     scanners"  which scan portions of the input that are syntac-
+     tically different from the rest (e.g., comments).
+
+     If the distinction between  inclusive  and  exclusive  start
+     conditions  is still a little vague, here's a simple example
+     illustrating the connection between the  two.   The  set  of
+     rules:
+
+         %s example
+         %%
+         <example>foo           /* do something */
+
+     is equivalent to
+
+         %x example
+         %%
+         <INITIAL,example>foo   /* do something */
+
+
+     The default rule (to ECHO any unmatched  character)  remains
+     active in start conditions.
+
+     BEGIN(0) returns to the original state where only the  rules
+     with no start conditions are active.  This state can also be
+     referred   to   as   the   start-condition   "INITIAL",   so
+     BEGIN(INITIAL)  is  equivalent to BEGIN(0). (The parentheses
+     around the start condition name are  not  required  but  are
+     considered good style.)
+
+     BEGIN actions can also be given  as  indented  code  at  the
+     beginning  of the rules section.  For example, the following
+     will cause the scanner to enter the "SPECIAL"  start  condi-
+     tion  whenever  yylex()  is  called  and the global variable
+     enter_special is true:
+
+                 int enter_special;
+
+         %x SPECIAL
+         %%
+                 if ( enter_special )
+                     BEGIN(SPECIAL);
+
+         <SPECIAL>blahblahblah
+         ...more rules follow...
+
+
+
+     To illustrate the  uses  of  start  conditions,  here  is  a
+     scanner  which  provides  two different interpretations of a
+     string like "123.456".  By default it will treat  it  as  as
+     three  tokens,  the  integer  "123",  a  dot  ('.'), and the
+     integer "456".  But if the string is preceded earlier in the
+     line  by  the  string  "expect-floats" it will treat it as a
+     single token, the floating-point number 123.456:
+
+         %{
+         #include <math.h>
+         %}
+         %s expect
+
+         %%
+         expect-floats        BEGIN(expect);
+
+         <expect>[0-9]+"."[0-9]+      {
+                     printf( "found a float, = %f\n",
+                             atof( yytext ) );
+                     }
+         <expect>\n           {
+                     /* that's the end of the line, so
+                      * we need another "expect-number"
+                      * before we'll recognize any more
+                      * numbers
+                      */
+                     BEGIN(INITIAL);
+                     }
+
+         [0-9]+      {
+                     printf( "found an integer, = %d\n",
+                             atoi( yytext ) );
+                     }
+
+         "."         printf( "found a dot\n" );
+
+     Here is a scanner which recognizes (and discards) C comments
+     while maintaining a count of the current input line.
+
+         %x comment
+         %%
+                 int line_num = 1;
+
+         "/*"         BEGIN(comment);
+
+         <comment>[^*\n]*        /* eat anything that's not a '*' */
+         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
+         <comment>\n             ++line_num;
+         <comment>"*"+"/"        BEGIN(INITIAL);
+
+     Note that start-conditions names are really  integer  values
+     and  can  be  stored  as  such.   Thus,  the  above could be
+     extended in the following fashion:
+
+         %x comment foo
+         %%
+                 int line_num = 1;
+                 int comment_caller;
+
+         "/*"         {
+                      comment_caller = INITIAL;
+                      BEGIN(comment);
+                      }
+
+         ...
+
+         <foo>"/*"    {
+                      comment_caller = foo;
+                      BEGIN(comment);
+                      }
+
+         <comment>[^*\n]*        /* eat anything that's not a '*' */
+         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
+         <comment>\n             ++line_num;
+         <comment>"*"+"/"        BEGIN(comment_caller);
+
+     One can then implement a "stack" of start  conditions  using
+     an  array  of integers.  (It is likely that such stacks will
+     become a full-fledged flex feature in  the  future.)   Note,
+     though,  that  start  conditions do not have their own name-
+     space; %s's and %x's declare names in the  same  fashion  as
+     #define's.
+
+
+
+

MULTIPLE INPUT BUFFERS

+     Some scanners (such as those which support "include"  files)
+     require   reading  from  several  input  streams.   As  flex
+     scanners do a large amount of buffering, one cannot  control
+     where  the  next input will be read from by simply writing a
+     YY_INPUT  which  is  sensitive  to  the  scanning   context.
+     YY_INPUT  is only called when the scanner reaches the end of
+     its buffer, which may be a long time after scanning a state-
+     ment such as an "include" which requires switching the input
+     source.
+
+     To negotiate  these  sorts  of  problems,  flex  provides  a
+     mechanism  for creating and switching between multiple input
+     buffers.  An input buffer is created by using:
+
+         YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+
+     which takes a FILE pointer and a size and creates  a  buffer
+     associated with the given file and large enough to hold size
+     characters (when in doubt, use YY_BUF_SIZE  for  the  size).
+     It  returns  a  YY_BUFFER_STATE  handle,  which  may then be
+     passed to other routines:
+
+         void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+
+     switches the scanner's input  buffer  so  subsequent  tokens
+     will  come  from new_buffer. Note that yy_switch_to_buffer()
+     may be used by yywrap() to  sets  things  up  for  continued
+     scanning, instead of opening a new file and pointing yyin at
+     it.
+
+         void yy_delete_buffer( YY_BUFFER_STATE buffer )
+
+     is used to reclaim the storage associated with a buffer.
+
+     yy_new_buffer() is an alias for yy_create_buffer(), provided
+     for  compatibility  with  the  C++ use of new and delete for
+     creating and destroying dynamic objects.
+
+     Finally,   the    YY_CURRENT_BUFFER    macro    returns    a
+     YY_BUFFER_STATE handle to the current buffer.
+
+     Here is an example of using these  features  for  writing  a
+     scanner  which expands include files (the <<EOF>> feature is
+     discussed below):
+
+         /* the "incl" state is used for picking up the name
+          * of an include file
+          */
+         %x incl
+
+         %{
+         #define MAX_INCLUDE_DEPTH 10
+         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+         int include_stack_ptr = 0;
+         %}
+
+         %%
+         include             BEGIN(incl);
+
+         [a-z]+              ECHO;
+         [^a-z\n]*\n?        ECHO;
+
+         <incl>[ \t]*      /* eat the whitespace */
+         <incl>[^ \t\n]+   { /* got the include file name */
+                 if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
+                     {
+                     fprintf( stderr, "Includes nested too deeply" );
+                     exit( 1 );
+                     }
+
+                 include_stack[include_stack_ptr++] =
+                     YY_CURRENT_BUFFER;
+
+                 yyin = fopen( yytext, "r" );
+
+                 if ( ! yyin )
+                     error( ... );
+
+                 yy_switch_to_buffer(
+                     yy_create_buffer( yyin, YY_BUF_SIZE ) );
+
+                 BEGIN(INITIAL);
+                 }
+
+         <<EOF>> {
+                 if ( --include_stack_ptr < 0 )
+                     {
+                     yyterminate();
+                     }
+
+                 else
+                     yy_switch_to_buffer(
+                          include_stack[include_stack_ptr] );
+                 }
+
+
+
+
+

END-OF-FILE RULES

+     The special rule "<<EOF>>" indicates actions which are to be
+     taken  when  an  end-of-file  is  encountered  and  yywrap()
+     returns non-zero (i.e., indicates no further files  to  pro-
+     cess).  The action must finish by doing one of four things:
+
+     -    the  special  YY_NEW_FILE  action,  if  yyin  has  been
+          pointed at a new file to process;
+
+     -    a return statement;
+
+     -    the special yyterminate() action;
+
+     -    or,    switching    to    a    new     buffer     using
+          yy_switch_to_buffer() as shown in the example above.
+
+     <<EOF>> rules may not be used with other patterns; they  may
+     only  be  qualified  with a list of start conditions.  If an
+     unqualified <<EOF>> rule is given, it applies to  all  start
+     conditions  which  do  not already have <<EOF>> actions.  To
+     specify an <<EOF>> rule for only the  initial  start  condi-
+     tion, use
+
+         <INITIAL><<EOF>>
+
+
+     These rules are useful for  catching  things  like  unclosed
+     comments.  An example:
+
+         %x quote
+         %%
+
+         ...other rules for dealing with quotes...
+
+         <quote><<EOF>>   {
+                  error( "unterminated quote" );
+                  yyterminate();
+                  }
+         <<EOF>>  {
+                  if ( *++filelist )
+                      {
+                      yyin = fopen( *filelist, "r" );
+                      YY_NEW_FILE;
+                      }
+                  else
+                     yyterminate();
+                  }
+
+
+
+
+

MISCELLANEOUS MACROS

+     The macro YY_USER_ACTION can  be  redefined  to  provide  an
+     action  which is always executed prior to the matched rule's
+     action.  For example, it could be #define'd to call  a  rou-
+     tine to convert yytext to lower-case.
+
+     The macro YY_USER_INIT may be redefined to provide an action
+     which  is  always executed before the first scan (and before
+     the scanner's internal initializations are done).  For exam-
+     ple,  it  could  be used to call a routine to read in a data
+     table or open a logging file.
+
+     In the generated scanner, the actions are  all  gathered  in
+     one  large  switch  statement  and separated using YY_BREAK,
+     which may be redefined.  By default, it is simply a "break",
+     to  separate  each  rule's action from the following rule's.
+     Redefining  YY_BREAK  allows,  for  example,  C++  users  to
+     #define  YY_BREAK  to  do  nothing (while being very careful
+     that every rule ends with a "break" or a "return"!) to avoid
+     suffering  from unreachable statement warnings where because
+     a rule's action ends with "return", the YY_BREAK is inacces-
+     sible.
+
+
+
+

INTERFACING WITH YACC

+     One of the main uses of flex is as a companion to  the  yacc
+     parser-generator.   yacc  parsers  expect  to call a routine
+     named yylex() to find the next input token.  The routine  is
+     supposed  to  return  the  type of the next token as well as
+     putting any associated value in the global  yylval.  To  use
+     flex  with  yacc,  one  specifies  the  -d option to yacc to
+     instruct it to generate the file y.tab.h containing  defini-
+     tions  of all the %tokens appearing in the yacc input.  This
+     file is then included in the flex scanner.  For example,  if
+     one of the tokens is "TOK_NUMBER", part of the scanner might
+     look like:
+
+         %{
+         #include "y.tab.h"
+         %}
+
+         %%
+
+         [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;
+
+
+
+
+

TRANSLATION TABLE

+     In the name of POSIX compliance, flex supports a translation
+     table  for  mapping input characters into groups.  The table
+     is specified in the first  section,  and  its  format  looks
+     like:
+
+         %t
+         1        abcd
+         2        ABCDEFGHIJKLMNOPQRSTUVWXYZ
+         52       0123456789
+         6        \t\ \n
+         %t
+
+     This example specifies that the characters  'a',  'b',  'c',
+     and  'd'  are  to  all  be  lumped into group #1, upper-case
+     letters in group #2, digits in group #52, tabs, blanks,  and
+     newlines  into group #6, and no other characters will appear
+     in the patterns.  The group numbers are actually disregarded
+     by  flex;  %t  serves,  though, to lump characters together.
+     Given the above table, for example, the pattern "a(AA)*5" is
+     equivalent  to "d(ZQ)*0".  They both say, "match any charac-
+     ter in group #1, followed by zero-or-more pairs  of  charac-
+     ters from group #2, followed by a character from group #52."
+     Thus %t provides a crude  way  for  introducing  equivalence
+     classes into the scanner specification.
+
+     Note that  the  -i  option  (see  below)  coupled  with  the
+     equivalence  classes which flex automatically generates take
+     care of virtually all the instances when one might  consider
+     using %t. But what the hell, it's there if you want it.
+
+
+
+

OPTIONS

+     flex has the following options:
+
+     -b   Generate  backtracking  information  to  lex.backtrack.
+          This  is  a  list of scanner states which require back-
+          tracking and the input characters on which they do  so.
+          By adding rules one can remove backtracking states.  If
+          all backtracking states are eliminated and -f or -F  is
+          used, the generated scanner will run faster (see the -p
+          flag).  Only users who wish to squeeze every last cycle
+          out  of  their  scanners  need worry about this option.
+          (See the section on PERFORMANCE CONSIDERATIONS below.)
+
+     -c   is a do-nothing, deprecated option included  for  POSIX
+          compliance.
+
+          NOTE: in previous releases of flex -c specified  table-
+          compression  options.   This functionality is now given
+          by the -C flag.  To ease the the impact of this change,
+          when  flex encounters -c, it currently issues a warning
+          message and assumes that -C was  desired  instead.   In
+          the future this "promotion" of -c to -C will go away in
+          the name of full POSIX  compliance  (unless  the  POSIX
+          meaning is removed first).
+
+     -d   makes the generated scanner run in debug  mode.   When-
+          ever   a   pattern   is   recognized   and  the  global
+          yy_flex_debug is non-zero (which is the  default),  the
+          scanner will write to stderr a line of the form:
+
+              --accepting rule at line 53 ("the matched text")
+
+          The line number refers to the location of the  rule  in
+          the  file defining the scanner (i.e., the file that was
+          fed to flex).  Messages are  also  generated  when  the
+          scanner  backtracks,  accepts the default rule, reaches
+          the end of its input buffer (or encounters  a  NUL;  at
+          this  point,  the  two  look  the  same  as  far as the
+          scanner's concerned), or reaches an end-of-file.
+
+     -f   specifies (take your pick) full table or fast  scanner.
+          No  table compression is done.  The result is large but
+          fast.  This option is equivalent to -Cf (see below).
+
+     -i   instructs flex to generate a case-insensitive  scanner.
+          The  case  of  letters given in the flex input patterns
+          will be ignored,  and  tokens  in  the  input  will  be
+          matched  regardless of case.  The matched text given in
+          yytext will have the preserved case (i.e., it will  not
+          be folded).
+
+     -n   is another do-nothing, deprecated option included  only
+          for POSIX compliance.
+
+     -p   generates a performance report to stderr.   The  report
+          consists  of  comments  regarding  features of the flex
+          input file which will cause a loss  of  performance  in
+          the resulting scanner.  Note that the use of REJECT and
+          variable trailing context  (see  the  BUGS  section  in
+          flex(1)) entails a substantial performance penalty; use
+          of yymore(), the ^ operator, and  the  -I  flag  entail
+          minor performance penalties.
+
+     -s   causes the default rule (that unmatched  scanner  input
+          is  echoed to stdout) to be suppressed.  If the scanner
+          encounters input that does not match any of its  rules,
+          it  aborts  with  an  error.  This option is useful for
+          finding holes in a scanner's rule set.
+
+     -t   instructs flex to write the  scanner  it  generates  to
+          standard output instead of lex.yy.c.
+
+     -v   specifies that flex should write to stderr a summary of
+          statistics regarding the scanner it generates.  Most of
+          the statistics are meaningless to the casual flex user,
+          but  the  first  line  identifies  the version of flex,
+          which is useful for figuring out where you  stand  with
+          respect  to  patches and new releases, and the next two
+          lines give the date when the scanner was created and  a
+          summary of the flags which were in effect.
+
+     -F   specifies that the fast  scanner  table  representation
+          should  be  used.  This representation is about as fast
+          as the full table representation  (-f),  and  for  some
+          sets  of patterns will be considerably smaller (and for
+          others, larger).  In general, if the pattern  set  con-
+          tains  both  "keywords"  and  a catch-all, "identifier"
+          rule, such as in the set:
+
+              "case"    return TOK_CASE;
+              "switch"  return TOK_SWITCH;
+              ...
+              "default" return TOK_DEFAULT;
+              [a-z]+    return TOK_ID;
+
+          then you're better off using the full table representa-
+          tion.  If only the "identifier" rule is present and you
+          then use a hash table or some such to detect  the  key-
+          words, you're better off using -F.
+
+          This option is equivalent to -CF (see below).
+
+     -I   instructs flex  to  generate  an  interactive  scanner.
+          Normally,  scanners generated by flex always look ahead
+          one character before deciding  that  a  rule  has  been
+          matched.   At  the cost of some scanning overhead, flex
+          will generate a scanner which  only  looks  ahead  when
+          needed.   Such  scanners are called interactive because
+          if you want to write a scanner for an interactive  sys-
+          tem such as a command shell, you will probably want the
+          user's input to  be  terminated  with  a  newline,  and
+          without  -I  the  user will have to type a character in
+          addition to the newline in order to  have  the  newline
+          recognized.  This leads to dreadful interactive perfor-
+          mance.
+
+          If all this seems  to  confusing,  here's  the  general
+          rule:  if  a  human  will  be  typing  in input to your
+          scanner, use -I, otherwise don't;  if  you  don't  care
+          about   squeezing  the  utmost  performance  from  your
+          scanner and you don't  want  to  make  any  assumptions
+          about the input to your scanner, use -I.
+
+          Note, -I cannot be used in  conjunction  with  full  or
+          fast tables, i.e., the -f, -F, -Cf, or -CF flags.
+
+     -L   instructs  flex  not  to  generate  #line   directives.
+          Without this option, flex peppers the generated scanner
+          with #line directives so error messages in the  actions
+          will  be correctly located with respect to the original
+          flex input file, and not to the fairly meaningless line
+          numbers  of  lex.yy.c.  (Unfortunately  flex  does  not
+          presently generate the necessary directives to  "retar-
+          get" the line numbers for those parts of lex.yy.c which
+          it generated.  So if there is an error in the generated
+          code, a meaningless line number is reported.)
+
+     -T   makes flex run in trace mode.  It will generate  a  lot
+          of  messages to stdout concerning the form of the input
+          and the resultant non-deterministic  and  deterministic
+          finite  automata.   This  option  is  mostly for use in
+          maintaining flex.
+
+     -8   instructs flex to generate an 8-bit scanner, i.e.,  one
+          which  can  recognize 8-bit characters.  On some sites,
+          flex is installed with this option as the default.   On
+          others,  the default is 7-bit characters.  To see which
+          is  the  case,  check  the  verbose  (-v)  output   for
+          "equivalence  classes  created".  If the denominator of
+          the number shown is 128, then by default flex  is  gen-
+          erating  7-bit  characters.   If  it  is  256, then the
+          default is 8-bit characters and  the  -8  flag  is  not
+          required  (but  may  be a good idea to keep the scanner
+          specification portable).  Feeding a 7-bit scanner 8-bit
+          characters  will  result in infinite loops, bus errors,
+          or other such fireworks, so  when  in  doubt,  use  the
+          flag.  Note that if equivalence classes are used, 8-bit
+          scanners take only slightly more table space than 7-bit
+          scanners  (128  bytes,  to  be  exact);  if equivalence
+          classes are not used, however, then the tables may grow
+          up to twice their 7-bit size.
+
+     -C[efmF]
+          controls the degree of table compression.
+          -Ce directs  flex  to  construct  equivalence  classes,
+          i.e.,  sets  of characters which have identical lexical
+          properties (for example,  if  the  only  appearance  of
+          digits  in  the  flex  input  is in the character class
+          "[0-9]" then the digits '0', '1', ..., '9' will all  be
+          put   in  the  same  equivalence  class).   Equivalence
+          classes usually give dramatic reductions in  the  final
+          table/object file sizes (typically a factor of 2-5) and
+          are pretty cheap performance-wise  (one  array  look-up
+          per character scanned).
+
+          -Cf specifies that the full scanner  tables  should  be
+          generated - flex should not compress the tables by tak-
+          ing advantages of similar transition functions for dif-
+          ferent states.
+
+          -CF specifies that the alternate fast scanner represen-
+          tation  (described  above  under the -F flag) should be
+          used.
+
+          -Cm directs flex to construct meta-equivalence classes,
+          which  are  sets of equivalence classes (or characters,
+          if equivalence classes are not  being  used)  that  are
+          commonly  used  together.  Meta-equivalence classes are
+          often a big win when using compressed tables, but  they
+          have  a  moderate  performance  impact (one or two "if"
+          tests and one array look-up per character scanned).
+
+          A lone -C specifies that the scanner tables  should  be
+          compressed  but  neither  equivalence classes nor meta-
+          equivalence classes should be used.
+
+          The options -Cf or  -CF  and  -Cm  do  not  make  sense
+          together - there is no opportunity for meta-equivalence
+          classes if the table is not being  compressed.   Other-
+          wise the options may be freely mixed.
+
+          The default setting is -Cem, which specifies that  flex
+          should   generate   equivalence   classes   and   meta-
+          equivalence classes.  This setting provides the highest
+          degree   of  table  compression.   You  can  trade  off
+          faster-executing scanners at the cost of larger  tables
+          with the following generally being true:
+
+              slowest & smallest
+                    -Cem
+                    -Cm
+                    -Ce
+                    -C
+                    -C{f,F}e
+                    -C{f,F}
+              fastest & largest
+
+          Note that scanners with the smallest tables are usually
+          generated and compiled the quickest, so during develop-
+          ment you will usually want to use the default,  maximal
+          compression.
+
+          -Cfe is often a good compromise between speed and  size
+          for production scanners.
+
+          -C options are not cumulative;  whenever  the  flag  is
+          encountered, the previous -C settings are forgotten.
+
+     -Sskeleton_file
+          overrides the default skeleton  file  from  which  flex
+          constructs its scanners.  You'll never need this option
+          unless you are doing flex maintenance or development.
+
+
+
+

PERFORMANCE CONSIDERATIONS

+     The main design goal of  flex  is  that  it  generate  high-
+     performance  scanners.   It  has  been optimized for dealing
+     well with large sets of rules.  Aside from  the  effects  of
+     table compression on scanner speed outlined above, there are
+     a  number  of  options/actions  which  degrade  performance.
+     These are, from most expensive to least:
+
+         REJECT
+
+         pattern sets that require backtracking
+         arbitrary trailing context
+
+         '^' beginning-of-line operator
+         yymore()
+
+     with the first three all being quite expensive and the  last
+     two being quite cheap.
+
+     REJECT should be avoided at all costs  when  performance  is
+     important.  It is a particularly expensive option.
+
+     Getting rid of backtracking is messy and  often  may  be  an
+     enormous amount of work for a complicated scanner.  In prin-
+     cipal, one begins  by  using  the  -b  flag  to  generate  a
+     lex.backtrack file.  For example, on the input
+
+         %%
+         foo        return TOK_KEYWORD;
+         foobar     return TOK_KEYWORD;
+
+     the file looks like:
+
+         State #6 is non-accepting -
+          associated rule line numbers:
+                2       3
+
+          out-transitions: [ o ]
+          jam-transitions: EOF [ \001-n  p-\177 ]
+
+         State #8 is non-accepting -
+          associated rule line numbers:
+                3
+          out-transitions: [ a ]
+          jam-transitions: EOF [ \001-`  b-\177 ]
+
+         State #9 is non-accepting -
+          associated rule line numbers:
+                3
+          out-transitions: [ r ]
+          jam-transitions: EOF [ \001-q  s-\177 ]
+
+         Compressed tables always backtrack.
+
+     The first few lines tell us that there's a scanner state  in
+     which  it  can  make  a  transition on an 'o' but not on any
+     other character,  and  that  in  that  state  the  currently
+     scanned text does not match any rule.  The state occurs when
+     trying to match the rules found at lines  2  and  3  in  the
+     input  file.  If the scanner is in that state and then reads
+     something other than an 'o', it will have  to  backtrack  to
+     find  a rule which is matched.  With a bit of headscratching
+     one can see that this must be the state it's in when it  has
+     seen  "fo".   When this has happened, if anything other than
+     another 'o' is seen, the scanner will have  to  back  up  to
+     simply match the 'f' (by the default rule).
+
+     The comment regarding State #8 indicates there's  a  problem
+     when  "foob"  has  been  scanned.   Indeed, on any character
+     other than a 'b', the scanner will have to back up to accept
+     "foo".   Similarly,  the  comment for State #9 concerns when
+     "fooba" has been scanned.
+
+     The final comment reminds us that there's no point going  to
+     all  the  trouble  of  removing  backtracking from the rules
+     unless we're using -f or -F, since  there's  no  performance
+     gain doing so with compressed scanners.
+
+     The way to remove the backtracking is to add "error" rules:
+
+         %%
+         foo         return TOK_KEYWORD;
+         foobar      return TOK_KEYWORD;
+
+         fooba       |
+         foob        |
+         fo          {
+                     /* false alarm, not really a keyword */
+                     return TOK_ID;
+                     }
+
+
+     Eliminating backtracking among a list of keywords  can  also
+     be done using a "catch-all" rule:
+
+         %%
+         foo         return TOK_KEYWORD;
+         foobar      return TOK_KEYWORD;
+
+         [a-z]+      return TOK_ID;
+
+     This is usually the best solution when appropriate.
+
+     Backtracking messages tend to cascade.  With  a  complicated
+     set  of rules it's not uncommon to get hundreds of messages.
+     If one can decipher them, though,  it  often  only  takes  a
+     dozen or so rules to eliminate the backtracking (though it's
+     easy to make a mistake and have an error  rule  accidentally
+     match a valid token.  A possible future flex feature will be
+     to automatically add rules to eliminate backtracking).
+
+     Variable trailing context (where both the leading and trail-
+     ing  parts  do  not  have a fixed length) entails almost the
+     same performance loss as  REJECT  (i.e.,  substantial).   So
+     when possible a rule like:
+
+         %%
+         mouse|rat/(cat|dog)   run();
+
+     is better written:
+
+         %%
+         mouse/cat|dog         run();
+         rat/cat|dog           run();
+
+     or as
+
+         %%
+         mouse|rat/cat         run();
+         mouse|rat/dog         run();
+
+     Note that here the special '|' action does not  provide  any
+     savings,  and  can  even  make  things  worse  (see  BUGS in
+     flex(1)).
+
+     Another area where the user can increase a scanner's perfor-
+     mance  (and  one that's easier to implement) arises from the
+     fact that the longer the  tokens  matched,  the  faster  the
+     scanner will run.  This is because with long tokens the pro-
+     cessing of most input characters takes place in the  (short)
+     inner  scanning  loop, and does not often have to go through
+     the additional work of setting up the  scanning  environment
+     (e.g.,  yytext)  for  the  action.  Recall the scanner for C
+     comments:
+
+         %x comment
+         %%
+                 int line_num = 1;
+
+         "/*"         BEGIN(comment);
+
+         <comment>[^*\n]*
+         <comment>"*"+[^*/\n]*
+         <comment>\n             ++line_num;
+         <comment>"*"+"/"        BEGIN(INITIAL);
+
+     This could be sped up by writing it as:
+
+         %x comment
+         %%
+                 int line_num = 1;
+
+         "/*"         BEGIN(comment);
+
+         <comment>[^*\n]*
+         <comment>[^*\n]*\n      ++line_num;
+         <comment>"*"+[^*/\n]*
+         <comment>"*"+[^*/\n]*\n ++line_num;
+         <comment>"*"+"/"        BEGIN(INITIAL);
+
+     Now instead of each  newline  requiring  the  processing  of
+     another  action,  recognizing  the newlines is "distributed"
+     over the other rules to keep the matched  text  as  long  as
+     possible.   Note  that  adding  rules does not slow down the
+     scanner!  The speed of the scanner  is  independent  of  the
+     number  of  rules or (modulo the considerations given at the
+     beginning of this section) how  complicated  the  rules  are
+     with regard to operators such as '*' and '|'.
+
+     A final example in speeding up a scanner: suppose  you  want
+     to  scan through a file containing identifiers and keywords,
+     one per line and with no other  extraneous  characters,  and
+     recognize all the keywords.  A natural first approach is:
+
+         %%
+         asm      |
+         auto     |
+         break    |
+         ... etc ...
+         volatile |
+         while    /* it's a keyword */
+
+         .|\n     /* it's not a keyword */
+
+     To eliminate the back-tracking, introduce a catch-all rule:
+
+         %%
+         asm      |
+         auto     |
+         break    |
+         ... etc ...
+         volatile |
+         while    /* it's a keyword */
+
+         [a-z]+   |
+         .|\n     /* it's not a keyword */
+
+     Now, if it's guaranteed that there's exactly  one  word  per
+     line,  then  we  can reduce the total number of matches by a
+     half by merging in the recognition of newlines with that  of
+     the other tokens:
+
+         %%
+         asm\n    |
+         auto\n   |
+         break\n  |
+         ... etc ...
+         volatile\n |
+         while\n  /* it's a keyword */
+
+         [a-z]+\n |
+         .|\n     /* it's not a keyword */
+
+     One has to be careful here,  as  we  have  now  reintroduced
+     backtracking into the scanner.  In particular, while we know
+     that there will never be any characters in the input  stream
+     other  than letters or newlines, flex can't figure this out,
+     and it will plan for possibly needing backtracking  when  it
+     has  scanned a token like "auto" and then the next character
+     is something other than a newline or a  letter.   Previously
+     it  would  then  just match the "auto" rule and be done, but
+     now it has no "auto" rule, only a "auto\n" rule.   To  elim-
+     inate  the  possibility  of  backtracking,  we  could either
+     duplicate all rules but without final newlines, or, since we
+     never  expect to encounter such an input and therefore don't
+     how it's classified, we can  introduce  one  more  catch-all
+     rule, this one which doesn't include a newline:
+
+         %%
+         asm\n    |
+         auto\n   |
+         break\n  |
+         ... etc ...
+         volatile\n |
+         while\n  /* it's a keyword */
+
+         [a-z]+\n |
+         [a-z]+   |
+         .|\n     /* it's not a keyword */
+
+     Compiled with -Cf, this is about as fast as one  can  get  a
+     flex scanner to go for this particular problem.
+
+     A final note:  flex is slow when  matching  NUL's,  particu-
+     larly  when  a  token contains multiple NUL's.  It's best to
+     write rules which match short amounts of text if it's  anti-
+     cipated that the text will often include NUL's.
+
+
+
+

INCOMPATIBILITIES WITH LEX AND POSIX

+     flex is a rewrite of the Unix lex tool (the two  implementa-
+     tions  do  not share any code, though), with some extensions
+     and incompatibilities, both of which are of concern to those
+     who  wish to write scanners acceptable to either implementa-
+     tion.  At present, the POSIX lex draft is very close to  the
+     original lex implementation, so some of these incompatibili-
+     ties are also in conflict with the  POSIX  draft.   But  the
+     intent  is  that except as noted below, flex as it presently
+     stands will ultimately be POSIX conformant (i.e., that those
+     areas  of  conflict with the POSIX draft will be resolved in
+     flex's favor).  Please bear in mind that  all  the  comments
+     which  follow are with regard to the POSIX draft standard of
+     Summer 1989, and  not  the  final  document  (or  subsequent
+     drafts); they are included so flex users can be aware of the
+     standardization issues and those areas where flex may in the
+     near  future  undergo  changes incompatible with its current
+     definition.
+
+     flex is fully compatible with lex with the following  excep-
+     tions:
+
+     -    The undocumented lex scanner internal variable yylineno
+          is  not  supported.   It  is  difficult to support this
+          option efficiently, since it requires  examining  every
+          character  scanned  and reexamining the characters when
+          the scanner backs up.  Things get more complicated when
+          the  end  of  buffer  or  file  is  reached or a NUL is
+          scanned (since the scan must then be restarted with the
+          proper  line  number  count),  or  the  user  uses  the
+          yyless(), unput(), or REJECT actions, or  the  multiple
+          input buffer functions.
+
+          The fix is to add rules which, upon seeing  a  newline,
+          increment  yylineno.   This is usually an easy process,
+          though it can be a drag if some  of  the  patterns  can
+          match multiple newlines along with other characters.
+
+          yylineno is not part of the POSIX draft.
+
+     -    The input() routine is not redefinable, though  it  may
+          be  called  to  read  characters following whatever has
+          been matched by a rule.  If input() encounters an  end-
+          of-file  the  normal  yywrap()  processing  is done.  A
+          ``real'' end-of-file is returned by input() as EOF.
+
+          Input is instead controlled by redefining the  YY_INPUT
+          macro.
+
+          The flex restriction that input() cannot  be  redefined
+          is in accordance with the POSIX draft, but YY_INPUT has
+          not yet been accepted  into  the  draft  (and  probably
+          won't;  it looks like the draft will simply not specify
+          any way of controlling the scanner's input  other  than
+          by making an initial assignment to yyin).
+
+     -    flex scanners do not use stdio for input.   Because  of
+          this,  when  writing  an  interactive  scanner one must
+          explicitly call fflush() on the stream associated  with
+          the terminal after writing out a prompt.  With lex such
+          writes are automatically flushed since lex scanners use
+          getchar() for their input.  Also, when writing interac-
+          tive scanners with flex, the -I flag must be used.
+
+     -    flex scanners are not as reentrant as lex scanners.  In
+          particular,  if  you have an interactive scanner and an
+          interrupt handler which long-jumps out of the  scanner,
+          and  the  scanner is subsequently called again, you may
+          get the following message:
+
+              fatal flex scanner internal error--end of buffer missed
+
+          To reenter the scanner, first use
+
+              yyrestart( yyin );
+
+
+     -    output() is not supported.  Output from the ECHO  macro
+          is done to the file-pointer yyout (default stdout).
+
+          The POSIX  draft  mentions  that  an  output()  routine
+          exists  but  currently  gives  no details as to what it
+          does.
+
+     -    lex does not support exclusive start  conditions  (%x),
+          though they are in the current POSIX draft.
+
+     -    When definitions are expanded, flex  encloses  them  in
+          parentheses.  With lex, the following:
+
+              NAME    [A-Z][A-Z0-9]*
+              %%
+              foo{NAME}?      printf( "Found it\n" );
+              %%
+
+          will not match the string "foo" because when the  macro
+          is  expanded  the rule is equivalent to "foo[A-Z][A-Z0-
+          9]*?"  and the precedence is such that the '?' is asso-
+          ciated  with  "[A-Z0-9]*".  With flex, the rule will be
+          expanded to "foo([A-Z][A-Z0-9]*)?" and  so  the  string
+          "foo" will match.  Note that because of this, the ^, $,
+          <s>, /, and <<EOF>> operators cannot be used in a  flex
+          definition.
+
+          The POSIX draft interpretation is the same as flex's.
+
+     -    To specify a character class which matches anything but
+          a  left  bracket  (']'),  in lex one can use "[^]]" but
+          with flex one must use "[^\]]".  The latter works  with
+          lex, too.
+
+     -    The lex %r (generate a Ratfor scanner)  option  is  not
+          supported.  It is not part of the POSIX draft.
+
+     -    If you are providing your  own  yywrap()  routine,  you
+          must  include a "#undef yywrap" in the definitions sec-
+          tion (section 1).  Note that the "#undef" will have  to
+          be enclosed in %{}'s.
+
+          The POSIX draft specifies that yywrap() is  a  function
+          and  this is very unlikely to change; so flex users are
+          warned that yywrap() is likely to be changed to a func-
+          tion in the near future.
+
+     -    After a call to unput(), yytext and  yyleng  are  unde-
+          fined until the next token is matched.  This is not the
+          case with lex or the present POSIX draft.
+
+     -    The precedence of the {} (numeric  range)  operator  is
+          different.   lex  interprets  "abc{1,3}" as "match one,
+          two, or  three  occurrences  of  'abc'",  whereas  flex
+          interprets  it  as "match 'ab' followed by one, two, or
+          three occurrences of 'c'".  The latter is in  agreement
+          with the current POSIX draft.
+
+     -    The precedence of the ^  operator  is  different.   lex
+          interprets  "^foo|bar"  as  "match  either 'foo' at the
+          beginning of a line, or 'bar' anywhere",  whereas  flex
+          interprets  it  as "match either 'foo' or 'bar' if they
+          come at the beginning of a line".   The  latter  is  in
+          agreement with the current POSIX draft.
+
+     -    To refer to yytext outside of the scanner source  file,
+          the  correct  definition  with  flex  is  "extern  char
+          *yytext" rather than "extern char yytext[]".   This  is
+          contrary  to  the  current  POSIX  draft but a point on
+          which flex will not be changing, as the array represen-
+          tation  entails  a  serious performance penalty.  It is
+          hoped that the POSIX draft will be emended  to  support
+          the  flex  variety  of declaration (as this is a fairly
+          painless change to require of lex users).
+
+     -    yyin is initialized by lex to be stdin;  flex,  on  the
+          other  hand,  initializes yyin to NULL and then assigns
+          it to stdin the first time the scanner is called,  pro-
+          viding yyin has not already been assigned to a non-NULL
+          value.  The difference is subtle, but the net effect is
+          that  with  flex  scanners,  yyin does not have a valid
+          value until the scanner has been called.
+
+     -    The special table-size declarations  such  as  %a  sup-
+          ported  by  lex are not required by flex scanners; flex
+          ignores them.
+
+     -    The name FLEX_SCANNER is #define'd so scanners  may  be
+          written for use with either flex or lex.
+
+     The following flex features are not included in lex  or  the
+     POSIX draft standard:
+
+         yyterminate()
+         <<EOF>>
+         YY_DECL
+         #line directives
+         %{}'s around actions
+         yyrestart()
+         comments beginning with '#' (deprecated)
+         multiple actions on a line
+
+     This last feature refers to the fact that with flex you  can
+     put  multiple actions on the same line, separated with semi-
+     colons, while with lex, the following
+
+         foo    handle_foo(); ++num_foos_seen;
+
+     is (rather surprisingly) truncated to
+
+         foo    handle_foo();
+
+     flex does not truncate the action.   Actions  that  are  not
+     enclosed  in  braces are simply terminated at the end of the
+     line.
+
+
+
+

DIAGNOSTICS

+     reject_used_but_not_detected          undefined           or
+     yymore_used_but_not_detected  undefined  -  These errors can
+     occur at compile time.  They indicate that the scanner  uses
+     REJECT  or yymore() but that flex failed to notice the fact,
+     meaning that flex scanned the first two sections looking for
+     occurrences  of  these  actions  and failed to find any, but
+     somehow you snuck some in (via a #include  file,  for  exam-
+     ple).  Make an explicit reference to the action in your flex
+     input  file.   (Note  that  previously  flex   supported   a
+     %used/%unused  mechanism for dealing with this problem; this
+     feature is still supported but now deprecated, and  will  go
+     away  soon unless the author hears from people who can argue
+     compellingly that they need it.)
+
+     flex scanner jammed - a scanner compiled with -s has encoun-
+     tered  an  input  string  which wasn't matched by any of its
+     rules.
+
+     flex input buffer overflowed -  a  scanner  rule  matched  a
+     string  long enough to overflow the scanner's internal input
+     buffer (16K bytes by default - controlled by YY_BUF_SIZE  in
+     "flex.skel".   Note  that  to  redefine this macro, you must
+     first #undefine it).
+
+     scanner  requires  -8  flag  -  Your  scanner  specification
+     includes  recognizing  8-bit  characters  and  you  did  not
+     specify the -8 flag (and your site has  not  installed  flex
+     with -8 as the default).
+
+     fatal flex scanner internal error--end of  buffer  missed  -
+     This  can  occur  in  an  scanner which is reentered after a
+     long-jump has jumped out (or over) the scanner's  activation
+     frame.  Before reentering the scanner, use:
+
+         yyrestart( yyin );
+
+
+     too many %t classes! - You managed to put every single char-
+     acter  into  its  own %t class.  flex requires that at least
+     one of the classes share characters.
+
+
+
+

DEFICIENCIES / BUGS

+     See flex(1).
+
+
+
+

SEE ALSO

+     flex(1), lex(1), yacc(1), sed(1), awk(1).
+
+     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator
+
+
+
+

AUTHOR

+     Vern Paxson, with the help of many ideas and  much  inspira-
+     tion  from Van Jacobson.  Original version by Jef Poskanzer.
+     The fast table representation is a partial implementation of
+     a  design done by Van Jacobson.  The implementation was done
+     by Kevin Gong and Vern Paxson.
+
+     Thanks to the many flex beta-testers, feedbackers, and  con-
+     tributors,  especially  Casey  Leedom, benson@odi.com, Keith
+     Bostic, Frederic Brehm, Nick  Christopher,  Jason  Coughlin,
+     Scott  David Daniels, Leo Eskin, Chris Faylor, Eric Goldman,
+     Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny,  Ronald  Lam-
+     precht,  Greg  Lee, Craig Leres, Mohamed el Lozy, Jim Meyer-
+     ing, Marc Nozell, Esmond Pitt, Jef Poskanzer,  Jim  Roskind,
+     Dave  Tallman,  Frank Whaley, Ken Yap, and those whose names
+     have slipped my marginal  mail-archiving  skills  but  whose
+     contributions are appreciated all the same.
+
+     Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob  Mul-
+     cahy,  Rich Salz, and Richard Stallman for help with various
+     distribution headaches.
+
+     Thanks to Esmond Pitt and Earle Horton for  8-bit  character
+     support; to Benson Margulies and Fred Burke for C++ support;
+     to Ove Ewerlid for the basics of support for NUL's;  and  to
+     Eric Hughes for the basics of support for multiple buffers.
+
+     Work is being done on extending flex to generate scanners in
+     which  the  state  machine is directly represented in C code
+     rather than tables.  These scanners  may  well  be  substan-
+     tially  faster  than those generated using -f or -F.  If you
+     are working in this area and  are  interested  in  comparing
+     notes and seeing whether redundant work can be avoided, con-
+     tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE).
+
+     This work was primarily done when I was  at  the  Real  Time
+     Systems  Group at the Lawrence Berkeley Laboratory in Berke-
+     ley, CA.  Many  thanks  to  all  there  for  the  support  I
+     received.
+
+     Send comments to:
+
+          Vern Paxson
+          Computer Science Department
+          4126 Upson Hall
+          Cornell University
+          Ithaca, NY 14853-7501
+
+          vern@cs.cornell.edu
+          decvax!cornell!vern
+
+
+
+
+
+
+
+
+
+
+Man(1) output converted with +man2html +
+ + diff --git a/modules/libcom/src/flex/gen.c b/modules/libcom/src/flex/gen.c new file mode 100644 index 000000000..aad884074 --- /dev/null +++ b/modules/libcom/src/flex/gen.c @@ -0,0 +1,1319 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* gen - actual generation (writing) of flex scanners */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +void gen_next_state (int); +void genecs (void); +void indent_put2s (char [], char []); +void indent_puts (char []); + + +static int indent_level = 0; /* each level is 4 spaces */ + +#define indent_up() (++indent_level) +#define indent_down() (--indent_level) +#define set_indent(indent_val) indent_level = indent_val + +/* *everything* is done in terms of arrays starting at 1, so provide + * a null entry for the zero element of all C arrays + */ +static char C_short_decl[] = "static const short int %s[%d] =\n { 0,\n"; +static char C_long_decl[] = "static const long int %s[%d] =\n { 0,\n"; +static char C_state_decl[] = + "static const yy_state_type %s[%d] =\n { 0,\n"; + + +/* indent to the current level */ + +void do_indent(void) +{ + int i = indent_level * 4; + + while ( i >= 8 ) + { + putchar( '\t' ); + i -= 8; + } + + while ( i > 0 ) + { + putchar( ' ' ); + --i; + } + } + + +/* generate the code to keep backtracking information */ + +void gen_backtracking(void) +{ + if ( reject || num_backtracking == 0 ) + return; + + if ( fullspd ) + indent_puts( "if ( yy_current_state[-1].yy_nxt )" ); + else + indent_puts( "if ( yy_accept[yy_current_state] )" ); + + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_last_accepting_state = yy_current_state;" ); + indent_puts( "yy_last_accepting_cpos = yy_cp;" ); + indent_puts( "}" ); + indent_down(); + } + + +/* generate the code to perform the backtrack */ + +void gen_bt_action(void) +{ + if ( reject || num_backtracking == 0 ) + return; + + set_indent( 3 ); + + indent_puts( "case 0: /* must backtrack */" ); + indent_puts( "/* undo the effects of YY_DO_BEFORE_ACTION */" ); + indent_puts( "*yy_cp = yy_hold_char;" ); + + if ( fullspd || fulltbl ) + indent_puts( "yy_cp = yy_last_accepting_cpos + 1;" ); + else + /* backtracking info for compressed tables is taken \after/ + * yy_cp has been incremented for the next state + */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + indent_puts( "goto yy_find_action;" ); + putchar( '\n' ); + + set_indent( 0 ); + } + + +/* genctbl - generates full speed compressed transition table + * + * synopsis + * genctbl(); + */ + +void genctbl(void) +{ + int i; + int end_of_buffer_action = num_rules + 1; + + /* table of verify for transition and offset to next state */ + printf( "static const struct yy_trans_info yy_transition[%d] =\n", + tblend + numecs + 1 ); + printf( " {\n" ); + + /* We want the transition to be represented as the offset to the + * next state, not the actual state number, which is what it currently is. + * The offset is base[nxt[i]] - base[chk[i]]. That's just the + * difference between the starting points of the two involved states + * (to - from). + * + * first, though, we need to find some way to put in our end-of-buffer + * flags and states. We do this by making a state with absolutely no + * transitions. We put it at the end of the table. + */ + /* at this point, we're guaranteed that there's enough room in nxt[] + * and chk[] to hold tblend + numecs entries. We need just two slots. + * One for the action and one for the end-of-buffer transition. We + * now *assume* that we're guaranteed the only character we'll try to + * index this nxt/chk pair with is EOB, i.e., 0, so we don't have to + * make sure there's room for jam entries for other characters. + */ + + base[lastdfa + 1] = tblend + 2; + nxt[tblend + 1] = end_of_buffer_action; + chk[tblend + 1] = numecs + 1; + chk[tblend + 2] = 1; /* anything but EOB */ + nxt[tblend + 2] = 0; /* so that "make test" won't show arb. differences */ + + /* make sure every state has a end-of-buffer transition and an action # */ + for ( i = 0; i <= lastdfa; ++i ) + { + int anum = dfaacc[i].dfaacc_state; + + chk[base[i]] = EOB_POSITION; + chk[base[i] - 1] = ACTION_POSITION; + nxt[base[i] - 1] = anum; /* action number */ + } + + for ( i = 0; i <= tblend; ++i ) + { + if ( chk[i] == EOB_POSITION ) + transition_struct_out( 0, base[lastdfa + 1] - i ); + + else if ( chk[i] == ACTION_POSITION ) + transition_struct_out( 0, nxt[i] ); + + else if ( chk[i] > numecs || chk[i] == 0 ) + transition_struct_out( 0, 0 ); /* unused slot */ + + else /* verify, transition */ + transition_struct_out( chk[i], base[nxt[i]] - (i - chk[i]) ); + } + + + /* here's the final, end-of-buffer state */ + transition_struct_out( chk[tblend + 1], nxt[tblend + 1] ); + transition_struct_out( chk[tblend + 2], nxt[tblend + 2] ); + + printf( " };\n" ); + printf( "\n" ); + + /* table of pointers to start states */ + printf( "static const struct yy_trans_info *yy_start_state_list[%d] =\n", + lastsc * 2 + 1 ); + printf( " {\n" ); + + for ( i = 0; i <= lastsc * 2; ++i ) + printf( " &yy_transition[%d],\n", base[i] ); + + dataend(); + + if ( useecs ) + genecs(); + } + + +/* generate equivalence-class tables */ + +void genecs(void) +{ + int i, j; + static char C_char_decl[] = "static const %s %s[%d] =\n { 0,\n"; + int numrows; + Char clower(); + + if ( numecs < csize ) + printf( C_char_decl, "YY_CHAR", "yy_ec", csize ); + else + printf( C_char_decl, "short", "yy_ec", csize ); + + for ( i = 1; i < csize; ++i ) + { + if ( caseins && (i >= 'A') && (i <= 'Z') ) + ecgroup[i] = ecgroup[clower( i )]; + + ecgroup[i] = abs( ecgroup[i] ); + mkdata( ecgroup[i] ); + } + + dataend(); + + if ( trace ) + { + char *readable_form(); + + fputs( "\n\nEquivalence Classes:\n\n", stderr ); + + numrows = csize / 8; + + for ( j = 0; j < numrows; ++j ) + { + for ( i = j; i < csize; i = i + numrows ) + { + fprintf( stderr, "%4s = %-2d", readable_form( i ), ecgroup[i] ); + + putc( ' ', stderr ); + } + + putc( '\n', stderr ); + } + } + } + + +/* generate the code to find the action number */ + +void gen_find_action(void) +{ + if ( fullspd ) + indent_puts( "yy_act = yy_current_state[-1].yy_nxt;" ); + + else if ( fulltbl ) + indent_puts( "yy_act = yy_accept[yy_current_state];" ); + + else if ( reject ) + { + indent_puts( "yy_current_state = *--yy_state_ptr;" ); + indent_puts( "yy_lp = yy_accept[yy_current_state];" ); + + puts( "find_rule: /* we branch to this label when backtracking */" ); + + indent_puts( "for ( ; ; ) /* until we find what rule we matched */" ); + + indent_up(); + + indent_puts( "{" ); + + indent_puts( "if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_act = yy_acclist[yy_lp];" ); + + if ( variable_trailing_context_rules ) + { + indent_puts( "if ( yy_act & YY_TRAILING_HEAD_MASK ||" ); + indent_puts( " yy_looking_for_trail_begin )" ); + indent_up(); + indent_puts( "{" ); + + indent_puts( "if ( yy_act == yy_looking_for_trail_begin )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_looking_for_trail_begin = 0;" ); + indent_puts( "yy_act &= ~YY_TRAILING_HEAD_MASK;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + + indent_puts( "}" ); + indent_down(); + + indent_puts( "else if ( yy_act & YY_TRAILING_MASK )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( + "yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK;" ); + indent_puts( + "yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK;" ); + + if ( real_reject ) + { + /* remember matched text in case we back up due to REJECT */ + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "yy_full_state = yy_state_ptr;" ); + indent_puts( "yy_full_lp = yy_lp;" ); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( "else" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "yy_full_state = yy_state_ptr;" ); + indent_puts( "yy_full_lp = yy_lp;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + + indent_puts( "++yy_lp;" ); + indent_puts( "goto find_rule;" ); + } + + else + { + /* remember matched text in case we back up due to trailing context + * plus REJECT + */ + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( "--yy_cp;" ); + + /* we could consolidate the following two lines with those at + * the beginning, but at the cost of complaints that we're + * branching inside a loop + */ + indent_puts( "yy_current_state = *--yy_state_ptr;" ); + indent_puts( "yy_lp = yy_accept[yy_current_state];" ); + + indent_puts( "}" ); + + indent_down(); + } + + else + /* compressed */ + indent_puts( "yy_act = yy_accept[yy_current_state];" ); + } + + +/* genftbl - generates full transition table + * + * synopsis + * genftbl(); + */ + +void genftbl(void) +{ + int i; + int end_of_buffer_action = num_rules + 1; + + printf( C_short_decl, "yy_accept", lastdfa + 1 ); + + + dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; + + for ( i = 1; i <= lastdfa; ++i ) + { + int anum = dfaacc[i].dfaacc_state; + + mkdata( anum ); + + if ( trace && anum ) + fprintf( stderr, "state # %d accepts: [%d]\n", i, anum ); + } + + dataend(); + + if ( useecs ) + genecs(); + + /* don't have to dump the actual full table entries - they were created + * on-the-fly + */ + } + + +/* generate the code to find the next compressed-table state */ + +void gen_next_compressed_state(char *char_map) +{ + indent_put2s( "YY_CHAR yy_c = %s;", char_map ); + + /* save the backtracking info \before/ computing the next state + * because we always compute one more state than needed - we + * always proceed until we reach a jam state + */ + gen_backtracking(); + + indent_puts( + "while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_current_state = yy_def[yy_current_state];" ); + + if ( usemecs ) + { + /* we've arrange it so that templates are never chained + * to one another. This means we can afford make a + * very simple test to see if we need to convert to + * yy_c's meta-equivalence class without worrying + * about erroneously looking up the meta-equivalence + * class twice + */ + do_indent(); + /* lastdfa + 2 is the beginning of the templates */ + printf( "if ( yy_current_state >= %d )\n", lastdfa + 2 ); + + indent_up(); + indent_puts( "yy_c = yy_meta[(int)yy_c];" ); + indent_down(); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( + "yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];" ); + } + + +/* generate the code to find the next match */ + +void gen_next_match(void) +{ + /* NOTE - changes in here should be reflected in gen_next_state() and + * gen_NUL_trans() + */ + char *char_map = useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp"; + char *char_map_2 = useecs ? "yy_ec[*++yy_cp]" : "*++yy_cp"; + + if ( fulltbl ) + { + indent_put2s( + "while ( (yy_current_state = yy_nxt[yy_current_state][%s]) > 0 )", + char_map ); + + indent_up(); + + if ( num_backtracking > 0 ) + { + indent_puts( "{" ); + gen_backtracking(); + putchar( '\n' ); + } + + indent_puts( "++yy_cp;" ); + + if ( num_backtracking > 0 ) + indent_puts( "}" ); + + indent_down(); + + putchar( '\n' ); + indent_puts( "yy_current_state = -yy_current_state;" ); + } + + else if ( fullspd ) + { + indent_puts( "{" ); + indent_puts( "const struct yy_trans_info *yy_trans_info;\n" ); + indent_puts( "YY_CHAR yy_c;\n" ); + indent_put2s( "for ( yy_c = %s;", char_map ); + indent_puts( + " (yy_trans_info = &yy_current_state[yy_c])->yy_verify == yy_c;" ); + indent_put2s( " yy_c = %s )", char_map_2 ); + + indent_up(); + + if ( num_backtracking > 0 ) + indent_puts( "{" ); + + indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); + + if ( num_backtracking > 0 ) + { + putchar( '\n' ); + gen_backtracking(); + indent_puts( "}" ); + } + + indent_down(); + indent_puts( "}" ); + } + + else + { /* compressed */ + indent_puts( "do" ); + + indent_up(); + indent_puts( "{" ); + + gen_next_state( false ); + + indent_puts( "++yy_cp;" ); + + indent_puts( "}" ); + indent_down(); + + do_indent(); + + if ( interactive ) + printf( "while ( yy_base[yy_current_state] != %d );\n", jambase ); + else + printf( "while ( yy_current_state != %d );\n", jamstate ); + + if ( ! reject && ! interactive ) + { + /* do the guaranteed-needed backtrack to figure out the match */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + } + } + } + + +/* generate the code to find the next state */ + +void gen_next_state(int worry_about_NULs) +{ /* NOTE - changes in here should be reflected in get_next_match() */ + char char_map[256]; + + if ( worry_about_NULs && ! nultrans ) + { + if ( useecs ) + (void) sprintf( char_map, "(*yy_cp ? yy_ec[(int)*yy_cp] : %d)", NUL_ec ); + else + (void) sprintf( char_map, "(*yy_cp ? *yy_cp : %d)", NUL_ec ); + } + + else + (void) strcpy( char_map, useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp" ); + + if ( worry_about_NULs && nultrans ) + { + if ( ! fulltbl && ! fullspd ) + /* compressed tables backtrack *before* they match */ + gen_backtracking(); + + indent_puts( "if ( *yy_cp )" ); + indent_up(); + indent_puts( "{" ); + } + + if ( fulltbl ) + indent_put2s( "yy_current_state = yy_nxt[yy_current_state][%s];", + char_map ); + + else if ( fullspd ) + indent_put2s( "yy_current_state += yy_current_state[%s].yy_nxt;", + char_map ); + + else + gen_next_compressed_state( char_map ); + + if ( worry_about_NULs && nultrans ) + { + indent_puts( "}" ); + indent_down(); + indent_puts( "else" ); + indent_up(); + indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); + indent_down(); + } + + if ( fullspd || fulltbl ) + gen_backtracking(); + + if ( reject ) + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + } + + +/* generate the code to make a NUL transition */ + +void gen_NUL_trans(void) +{ /* NOTE - changes in here should be reflected in get_next_match() */ + int need_backtracking = (num_backtracking > 0 && ! reject); + + if ( need_backtracking ) + /* we'll need yy_cp lying around for the gen_backtracking() */ + indent_puts( "YY_CHAR *yy_cp = yy_c_buf_p;" ); + + putchar( '\n' ); + + if ( nultrans ) + { + indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); + indent_puts( "yy_is_jam = (yy_current_state == 0);" ); + } + + else if ( fulltbl ) + { + do_indent(); + printf( "yy_current_state = yy_nxt[yy_current_state][%d];\n", + NUL_ec ); + indent_puts( "yy_is_jam = (yy_current_state <= 0);" ); + } + + else if ( fullspd ) + { + do_indent(); + printf( "int yy_c = %d;\n", NUL_ec ); + + indent_puts( + "const struct yy_trans_info *yy_trans_info;\n" ); + indent_puts( "yy_trans_info = &yy_current_state[yy_c];" ); + indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); + + indent_puts( "yy_is_jam = (yy_trans_info->yy_verify != yy_c);" ); + } + + else + { + char NUL_ec_str[20]; + + (void) sprintf( NUL_ec_str, "%d", NUL_ec ); + gen_next_compressed_state( NUL_ec_str ); + + if ( reject ) + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + + do_indent(); + + if ( interactive ) + printf( "yy_is_jam = (yy_base[yy_current_state] == %d);\n", + jambase ); + else + printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate ); + } + + /* if we've entered an accepting state, backtrack; note that + * compressed tables have *already* done such backtracking, so + * we needn't bother with it again + */ + if ( need_backtracking && (fullspd || fulltbl) ) + { + putchar( '\n' ); + indent_puts( "if ( ! yy_is_jam )" ); + indent_up(); + indent_puts( "{" ); + gen_backtracking(); + indent_puts( "}" ); + indent_down(); + } + } + + +/* generate the code to find the start state */ + +void gen_start_state(void) +{ + if ( fullspd ) + indent_put2s( "yy_current_state = yy_start_state_list[yy_start%s];", + bol_needed ? " + (yy_bp[-1] == '\\n' ? 1 : 0)" : "" ); + + else + { + indent_puts( "yy_current_state = yy_start;" ); + + if ( bol_needed ) + { + indent_puts( "if ( yy_bp[-1] == '\\n' )" ); + indent_up(); + indent_puts( "++yy_current_state;" ); + indent_down(); + } + + if ( reject ) + { + /* set up for storing up states */ + indent_puts( "yy_state_ptr = yy_state_buf;" ); + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + } + } + } + + +/* gentabs - generate data statements for the transition tables + * + * synopsis + * gentabs(); + */ + +void gentabs(void) +{ + int i, j, k, *accset, nacc, *acc_array, total_states; + int end_of_buffer_action = num_rules + 1; + + /* *everything* is done in terms of arrays starting at 1, so provide + * a null entry for the zero element of all C arrays + */ + static char C_char_decl[] = + "static const YY_CHAR %s[%d] =\n { 0,\n"; + + acc_array = allocate_integer_array( current_max_dfas ); + nummt = 0; + + /* the compressed table format jams by entering the "jam state", + * losing information about the previous state in the process. + * In order to recover the previous state, we effectively need + * to keep backtracking information. + */ + ++num_backtracking; + + if ( reject ) + { + /* write out accepting list and pointer list + * + * first we generate the "yy_acclist" array. In the process, we compute + * the indices that will go into the "yy_accept" array, and save the + * indices in the dfaacc array + */ + int EOB_accepting_list[2]; + + /* set up accepting structures for the End Of Buffer state */ + EOB_accepting_list[0] = 0; + EOB_accepting_list[1] = end_of_buffer_action; + accsiz[end_of_buffer_state] = 1; + dfaacc[end_of_buffer_state].dfaacc_set = EOB_accepting_list; + + printf( C_short_decl, "yy_acclist", max( numas, 1 ) + 1 ); + + j = 1; /* index into "yy_acclist" array */ + + for ( i = 1; i <= lastdfa; ++i ) + { + acc_array[i] = j; + + if ( accsiz[i] != 0 ) + { + accset = dfaacc[i].dfaacc_set; + nacc = accsiz[i]; + + if ( trace ) + fprintf( stderr, "state # %d accepts: ", i ); + + for ( k = 1; k <= nacc; ++k ) + { + int accnum = accset[k]; + + ++j; + + if ( variable_trailing_context_rules && + ! (accnum & YY_TRAILING_HEAD_MASK) && + accnum > 0 && accnum <= num_rules && + rule_type[accnum] == RULE_VARIABLE ) + { + /* special hack to flag accepting number as part + * of trailing context rule + */ + accnum |= YY_TRAILING_MASK; + } + + mkdata( accnum ); + + if ( trace ) + { + fprintf( stderr, "[%d]", accset[k] ); + + if ( k < nacc ) + fputs( ", ", stderr ); + else + putc( '\n', stderr ); + } + } + } + } + + /* add accepting number for the "jam" state */ + acc_array[i] = j; + + dataend(); + } + + else + { + dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; + + for ( i = 1; i <= lastdfa; ++i ) + acc_array[i] = dfaacc[i].dfaacc_state; + + /* add accepting number for jam state */ + acc_array[i] = 0; + } + + /* spit out "yy_accept" array. If we're doing "reject", it'll be pointers + * into the "yy_acclist" array. Otherwise it's actual accepting numbers. + * In either case, we just dump the numbers. + */ + + /* "lastdfa + 2" is the size of "yy_accept"; includes room for C arrays + * beginning at 0 and for "jam" state + */ + k = lastdfa + 2; + + if ( reject ) + /* we put a "cap" on the table associating lists of accepting + * numbers with state numbers. This is needed because we tell + * where the end of an accepting list is by looking at where + * the list for the next state starts. + */ + ++k; + + printf( C_short_decl, "yy_accept", k ); + + for ( i = 1; i <= lastdfa; ++i ) + { + mkdata( acc_array[i] ); + + if ( ! reject && trace && acc_array[i] ) + fprintf( stderr, "state # %d accepts: [%d]\n", i, acc_array[i] ); + } + + /* add entry for "jam" state */ + mkdata( acc_array[i] ); + + if ( reject ) + /* add "cap" for the list */ + mkdata( acc_array[i] ); + + dataend(); + + if ( useecs ) + genecs(); + + if ( usemecs ) + { + /* write out meta-equivalence classes (used to index templates with) */ + + if ( trace ) + fputs( "\n\nMeta-Equivalence Classes:\n", stderr ); + + printf( C_char_decl, "yy_meta", numecs + 1 ); + + for ( i = 1; i <= numecs; ++i ) + { + if ( trace ) + fprintf( stderr, "%d = %d\n", i, abs( tecbck[i] ) ); + + mkdata( abs( tecbck[i] ) ); + } + + dataend(); + } + + total_states = lastdfa + numtemps; + + printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_base", total_states + 1 ); + + for ( i = 1; i <= lastdfa; ++i ) + { + int d = def[i]; + + if ( base[i] == JAMSTATE ) + base[i] = jambase; + + if ( d == JAMSTATE ) + def[i] = jamstate; + + else if ( d < 0 ) + { + /* template reference */ + ++tmpuses; + def[i] = lastdfa - d + 1; + } + + mkdata( base[i] ); + } + + /* generate jam state's base index */ + mkdata( base[i] ); + + for ( ++i /* skip jam state */; i <= total_states; ++i ) + { + mkdata( base[i] ); + def[i] = jamstate; + } + + dataend(); + + printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_def", total_states + 1 ); + + for ( i = 1; i <= total_states; ++i ) + mkdata( def[i] ); + + dataend(); + + printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_nxt", tblend + 1 ); + + for ( i = 1; i <= tblend; ++i ) + { + if ( nxt[i] == 0 || chk[i] == 0 ) + nxt[i] = jamstate; /* new state is the JAM state */ + + mkdata( nxt[i] ); + } + + dataend(); + + printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_chk", tblend + 1 ); + + for ( i = 1; i <= tblend; ++i ) + { + if ( chk[i] == 0 ) + ++nummt; + + mkdata( chk[i] ); + } + + dataend(); + } + + +/* write out a formatted string (with a secondary string argument) at the + * current indentation level, adding a final newline + */ + +void indent_put2s(char *fmt, char *arg) +{ + do_indent(); + printf( fmt, arg ); + putchar( '\n' ); + } + + +/* write out a string at the current indentation level, adding a final + * newline + */ + +void indent_puts(char *str) +{ + do_indent(); + puts( str ); + } + + +/* make_tables - generate transition tables + * + * synopsis + * make_tables(); + * + * Generates transition tables and finishes generating output file + */ + +void make_tables(void) +{ + int i; + int did_eof_rule = false; + + skelout(); + + /* first, take care of YY_DO_BEFORE_ACTION depending on yymore being used */ + set_indent( 2 ); + + if ( yymore_used ) + { + indent_puts( "yytext -= yy_more_len; \\" ); + indent_puts( "yyleng = yy_cp - yytext; \\" ); + } + + else + indent_puts( "yyleng = yy_cp - yy_bp; \\" ); + + set_indent( 0 ); + + skelout(); + + + printf( "#define YY_END_OF_BUFFER %d\n", num_rules + 1 ); + + if ( fullspd ) + { /* need to define the transet type as a size large + * enough to hold the biggest offset + */ + int total_table_size = tblend + numecs + 1; + char *trans_offset_type = + total_table_size > MAX_SHORT ? "long" : "short"; + + set_indent( 0 ); + indent_puts( "struct yy_trans_info" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "short yy_verify;" ); + + /* in cases where its sister yy_verify *is* a "yes, there is a + * transition", yy_nxt is the offset (in records) to the next state. + * In most cases where there is no transition, the value of yy_nxt + * is irrelevant. If yy_nxt is the -1th record of a state, though, + * then yy_nxt is the action number for that state + */ + + indent_put2s( "%s yy_nxt;", trans_offset_type ); + indent_puts( "};" ); + indent_down(); + + indent_puts( "typedef const struct yy_trans_info *yy_state_type;" ); + } + + else + indent_puts( "typedef int yy_state_type;" ); + + if ( fullspd ) + genctbl(); + + else if ( fulltbl ) + genftbl(); + + else + gentabs(); + + if ( num_backtracking > 0 ) + { + indent_puts( "static yy_state_type yy_last_accepting_state;" ); + indent_puts( "static YY_CHAR *yy_last_accepting_cpos;\n" ); + } + + if ( nultrans ) + { + printf( C_state_decl, "yy_NUL_trans", lastdfa + 1 ); + + for ( i = 1; i <= lastdfa; ++i ) + { + if ( fullspd ) + { + if ( nultrans ) + printf( " &yy_transition[%d],\n", base[i] ); + else + printf( " 0,\n" ); + } + + else + mkdata( nultrans[i] ); + } + + dataend(); + } + + if ( ddebug ) + { /* spit out table mapping rules to line numbers */ + indent_puts( "extern int yy_flex_debug;" ); + indent_puts( "int yy_flex_debug = 1;\n" ); + + printf( C_short_decl, "yy_rule_linenum", num_rules ); + for ( i = 1; i < num_rules; ++i ) + mkdata( rule_linenum[i] ); + dataend(); + } + + if ( reject ) + { + /* declare state buffer variables */ + puts( + "static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;" ); + puts( "static YY_CHAR *yy_full_match;" ); + puts( "static int yy_lp;" ); + + if ( variable_trailing_context_rules ) + { + puts( "static int yy_looking_for_trail_begin = 0;" ); + puts( "static int yy_full_lp;" ); + puts( "static int *yy_full_state;" ); + printf( "#define YY_TRAILING_MASK 0x%x\n", YY_TRAILING_MASK ); + printf( "#define YY_TRAILING_HEAD_MASK 0x%x\n", + YY_TRAILING_HEAD_MASK ); + } + + puts( "#define REJECT \\" ); + puts( "{ \\" ); + puts( + "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \\" ); + puts( + "yy_cp = yy_full_match; /* restore poss. backed-over text */ \\" ); + + if ( variable_trailing_context_rules ) + { + puts( "yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \\" ); + puts( + "yy_state_ptr = yy_full_state; /* restore orig. state */ \\" ); + puts( + "yy_current_state = *yy_state_ptr; /* restore curr. state */ \\" ); + } + + puts( "++yy_lp; \\" ); + puts( "goto find_rule; \\" ); + puts( "}" ); + } + + else + { + puts( "/* the intent behind this definition is that it'll catch" ); + puts( " * any uses of REJECT which flex missed" ); + puts( " */" ); + puts( "#define REJECT reject_used_but_not_detected" ); + } + + if ( yymore_used ) + { + indent_puts( "static int yy_more_flag = 0;" ); + indent_puts( "static int yy_doing_yy_more = 0;" ); + indent_puts( "static int yy_more_len = 0;" ); + indent_puts( + "#define yymore() { yy_more_flag = 1; }" ); + indent_puts( + "#define YY_MORE_ADJ (yy_doing_yy_more ? yy_more_len : 0)" ); + } + + else + { + indent_puts( "#define yymore() yymore_used_but_not_detected" ); + indent_puts( "#define YY_MORE_ADJ 0" ); + } + + skelout(); + + if ( ferror( temp_action_file ) ) + flexfatal( "error occurred when writing temporary action file" ); + + else if ( fclose( temp_action_file ) ) + flexfatal( "error occurred when closing temporary action file" ); + + temp_action_file = fopen( action_file_name, "r" ); + + if ( temp_action_file == NULL ) + flexfatal( "could not re-open temporary action file" ); + + /* copy prolog from action_file to output file */ + action_out(); + + skelout(); + + set_indent( 2 ); + + if ( yymore_used ) + { + indent_puts( "yy_more_len = 0;" ); + indent_puts( "yy_doing_yy_more = yy_more_flag;" ); + indent_puts( "if ( yy_doing_yy_more )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_more_len = yyleng;" ); + indent_puts( "yy_more_flag = 0;" ); + indent_puts( "}" ); + indent_down(); + } + + skelout(); + + gen_start_state(); + + /* note, don't use any indentation */ + puts( "yy_match:" ); + gen_next_match(); + + skelout(); + set_indent( 2 ); + gen_find_action(); + + skelout(); + if ( ddebug ) + { + indent_puts( "if ( yy_flex_debug )" ); + indent_up(); + + indent_puts( "{" ); + indent_puts( "if ( yy_act == 0 )" ); + indent_up(); + indent_puts( "fprintf( stderr, \"--scanner backtracking\\n\" );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act < %d )\n", num_rules ); + indent_up(); + indent_puts( + "fprintf( stderr, \"--accepting rule at line %d (\\\"%s\\\")\\n\"," ); + indent_puts( " yy_rule_linenum[yy_act], yytext );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act == %d )\n", num_rules ); + indent_up(); + indent_puts( + "fprintf( stderr, \"--accepting default rule (\\\"%s\\\")\\n\"," ); + indent_puts( " yytext );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act == %d )\n", num_rules + 1 ); + indent_up(); + indent_puts( "fprintf( stderr, \"--(end of buffer or a NUL)\\n\" );" ); + indent_down(); + + do_indent(); + printf( "else\n" ); + indent_up(); + indent_puts( "fprintf( stderr, \"--EOF\\n\" );" ); + indent_down(); + + indent_puts( "}" ); + indent_down(); + } + + /* copy actions from action_file to output file */ + skelout(); + indent_up(); + gen_bt_action(); + action_out(); + + /* generate cases for any missing EOF rules */ + for ( i = 1; i <= lastsc; ++i ) + if ( ! sceof[i] ) + { + do_indent(); + printf( "case YY_STATE_EOF(%s):\n", scname[i] ); + did_eof_rule = true; + } + + if ( did_eof_rule ) + { + indent_up(); + indent_puts( "yyterminate();" ); + indent_down(); + } + + + /* generate code for handling NUL's, if needed */ + + /* first, deal with backtracking and setting up yy_cp if the scanner + * finds that it should JAM on the NUL + */ + skelout(); + set_indent( 7 ); + + if ( fullspd || fulltbl ) + indent_puts( "yy_cp = yy_c_buf_p;" ); + + else + { /* compressed table */ + if ( ! reject && ! interactive ) + { + /* do the guaranteed-needed backtrack to figure out the match */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + } + } + + + /* generate code for yy_get_previous_state() */ + set_indent( 1 ); + skelout(); + + if ( bol_needed ) + indent_puts( "YY_CHAR *yy_bp = yytext;\n" ); + + gen_start_state(); + + set_indent( 2 ); + skelout(); + gen_next_state( true ); + + set_indent( 1 ); + skelout(); + gen_NUL_trans(); + + skelout(); + + /* copy remainder of input to output */ + + line_directive_out( stdout ); + (void) flexscan(); /* copy remainder of input to output */ + } diff --git a/modules/libcom/src/flex/libmain.c b/modules/libcom/src/flex/libmain.c new file mode 100644 index 000000000..878aa21dc --- /dev/null +++ b/modules/libcom/src/flex/libmain.c @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* libmain - flex run-time support library "main" function */ + +extern int yylex(); + +int main(int argc, char *argv[]) +{ + return yylex(); +} diff --git a/modules/libcom/src/flex/misc.c b/modules/libcom/src/flex/misc.c new file mode 100644 index 000000000..b8a7a0399 --- /dev/null +++ b/modules/libcom/src/flex/misc.c @@ -0,0 +1,771 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* misc - miscellaneous flex routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include "flexdef.h" +#include + +/* ANSI C does not guarantee that isascii() is defined */ +#ifndef isascii +#define isascii(c) ((c) <= 0177) +#endif + + + +/* declare functions that have forward references */ + +void dataflush (void); +int otoi (Char []); + + +/* action_out - write the actions from the temporary file to lex.yy.c + * + * synopsis + * action_out(); + * + * Copies the action file up to %% (or end-of-file) to lex.yy.c + */ + +void action_out(void) +{ + char buf[MAXLINE]; + + while ( fgets( buf, MAXLINE, temp_action_file ) != NULL ) + if ( buf[0] == '%' && buf[1] == '%' ) + break; + else + fputs( buf, stdout ); + } + + +/* allocate_array - allocate memory for an integer array of the given size */ + +void *allocate_array(int size, int element_size) +{ + void *mem; + + /* on 16-bit int machines (e.g., 80286) we might be trying to + * allocate more than a signed int can hold, and that won't + * work. Cheap test: + */ + if ( element_size * size <= 0 ) + flexfatal( "request for < 1 byte in allocate_array()" ); + + mem = (void *) malloc( (unsigned) (element_size * size) ); + + if ( mem == NULL ) + flexfatal( "memory allocation failed in allocate_array()" ); + + return ( mem ); + } + + +/* all_lower - true if a string is all lower-case + * + * synopsis: + * Char *str; + * int all_lower(); + * true/false = all_lower( str ); + */ + +int all_lower(Char *str) +{ + while ( *str ) + { + if ( ! isascii( (int) *str ) || ! islower( (int) *str ) ) + return ( 0 ); + ++str; + } + + return ( 1 ); + } + + +/* all_upper - true if a string is all upper-case + * + * synopsis: + * Char *str; + * int all_upper(); + * true/false = all_upper( str ); + */ + +int all_upper(Char *str) +{ + while ( *str ) + { + if ( ! isascii( (int) *str ) || ! isupper( (int) *str ) ) + return ( 0 ); + ++str; + } + + return ( 1 ); + } + + +/* bubble - bubble sort an integer array in increasing order + * + * synopsis + * int v[n], n; + * bubble( v, n ); + * + * description + * sorts the first n elements of array v and replaces them in + * increasing order. + * + * passed + * v - the array to be sorted + * n - the number of elements of 'v' to be sorted */ + +void bubble(int v[], int n) +{ + int i, j, k; + + for ( i = n; i > 1; --i ) + for ( j = 1; j < i; ++j ) + if ( v[j] > v[j + 1] ) /* compare */ + { + k = v[j]; /* exchange */ + v[j] = v[j + 1]; + v[j + 1] = k; + } + } + + +/* clower - replace upper-case letter to lower-case + * + * synopsis: + * Char clower(); + * int c; + * c = clower( c ); + */ + +Char clower(int c) +{ + return ( (isascii( c ) && isupper( c )) ? tolower( c ) : c ); + } + + +/* copy_string - returns a dynamically allocated copy of a string + * + * synopsis + * char *str, *copy, *copy_string(); + * copy = copy_string( str ); + */ + +char *copy_string(char *str) +{ + char *c; + char *copy; + + /* find length */ + for ( c = str; *c; ++c ) + ; + + copy = malloc( (unsigned) ((c - str + 1) * sizeof( char )) ); + + if ( copy == NULL ) + flexfatal( "dynamic memory failure in copy_string()" ); + + for ( c = copy; (*c++ = *str++); ) + ; + + return ( copy ); + } + + +/* copy_unsigned_string - + * returns a dynamically allocated copy of a (potentially) unsigned string + * + * synopsis + * Char *str, *copy, *copy_unsigned_string(); + * copy = copy_unsigned_string( str ); + */ + +Char *copy_unsigned_string(Char *str) +{ + Char *c; + Char *copy; + + /* find length */ + for ( c = str; *c; ++c ) + ; + + copy = (Char *) malloc( (unsigned) ((c - str + 1) * sizeof( Char )) ); + + if ( copy == NULL ) + flexfatal( "dynamic memory failure in copy_unsigned_string()" ); + + for ( c = copy; (*c++ = *str++); ) + ; + + return ( copy ); + } + + +/* cshell - shell sort a character array in increasing order + * + * synopsis + * + * Char v[n]; + * int n, special_case_0; + * cshell( v, n, special_case_0 ); + * + * description + * does a shell sort of the first n elements of array v. + * If special_case_0 is true, then any element equal to 0 + * is instead assumed to have infinite weight. + * + * passed + * v - array to be sorted + * n - number of elements of v to be sorted + */ + +void cshell(Char *v, int n, int special_case_0) +{ + int gap, i, j, jg; + Char k; + + for ( gap = n / 2; gap > 0; gap = gap / 2 ) + for ( i = gap; i < n; ++i ) + for ( j = i - gap; j >= 0; j = j - gap ) + { + jg = j + gap; + + if ( special_case_0 ) + { + if ( v[jg] == 0 ) + break; + + else if ( v[j] != 0 && v[j] <= v[jg] ) + break; + } + + else if ( v[j] <= v[jg] ) + break; + + k = v[j]; + v[j] = v[jg]; + v[jg] = k; + } + } + + +/* dataend - finish up a block of data declarations + * + * synopsis + * dataend(); + */ + +void dataend(void) +{ + if ( datapos > 0 ) + dataflush(); + + /* add terminator for initialization */ + puts( " } ;\n" ); + + dataline = 0; + datapos = 0; + } + + + +/* dataflush - flush generated data statements + * + * synopsis + * dataflush(); + */ + +void dataflush(void) +{ + putchar( '\n' ); + + if ( ++dataline >= NUMDATALINES ) + { + /* put out a blank line so that the table is grouped into + * large blocks that enable the user to find elements easily + */ + putchar( '\n' ); + dataline = 0; + } + + /* reset the number of characters written on the current line */ + datapos = 0; + } + + +/* flexerror - report an error message and terminate + * + * synopsis + * char msg[]; + * flexerror( msg ); + */ + +void flexerror(char *msg) +{ + fprintf( stderr, "%s: %s\n", program_name, msg ); + + flexend( 1 ); + } + + +/* flexfatal - report a fatal error message and terminate + * + * synopsis + * char msg[]; + * flexfatal( msg ); + */ + +void flexfatal(char *msg) +{ + fprintf( stderr, "%s: fatal internal error, %s\n", program_name, msg ); + flexend( 1 ); + } + + +/* flex_gettime - return current time + * + * synopsis + * char *flex_gettime(), *time_str; + * time_str = flex_gettime(); + * + * note + * the routine name has the "flex_" prefix because of name clashes + * with Turbo-C + */ + +/* include sys/types.h to use time_t and make lint happy */ + +#ifndef MS_DOS +#include +#endif + +#ifdef MS_DOS +#include +typedef long time_t; +#endif + +char *flex_gettime(void) +{ + time_t t, time(time_t *); + char *result, *ctime(const time_t *), *copy_string(char *str); + + t = time( NULL ); + + result = copy_string( ctime( &t ) ); + + /* get rid of trailing newline */ + result[24] = '\0'; + + return ( result ); + } + + +/* lerrif - report an error message formatted with one integer argument + * + * synopsis + * char msg[]; + * int arg; + * lerrif( msg, arg ); + */ + +void lerrif(char *msg, int arg) +{ + char errmsg[MAXLINE]; + (void) sprintf( errmsg, msg, arg ); + flexerror( errmsg ); + } + + +/* lerrsf - report an error message formatted with one string argument + * + * synopsis + * char msg[], arg[]; + * lerrsf( msg, arg ); + */ + +void lerrsf(char *msg, char *arg) +{ + char errmsg[MAXLINE]; + + (void) sprintf( errmsg, msg, arg ); + flexerror( errmsg ); + } + + +/* htoi - convert a hexadecimal digit string to an integer value + * + * synopsis: + * int val, htoi(); + * Char str[]; + * val = htoi( str ); + */ + +int htoi(unsigned char *str) +{ + int result; + + (void) sscanf( (char *) str, "%x", &result ); + + return ( result ); + } + + +/* is_hex_digit - returns true if a character is a valid hex digit, false + * otherwise + * + * synopsis: + * int true_or_false, is_hex_digit(); + * int ch; + * val = is_hex_digit( ch ); + */ + +int is_hex_digit(int ch) +{ + if ( isdigit( ch ) ) + return ( 1 ); + + switch ( clower( ch ) ) + { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return ( 1 ); + + default: + return ( 0 ); + } + } + + +/* line_directive_out - spit out a "# line" statement */ + +void line_directive_out(FILE *output_file_name) +{ + if ( infilename && gen_line_dirs ) + fprintf( output_file_name, "# line %d \"%s\"\n", linenum, infilename ); + } + + +/* mk2data - generate a data statement for a two-dimensional array + * + * synopsis + * int value; + * mk2data( value ); + * + * generates a data statement initializing the current 2-D array to "value" + */ +void mk2data(int value) +{ + if ( datapos >= NUMDATAITEMS ) + { + putchar( ',' ); + dataflush(); + } + + if ( datapos == 0 ) + /* indent */ + fputs( " ", stdout ); + + else + putchar( ',' ); + + ++datapos; + + printf( "%5d", value ); + } + + +/* mkdata - generate a data statement + * + * synopsis + * int value; + * mkdata( value ); + * + * generates a data statement initializing the current array element to + * "value" + */ +void mkdata(int value) +{ + if ( datapos >= NUMDATAITEMS ) + { + putchar( ',' ); + dataflush(); + } + + if ( datapos == 0 ) + /* indent */ + fputs( " ", stdout ); + + else + putchar( ',' ); + + ++datapos; + + printf( "%5d", value ); + } + + +/* myctoi - return the integer represented by a string of digits + * + * synopsis + * Char array[]; + * int val, myctoi(); + * val = myctoi( array ); + * + */ + +int myctoi(Char *array) +{ + int val = 0; + + (void) sscanf( (char *) array, "%d", &val ); + + return ( val ); + } + + +/* myesc - return character corresponding to escape sequence + * + * synopsis + * Char array[], c, myesc(); + * c = myesc( array ); + * + */ + +Char myesc(Char *array) +{ + Char c, esc_char; + int sptr; + + switch ( array[1] ) + { + case 'a': return ( '\a' ); + case 'b': return ( '\b' ); + case 'f': return ( '\f' ); + case 'n': return ( '\n' ); + case 'r': return ( '\r' ); + case 't': return ( '\t' ); + case 'v': return ( '\v' ); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { /* \ */ + sptr = 1; + + while ( isascii( array[sptr] ) && isdigit( array[sptr] ) ) + /* don't increment inside loop control because if + * isdigit() is a macro it might expand into multiple + * increments ... + */ + ++sptr; + + c = array[sptr]; + array[sptr] = '\0'; + + esc_char = otoi( array + 1 ); + + array[sptr] = c; + + return ( esc_char ); + } + + case 'x': + { /* \x */ + int sptr = 2; + + while ( isascii( array[sptr] ) && is_hex_digit( array[sptr] ) ) + /* don't increment inside loop control because if + * isdigit() is a macro it might expand into multiple + * increments ... + */ + ++sptr; + + c = array[sptr]; + array[sptr] = '\0'; + + esc_char = htoi( array + 2 ); + + array[sptr] = c; + + return ( esc_char ); + } + + default: + return ( array[1] ); + } + } + + +/* otoi - convert an octal digit string to an integer value + * + * synopsis: + * int val, otoi(); + * Char str[]; + * val = otoi( str ); + */ + +int otoi(Char *str) +{ + int result; + + (void) sscanf( (char *) str, "%o", &result ); + + return ( result ); + } + + +/* readable_form - return the the human-readable form of a character + * + * synopsis: + * int c; + * char *readable_form(); + * = readable_form( c ); + * + * The returned string is in static storage. + */ + +char *readable_form(int c) +{ + static char rform[10]; + + if ( (c >= 0 && c < 32) || c >= 127 ) + { + switch ( c ) + { + case '\n': return ( "\\n" ); + case '\t': return ( "\\t" ); + case '\f': return ( "\\f" ); + case '\r': return ( "\\r" ); + case '\b': return ( "\\b" ); + + default: + (void) sprintf( rform, "\\%.3o", c ); + return ( rform ); + } + } + + else if ( c == ' ' ) + return ( "' '" ); + + else + { + rform[0] = c; + rform[1] = '\0'; + + return ( rform ); + } + } + + +/* reallocate_array - increase the size of a dynamic array */ + +void *reallocate_array(void *array, int size, int element_size) +{ + void *new_array; + + /* same worry as in allocate_array(): */ + if ( size * element_size <= 0 ) + flexfatal( "attempt to increase array size by less than 1 byte" ); + + new_array = + (void *) realloc( (char *)array, (unsigned) (size * element_size )); + + if ( new_array == NULL ) + flexfatal( "attempt to increase array size failed" ); + + return ( new_array ); + } + + +/* skelout - write out one section of the skeleton file + * + * synopsis + * skelout(); + * + * DESCRIPTION + * Copies from skelfile to stdout until a line beginning with "%%" or + * EOF is found. + */ +void skelout(void) +{ + char buf[MAXLINE]; + + while ( fgets( buf, MAXLINE, skelfile ) != NULL ) + if ( buf[0] == '%' && buf[1] == '%' ) + break; + else + fputs( buf, stdout ); + } + + +/* transition_struct_out - output a yy_trans_info structure + * + * synopsis + * int element_v, element_n; + * transition_struct_out( element_v, element_n ); + * + * outputs the yy_trans_info structure with the two elements, element_v and + * element_n. Formats the output with spaces and carriage returns. + */ + +void transition_struct_out(int element_v, int element_n) +{ + printf( "%7d, %5d,", element_v, element_n ); + + datapos += TRANS_STRUCT_PRINT_LENGTH; + + if ( datapos >= 75 ) + { + putchar( '\n' ); + + if ( ++dataline % 10 == 0 ) + putchar( '\n' ); + + datapos = 0; + } + } diff --git a/modules/libcom/src/flex/nfa.c b/modules/libcom/src/flex/nfa.c new file mode 100644 index 000000000..b57c6d646 --- /dev/null +++ b/modules/libcom/src/flex/nfa.c @@ -0,0 +1,689 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* nfa - NFA construction routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +int dupmachine (int); +void mkxtion (int, int); + + +/* add_accept - add an accepting state to a machine + * + * synopsis + * + * add_accept( mach, accepting_number ); + * + * accepting_number becomes mach's accepting number. + */ + +void add_accept(int mach, int accepting_number) +{ + /* hang the accepting number off an epsilon state. if it is associated + * with a state that has a non-epsilon out-transition, then the state + * will accept BEFORE it makes that transition, i.e., one character + * too soon + */ + + if ( transchar[finalst[mach]] == SYM_EPSILON ) + accptnum[finalst[mach]] = accepting_number; + + else + { + int astate = mkstate( SYM_EPSILON ); + accptnum[astate] = accepting_number; + mach = link_machines( mach, astate ); + } + } + + +/* copysingl - make a given number of copies of a singleton machine + * + * synopsis + * + * newsng = copysingl( singl, num ); + * + * newsng - a new singleton composed of num copies of singl + * singl - a singleton machine + * num - the number of copies of singl to be present in newsng + */ + +int copysingl(int singl, int num) +{ + int copy, i; + + copy = mkstate( SYM_EPSILON ); + + for ( i = 1; i <= num; ++i ) + copy = link_machines( copy, dupmachine( singl ) ); + + return ( copy ); + } + + +/* dumpnfa - debugging routine to write out an nfa + * + * synopsis + * int state1; + * dumpnfa( state1 ); + */ + +void dumpnfa(int state1) +{ + int sym, tsp1, tsp2, anum, ns; + + fprintf( stderr, "\n\n********** beginning dump of nfa with start state %d\n", + state1 ); + + /* we probably should loop starting at firstst[state1] and going to + * lastst[state1], but they're not maintained properly when we "or" + * all of the rules together. So we use our knowledge that the machine + * starts at state 1 and ends at lastnfa. + */ + + /* for ( ns = firstst[state1]; ns <= lastst[state1]; ++ns ) */ + for ( ns = 1; ns <= lastnfa; ++ns ) + { + fprintf( stderr, "state # %4d\t", ns ); + + sym = transchar[ns]; + tsp1 = trans1[ns]; + tsp2 = trans2[ns]; + anum = accptnum[ns]; + + fprintf( stderr, "%3d: %4d, %4d", sym, tsp1, tsp2 ); + + if ( anum != NIL ) + fprintf( stderr, " [%d]", anum ); + + fprintf( stderr, "\n" ); + } + + fprintf( stderr, "********** end of dump\n" ); + } + + +/* dupmachine - make a duplicate of a given machine + * + * synopsis + * + * copy = dupmachine( mach ); + * + * copy - holds duplicate of mach + * mach - machine to be duplicated + * + * note that the copy of mach is NOT an exact duplicate; rather, all the + * transition states values are adjusted so that the copy is self-contained, + * as the original should have been. + * + * also note that the original MUST be contiguous, with its low and high + * states accessible by the arrays firstst and lastst + */ + +int dupmachine(int mach) +{ + int i, init, state_offset; + int state = 0; + int last = lastst[mach]; + + for ( i = firstst[mach]; i <= last; ++i ) + { + state = mkstate( transchar[i] ); + + if ( trans1[i] != NO_TRANSITION ) + { + mkxtion( finalst[state], trans1[i] + state - i ); + + if ( transchar[i] == SYM_EPSILON && trans2[i] != NO_TRANSITION ) + mkxtion( finalst[state], trans2[i] + state - i ); + } + + accptnum[state] = accptnum[i]; + } + + if ( state == 0 ) + flexfatal( "empty machine in dupmachine()" ); + + state_offset = state - i + 1; + + init = mach + state_offset; + firstst[init] = firstst[mach] + state_offset; + finalst[init] = finalst[mach] + state_offset; + lastst[init] = lastst[mach] + state_offset; + + return ( init ); + } + + +/* finish_rule - finish up the processing for a rule + * + * synopsis + * + * finish_rule( mach, variable_trail_rule, headcnt, trailcnt ); + * + * An accepting number is added to the given machine. If variable_trail_rule + * is true then the rule has trailing context and both the head and trail + * are variable size. Otherwise if headcnt or trailcnt is non-zero then + * the machine recognizes a pattern with trailing context and headcnt is + * the number of characters in the matched part of the pattern, or zero + * if the matched part has variable length. trailcnt is the number of + * trailing context characters in the pattern, or zero if the trailing + * context has variable length. + */ + +void finish_rule(int mach, int variable_trail_rule, int headcnt, int trailcnt) +{ + add_accept( mach, num_rules ); + + /* we did this in new_rule(), but it often gets the wrong + * number because we do it before we start parsing the current rule + */ + rule_linenum[num_rules] = linenum; + + /* if this is a continued action, then the line-number has + * already been updated, giving us the wrong number + */ + if ( continued_action ) + --rule_linenum[num_rules]; + + fprintf( temp_action_file, "case %d:\n", num_rules ); + + if ( variable_trail_rule ) + { + rule_type[num_rules] = RULE_VARIABLE; + + if ( performance_report ) + fprintf( stderr, "Variable trailing context rule at line %d\n", + rule_linenum[num_rules] ); + + variable_trailing_context_rules = true; + } + + else + { + rule_type[num_rules] = RULE_NORMAL; + + if ( headcnt > 0 || trailcnt > 0 ) + { + /* do trailing context magic to not match the trailing characters */ + char *scanner_cp = "yy_c_buf_p = yy_cp"; + char *scanner_bp = "yy_bp"; + + fprintf( temp_action_file, + "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */\n" ); + + if ( headcnt > 0 ) + fprintf( temp_action_file, "%s = %s + %d;\n", + scanner_cp, scanner_bp, headcnt ); + + else + fprintf( temp_action_file, + "%s -= %d;\n", scanner_cp, trailcnt ); + + fprintf( temp_action_file, + "YY_DO_BEFORE_ACTION; /* set up yytext again */\n" ); + } + } + + line_directive_out( temp_action_file ); + } + + +/* link_machines - connect two machines together + * + * synopsis + * + * new = link_machines( first, last ); + * + * new - a machine constructed by connecting first to last + * first - the machine whose successor is to be last + * last - the machine whose predecessor is to be first + * + * note: this routine concatenates the machine first with the machine + * last to produce a machine new which will pattern-match first first + * and then last, and will fail if either of the sub-patterns fails. + * FIRST is set to new by the operation. last is unmolested. + */ + +int link_machines(int first, int last) +{ + if ( first == NIL ) + return ( last ); + + else if ( last == NIL ) + return ( first ); + + else + { + mkxtion( finalst[first], last ); + finalst[first] = finalst[last]; + lastst[first] = max( lastst[first], lastst[last] ); + firstst[first] = min( firstst[first], firstst[last] ); + + return ( first ); + } + } + + +/* mark_beginning_as_normal - mark each "beginning" state in a machine + * as being a "normal" (i.e., not trailing context- + * associated) states + * + * synopsis + * + * mark_beginning_as_normal( mach ) + * + * mach - machine to mark + * + * The "beginning" states are the epsilon closure of the first state + */ + +void mark_beginning_as_normal(int mach) +{ + switch ( state_type[mach] ) + { + case STATE_NORMAL: + /* oh, we've already visited here */ + return; + + case STATE_TRAILING_CONTEXT: + state_type[mach] = STATE_NORMAL; + + if ( transchar[mach] == SYM_EPSILON ) + { + if ( trans1[mach] != NO_TRANSITION ) + mark_beginning_as_normal( trans1[mach] ); + + if ( trans2[mach] != NO_TRANSITION ) + mark_beginning_as_normal( trans2[mach] ); + } + break; + + default: + flexerror( "bad state type in mark_beginning_as_normal()" ); + break; + } + } + + +/* mkbranch - make a machine that branches to two machines + * + * synopsis + * + * branch = mkbranch( first, second ); + * + * branch - a machine which matches either first's pattern or second's + * first, second - machines whose patterns are to be or'ed (the | operator) + * + * note that first and second are NEITHER destroyed by the operation. Also, + * the resulting machine CANNOT be used with any other "mk" operation except + * more mkbranch's. Compare with mkor() + */ + +int mkbranch(int first, int second) +{ + int eps; + + if ( first == NO_TRANSITION ) + return ( second ); + + else if ( second == NO_TRANSITION ) + return ( first ); + + eps = mkstate( SYM_EPSILON ); + + mkxtion( eps, first ); + mkxtion( eps, second ); + + return ( eps ); + } + + +/* mkclos - convert a machine into a closure + * + * synopsis + * new = mkclos( state ); + * + * new - a new state which matches the closure of "state" + */ + +int mkclos(int state) +{ + return ( mkopt( mkposcl( state ) ) ); + } + + +/* mkopt - make a machine optional + * + * synopsis + * + * new = mkopt( mach ); + * + * new - a machine which optionally matches whatever mach matched + * mach - the machine to make optional + * + * notes: + * 1. mach must be the last machine created + * 2. mach is destroyed by the call + */ + +int mkopt(int mach) +{ + int eps; + + if ( ! SUPER_FREE_EPSILON(finalst[mach]) ) + { + eps = mkstate( SYM_EPSILON ); + mach = link_machines( mach, eps ); + } + + /* can't skimp on the following if FREE_EPSILON(mach) is true because + * some state interior to "mach" might point back to the beginning + * for a closure + */ + eps = mkstate( SYM_EPSILON ); + mach = link_machines( eps, mach ); + + mkxtion( mach, finalst[mach] ); + + return ( mach ); + } + + +/* mkor - make a machine that matches either one of two machines + * + * synopsis + * + * new = mkor( first, second ); + * + * new - a machine which matches either first's pattern or second's + * first, second - machines whose patterns are to be or'ed (the | operator) + * + * note that first and second are both destroyed by the operation + * the code is rather convoluted because an attempt is made to minimize + * the number of epsilon states needed + */ + +int mkor(int first, int second) +{ + int eps, orend; + + if ( first == NIL ) + return ( second ); + + else if ( second == NIL ) + return ( first ); + + else + { + /* see comment in mkopt() about why we can't use the first state + * of "first" or "second" if they satisfy "FREE_EPSILON" + */ + eps = mkstate( SYM_EPSILON ); + + first = link_machines( eps, first ); + + mkxtion( first, second ); + + if ( SUPER_FREE_EPSILON(finalst[first]) && + accptnum[finalst[first]] == NIL ) + { + orend = finalst[first]; + mkxtion( finalst[second], orend ); + } + + else if ( SUPER_FREE_EPSILON(finalst[second]) && + accptnum[finalst[second]] == NIL ) + { + orend = finalst[second]; + mkxtion( finalst[first], orend ); + } + + else + { + eps = mkstate( SYM_EPSILON ); + + first = link_machines( first, eps ); + orend = finalst[first]; + + mkxtion( finalst[second], orend ); + } + } + + finalst[first] = orend; + return ( first ); + } + + +/* mkposcl - convert a machine into a positive closure + * + * synopsis + * new = mkposcl( state ); + * + * new - a machine matching the positive closure of "state" + */ + +int mkposcl(int state) +{ + int eps; + + if ( SUPER_FREE_EPSILON(finalst[state]) ) + { + mkxtion( finalst[state], state ); + return ( state ); + } + + else + { + eps = mkstate( SYM_EPSILON ); + mkxtion( eps, state ); + return ( link_machines( state, eps ) ); + } + } + + +/* mkrep - make a replicated machine + * + * synopsis + * new = mkrep( mach, lb, ub ); + * + * new - a machine that matches whatever "mach" matched from "lb" + * number of times to "ub" number of times + * + * note + * if "ub" is INFINITY then "new" matches "lb" or more occurrences of "mach" + */ + +int mkrep(int mach, int lb, int ub) +{ + int base_mach, tail, copy, i; + + base_mach = copysingl( mach, lb - 1 ); + + if ( ub == INFINITY ) + { + copy = dupmachine( mach ); + mach = link_machines( mach, + link_machines( base_mach, mkclos( copy ) ) ); + } + + else + { + tail = mkstate( SYM_EPSILON ); + + for ( i = lb; i < ub; ++i ) + { + copy = dupmachine( mach ); + tail = mkopt( link_machines( copy, tail ) ); + } + + mach = link_machines( mach, link_machines( base_mach, tail ) ); + } + + return ( mach ); + } + + +/* mkstate - create a state with a transition on a given symbol + * + * synopsis + * + * state = mkstate( sym ); + * + * state - a new state matching sym + * sym - the symbol the new state is to have an out-transition on + * + * note that this routine makes new states in ascending order through the + * state array (and increments LASTNFA accordingly). The routine DUPMACHINE + * relies on machines being made in ascending order and that they are + * CONTIGUOUS. Change it and you will have to rewrite DUPMACHINE (kludge + * that it admittedly is) + */ + +int mkstate(int sym) +{ + if ( ++lastnfa >= current_mns ) + { + if ( (current_mns += MNS_INCREMENT) >= MAXIMUM_MNS ) + lerrif( "input rules are too complicated (>= %d NFA states)", + current_mns ); + + ++num_reallocs; + + firstst = reallocate_integer_array( firstst, current_mns ); + lastst = reallocate_integer_array( lastst, current_mns ); + finalst = reallocate_integer_array( finalst, current_mns ); + transchar = reallocate_integer_array( transchar, current_mns ); + trans1 = reallocate_integer_array( trans1, current_mns ); + trans2 = reallocate_integer_array( trans2, current_mns ); + accptnum = reallocate_integer_array( accptnum, current_mns ); + assoc_rule = reallocate_integer_array( assoc_rule, current_mns ); + state_type = reallocate_integer_array( state_type, current_mns ); + } + + firstst[lastnfa] = lastnfa; + finalst[lastnfa] = lastnfa; + lastst[lastnfa] = lastnfa; + transchar[lastnfa] = sym; + trans1[lastnfa] = NO_TRANSITION; + trans2[lastnfa] = NO_TRANSITION; + accptnum[lastnfa] = NIL; + assoc_rule[lastnfa] = num_rules; + state_type[lastnfa] = current_state_type; + + /* fix up equivalence classes base on this transition. Note that any + * character which has its own transition gets its own equivalence class. + * Thus only characters which are only in character classes have a chance + * at being in the same equivalence class. E.g. "a|b" puts 'a' and 'b' + * into two different equivalence classes. "[ab]" puts them in the same + * equivalence class (barring other differences elsewhere in the input). + */ + + if ( sym < 0 ) + { + /* we don't have to update the equivalence classes since that was + * already done when the ccl was created for the first time + */ + } + + else if ( sym == SYM_EPSILON ) + ++numeps; + + else + { + if ( useecs ) + /* map NUL's to csize */ + mkechar( sym ? sym : csize, nextecm, ecgroup ); + } + + return ( lastnfa ); + } + + +/* mkxtion - make a transition from one state to another + * + * synopsis + * + * mkxtion( statefrom, stateto ); + * + * statefrom - the state from which the transition is to be made + * stateto - the state to which the transition is to be made + */ + +void mkxtion(int statefrom, int stateto) +{ + if ( trans1[statefrom] == NO_TRANSITION ) + trans1[statefrom] = stateto; + + else if ( (transchar[statefrom] != SYM_EPSILON) || + (trans2[statefrom] != NO_TRANSITION) ) + flexfatal( "found too many transitions in mkxtion()" ); + + else + { /* second out-transition for an epsilon state */ + ++eps2; + trans2[statefrom] = stateto; + } + } + +/* new_rule - initialize for a new rule + * + * synopsis + * + * new_rule(); + * + * the global num_rules is incremented and the any corresponding dynamic + * arrays (such as rule_type[]) are grown as needed. + */ + +void new_rule(void) +{ + if ( ++num_rules >= current_max_rules ) + { + ++num_reallocs; + current_max_rules += MAX_RULES_INCREMENT; + rule_type = reallocate_integer_array( rule_type, current_max_rules ); + rule_linenum = + reallocate_integer_array( rule_linenum, current_max_rules ); + } + + if ( num_rules > MAX_RULE ) + lerrif( "too many rules (> %d)!", MAX_RULE ); + + rule_linenum[num_rules] = linenum; + } diff --git a/modules/libcom/src/flex/parse.y b/modules/libcom/src/flex/parse.y new file mode 100644 index 000000000..dff931578 --- /dev/null +++ b/modules/libcom/src/flex/parse.y @@ -0,0 +1,702 @@ + +/* parse.y - parser for flex input */ + +%token CHAR NUMBER SECTEND SCDECL XSCDECL WHITESPACE NAME PREVCCL EOF_OP + +%{ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + +int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, actvp, rulelen; +int trlcontxt, xcluflg, cclsorted, varlength, variable_trail_rule; +Char clower(); + +static int madeany = false; /* whether we've made the '.' character class */ +int previous_continued_action; /* whether the previous rule's action was '|' */ + +%} + +%% +goal : initlex sect1 sect1end sect2 initforrule + { /* add default rule */ + int def_rule; + + pat = cclinit(); + cclnegate( pat ); + + def_rule = mkstate( -pat ); + + finish_rule( def_rule, false, 0, 0 ); + + for ( i = 1; i <= lastsc; ++i ) + scset[i] = mkbranch( scset[i], def_rule ); + + if ( spprdflt ) + fputs( "YY_FATAL_ERROR( \"flex scanner jammed\" )", + temp_action_file ); + else + fputs( "ECHO", temp_action_file ); + + fputs( ";\n\tYY_BREAK\n", temp_action_file ); + } + ; + +initlex : + { + /* initialize for processing rules */ + + /* create default DFA start condition */ + scinstal( "INITIAL", false ); + } + ; + +sect1 : sect1 startconddecl WHITESPACE namelist1 '\n' + | + | error '\n' + { synerr( "unknown error processing section 1" ); } + ; + +sect1end : SECTEND + ; + +startconddecl : SCDECL + { + /* these productions are separate from the s1object + * rule because the semantics must be done before + * we parse the remainder of an s1object + */ + + xcluflg = false; + } + + | XSCDECL + { xcluflg = true; } + ; + +namelist1 : namelist1 WHITESPACE NAME + { scinstal( nmstr, xcluflg ); } + + | NAME + { scinstal( nmstr, xcluflg ); } + + | error + { synerr( "bad start condition list" ); } + ; + +sect2 : sect2 initforrule flexrule '\n' + | + ; + +initforrule : + { + /* initialize for a parse of one rule */ + trlcontxt = variable_trail_rule = varlength = false; + trailcnt = headcnt = rulelen = 0; + current_state_type = STATE_NORMAL; + previous_continued_action = continued_action; + new_rule(); + } + ; + +flexrule : scon '^' rule + { + pat = $3; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= actvp; ++i ) + scbol[actvsc[i]] = + mkbranch( scbol[actvsc[i]], pat ); + + if ( ! bol_needed ) + { + bol_needed = true; + + if ( performance_report ) + pinpoint_message( + "'^' operator results in sub-optimal performance" ); + } + } + + | scon rule + { + pat = $2; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= actvp; ++i ) + scset[actvsc[i]] = + mkbranch( scset[actvsc[i]], pat ); + } + + | '^' rule + { + pat = $2; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + /* add to all non-exclusive start conditions, + * including the default (0) start condition + */ + + for ( i = 1; i <= lastsc; ++i ) + if ( ! scxclu[i] ) + scbol[i] = mkbranch( scbol[i], pat ); + + if ( ! bol_needed ) + { + bol_needed = true; + + if ( performance_report ) + pinpoint_message( + "'^' operator results in sub-optimal performance" ); + } + } + + | rule + { + pat = $1; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= lastsc; ++i ) + if ( ! scxclu[i] ) + scset[i] = mkbranch( scset[i], pat ); + } + + | scon EOF_OP + { build_eof_action(); } + + | EOF_OP + { + /* this EOF applies to all start conditions + * which don't already have EOF actions + */ + actvp = 0; + + for ( i = 1; i <= lastsc; ++i ) + if ( ! sceof[i] ) + actvsc[++actvp] = i; + + if ( actvp == 0 ) + pinpoint_message( + "warning - all start conditions already have <> rules" ); + + else + build_eof_action(); + } + + | error + { synerr( "unrecognized rule" ); } + ; + +scon : '<' namelist2 '>' + ; + +namelist2 : namelist2 ',' NAME + { + if ( (scnum = sclookup( nmstr )) == 0 ) + format_pinpoint_message( + "undeclared start condition %s", nmstr ); + + else + actvsc[++actvp] = scnum; + } + + | NAME + { + if ( (scnum = sclookup( nmstr )) == 0 ) + format_pinpoint_message( + "undeclared start condition %s", nmstr ); + else + actvsc[actvp = 1] = scnum; + } + + | error + { synerr( "bad start condition list" ); } + ; + +rule : re2 re + { + if ( transchar[lastst[$2]] != SYM_EPSILON ) + /* provide final transition \now/ so it + * will be marked as a trailing context + * state + */ + $2 = link_machines( $2, mkstate( SYM_EPSILON ) ); + + mark_beginning_as_normal( $2 ); + current_state_type = STATE_NORMAL; + + if ( previous_continued_action ) + { + /* we need to treat this as variable trailing + * context so that the backup does not happen + * in the action but before the action switch + * statement. If the backup happens in the + * action, then the rules "falling into" this + * one's action will *also* do the backup, + * erroneously. + */ + if ( ! varlength || headcnt != 0 ) + { + fprintf( stderr, + "%s: warning - trailing context rule at line %d made variable because\n", + program_name, linenum ); + fprintf( stderr, + " of preceding '|' action\n" ); + } + + /* mark as variable */ + varlength = true; + headcnt = 0; + } + + if ( varlength && headcnt == 0 ) + { /* variable trailing context rule */ + /* mark the first part of the rule as the accepting + * "head" part of a trailing context rule + */ + /* by the way, we didn't do this at the beginning + * of this production because back then + * current_state_type was set up for a trail + * rule, and add_accept() can create a new + * state ... + */ + add_accept( $1, num_rules | YY_TRAILING_HEAD_MASK ); + variable_trail_rule = true; + } + + else + trailcnt = rulelen; + + $$ = link_machines( $1, $2 ); + } + + | re2 re '$' + { synerr( "trailing context used twice" ); } + + | re '$' + { + if ( trlcontxt ) + { + synerr( "trailing context used twice" ); + $$ = mkstate( SYM_EPSILON ); + } + + else if ( previous_continued_action ) + { + /* see the comment in the rule for "re2 re" + * above + */ + if ( ! varlength || headcnt != 0 ) + { + fprintf( stderr, + "%s: warning - trailing context rule at line %d made variable because\n", + program_name, linenum ); + fprintf( stderr, + " of preceding '|' action\n" ); + } + + /* mark as variable */ + varlength = true; + headcnt = 0; + } + + trlcontxt = true; + + if ( ! varlength ) + headcnt = rulelen; + + ++rulelen; + trailcnt = 1; + + eps = mkstate( SYM_EPSILON ); + $$ = link_machines( $1, + link_machines( eps, mkstate( '\n' ) ) ); + } + + | re + { + $$ = $1; + + if ( trlcontxt ) + { + if ( varlength && headcnt == 0 ) + /* both head and trail are variable-length */ + variable_trail_rule = true; + else + trailcnt = rulelen; + } + } + ; + + +re : re '|' series + { + varlength = true; + $$ = mkor( $1, $3 ); + } + + | series + { $$ = $1; } + ; + + +re2 : re '/' + { + /* this rule is written separately so + * the reduction will occur before the trailing + * series is parsed + */ + + if ( trlcontxt ) + synerr( "trailing context used twice" ); + else + trlcontxt = true; + + if ( varlength ) + /* we hope the trailing context is fixed-length */ + varlength = false; + else + headcnt = rulelen; + + rulelen = 0; + + current_state_type = STATE_TRAILING_CONTEXT; + $$ = $1; + } + ; + +series : series singleton + { + /* this is where concatenation of adjacent patterns + * gets done + */ + $$ = link_machines( $1, $2 ); + } + + | singleton + { $$ = $1; } + ; + +singleton : singleton '*' + { + varlength = true; + + $$ = mkclos( $1 ); + } + + | singleton '+' + { + varlength = true; + + $$ = mkposcl( $1 ); + } + + | singleton '?' + { + varlength = true; + + $$ = mkopt( $1 ); + } + + | singleton '{' NUMBER ',' NUMBER '}' + { + varlength = true; + + if ( $3 > $5 || $3 < 0 ) + { + synerr( "bad iteration values" ); + $$ = $1; + } + else + { + if ( $3 == 0 ) + $$ = mkopt( mkrep( $1, $3, $5 ) ); + else + $$ = mkrep( $1, $3, $5 ); + } + } + + | singleton '{' NUMBER ',' '}' + { + varlength = true; + + if ( $3 <= 0 ) + { + synerr( "iteration value must be positive" ); + $$ = $1; + } + + else + $$ = mkrep( $1, $3, INFINITY ); + } + + | singleton '{' NUMBER '}' + { + /* the singleton could be something like "(foo)", + * in which case we have no idea what its length + * is, so we punt here. + */ + varlength = true; + + if ( $3 <= 0 ) + { + synerr( "iteration value must be positive" ); + $$ = $1; + } + + else + $$ = link_machines( $1, copysingl( $1, $3 - 1 ) ); + } + + | '.' + { + if ( ! madeany ) + { + /* create the '.' character class */ + anyccl = cclinit(); + ccladd( anyccl, '\n' ); + cclnegate( anyccl ); + + if ( useecs ) + mkeccl( ccltbl + cclmap[anyccl], + ccllen[anyccl], nextecm, + ecgroup, csize, csize ); + + madeany = true; + } + + ++rulelen; + + $$ = mkstate( -anyccl ); + } + + | fullccl + { + if ( ! cclsorted ) + /* sort characters for fast searching. We use a + * shell sort since this list could be large. + */ + cshell( ccltbl + cclmap[$1], ccllen[$1], true ); + + if ( useecs ) + mkeccl( ccltbl + cclmap[$1], ccllen[$1], + nextecm, ecgroup, csize, csize ); + + ++rulelen; + + $$ = mkstate( -$1 ); + } + + | PREVCCL + { + ++rulelen; + + $$ = mkstate( -$1 ); + } + + | '"' string '"' + { $$ = $2; } + + | '(' re ')' + { $$ = $2; } + + | CHAR + { + ++rulelen; + + if ( caseins && $1 >= 'A' && $1 <= 'Z' ) + $1 = clower( $1 ); + + $$ = mkstate( $1 ); + } + ; + +fullccl : '[' ccl ']' + { $$ = $2; } + + | '[' '^' ccl ']' + { + /* *Sigh* - to be compatible Unix lex, negated ccls + * match newlines + */ +#ifdef NOTDEF + ccladd( $3, '\n' ); /* negated ccls don't match '\n' */ + cclsorted = false; /* because we added the newline */ +#endif + cclnegate( $3 ); + $$ = $3; + } + ; + +ccl : ccl CHAR '-' CHAR + { + if ( $2 > $4 ) + synerr( "negative range in character class" ); + + else + { + if ( caseins ) + { + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + if ( $4 >= 'A' && $4 <= 'Z' ) + $4 = clower( $4 ); + } + + for ( i = $2; i <= $4; ++i ) + ccladd( $1, i ); + + /* keep track if this ccl is staying in alphabetical + * order + */ + cclsorted = cclsorted && ($2 > lastchar); + lastchar = $4; + } + + $$ = $1; + } + + | ccl CHAR + { + if ( caseins ) + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + + ccladd( $1, $2 ); + cclsorted = cclsorted && ($2 > lastchar); + lastchar = $2; + $$ = $1; + } + + | + { + cclsorted = true; + lastchar = 0; + $$ = cclinit(); + } + ; + +string : string CHAR + { + if ( caseins ) + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + + ++rulelen; + + $$ = link_machines( $1, mkstate( $2 ) ); + } + + | + { $$ = mkstate( SYM_EPSILON ); } + ; + +%% + + +/* build_eof_action - build the "<>" action for the active start + * conditions + */ + +void build_eof_action() + + { + int i; + + for ( i = 1; i <= actvp; ++i ) + { + if ( sceof[actvsc[i]] ) + format_pinpoint_message( + "multiple <> rules for start condition %s", + scname[actvsc[i]] ); + + else + { + sceof[actvsc[i]] = true; + fprintf( temp_action_file, "case YY_STATE_EOF(%s):\n", + scname[actvsc[i]] ); + } + } + + line_directive_out( temp_action_file ); + } + + +/* synerr - report a syntax error */ + +void synerr( str ) +char str[]; + + { + syntaxerror = true; + pinpoint_message( str ); + } + + +/* format_pinpoint_message - write out a message formatted with one string, + * pinpointing its location + */ + +void format_pinpoint_message( msg, arg ) +char msg[], arg[]; + + { + char errmsg[MAXLINE]; + + (void) sprintf( errmsg, msg, arg ); + pinpoint_message( errmsg ); + } + + +/* pinpoint_message - write out a message, pinpointing its location */ + +void pinpoint_message( str ) +char str[]; + + { + fprintf( stderr, "\"%s\", line %d: %s\n", infilename, linenum, str ); + } + + +/* yyerror - eat up an error message from the parser; + * currently, messages are ignore + */ + +void yyerror( msg ) +char msg[]; + + { + } + +#include "scan.c" +#include "yylex.c" +#include "flex.c" + diff --git a/modules/libcom/src/flex/scan.c b/modules/libcom/src/flex/scan.c new file mode 100644 index 000000000..a12c020bf --- /dev/null +++ b/modules/libcom/src/flex/scan.c @@ -0,0 +1,2251 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton */ + +#define FLEX_SCANNER + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +#else /* ! __cplusplus */ +#ifdef __GNUC__ +#include +void *malloc( size_t ); +void free( void* ); +#else +#include +#endif /* __GNUC__ */ + +#endif /* ! __cplusplus */ + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +#define YY_CHAR unsigned char +# line 1 "scan.l" +#define INITIAL 0 +/* scan.l - scanner for flex input */ +# line 5 "scan.l" +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#undef yywrap + +#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) +#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); + +#undef YY_DECL +#define YY_DECL \ + int flexscan() + +#define RETURNCHAR \ + yylval = yytext[0]; \ + return ( CHAR ); + +#define RETURNNAME \ + (void) strcpy( nmstr, (char *) yytext ); \ + return ( NAME ); + +#define PUT_BACK_STRING(str, start) \ + for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ + unput((str)[i]) + +#define CHECK_REJECT(str) \ + if ( all_upper( str ) ) \ + reject = true; + +#define CHECK_YYMORE(str) \ + if ( all_lower( str ) ) \ + yymore_used = true; +#define SECT2 1 +#define SECT2PROLOG 2 +#define SECT3 3 +#define CODEBLOCK 4 +#define PICKUPDEF 5 +#define SC 6 +#define CARETISBOL 7 +#define NUM 8 +#define QUOTE 9 +#define FIRSTCCL 10 +#define CCL 11 +#define ACTION 12 +#define RECOVER 13 +#define BRACEERROR 14 +#define C_COMMENT 15 +#define ACTION_COMMENT 16 +#define ACTION_STRING 17 +#define PERCENT_BRACE_ACTION 18 +#define USED_LIST 19 +#define CODEBLOCK_2 20 +#define XLATION 21 +# line 76 "scan.l" + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ + yyleng = yy_cp - yy_bp; \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +#define YY_END_OF_BUFFER 121 +typedef int yy_state_type; +static const short int yy_acclist[482] = + { 0, + 16444,16444, 119, 119, 121, 19, 120, 7, 19, 120, + 18, 120, 19, 120, 19, 120, 16, 19, 120, 1, + 7, 19, 120, 17, 18, 120, 19, 120, 19, 120, + 19, 120, 19, 120, 15, 16, 19, 120, 67, 120, + 59, 67, 120,16444, 8252, 68, 120, 67, 120, 53, + 67, 120, 67, 120, 66, 67, 120, 51, 67, 120, + 67, 120, 67, 120, 67, 120, 50, 59, 67, 120, + 16444, 49, 8252, 61, 68, 120, 67, 120, 67, 120, + 52, 67, 120, 120, 47, 120, 120, 119, 120, 119, + 120, 119, 120, 28, 120, 29, 120, 28, 120, 28, + + 120, 28, 120, 28, 120, 28, 120, 31, 120, 30, + 120, 32, 120, 120, 73, 120, 120, 69, 73, 120, + 70, 73, 120, 72, 73, 120, 74, 120, 88, 120, + 89, 120, 88, 120, 86, 88, 120, 85, 88, 120, + 87, 88, 120, 75, 120, 77, 120, 120, 76, 120, + 75, 120, 81, 120, 80, 81, 120, 81, 120, 81, + 120, 83, 120, 83, 120, 83, 120, 84, 120, 99, + 105, 120, 104, 120, 105, 120, 103, 105, 120, 105, + 120, 105, 120, 100, 105, 120, 100, 105, 120, 100, + 105, 120, 97, 105, 120, 98, 105, 120, 120, 33, + + 120, 120, 91, 120, 120, 90, 120, 22, 120, 24, + 120, 120, 23, 120, 107, 110, 120, 109, 120, 110, + 120, 108, 110, 120, 111, 115, 120, 113, 120, 115, + 120, 114, 115, 120, 115, 120, 95, 120, 95, 120, + 96, 120, 95, 120, 95, 120, 95, 120, 95, 120, + 95, 120, 38, 120, 35, 120, 34, 120, 120, 38, + 120, 38, 120, 44, 120, 42, 44, 120, 45, 120, + 44, 120, 44, 120, 44, 120, 41, 44, 120, 41, + 42, 44, 120, 41, 44, 120, 41, 44, 120, 40, + 41, 44, 120, 41, 44, 120, 7, 18, 16, 1, + + 7, 17, 18, 2, 14, 8, 14, 12, 4, 5, + 3, 15, 16, 59,16444, 8252, 8252, 68, 56, 117, + 117, 117, 55, 54, 55, 50, 59,16444, 49, 8252, + 61, 49, 8252, 61, 68, 63, 50, 47, 46, 119, + 119, 119, 28, 29, 28, 28, 28, 28, 31, 30, + 32, 71, 72, 89, 85, 77, 118, 118, 118, 78, + 79, 82, 99, 104, 102, 101, 100, 100, 100, 33, + 91, 22, 24, 20, 107, 109, 106, 111, 113, 112, + 95, 95, 95, 96, 92, 95, 95, 95, 95, 38, + 35, 34, 38, 38, 42, 45, 43, 43, 43, 42, + + 40, 13, 14, 8, 8, 14, 12, 4, 5, 6, + 57, 58, 64, 117, 117, 55, 55, 65, 63, 28, + 28, 28, 25, 118, 118, 100, 100, 21, 92, 95, + 92, 95, 95, 38, 38, 39, 43, 43, 11, 4, + 11, 13, 5, 117, 28, 28, 118, 100, 100, 95, + 95, 38, 38, 43, 9, 28, 28, 100, 100, 95, + 95, 38, 38, 26, 28, 27, 28, 93, 100, 94, + 100, 93, 95, 94, 95, 36, 38, 37, 38, 10, + 62 + } ; + +static const short int yy_accept[392] = + { 0, + 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 8, 11, 13, 15, + 17, 20, 24, 27, 29, 31, 33, 35, 39, 41, + 45, 48, 50, 53, 55, 58, 61, 63, 65, 67, + 72, 77, 79, 81, 84, 85, 87, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, + 114, 115, 117, 118, 121, 124, 127, 129, 131, 133, + + 135, 138, 141, 144, 146, 148, 149, 151, 153, 155, + 158, 160, 162, 164, 166, 168, 170, 173, 175, 177, + 180, 182, 184, 187, 190, 193, 196, 199, 200, 202, + 203, 205, 206, 208, 210, 212, 213, 215, 218, 220, + 222, 225, 228, 230, 232, 235, 237, 239, 241, 243, + 245, 247, 249, 251, 253, 255, 257, 259, 260, 262, + 264, 266, 269, 271, 273, 275, 277, 280, 284, 287, + 290, 294, 297, 298, 299, 299, 299, 300, 302, 304, + 304, 304, 305, 305, 306, 308, 308, 309, 310, 310, + 310, 311, 311, 312, 314, 316, 317, 317, 317, 317, + + 319, 320, 320, 320, 320, 321, 322, 323, 324, 325, + 326, 329, 332, 332, 336, 337, 338, 338, 339, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 365, 366, 366, 367, + 368, 369, 370, 370, 371, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 397, 398, 399, 400, 401, 402, 402, 403, 403, + 404, 405, 407, 407, 408, 409, 409, 409, 409, 410, + + 410, 411, 411, 412, 412, 413, 413, 413, 414, 414, + 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 424, 424, 425, 426, 427, 428, 428, 429, 429, + 431, 432, 433, 434, 435, 436, 436, 437, 437, 438, + 439, 439, 440, 440, 441, 441, 443, 443, 443, 443, + 444, 444, 444, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 455, 456, 456, 456, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 464, 464, + 466, 468, 470, 472, 474, 476, 478, 480, 481, 482, + 482 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 4, 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6, 1, 7, 8, 9, 10, 1, 11, 12, + 12, 13, 12, 14, 15, 12, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, 1, 18, + 1, 19, 12, 1, 25, 26, 27, 28, 29, 30, + 24, 24, 24, 31, 32, 24, 33, 34, 35, 32, + 24, 36, 37, 38, 39, 24, 24, 40, 41, 24, + 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, + + 29, 30, 24, 24, 24, 31, 32, 24, 33, 34, + 35, 32, 24, 36, 37, 38, 39, 24, 24, 40, + 41, 24, 42, 43, 44, 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 + } ; + +static const YY_CHAR yy_meta[45] = + { 0, + 1, 2, 3, 2, 4, 2, 5, 1, 1, 1, + 6, 1, 7, 1, 8, 6, 9, 1, 1, 1, + 10, 11, 1, 12, 13, 13, 13, 13, 13, 13, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 6, 1, 14 + } ; + +static const short int yy_base[454] = + { 0, + 0, 44, 87, 129, 92, 99, 106, 107, 172, 1520, + 111, 116, 216, 0, 1506, 1464, 123, 258, 141, 148, + 259, 262, 266, 286, 308, 0, 120, 151, 261, 350, + 155, 265, 281, 287, 351, 354, 394, 0, 437, 477, + 0, 0, 517, 537, 1475, 1567, 293, 1567, 1471, 1394, + 0, 360, 1567, 1416, 154, 549, 1405, 0, 1567, 590, + 1567, 1402, 1567, 365, 1567, 1386, 1382, 84, 633, 676, + 1567, 1396, 350, 1567, 158, 0, 161, 301, 1567, 371, + 0, 1567, 1395, 0, 1365, 1352, 1341, 0, 375, 1567, + 1381, 1567, 1567, 1567, 1347, 0, 1567, 1567, 1567, 1360, + + 1567, 1341, 1567, 1567, 1567, 1354, 1567, 427, 1567, 1567, + 428, 1341, 1567, 0, 429, 1567, 0, 1567, 1343, 1567, + 282, 1330, 0, 1313, 1291, 1567, 1567, 375, 1567, 379, + 1567, 1326, 1567, 0, 1567, 1325, 1293, 0, 1567, 1305, + 1279, 0, 1567, 1291, 1567, 0, 0, 381, 1567, 1283, + 1241, 0, 1255, 1242, 0, 384, 1567, 1274, 1241, 1228, + 1567, 445, 1567, 1253, 1217, 431, 1567, 448, 1239, 1203, + 1219, 436, 453, 1567, 1232, 458, 0, 482, 1567, 1218, + 467, 1567, 472, 487, 488, 1210, 493, 0, 498, 239, + 0, 493, 1567, 0, 0, 1567, 1212, 1169, 502, 1567, + + 1567, 1181, 487, 489, 1567, 1185, 0, 0, 1567, 705, + 0, 1567, 1198, 1567, 0, 1567, 507, 0, 511, 1567, + 512, 1567, 521, 0, 1567, 0, 1143, 1140, 749, 0, + 526, 1567, 1567, 0, 1567, 1155, 1567, 1567, 1129, 0, + 1567, 1567, 1567, 0, 1567, 514, 1567, 1140, 1567, 0, + 1108, 1099, 528, 1567, 531, 1567, 0, 1567, 541, 0, + 1567, 1567, 0, 1567, 1567, 0, 546, 1087, 1567, 793, + 0, 1095, 1092, 0, 547, 1567, 1062, 1059, 558, 1567, + 563, 1567, 842, 0, 573, 841, 599, 1567, 854, 604, + 0, 605, 552, 610, 0, 615, 817, 826, 0, 558, + + 1567, 567, 1567, 568, 1567, 577, 819, 1567, 605, 821, + 836, 0, 0, 0, 1567, 0, 816, 809, 0, 1567, + 594, 610, 824, 0, 811, 804, 620, 1567, 625, 0, + 0, 767, 713, 718, 693, 685, 1567, 724, 709, 0, + 626, 1567, 633, 0, 690, 1567, 680, 688, 696, 0, + 693, 680, 835, 1567, 694, 682, 1567, 690, 664, 657, + 596, 597, 562, 1567, 516, 1567, 518, 677, 682, 436, + 437, 241, 238, 132, 136, 105, 98, 78, 79, 0, + 0, 0, 0, 0, 0, 0, 0, 1567, 1567, 1567, + 865, 879, 893, 907, 921, 935, 949, 963, 977, 991, + + 1005, 1019, 1033, 1047, 1061, 1075, 1082, 1095, 1109, 1115, + 1128, 1142, 1156, 1170, 1184, 1198, 1205, 1218, 1225, 1238, + 1252, 1266, 1280, 1291, 1298, 1311, 1325, 1339, 1353, 1367, + 1381, 1388, 1401, 1415, 1429, 693, 695, 1443, 1457, 360, + 1471, 1484, 380, 1498, 700, 1512, 1519, 1525, 701, 1538, + 702, 1552, 703 + } ; + +static const short int yy_def[454] = + { 0, + 390, 390, 391, 391, 392, 392, 393, 393, 390, 9, + 394, 394, 390, 13, 395, 395, 396, 396, 397, 397, + 398, 398, 399, 399, 390, 25, 400, 400, 395, 395, + 401, 401, 402, 402, 403, 403, 390, 37, 404, 404, + 37, 37, 405, 406, 390, 390, 390, 390, 390, 390, + 407, 390, 390, 390, 408, 409, 390, 410, 390, 390, + 390, 390, 390, 390, 390, 390, 411, 412, 390, 390, + 390, 390, 390, 390, 413, 414, 413, 415, 390, 415, + 416, 390, 390, 417, 417, 417, 416, 418, 390, 390, + 390, 390, 390, 390, 390, 419, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 412, 390, 390, + 420, 421, 390, 422, 412, 390, 423, 390, 390, 390, + 424, 390, 425, 425, 425, 390, 390, 426, 390, 426, + 390, 390, 390, 427, 390, 390, 390, 428, 390, 390, + 390, 429, 390, 390, 390, 430, 431, 431, 390, 390, + 431, 432, 432, 432, 433, 390, 390, 390, 433, 433, + 390, 390, 390, 390, 390, 434, 390, 390, 390, 390, + 390, 434, 390, 390, 390, 390, 407, 390, 390, 390, + 408, 390, 408, 390, 435, 390, 390, 436, 390, 390, + 437, 438, 390, 410, 60, 390, 390, 390, 439, 390, + + 390, 390, 411, 411, 390, 390, 440, 441, 390, 441, + 70, 390, 390, 390, 442, 390, 413, 414, 413, 390, + 415, 390, 415, 416, 390, 417, 417, 417, 390, 418, + 390, 390, 390, 419, 390, 390, 390, 390, 390, 443, + 390, 390, 390, 423, 390, 424, 390, 424, 390, 425, + 425, 425, 426, 390, 426, 390, 427, 390, 444, 428, + 390, 390, 429, 390, 390, 431, 431, 431, 390, 390, + 432, 432, 432, 433, 390, 390, 433, 433, 390, 390, + 390, 390, 390, 445, 390, 390, 390, 390, 390, 390, + 435, 435, 446, 390, 447, 446, 390, 390, 448, 438, + + 390, 438, 390, 439, 390, 439, 390, 390, 411, 411, + 390, 449, 441, 210, 390, 442, 417, 417, 229, 390, + 450, 450, 390, 451, 425, 425, 444, 390, 444, 270, + 452, 432, 432, 433, 433, 390, 390, 390, 390, 453, + 446, 390, 446, 447, 446, 390, 446, 390, 390, 448, + 390, 411, 310, 390, 417, 417, 390, 425, 425, 432, + 432, 433, 433, 390, 390, 390, 390, 411, 411, 417, + 417, 425, 425, 432, 432, 433, 433, 390, 390, 417, + 417, 425, 425, 432, 432, 433, 433, 390, 390, 0, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390 + } ; + +static const short int yy_nxt[1612] = + { 0, + 46, 47, 48, 47, 49, 47, 46, 46, 46, 50, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 46, 46, 46, 46, 52, 53, 52, 54, 52, + 46, 55, 46, 56, 46, 46, 46, 46, 46, 57, + 46, 46, 46, 46, 46, 46, 46, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 46, 46, 46, 60, 61, + 60, 62, 60, 63, 76, 64, 77, 389, 65, 65, + + 206, 76, 65, 77, 66, 388, 67, 68, 79, 79, + 80, 80, 89, 90, 89, 91, 89, 89, 90, 89, + 91, 89, 129, 207, 130, 99, 387, 100, 69, 65, + 70, 71, 70, 72, 70, 63, 101, 64, 73, 102, + 65, 65, 386, 105, 65, 106, 66, 107, 67, 68, + 105, 74, 106, 129, 107, 130, 182, 135, 183, 136, + 218, 108, 219, 218, 385, 219, 103, 137, 108, 384, + 69, 65, 81, 81, 82, 81, 83, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 84, 84, 84, 84, 84, + + 84, 84, 84, 84, 84, 84, 84, 85, 84, 84, + 84, 84, 86, 81, 81, 81, 92, 92, 93, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 94, + 92, 92, 92, 92, 95, 92, 92, 92, 92, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 92, 92, 92, + 99, 93, 100, 131, 93, 132, 383, 135, 93, 136, + 93, 101, 297, 110, 102, 298, 110, 137, 382, 111, + 114, 112, 111, 139, 112, 140, 115, 116, 93, 139, + 93, 140, 247, 141, 173, 174, 173, 175, 173, 141, + + 114, 103, 248, 222, 133, 223, 115, 116, 117, 117, + 118, 117, 119, 117, 120, 117, 117, 117, 121, 117, + 117, 117, 117, 122, 117, 117, 117, 117, 117, 117, + 117, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 124, 123, 123, 123, 123, 125, 126, + 117, 127, 131, 143, 132, 144, 143, 145, 144, 215, + 145, 178, 179, 178, 180, 178, 201, 201, 312, 201, + 201, 146, 312, 222, 146, 223, 231, 254, 231, 255, + 231, 254, 267, 255, 267, 275, 267, 275, 324, 275, + 268, 216, 324, 133, 147, 148, 149, 148, 150, 148, + + 147, 147, 147, 151, 147, 147, 147, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 147, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, + 152, 152, 152, 152, 154, 147, 147, 147, 156, 157, + 156, 158, 156, 206, 239, 206, 279, 283, 279, 285, + 279, 285, 283, 285, 173, 174, 173, 175, 173, 287, + 288, 287, 289, 287, 286, 381, 207, 240, 207, 182, + 284, 183, 159, 380, 182, 284, 183, 160, 156, 157, + 156, 158, 156, 178, 179, 178, 180, 178, 290, 292, + 290, 292, 290, 292, 294, 301, 294, 302, 294, 287, + + 288, 287, 289, 296, 305, 309, 306, 204, 308, 218, + 203, 219, 159, 218, 222, 219, 223, 160, 162, 163, + 162, 164, 162, 222, 247, 223, 165, 231, 310, 231, + 254, 231, 255, 254, 248, 255, 379, 166, 168, 163, + 168, 169, 168, 328, 378, 329, 170, 267, 275, 267, + 275, 267, 275, 171, 342, 268, 343, 172, 185, 279, + 301, 279, 302, 279, 336, 337, 336, 338, 336, 301, + 305, 302, 306, 186, 285, 187, 285, 186, 285, 305, + 186, 306, 186, 186, 187, 188, 189, 190, 191, 286, + 192, 195, 196, 195, 197, 195, 320, 377, 321, 198, + + 287, 288, 287, 289, 287, 290, 292, 290, 292, 290, + 292, 294, 320, 294, 321, 294, 345, 346, 345, 347, + 345, 352, 328, 376, 329, 204, 308, 328, 342, 329, + 343, 375, 199, 208, 208, 342, 208, 343, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 209, + 208, 208, 208, 208, 208, 208, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 208, 208, 208, 211, 212, 211, + 213, 211, 346, 374, 343, 198, 336, 337, 336, 338, + 336, 345, 346, 345, 347, 345, 368, 204, 308, 373, + + 204, 308, 204, 308, 295, 295, 299, 299, 340, 354, + 357, 364, 340, 354, 357, 364, 372, 371, 199, 314, + 370, 314, 367, 366, 365, 282, 337, 363, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 362, 361, 315, 319, + 319, 320, 319, 321, 319, 319, 319, 319, 319, 319, + 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + 319, 319, 322, 322, 322, 322, 322, 322, 322, 322, + 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + 319, 319, 319, 330, 330, 360, 330, 331, 330, 330, + + 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, + 330, 330, 330, 330, 330, 330, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, + 331, 331, 331, 331, 330, 330, 330, 353, 359, 358, + 238, 204, 308, 356, 355, 353, 353, 353, 353, 353, + 353, 369, 205, 351, 349, 348, 288, 286, 339, 369, + 369, 369, 369, 369, 369, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 78, 78, 78, 78, 78, 78, 78, + + 78, 78, 78, 78, 78, 78, 78, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 167, 167, 167, 167, 167, + 167, 167, 167, 167, 167, 167, 167, 167, 167, 177, + 177, 335, 334, 177, 177, 181, 181, 181, 181, 181, + + 181, 181, 181, 181, 181, 181, 181, 181, 181, 184, + 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 194, 194, 333, 332, 194, 194, 203, 203, + 270, 326, 203, 203, 203, 203, 203, 203, 325, 203, + 203, 203, 205, 205, 246, 323, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 220, 236, 318, 317, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 224, 224, + + 212, 311, 224, 224, 224, 224, 224, 224, 224, 307, + 303, 224, 226, 226, 196, 293, 226, 226, 230, 230, + 179, 230, 230, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 234, 234, 174, 286, 234, 234, 238, 238, + 281, 280, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 241, 241, 281, 280, 241, 241, 241, 241, + 241, 241, 241, 241, 241, 241, 243, 243, 278, 277, + 243, 243, 243, 243, 243, 243, 276, 243, 243, 243, + 244, 244, 273, 272, 270, 269, 244, 244, 244, 244, + 244, 246, 246, 264, 262, 246, 246, 246, 246, 246, + + 246, 246, 246, 246, 246, 250, 250, 261, 259, 250, + 250, 253, 253, 253, 253, 253, 253, 253, 253, 253, + 253, 253, 253, 253, 253, 257, 257, 258, 256, 257, + 257, 252, 257, 257, 257, 257, 257, 257, 257, 260, + 260, 251, 249, 260, 260, 245, 260, 260, 260, 260, + 260, 260, 260, 263, 263, 242, 237, 236, 263, 263, + 263, 263, 235, 263, 263, 263, 263, 265, 265, 233, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 266, 266, 232, 229, 266, 266, 266, 266, 266, + 266, 266, 228, 227, 266, 271, 271, 225, 214, 271, + + 271, 274, 204, 202, 200, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 282, 282, 193, 179, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 291, + 291, 176, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 313, 313, 174, 390, 313, 313, 313, 313, 313, + 313, 313, 313, 313, 316, 316, 97, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 327, 327, + + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 344, 344, 97, 87, + 344, 344, 350, 350, 390, 390, 350, 350, 322, 322, + 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + 322, 322, 331, 331, 390, 331, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 45, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390 + } ; + +static const short int yy_chk[1612] = + { 0, + 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, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 5, 3, 5, 379, 3, 3, + + 68, 6, 3, 6, 3, 378, 3, 3, 7, 8, + 7, 8, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 27, 68, 27, 17, 377, 17, 3, 3, + 4, 4, 4, 4, 4, 4, 17, 4, 4, 17, + 4, 4, 376, 19, 4, 19, 4, 19, 4, 4, + 20, 4, 20, 28, 20, 28, 55, 31, 55, 31, + 75, 19, 75, 77, 375, 77, 17, 31, 20, 374, + 4, 4, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 18, 21, 18, 29, 22, 29, 373, 32, 23, 32, + 23, 18, 190, 21, 18, 190, 22, 32, 372, 21, + 23, 21, 22, 33, 22, 33, 23, 23, 24, 34, + 24, 34, 121, 33, 47, 47, 47, 47, 47, 34, + + 24, 18, 121, 78, 29, 78, 24, 24, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 30, 35, 30, 35, 36, 35, 36, 73, + 36, 52, 52, 52, 52, 52, 64, 64, 440, 64, + 64, 35, 440, 80, 36, 80, 89, 128, 89, 128, + 89, 130, 148, 130, 148, 156, 148, 156, 443, 156, + 148, 73, 443, 30, 37, 37, 37, 37, 37, 37, + + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 39, 39, + 39, 39, 39, 108, 111, 115, 162, 166, 162, 168, + 162, 168, 172, 168, 173, 173, 173, 173, 173, 176, + 176, 176, 176, 176, 168, 371, 108, 111, 115, 181, + 166, 181, 39, 370, 183, 172, 183, 39, 40, 40, + 40, 40, 40, 178, 178, 178, 178, 178, 184, 185, + 184, 185, 184, 185, 187, 192, 187, 192, 187, 189, + + 189, 189, 189, 189, 199, 204, 199, 203, 203, 217, + 204, 217, 40, 219, 221, 219, 221, 40, 43, 43, + 43, 43, 43, 223, 246, 223, 43, 231, 204, 231, + 253, 231, 253, 255, 246, 255, 367, 43, 44, 44, + 44, 44, 44, 259, 365, 259, 44, 267, 275, 267, + 275, 267, 275, 44, 293, 267, 293, 44, 56, 279, + 300, 279, 300, 279, 281, 281, 281, 281, 281, 302, + 304, 302, 304, 56, 285, 56, 285, 56, 285, 306, + 56, 306, 56, 56, 56, 56, 56, 56, 56, 285, + 56, 60, 60, 60, 60, 60, 321, 363, 321, 60, + + 287, 287, 287, 287, 287, 290, 292, 290, 292, 290, + 292, 294, 322, 294, 322, 294, 296, 296, 296, 296, + 296, 309, 327, 362, 327, 309, 309, 329, 341, 329, + 341, 361, 60, 69, 69, 343, 69, 343, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, + 70, 70, 347, 360, 347, 70, 336, 336, 336, 336, + 336, 345, 345, 345, 345, 345, 352, 368, 368, 359, + + 352, 352, 369, 369, 436, 436, 437, 437, 445, 449, + 451, 453, 445, 449, 451, 453, 358, 356, 70, 210, + 355, 210, 351, 349, 348, 339, 338, 335, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 334, 333, 210, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 270, 270, 332, 270, 270, 270, 270, + + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 310, 326, 325, + 323, 310, 310, 318, 317, 310, 310, 310, 310, 310, + 310, 353, 311, 307, 298, 297, 289, 286, 283, 353, + 353, 353, 353, 353, 353, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 393, 393, 393, 393, 393, 393, 393, + + 393, 393, 393, 393, 393, 393, 393, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 395, 395, 395, 395, 395, 395, 395, 395, 395, + 395, 395, 395, 395, 395, 396, 396, 396, 396, 396, + 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, + 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, + 397, 397, 397, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 399, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, + 399, 400, 400, 400, 400, 400, 400, 400, 400, 400, + + 400, 400, 400, 400, 400, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 402, + 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, + 402, 402, 402, 403, 403, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 403, 403, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, + 407, 278, 277, 407, 407, 408, 408, 408, 408, 408, + + 408, 408, 408, 408, 408, 408, 408, 408, 408, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 410, 410, 273, 272, 410, 410, 411, 411, + 268, 252, 411, 411, 411, 411, 411, 411, 251, 411, + 411, 411, 412, 412, 248, 239, 412, 412, 412, 412, + 412, 412, 412, 412, 412, 412, 413, 413, 413, 413, + 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, + 414, 236, 228, 227, 414, 414, 414, 414, 414, 414, + 414, 414, 414, 414, 415, 415, 415, 415, 415, 415, + 415, 415, 415, 415, 415, 415, 415, 415, 416, 416, + + 213, 206, 416, 416, 416, 416, 416, 416, 416, 202, + 198, 416, 417, 417, 197, 186, 417, 417, 418, 418, + 180, 418, 418, 418, 418, 418, 418, 418, 418, 418, + 418, 418, 419, 419, 175, 171, 419, 419, 420, 420, + 170, 169, 420, 420, 420, 420, 420, 420, 420, 420, + 420, 420, 421, 421, 165, 164, 421, 421, 421, 421, + 421, 421, 421, 421, 421, 421, 422, 422, 160, 159, + 422, 422, 422, 422, 422, 422, 158, 422, 422, 422, + 423, 423, 154, 153, 151, 150, 423, 423, 423, 423, + 423, 424, 424, 144, 141, 424, 424, 424, 424, 424, + + 424, 424, 424, 424, 424, 425, 425, 140, 137, 425, + 425, 426, 426, 426, 426, 426, 426, 426, 426, 426, + 426, 426, 426, 426, 426, 427, 427, 136, 132, 427, + 427, 125, 427, 427, 427, 427, 427, 427, 427, 428, + 428, 124, 122, 428, 428, 119, 428, 428, 428, 428, + 428, 428, 428, 429, 429, 112, 106, 102, 429, 429, + 429, 429, 100, 429, 429, 429, 429, 430, 430, 95, + 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, + 430, 431, 431, 91, 87, 431, 431, 431, 431, 431, + 431, 431, 86, 85, 431, 432, 432, 83, 72, 432, + + 432, 433, 67, 66, 62, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 434, 434, 57, 54, 434, + 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, + 435, 50, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 438, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 438, 439, 439, 439, + 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, + 439, 441, 441, 49, 45, 441, 441, 441, 441, 441, + 441, 441, 441, 441, 442, 442, 16, 442, 442, 442, + 442, 442, 442, 442, 442, 442, 442, 442, 444, 444, + + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 444, 446, 446, 446, 446, 446, 446, 446, 446, + 446, 446, 446, 446, 446, 446, 447, 447, 15, 10, + 447, 447, 448, 448, 0, 0, 448, 448, 450, 450, + 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, + 450, 450, 452, 452, 0, 452, 452, 452, 452, 452, + 452, 452, 452, 452, 452, 452, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390 + } ; + +static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr; +static YY_CHAR *yy_full_match; +static int yy_lp; +static int yy_looking_for_trail_begin = 0; +static int yy_full_lp; +static int *yy_full_state; +#define YY_TRAILING_MASK 0x2000 +#define YY_TRAILING_HEAD_MASK 0x4000 +#define REJECT \ +{ \ +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ +yy_cp = yy_full_match; /* restore poss. backed-over text */ \ +yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \ +yy_state_ptr = yy_full_state; /* restore orig. state */ \ +yy_current_state = *yy_state_ptr; /* restore curr. state */ \ +++yy_lp; \ +goto find_rule; \ +} +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +void yy_load_buffer_state ( void ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +YY_DECL + { + yy_state_type yy_current_state; + YY_CHAR *yy_cp, *yy_bp; + int yy_act; + + + static int bracelevel, didadef; + int i, indented_code = false, checking_used = false, new_xlation = false; + int doing_codeblock = false; + Char nmdef[MAXLINE], myesc(); + + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + if ( yy_bp[-1] == '\n' ) + ++yy_current_state; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[(int)*yy_cp]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + ++yy_cp; + } + while ( yy_current_state != 390 ); + +yy_find_action: + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backtracking */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[yy_lp]; + if ( yy_act & YY_TRAILING_HEAD_MASK || + yy_looking_for_trail_begin ) + { + if ( yy_act == yy_looking_for_trail_begin ) + { + yy_looking_for_trail_begin = 0; + yy_act &= ~YY_TRAILING_HEAD_MASK; + break; + } + } + else if ( yy_act & YY_TRAILING_MASK ) + { + yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK; + yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK; + } + else + { + yy_full_match = yy_cp; + yy_full_state = yy_state_ptr; + yy_full_lp = yy_lp; + break; + } + ++yy_lp; + goto find_rule; + } + --yy_cp; + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + + + switch ( yy_act ) + { +case 1: +# line 82 "scan.l" +indented_code = true; BEGIN(CODEBLOCK); + YY_BREAK +case 2: +# line 83 "scan.l" +++linenum; /* treat as a comment */ + YY_BREAK +case 3: +# line 84 "scan.l" +ECHO; BEGIN(C_COMMENT); + YY_BREAK +case 4: +# line 85 "scan.l" +return ( SCDECL ); + YY_BREAK +case 5: +# line 86 "scan.l" +return ( XSCDECL ); + YY_BREAK +case 6: +# line 87 "scan.l" +{ + ++linenum; + line_directive_out( stdout ); + indented_code = false; + BEGIN(CODEBLOCK); + } + YY_BREAK +case 7: +# line 94 "scan.l" +return ( WHITESPACE ); + YY_BREAK +case 8: +# line 96 "scan.l" +{ + sectnum = 2; + line_directive_out( stdout ); + BEGIN(SECT2PROLOG); + return ( SECTEND ); + } + YY_BREAK +case 9: +# line 103 "scan.l" +{ + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_USED; BEGIN(USED_LIST); + } + YY_BREAK +case 10: +# line 107 "scan.l" +{ + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + } + YY_BREAK +case 11: +# line 114 "scan.l" +{ +#ifdef NOTDEF + fprintf( stderr, + "old-style lex command at line %d ignored:\n\t%s", + linenum, yytext ); +#endif + ++linenum; + } + YY_BREAK +case 12: +# line 123 "scan.l" +/* ignore old lex directive */ + YY_BREAK +case 13: +# line 125 "scan.l" +{ + ++linenum; + xlation = + (int *) malloc( sizeof( int ) * (unsigned) csize ); + + if ( ! xlation ) + flexfatal( + "dynamic memory failure building %t table" ); + + for ( i = 0; i < csize; ++i ) + xlation[i] = 0; + + num_xlations = 0; + + BEGIN(XLATION); + } + YY_BREAK +case 14: +# line 142 "scan.l" +synerr( "unrecognized '%' directive" ); + YY_BREAK +case 15: +# line 144 "scan.l" +{ + (void) strcpy( nmstr, (char *) yytext ); + didadef = false; + BEGIN(PICKUPDEF); + } + YY_BREAK +case 16: +# line 150 "scan.l" +RETURNNAME; + YY_BREAK +case 17: +# line 151 "scan.l" +++linenum; /* allows blank lines in section 1 */ + YY_BREAK +case 18: +# line 152 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 19: +# line 153 "scan.l" +synerr( "illegal character" ); BEGIN(RECOVER); + YY_BREAK +case 20: +# line 156 "scan.l" +ECHO; BEGIN(INITIAL); + YY_BREAK +case 21: +# line 157 "scan.l" +++linenum; ECHO; BEGIN(INITIAL); + YY_BREAK +case 22: +# line 158 "scan.l" +ECHO; + YY_BREAK +case 23: +# line 159 "scan.l" +ECHO; + YY_BREAK +case 24: +# line 160 "scan.l" +++linenum; ECHO; + YY_BREAK +case 25: +# line 163 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 26: +# line 164 "scan.l" +ECHO; CHECK_REJECT(yytext); + YY_BREAK +case 27: +# line 165 "scan.l" +ECHO; CHECK_YYMORE(yytext); + YY_BREAK +case 28: +# line 166 "scan.l" +ECHO; + YY_BREAK +case 29: +# line 167 "scan.l" +{ + ++linenum; + ECHO; + if ( indented_code ) + BEGIN(INITIAL); + } + YY_BREAK +case 30: +# line 175 "scan.l" +/* separates name and definition */ + YY_BREAK +case 31: +# line 177 "scan.l" +{ + (void) strcpy( (char *) nmdef, (char *) yytext ); + + for ( i = strlen( (char *) nmdef ) - 1; + i >= 0 && + (nmdef[i] == ' ' || nmdef[i] == '\t'); + --i ) + ; + + nmdef[i + 1] = '\0'; + + ndinstal( nmstr, nmdef ); + didadef = true; + } + YY_BREAK +case 32: +# line 192 "scan.l" +{ + if ( ! didadef ) + synerr( "incomplete name definition" ); + BEGIN(INITIAL); + ++linenum; + } + YY_BREAK +case 33: +# line 199 "scan.l" +++linenum; BEGIN(INITIAL); RETURNNAME; + YY_BREAK +case 34: +# line 202 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 35: +# line 203 "scan.l" + + YY_BREAK +case 36: +# line 204 "scan.l" +{ + if ( all_upper( yytext ) ) + reject_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } + YY_BREAK +case 37: +# line 210 "scan.l" +{ + if ( all_lower( yytext ) ) + yymore_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } + YY_BREAK +case 38: +# line 216 "scan.l" +synerr( "unrecognized %used/%unused construct" ); + YY_BREAK +case 39: +# line 219 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 40: +# line 220 "scan.l" +++num_xlations; new_xlation = true; + YY_BREAK +case 41: +# line 221 "scan.l" +synerr( "bad row in translation table" ); + YY_BREAK +case 42: +# line 222 "scan.l" +/* ignore whitespace */ + YY_BREAK +case 43: +# line 224 "scan.l" +{ + xlation[myesc( yytext )] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + YY_BREAK +case 44: +# line 229 "scan.l" +{ + xlation[yytext[0]] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + YY_BREAK +case 45: +# line 235 "scan.l" +++linenum; + YY_BREAK +case 46: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 238 "scan.l" +{ + ++linenum; + ACTION_ECHO; + MARK_END_OF_PROLOG; + BEGIN(SECT2); + } + YY_BREAK +case 47: +# line 245 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case YY_STATE_EOF(SECT2PROLOG): +# line 247 "scan.l" +MARK_END_OF_PROLOG; yyterminate(); + YY_BREAK +case 49: +# line 249 "scan.l" +++linenum; /* allow blank lines in section 2 */ + YY_BREAK +case 50: +# line 251 "scan.l" +{ + indented_code = (yytext[0] != '%'); + doing_codeblock = true; + bracelevel = 1; + + if ( indented_code ) + ACTION_ECHO; + + BEGIN(CODEBLOCK_2); + } + YY_BREAK +case 51: +# line 262 "scan.l" +BEGIN(SC); return ( '<' ); + YY_BREAK +case 52: +# line 263 "scan.l" +return ( '^' ); + YY_BREAK +case 53: +# line 264 "scan.l" +BEGIN(QUOTE); return ( '"' ); + YY_BREAK +case 54: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 265 "scan.l" +BEGIN(NUM); return ( '{' ); + YY_BREAK +case 55: +# line 266 "scan.l" +BEGIN(BRACEERROR); + YY_BREAK +case 56: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 267 "scan.l" +return ( '$' ); + YY_BREAK +case 57: +# line 269 "scan.l" +{ + bracelevel = 1; + BEGIN(PERCENT_BRACE_ACTION); + return ( '\n' ); + } + YY_BREAK +case 58: +# line 274 "scan.l" +continued_action = true; ++linenum; return ( '\n' ); + YY_BREAK +case 59: +# line 276 "scan.l" +{ + /* this rule is separate from the one below because + * otherwise we get variable trailing context, so + * we can't build the scanner using -{f,F} + */ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + YY_BREAK +case 60: +# line 287 "scan.l" +{ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + YY_BREAK +case 61: +# line 294 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 62: +# line 296 "scan.l" +return ( EOF_OP ); + YY_BREAK +case 63: +# line 298 "scan.l" +{ + sectnum = 3; + BEGIN(SECT3); + return ( EOF ); /* to stop the parser */ + } + YY_BREAK +case 64: +# line 304 "scan.l" +{ + int cclval; + + (void) strcpy( nmstr, (char *) yytext ); + + /* check to see if we've already encountered this ccl */ + if ( (cclval = ccllookup( (Char *) nmstr )) ) + { + yylval = cclval; + ++cclreuse; + return ( PREVCCL ); + } + else + { + /* we fudge a bit. We know that this ccl will + * soon be numbered as lastccl + 1 by cclinit + */ + cclinstal( (Char *) nmstr, lastccl + 1 ); + + /* push back everything but the leading bracket + * so the ccl can be rescanned + */ + PUT_BACK_STRING((Char *) nmstr, 1); + + BEGIN(FIRSTCCL); + return ( '[' ); + } + } + YY_BREAK +case 65: +# line 333 "scan.l" +{ + Char *nmdefptr; + Char *ndlookup(); + + (void) strcpy( nmstr, (char *) yytext ); + nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ + + /* lookup from "nmstr + 1" to chop leading brace */ + if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) + synerr( "undefined {name}" ); + + else + { /* push back name surrounded by ()'s */ + unput(')'); + PUT_BACK_STRING(nmdefptr, 0); + unput('('); + } + } + YY_BREAK +case 66: +# line 352 "scan.l" +return ( yytext[0] ); + YY_BREAK +case 67: +# line 353 "scan.l" +RETURNCHAR; + YY_BREAK +case 68: +# line 354 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 69: +# line 357 "scan.l" +return ( ',' ); + YY_BREAK +case 70: +# line 358 "scan.l" +BEGIN(SECT2); return ( '>' ); + YY_BREAK +case 71: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 359 "scan.l" +BEGIN(CARETISBOL); return ( '>' ); + YY_BREAK +case 72: +# line 360 "scan.l" +RETURNNAME; + YY_BREAK +case 73: +# line 361 "scan.l" +synerr( "bad start condition name" ); + YY_BREAK +case 74: +# line 363 "scan.l" +BEGIN(SECT2); return ( '^' ); + YY_BREAK +case 75: +# line 366 "scan.l" +RETURNCHAR; + YY_BREAK +case 76: +# line 367 "scan.l" +BEGIN(SECT2); return ( '"' ); + YY_BREAK +case 77: +# line 369 "scan.l" +{ + synerr( "missing quote" ); + BEGIN(SECT2); + ++linenum; + return ( '"' ); + } + YY_BREAK +case 78: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 377 "scan.l" +BEGIN(CCL); return ( '^' ); + YY_BREAK +case 79: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 378 "scan.l" +return ( '^' ); + YY_BREAK +case 80: +# line 379 "scan.l" +BEGIN(CCL); yylval = '-'; return ( CHAR ); + YY_BREAK +case 81: +# line 380 "scan.l" +BEGIN(CCL); RETURNCHAR; + YY_BREAK +case 82: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 382 "scan.l" +return ( '-' ); + YY_BREAK +case 83: +# line 383 "scan.l" +RETURNCHAR; + YY_BREAK +case 84: +# line 384 "scan.l" +BEGIN(SECT2); return ( ']' ); + YY_BREAK +case 85: +# line 387 "scan.l" +{ + yylval = myctoi( yytext ); + return ( NUMBER ); + } + YY_BREAK +case 86: +# line 392 "scan.l" +return ( ',' ); + YY_BREAK +case 87: +# line 393 "scan.l" +BEGIN(SECT2); return ( '}' ); + YY_BREAK +case 88: +# line 395 "scan.l" +{ + synerr( "bad character inside {}'s" ); + BEGIN(SECT2); + return ( '}' ); + } + YY_BREAK +case 89: +# line 401 "scan.l" +{ + synerr( "missing }" ); + BEGIN(SECT2); + ++linenum; + return ( '}' ); + } + YY_BREAK +case 90: +# line 409 "scan.l" +synerr( "bad name in {}'s" ); BEGIN(SECT2); + YY_BREAK +case 91: +# line 410 "scan.l" +synerr( "missing }" ); ++linenum; BEGIN(SECT2); + YY_BREAK +case 92: +# line 413 "scan.l" +bracelevel = 0; + YY_BREAK +case 93: +# line 414 "scan.l" +{ + ACTION_ECHO; + CHECK_REJECT(yytext); + } + YY_BREAK +case 94: +# line 418 "scan.l" +{ + ACTION_ECHO; + CHECK_YYMORE(yytext); + } + YY_BREAK +case 95: +# line 422 "scan.l" +ACTION_ECHO; + YY_BREAK +case 96: +# line 423 "scan.l" +{ + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 || + (doing_codeblock && indented_code) ) + { + if ( ! doing_codeblock ) + fputs( "\tYY_BREAK\n", temp_action_file ); + + doing_codeblock = false; + BEGIN(SECT2); + } + } + YY_BREAK + /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ +case 97: +# line 439 "scan.l" +ACTION_ECHO; ++bracelevel; + YY_BREAK +case 98: +# line 440 "scan.l" +ACTION_ECHO; --bracelevel; + YY_BREAK +case 99: +# line 441 "scan.l" +ACTION_ECHO; + YY_BREAK +case 100: +# line 442 "scan.l" +ACTION_ECHO; + YY_BREAK +case 101: +# line 443 "scan.l" +ACTION_ECHO; BEGIN(ACTION_COMMENT); + YY_BREAK +case 102: +# line 444 "scan.l" +ACTION_ECHO; /* character constant */ + YY_BREAK +case 103: +# line 445 "scan.l" +ACTION_ECHO; BEGIN(ACTION_STRING); + YY_BREAK +case 104: +# line 446 "scan.l" +{ + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 ) + { + fputs( "\tYY_BREAK\n", temp_action_file ); + BEGIN(SECT2); + } + } + YY_BREAK +case 105: +# line 455 "scan.l" +ACTION_ECHO; + YY_BREAK +case 106: +# line 457 "scan.l" +ACTION_ECHO; BEGIN(ACTION); + YY_BREAK +case 107: +# line 458 "scan.l" +ACTION_ECHO; + YY_BREAK +case 108: +# line 459 "scan.l" +ACTION_ECHO; + YY_BREAK +case 109: +# line 460 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case 110: +# line 461 "scan.l" +ACTION_ECHO; + YY_BREAK +case 111: +# line 463 "scan.l" +ACTION_ECHO; + YY_BREAK +case 112: +# line 464 "scan.l" +ACTION_ECHO; + YY_BREAK +case 113: +# line 465 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case 114: +# line 466 "scan.l" +ACTION_ECHO; BEGIN(ACTION); + YY_BREAK +case 115: +# line 467 "scan.l" +ACTION_ECHO; + YY_BREAK +case YY_STATE_EOF(ACTION): +case YY_STATE_EOF(ACTION_COMMENT): +case YY_STATE_EOF(ACTION_STRING): +# line 469 "scan.l" +{ + synerr( "EOF encountered inside an action" ); + yyterminate(); + } + YY_BREAK +case 117: +# line 475 "scan.l" +{ + yylval = myesc( yytext ); + return ( CHAR ); + } + YY_BREAK +case 118: +# line 480 "scan.l" +{ + yylval = myesc( yytext ); + BEGIN(CCL); + return ( CHAR ); + } + YY_BREAK +case 119: +# line 487 "scan.l" +ECHO; + YY_BREAK +case 120: +# line 488 "scan.l" +ECHO; + YY_BREAK + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(SECT2): + case YY_STATE_EOF(SECT3): + case YY_STATE_EOF(CODEBLOCK): + case YY_STATE_EOF(PICKUPDEF): + case YY_STATE_EOF(SC): + case YY_STATE_EOF(CARETISBOL): + case YY_STATE_EOF(NUM): + case YY_STATE_EOF(QUOTE): + case YY_STATE_EOF(FIRSTCCL): + case YY_STATE_EOF(CCL): + case YY_STATE_EOF(RECOVER): + case YY_STATE_EOF(BRACEERROR): + case YY_STATE_EOF(C_COMMENT): + case YY_STATE_EOF(PERCENT_BRACE_ACTION): + case YY_STATE_EOF(USED_LIST): + case YY_STATE_EOF(CODEBLOCK_2): + case YY_STATE_EOF(XLATION): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + yy_state_type yy_current_state; + YY_CHAR *yy_cp; + + YY_CHAR *yy_bp = yytext; + + yy_current_state = yy_start; + if ( yy_bp[-1] == '\n' ) + ++yy_current_state; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[(int)*yy_cp] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) + { + int yy_is_jam; + + YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + yy_is_jam = (yy_current_state == 390); + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, YY_CHAR *yy_bp ) + { + YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +void yyrestart( FILE *input_file ) + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } +# line 488 "scan.l" + + + +int yywrap() + + { + if ( --num_input_files > 0 ) + { + set_input_file( *++input_files ); + return ( 0 ); + } + + else + return ( 1 ); + } + + +/* set_input_file - open the given file (if NULL, stdin) for scanning */ + +void set_input_file( file ) +char *file; + + { + if ( file ) + { + infilename = file; + yyin = fopen( infilename, "r" ); + + if ( yyin == NULL ) + lerrsf( "can't open %s", file ); + } + + else + { + yyin = stdin; + infilename = ""; + } + } diff --git a/modules/libcom/src/flex/scan.l.DISTRIB b/modules/libcom/src/flex/scan.l.DISTRIB new file mode 100644 index 000000000..a566181d3 --- /dev/null +++ b/modules/libcom/src/flex/scan.l.DISTRIB @@ -0,0 +1,525 @@ + +/* scan.l - scanner for flex input */ + +%{ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#undef yywrap + +#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) +#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); + +#undef YY_DECL +#define YY_DECL \ + int flexscan() + +#define RETURNCHAR \ + yylval = yytext[0]; \ + return ( CHAR ); + +#define RETURNNAME \ + (void) strcpy( nmstr, (char *) yytext ); \ + return ( NAME ); + +#define PUT_BACK_STRING(str, start) \ + for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ + unput((str)[i]) + +#define CHECK_REJECT(str) \ + if ( all_upper( str ) ) \ + reject = true; + +#define CHECK_YYMORE(str) \ + if ( all_lower( str ) ) \ + yymore_used = true; +%} + +%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE +%x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT ACTION_COMMENT +%x ACTION_STRING PERCENT_BRACE_ACTION USED_LIST CODEBLOCK_2 XLATION + +WS [ \t\f]+ +OPTWS [ \t\f]* +NOT_WS [^ \t\f\r\n] + +NAME [a-z_][a-z_0-9-]* +NOT_NAME [^a-z_\r\n]+ + +SCNAME {NAME} + +ESCSEQ \\([^\r\n]|[0-9]{1,3}|x[0-9a-f]{1,2}) + +%% + static int bracelevel, didadef; + int i, indented_code = false, checking_used = false, new_xlation = false; + int doing_codeblock = false; + Char nmdef[MAXLINE], myesc(); + +^{WS} indented_code = true; BEGIN(CODEBLOCK); +^#.*\r?\n ++linenum; /* treat as a comment */ +^"/*" ECHO; BEGIN(C_COMMENT); +^"%s"{NAME}? return ( SCDECL ); +^"%x"{NAME}? return ( XSCDECL ); +^"%{".*\r?\n { + ++linenum; + line_directive_out( stdout ); + indented_code = false; + BEGIN(CODEBLOCK); + } + +{WS} return ( WHITESPACE ); + +^"%%".* { + sectnum = 2; + line_directive_out( stdout ); + BEGIN(SECT2PROLOG); + return ( SECTEND ); + } + +^"%used" { + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_USED; BEGIN(USED_LIST); + } +^"%unused" { + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + } + + +^"%"[aeknopt]" ".*\r?\n { +#ifdef NOTDEF + fprintf( stderr, + "old-style lex command at line %d ignored:\n\t%s", + linenum, yytext ); +#endif + ++linenum; + } + +^"%"[cr]{OPTWS} /* ignore old lex directive */ + +%t{OPTWS}\r?\n { + ++linenum; + xlation = + (int *) malloc( sizeof( int ) * (unsigned) csize ); + + if ( ! xlation ) + flexfatal( + "dynamic memory failure building %t table" ); + + for ( i = 0; i < csize; ++i ) + xlation[i] = 0; + + num_xlations = 0; + + BEGIN(XLATION); + } + +^"%"[^sxanpekotcru{}]{OPTWS} synerr( "unrecognized '%' directive" ); + +^{NAME} { + (void) strcpy( nmstr, (char *) yytext ); + didadef = false; + BEGIN(PICKUPDEF); + } + +{SCNAME} RETURNNAME; +^{OPTWS}\r?\n ++linenum; /* allows blank lines in section 1 */ +{OPTWS}\r?\n ++linenum; return ( '\n' ); +. synerr( "illegal character" ); BEGIN(RECOVER); + + +"*/" ECHO; BEGIN(INITIAL); +"*/".*\r?\n ++linenum; ECHO; BEGIN(INITIAL); +[^*\r\n]+ ECHO; +"*" ECHO; +\r?\n ++linenum; ECHO; + + +^"%}".*\r?\n ++linenum; BEGIN(INITIAL); +"reject" ECHO; CHECK_REJECT(yytext); +"yymore" ECHO; CHECK_YYMORE(yytext); +{NAME}|{NOT_NAME}|. ECHO; +\r?\n { + ++linenum; + ECHO; + if ( indented_code ) + BEGIN(INITIAL); + } + + +{WS} /* separates name and definition */ + +{NOT_WS}.* { + (void) strcpy( (char *) nmdef, (char *) yytext ); + + for ( i = strlen( (char *) nmdef ) - 1; + i >= 0 && + (nmdef[i] == ' ' || nmdef[i] == '\t'); + --i ) + ; + + nmdef[i + 1] = '\0'; + + ndinstal( nmstr, nmdef ); + didadef = true; + } + +\r?\n { + if ( ! didadef ) + synerr( "incomplete name definition" ); + BEGIN(INITIAL); + ++linenum; + } + +.*\r?\n ++linenum; BEGIN(INITIAL); RETURNNAME; + + +\r?\n ++linenum; BEGIN(INITIAL); +{WS} +"reject" { + if ( all_upper( yytext ) ) + reject_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } +"yymore" { + if ( all_lower( yytext ) ) + yymore_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } +{NOT_WS}+ synerr( "unrecognized %used/%unused construct" ); + + +"%t"{OPTWS}\r?\n ++linenum; BEGIN(INITIAL); +^{OPTWS}[0-9]+ ++num_xlations; new_xlation = true; +^. synerr( "bad row in translation table" ); +{WS} /* ignore whitespace */ + +{ESCSEQ} { + xlation[myesc( yytext )] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } +. { + xlation[yytext[0]] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + +\r?\n ++linenum; + + +.*\r?\n/{NOT_WS} { + ++linenum; + ACTION_ECHO; + MARK_END_OF_PROLOG; + BEGIN(SECT2); + } + +.*\r?\n ++linenum; ACTION_ECHO; + +<> MARK_END_OF_PROLOG; yyterminate(); + +^{OPTWS}\r?\n ++linenum; /* allow blank lines in section 2 */ + +^({WS}|"%{") { + indented_code = (yytext[0] != '%'); + doing_codeblock = true; + bracelevel = 1; + + if ( indented_code ) + ACTION_ECHO; + + BEGIN(CODEBLOCK_2); + } + +"<" BEGIN(SC); return ( '<' ); +^"^" return ( '^' ); +\" BEGIN(QUOTE); return ( '"' ); +"{"/[0-9] BEGIN(NUM); return ( '{' ); +"{"[^0-9\r\n][^}\r\n]* BEGIN(BRACEERROR); +"$"/[ \t\r\n] return ( '$' ); + +{WS}"%{" { + bracelevel = 1; + BEGIN(PERCENT_BRACE_ACTION); + return ( '\n' ); + } +{WS}"|".*\r?\n continued_action = true; ++linenum; return ( '\n' ); + +{WS} { + /* this rule is separate from the one below because + * otherwise we get variable trailing context, so + * we can't build the scanner using -{f,F} + */ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + +{OPTWS}/\r?\n { + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + +^{OPTWS}\r?\n ++linenum; return ( '\n' ); + +"<>" return ( EOF_OP ); + +^"%%".* { + sectnum = 3; + BEGIN(SECT3); + return ( EOF ); /* to stop the parser */ + } + +"["([^\\\]\r\n]|{ESCSEQ})+"]" { + int cclval; + + (void) strcpy( nmstr, (char *) yytext ); + + /* check to see if we've already encountered this ccl */ + if ( (cclval = ccllookup( (Char *) nmstr )) ) + { + yylval = cclval; + ++cclreuse; + return ( PREVCCL ); + } + else + { + /* we fudge a bit. We know that this ccl will + * soon be numbered as lastccl + 1 by cclinit + */ + cclinstal( (Char *) nmstr, lastccl + 1 ); + + /* push back everything but the leading bracket + * so the ccl can be rescanned + */ + PUT_BACK_STRING((Char *) nmstr, 1); + + BEGIN(FIRSTCCL); + return ( '[' ); + } + } + +"{"{NAME}"}" { + register Char *nmdefptr; + Char *ndlookup(); + + (void) strcpy( nmstr, (char *) yytext ); + nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ + + /* lookup from "nmstr + 1" to chop leading brace */ + if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) + synerr( "undefined {name}" ); + + else + { /* push back name surrounded by ()'s */ + unput(')'); + PUT_BACK_STRING(nmdefptr, 0); + unput('('); + } + } + +[/|*+?.()] return ( yytext[0] ); +. RETURNCHAR; +\r?\n ++linenum; return ( '\n' ); + + +"," return ( ',' ); +">" BEGIN(SECT2); return ( '>' ); +">"/"^" BEGIN(CARETISBOL); return ( '>' ); +{SCNAME} RETURNNAME; +. synerr( "bad start condition name" ); + +"^" BEGIN(SECT2); return ( '^' ); + + +[^"\r\n] RETURNCHAR; +\" BEGIN(SECT2); return ( '"' ); + +\r?\n { + synerr( "missing quote" ); + BEGIN(SECT2); + ++linenum; + return ( '"' ); + } + + +"^"/[^-\r\n] BEGIN(CCL); return ( '^' ); +"^"/- return ( '^' ); +- BEGIN(CCL); yylval = '-'; return ( CHAR ); +. BEGIN(CCL); RETURNCHAR; + +-/[^\]\r\n] return ( '-' ); +[^\]\r\n] RETURNCHAR; +"]" BEGIN(SECT2); return ( ']' ); + + +[0-9]+ { + yylval = myctoi( yytext ); + return ( NUMBER ); + } + +"," return ( ',' ); +"}" BEGIN(SECT2); return ( '}' ); + +. { + synerr( "bad character inside {}'s" ); + BEGIN(SECT2); + return ( '}' ); + } + +\r?\n { + synerr( "missing }" ); + BEGIN(SECT2); + ++linenum; + return ( '}' ); + } + + +"}" synerr( "bad name in {}'s" ); BEGIN(SECT2); +\r?\n synerr( "missing }" ); ++linenum; BEGIN(SECT2); + + +{OPTWS}"%}".* bracelevel = 0; +"reject" { + ACTION_ECHO; + CHECK_REJECT(yytext); + } +"yymore" { + ACTION_ECHO; + CHECK_YYMORE(yytext); + } +{NAME}|{NOT_NAME}|. ACTION_ECHO; +\r?\n { + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 || + (doing_codeblock && indented_code) ) + { + if ( ! doing_codeblock ) + fputs( "\tYY_BREAK\n", temp_action_file ); + + doing_codeblock = false; + BEGIN(SECT2); + } + } + + + /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ +"{" ACTION_ECHO; ++bracelevel; +"}" ACTION_ECHO; --bracelevel; +[^a-z_{}"'/\r\n]+ ACTION_ECHO; +{NAME} ACTION_ECHO; +"/*" ACTION_ECHO; BEGIN(ACTION_COMMENT); +"'"([^'\\\r\n]|\\.)*"'" ACTION_ECHO; /* character constant */ +\" ACTION_ECHO; BEGIN(ACTION_STRING); +\r?\n { + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 ) + { + fputs( "\tYY_BREAK\n", temp_action_file ); + BEGIN(SECT2); + } + } +. ACTION_ECHO; + +"*/" ACTION_ECHO; BEGIN(ACTION); +[^*\r\n]+ ACTION_ECHO; +"*" ACTION_ECHO; +\r?\n ++linenum; ACTION_ECHO; +. ACTION_ECHO; + +[^"\\\r\n]+ ACTION_ECHO; +\\. ACTION_ECHO; +\r?\n ++linenum; ACTION_ECHO; +\" ACTION_ECHO; BEGIN(ACTION); +. ACTION_ECHO; + +<> { + synerr( "EOF encountered inside an action" ); + yyterminate(); + } + + +{ESCSEQ} { + yylval = myesc( yytext ); + return ( CHAR ); + } + +{ESCSEQ} { + yylval = myesc( yytext ); + BEGIN(CCL); + return ( CHAR ); + } + + +.*(\r?\n?) ECHO; +%% + + +int yywrap() + + { + if ( --num_input_files > 0 ) + { + set_input_file( *++input_files ); + return ( 0 ); + } + + else + return ( 1 ); + } + + +/* set_input_file - open the given file (if NULL, stdin) for scanning */ + +void set_input_file( file ) +char *file; + + { + if ( file ) + { + infilename = file; + yyin = fopen( infilename, "r" ); + + if ( yyin == NULL ) + lerrsf( "can't open %s", file ); + } + + else + { + yyin = stdin; + infilename = ""; + } + } diff --git a/modules/libcom/src/flex/sym.c b/modules/libcom/src/flex/sym.c new file mode 100644 index 000000000..45e1c5409 --- /dev/null +++ b/modules/libcom/src/flex/sym.c @@ -0,0 +1,290 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* sym - symbol table routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +int hashfunct (char[], int); + + +struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; +struct hash_entry *sctbl[START_COND_HASH_SIZE]; +struct hash_entry *ccltab[CCL_HASH_SIZE]; + +struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size); + + +/* addsym - add symbol and definitions to symbol table + * + * synopsis + * char sym[], *str_def; + * int int_def; + * hash_table table; + * int table_size; + * 0 / -1 = addsym( sym, def, int_def, table, table_size ); + * + * -1 is returned if the symbol already exists, and the change not made. + */ + +int addsym(char *sym, char *str_def, int int_def, struct hash_entry **table, int table_size) +{ + int hash_val = hashfunct( sym, table_size ); + struct hash_entry *sym_entry = table[hash_val]; + struct hash_entry *new_entry; + struct hash_entry *successor; + + while ( sym_entry ) + { + if ( ! strcmp( sym, sym_entry->name ) ) + { /* entry already exists */ + return ( -1 ); + } + + sym_entry = sym_entry->next; + } + + /* create new entry */ + new_entry = (struct hash_entry *) malloc( sizeof( struct hash_entry ) ); + + if ( new_entry == NULL ) + flexfatal( "symbol table memory allocation failed" ); + + if ( (successor = table[hash_val]) ) + { + new_entry->next = successor; + successor->prev = new_entry; + } + else + new_entry->next = NULL; + + new_entry->prev = NULL; + new_entry->name = sym; + new_entry->str_val = str_def; + new_entry->int_val = int_def; + + table[hash_val] = new_entry; + + return ( 0 ); + } + + +/* cclinstal - save the text of a character class + * + * synopsis + * Char ccltxt[]; + * int cclnum; + * cclinstal( ccltxt, cclnum ); + */ + +void cclinstal(Char *ccltxt, int cclnum) +{ + /* we don't bother checking the return status because we are not called + * unless the symbol is new + */ + Char *copy_unsigned_string(); + + (void) addsym( (char *) copy_unsigned_string( ccltxt ), (char *) 0, cclnum, + ccltab, CCL_HASH_SIZE ); + } + + +/* ccllookup - lookup the number associated with character class text + * + * synopsis + * Char ccltxt[]; + * int ccllookup, cclval; + * cclval/0 = ccllookup( ccltxt ); + */ + +int ccllookup(Char *ccltxt) +{ + return ( findsym( (char *) ccltxt, ccltab, CCL_HASH_SIZE )->int_val ); + } + + +/* findsym - find symbol in symbol table + * + * synopsis + * char sym[]; + * hash_table table; + * int table_size; + * struct hash_entry *sym_entry, *findsym(); + * sym_entry = findsym( sym, table, table_size ); + */ + +struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size) +{ + struct hash_entry *sym_entry = table[hashfunct( sym, table_size )]; + static struct hash_entry empty_entry = + { + (struct hash_entry *) 0, (struct hash_entry *) 0, NULL, NULL, 0, + } ; + + while ( sym_entry ) + { + if ( ! strcmp( sym, sym_entry->name ) ) + return ( sym_entry ); + sym_entry = sym_entry->next; + } + + return ( &empty_entry ); + } + + +/* hashfunct - compute the hash value for "str" and hash size "hash_size" + * + * synopsis + * char str[]; + * int hash_size, hash_val; + * hash_val = hashfunct( str, hash_size ); + */ + +int hashfunct(char *str, int hash_size) +{ + int hashval; + int locstr; + + hashval = 0; + locstr = 0; + + while ( str[locstr] ) + hashval = ((hashval << 1) + str[locstr++]) % hash_size; + + return ( hashval ); + } + + +/* ndinstal - install a name definition + * + * synopsis + * char nd[]; + * Char def[]; + * ndinstal( nd, def ); + */ + +void ndinstal(char *nd, Char *def) +{ + char *copy_string(); + Char *copy_unsigned_string(); + + if ( addsym( copy_string( nd ), (char *) copy_unsigned_string( def ), 0, + ndtbl, NAME_TABLE_HASH_SIZE ) ) + synerr( "name defined twice" ); + } + + +/* ndlookup - lookup a name definition + * + * synopsis + * char nd[], *def; + * char *ndlookup(); + * def/NULL = ndlookup( nd ); + */ + +Char *ndlookup(char *nd) +{ + return ( (Char *) findsym( nd, ndtbl, NAME_TABLE_HASH_SIZE )->str_val ); + } + + +/* scinstal - make a start condition + * + * synopsis + * char str[]; + * int xcluflg; + * scinstal( str, xcluflg ); + * + * NOTE + * the start condition is Exclusive if xcluflg is true + */ + +void scinstal(char *str, int xcluflg) +{ + char *copy_string(); + + /* bit of a hack. We know how the default start-condition is + * declared, and don't put out a define for it, because it + * would come out as "#define 0 1" + */ + /* actually, this is no longer the case. The default start-condition + * is now called "INITIAL". But we keep the following for the sake + * of future robustness. + */ + + if ( strcmp( str, "0" ) ) + printf( "#define %s %d\n", str, lastsc ); + + if ( ++lastsc >= current_max_scs ) + { + current_max_scs += MAX_SCS_INCREMENT; + + ++num_reallocs; + + scset = reallocate_integer_array( scset, current_max_scs ); + scbol = reallocate_integer_array( scbol, current_max_scs ); + scxclu = reallocate_integer_array( scxclu, current_max_scs ); + sceof = reallocate_integer_array( sceof, current_max_scs ); + scname = reallocate_char_ptr_array( scname, current_max_scs ); + actvsc = reallocate_integer_array( actvsc, current_max_scs ); + } + + scname[lastsc] = copy_string( str ); + + if ( addsym( scname[lastsc], (char *) 0, lastsc, + sctbl, START_COND_HASH_SIZE ) ) + format_pinpoint_message( "start condition %s declared twice", str ); + + scset[lastsc] = mkstate( SYM_EPSILON ); + scbol[lastsc] = mkstate( SYM_EPSILON ); + scxclu[lastsc] = xcluflg; + sceof[lastsc] = false; + } + + +/* sclookup - lookup the number associated with a start condition + * + * synopsis + * char str[], scnum; + * int sclookup; + * scnum/0 = sclookup( str ); + */ + +int sclookup(char *str) +{ + return ( findsym( str, sctbl, START_COND_HASH_SIZE )->int_val ); + } diff --git a/modules/libcom/src/flex/tblcmp.c b/modules/libcom/src/flex/tblcmp.c new file mode 100644 index 000000000..3e4c4744b --- /dev/null +++ b/modules/libcom/src/flex/tblcmp.c @@ -0,0 +1,904 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* tblcmp - table compression routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "flexdef.h" + + +/* declarations for functions that have forward references */ + +void mkentry (int*, int, int, int, int); +void mkprot (int[], int, int); +void mktemplate (int[], int, int); +void mv2front (int); +int tbldiff (int[], int, int[]); + + +/* bldtbl - build table entries for dfa state + * + * synopsis + * int state[numecs], statenum, totaltrans, comstate, comfreq; + * bldtbl( state, statenum, totaltrans, comstate, comfreq ); + * + * State is the statenum'th dfa state. It is indexed by equivalence class and + * gives the number of the state to enter for a given equivalence class. + * totaltrans is the total number of transitions out of the state. Comstate + * is that state which is the destination of the most transitions out of State. + * Comfreq is how many transitions there are out of State to Comstate. + * + * A note on terminology: + * "protos" are transition tables which have a high probability of + * either being redundant (a state processed later will have an identical + * transition table) or nearly redundant (a state processed later will have + * many of the same out-transitions). A "most recently used" queue of + * protos is kept around with the hope that most states will find a proto + * which is similar enough to be usable, and therefore compacting the + * output tables. + * "templates" are a special type of proto. If a transition table is + * homogeneous or nearly homogeneous (all transitions go to the same + * destination) then the odds are good that future states will also go + * to the same destination state on basically the same character set. + * These homogeneous states are so common when dealing with large rule + * sets that they merit special attention. If the transition table were + * simply made into a proto, then (typically) each subsequent, similar + * state will differ from the proto for two out-transitions. One of these + * out-transitions will be that character on which the proto does not go + * to the common destination, and one will be that character on which the + * state does not go to the common destination. Templates, on the other + * hand, go to the common state on EVERY transition character, and therefore + * cost only one difference. + */ + +void bldtbl(int *state, int statenum, int totaltrans, int comstate, int comfreq) +{ + int extptr, extrct[2][CSIZE + 1]; + int mindiff, minprot, i, d; + int checkcom; + + /* If extptr is 0 then the first array of extrct holds the result of the + * "best difference" to date, which is those transitions which occur in + * "state" but not in the proto which, to date, has the fewest differences + * between itself and "state". If extptr is 1 then the second array of + * extrct hold the best difference. The two arrays are toggled + * between so that the best difference to date can be kept around and + * also a difference just created by checking against a candidate "best" + * proto. + */ + + extptr = 0; + + /* if the state has too few out-transitions, don't bother trying to + * compact its tables + */ + + if ( (totaltrans * 100) < (numecs * PROTO_SIZE_PERCENTAGE) ) + mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); + + else + { + /* checkcom is true if we should only check "state" against + * protos which have the same "comstate" value + */ + + checkcom = comfreq * 100 > totaltrans * CHECK_COM_PERCENTAGE; + + minprot = firstprot; + mindiff = totaltrans; + + if ( checkcom ) + { + /* find first proto which has the same "comstate" */ + for ( i = firstprot; i != NIL; i = protnext[i] ) + if ( protcomst[i] == comstate ) + { + minprot = i; + mindiff = tbldiff( state, minprot, extrct[extptr] ); + break; + } + } + + else + { + /* since we've decided that the most common destination out + * of "state" does not occur with a high enough frequency, + * we set the "comstate" to zero, assuring that if this state + * is entered into the proto list, it will not be considered + * a template. + */ + comstate = 0; + + if ( firstprot != NIL ) + { + minprot = firstprot; + mindiff = tbldiff( state, minprot, extrct[extptr] ); + } + } + + /* we now have the first interesting proto in "minprot". If + * it matches within the tolerances set for the first proto, + * we don't want to bother scanning the rest of the proto list + * to see if we have any other reasonable matches. + */ + + if ( mindiff * 100 > totaltrans * FIRST_MATCH_DIFF_PERCENTAGE ) + { /* not a good enough match. Scan the rest of the protos */ + for ( i = minprot; i != NIL; i = protnext[i] ) + { + d = tbldiff( state, i, extrct[1 - extptr] ); + if ( d < mindiff ) + { + extptr = 1 - extptr; + mindiff = d; + minprot = i; + } + } + } + + /* check if the proto we've decided on as our best bet is close + * enough to the state we want to match to be usable + */ + + if ( mindiff * 100 > totaltrans * ACCEPTABLE_DIFF_PERCENTAGE ) + { + /* no good. If the state is homogeneous enough, we make a + * template out of it. Otherwise, we make a proto. + */ + + if ( comfreq * 100 >= totaltrans * TEMPLATE_SAME_PERCENTAGE ) + mktemplate( state, statenum, comstate ); + + else + { + mkprot( state, statenum, comstate ); + mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); + } + } + + else + { /* use the proto */ + mkentry( extrct[extptr], numecs, statenum, + prottbl[minprot], mindiff ); + + /* if this state was sufficiently different from the proto + * we built it from, make it, too, a proto + */ + + if ( mindiff * 100 >= totaltrans * NEW_PROTO_DIFF_PERCENTAGE ) + mkprot( state, statenum, comstate ); + + /* since mkprot added a new proto to the proto queue, it's possible + * that "minprot" is no longer on the proto queue (if it happened + * to have been the last entry, it would have been bumped off). + * If it's not there, then the new proto took its physical place + * (though logically the new proto is at the beginning of the + * queue), so in that case the following call will do nothing. + */ + + mv2front( minprot ); + } + } + } + + +/* cmptmps - compress template table entries + * + * synopsis + * cmptmps(); + * + * template tables are compressed by using the 'template equivalence + * classes', which are collections of transition character equivalence + * classes which always appear together in templates - really meta-equivalence + * classes. until this point, the tables for templates have been stored + * up at the top end of the nxt array; they will now be compressed and have + * table entries made for them. + */ + +void cmptmps(void) +{ + int tmpstorage[CSIZE + 1]; + int *tmp = tmpstorage, i, j; + int totaltrans, trans; + + peakpairs = numtemps * numecs + tblend; + + if ( usemecs ) + { + /* create equivalence classes base on data gathered on template + * transitions + */ + + nummecs = cre8ecs( tecfwd, tecbck, numecs ); + } + + else + nummecs = numecs; + + if ( lastdfa + numtemps + 1 >= current_max_dfas ) + increase_max_dfas(); + + /* loop through each template */ + + for ( i = 1; i <= numtemps; ++i ) + { + totaltrans = 0; /* number of non-jam transitions out of this template */ + + for ( j = 1; j <= numecs; ++j ) + { + trans = tnxt[numecs * i + j]; + + if ( usemecs ) + { + /* the absolute value of tecbck is the meta-equivalence class + * of a given equivalence class, as set up by cre8ecs + */ + if ( tecbck[j] > 0 ) + { + tmp[tecbck[j]] = trans; + + if ( trans > 0 ) + ++totaltrans; + } + } + + else + { + tmp[j] = trans; + + if ( trans > 0 ) + ++totaltrans; + } + } + + /* it is assumed (in a rather subtle way) in the skeleton that + * if we're using meta-equivalence classes, the def[] entry for + * all templates is the jam template, i.e., templates never default + * to other non-jam table entries (e.g., another template) + */ + + /* leave room for the jam-state after the last real state */ + mkentry( tmp, nummecs, lastdfa + i + 1, JAMSTATE, totaltrans ); + } + } + + + +/* expand_nxt_chk - expand the next check arrays */ + +void expand_nxt_chk(void) +{ + int old_max = current_max_xpairs; + + current_max_xpairs += MAX_XPAIRS_INCREMENT; + + ++num_reallocs; + + nxt = reallocate_integer_array( nxt, current_max_xpairs ); + chk = reallocate_integer_array( chk, current_max_xpairs ); + + memset( (char *) (chk + old_max), 0, + MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) ); + } + + +/* find_table_space - finds a space in the table for a state to be placed + * + * synopsis + * int *state, numtrans, block_start; + * int find_table_space(); + * + * block_start = find_table_space( state, numtrans ); + * + * State is the state to be added to the full speed transition table. + * Numtrans is the number of out-transitions for the state. + * + * find_table_space() returns the position of the start of the first block (in + * chk) able to accommodate the state + * + * In determining if a state will or will not fit, find_table_space() must take + * into account the fact that an end-of-buffer state will be added at [0], + * and an action number will be added in [-1]. + */ + +int find_table_space(int *state, int numtrans) +{ + /* firstfree is the position of the first possible occurrence of two + * consecutive unused records in the chk and nxt arrays + */ + int i; + int *state_ptr, *chk_ptr; + int *ptr_to_last_entry_in_state; + + /* if there are too many out-transitions, put the state at the end of + * nxt and chk + */ + if ( numtrans > MAX_XTIONS_FULL_INTERIOR_FIT ) + { + /* if table is empty, return the first available spot in chk/nxt, + * which should be 1 + */ + if ( tblend < 2 ) + return ( 1 ); + + i = tblend - numecs; /* start searching for table space near the + * end of chk/nxt arrays + */ + } + + else + i = firstfree; /* start searching for table space from the + * beginning (skipping only the elements + * which will definitely not hold the new + * state) + */ + + while ( 1 ) /* loops until a space is found */ + { + if ( i + numecs > current_max_xpairs ) + expand_nxt_chk(); + + /* loops until space for end-of-buffer and action number are found */ + while ( 1 ) + { + if ( chk[i - 1] == 0 ) /* check for action number space */ + { + if ( chk[i] == 0 ) /* check for end-of-buffer space */ + break; + + else + i += 2; /* since i != 0, there is no use checking to + * see if (++i) - 1 == 0, because that's the + * same as i == 0, so we skip a space + */ + } + + else + ++i; + + if ( i + numecs > current_max_xpairs ) + expand_nxt_chk(); + } + + /* if we started search from the beginning, store the new firstfree for + * the next call of find_table_space() + */ + if ( numtrans <= MAX_XTIONS_FULL_INTERIOR_FIT ) + firstfree = i + 1; + + /* check to see if all elements in chk (and therefore nxt) that are + * needed for the new state have not yet been taken + */ + + state_ptr = &state[1]; + ptr_to_last_entry_in_state = &chk[i + numecs + 1]; + + for ( chk_ptr = &chk[i + 1]; chk_ptr != ptr_to_last_entry_in_state; + ++chk_ptr ) + if ( *(state_ptr++) != 0 && *chk_ptr != 0 ) + break; + + if ( chk_ptr == ptr_to_last_entry_in_state ) + return ( i ); + + else + ++i; + } + } + + +/* inittbl - initialize transition tables + * + * synopsis + * inittbl(); + * + * Initializes "firstfree" to be one beyond the end of the table. Initializes + * all "chk" entries to be zero. Note that templates are built in their + * own tbase/tdef tables. They are shifted down to be contiguous + * with the non-template entries during table generation. + */ +void inittbl(void) +{ + int i; + + memset( (char *) chk, 0, + current_max_xpairs * sizeof( int ) / sizeof( char ) ); + + tblend = 0; + firstfree = tblend + 1; + numtemps = 0; + + if ( usemecs ) + { + /* set up doubly-linked meta-equivalence classes + * these are sets of equivalence classes which all have identical + * transitions out of TEMPLATES + */ + + tecbck[1] = NIL; + + for ( i = 2; i <= numecs; ++i ) + { + tecbck[i] = i - 1; + tecfwd[i - 1] = i; + } + + tecfwd[numecs] = NIL; + } + } + + +/* mkdeftbl - make the default, "jam" table entries + * + * synopsis + * mkdeftbl(); + */ + +void mkdeftbl(void) +{ + int i; + + jamstate = lastdfa + 1; + + ++tblend; /* room for transition on end-of-buffer character */ + + if ( tblend + numecs > current_max_xpairs ) + expand_nxt_chk(); + + /* add in default end-of-buffer transition */ + nxt[tblend] = end_of_buffer_state; + chk[tblend] = jamstate; + + for ( i = 1; i <= numecs; ++i ) + { + nxt[tblend + i] = 0; + chk[tblend + i] = jamstate; + } + + jambase = tblend; + + base[jamstate] = jambase; + def[jamstate] = 0; + + tblend += numecs; + ++numtemps; + } + + +/* mkentry - create base/def and nxt/chk entries for transition array + * + * synopsis + * int state[numchars + 1], numchars, statenum, deflink, totaltrans; + * mkentry( state, numchars, statenum, deflink, totaltrans ); + * + * "state" is a transition array "numchars" characters in size, "statenum" + * is the offset to be used into the base/def tables, and "deflink" is the + * entry to put in the "def" table entry. If "deflink" is equal to + * "JAMSTATE", then no attempt will be made to fit zero entries of "state" + * (i.e., jam entries) into the table. It is assumed that by linking to + * "JAMSTATE" they will be taken care of. In any case, entries in "state" + * marking transitions to "SAME_TRANS" are treated as though they will be + * taken care of by whereever "deflink" points. "totaltrans" is the total + * number of transitions out of the state. If it is below a certain threshold, + * the tables are searched for an interior spot that will accommodate the + * state array. + */ + +void mkentry(int *state, int numchars, int statenum, int deflink, int totaltrans) +{ + int minec, maxec, i, baseaddr; + int tblbase, tbllast; + + if ( totaltrans == 0 ) + { /* there are no out-transitions */ + if ( deflink == JAMSTATE ) + base[statenum] = JAMSTATE; + else + base[statenum] = 0; + + def[statenum] = deflink; + return; + } + + for ( minec = 1; minec <= numchars; ++minec ) + { + if ( state[minec] != SAME_TRANS ) + if ( state[minec] != 0 || deflink != JAMSTATE ) + break; + } + + if ( totaltrans == 1 ) + { + /* there's only one out-transition. Save it for later to fill + * in holes in the tables. + */ + stack1( statenum, minec, state[minec], deflink ); + return; + } + + for ( maxec = numchars; maxec > 0; --maxec ) + { + if ( state[maxec] != SAME_TRANS ) + if ( state[maxec] != 0 || deflink != JAMSTATE ) + break; + } + + /* Whether we try to fit the state table in the middle of the table + * entries we have already generated, or if we just take the state + * table at the end of the nxt/chk tables, we must make sure that we + * have a valid base address (i.e., non-negative). Note that not only are + * negative base addresses dangerous at run-time (because indexing the + * next array with one and a low-valued character might generate an + * array-out-of-bounds error message), but at compile-time negative + * base addresses denote TEMPLATES. + */ + + /* find the first transition of state that we need to worry about. */ + if ( totaltrans * 100 <= numchars * INTERIOR_FIT_PERCENTAGE ) + { /* attempt to squeeze it into the middle of the tabls */ + baseaddr = firstfree; + + while ( baseaddr < minec ) + { + /* using baseaddr would result in a negative base address below + * find the next free slot + */ + for ( ++baseaddr; chk[baseaddr] != 0; ++baseaddr ) + ; + } + + if ( baseaddr + maxec - minec >= current_max_xpairs ) + expand_nxt_chk(); + + for ( i = minec; i <= maxec; ++i ) + if ( state[i] != SAME_TRANS ) + if ( state[i] != 0 || deflink != JAMSTATE ) + if ( chk[baseaddr + i - minec] != 0 ) + { /* baseaddr unsuitable - find another */ + for ( ++baseaddr; + baseaddr < current_max_xpairs && + chk[baseaddr] != 0; + ++baseaddr ) + ; + + if ( baseaddr + maxec - minec >= current_max_xpairs ) + expand_nxt_chk(); + + /* reset the loop counter so we'll start all + * over again next time it's incremented + */ + + i = minec - 1; + } + } + + else + { + /* ensure that the base address we eventually generate is + * non-negative + */ + baseaddr = max( tblend + 1, minec ); + } + + tblbase = baseaddr - minec; + tbllast = tblbase + maxec; + + if ( tbllast >= current_max_xpairs ) + expand_nxt_chk(); + + base[statenum] = tblbase; + def[statenum] = deflink; + + for ( i = minec; i <= maxec; ++i ) + if ( state[i] != SAME_TRANS ) + if ( state[i] != 0 || deflink != JAMSTATE ) + { + nxt[tblbase + i] = state[i]; + chk[tblbase + i] = statenum; + } + + if ( baseaddr == firstfree ) + /* find next free slot in tables */ + for ( ++firstfree; chk[firstfree] != 0; ++firstfree ) + ; + + tblend = max( tblend, tbllast ); + } + + +/* mk1tbl - create table entries for a state (or state fragment) which + * has only one out-transition + * + * synopsis + * int state, sym, onenxt, onedef; + * mk1tbl( state, sym, onenxt, onedef ); + */ + +void mk1tbl(int state, int sym, int onenxt, int onedef) +{ + if ( firstfree < sym ) + firstfree = sym; + + while ( chk[firstfree] != 0 ) + if ( ++firstfree >= current_max_xpairs ) + expand_nxt_chk(); + + base[state] = firstfree - sym; + def[state] = onedef; + chk[firstfree] = state; + nxt[firstfree] = onenxt; + + if ( firstfree > tblend ) + { + tblend = firstfree++; + + if ( firstfree >= current_max_xpairs ) + expand_nxt_chk(); + } + } + + +/* mkprot - create new proto entry + * + * synopsis + * int state[], statenum, comstate; + * mkprot( state, statenum, comstate ); + */ + +void mkprot(int *state, int statenum, int comstate) +{ + int i, slot, tblbase; + + if ( ++numprots >= MSP || numecs * numprots >= PROT_SAVE_SIZE ) + { + /* gotta make room for the new proto by dropping last entry in + * the queue + */ + slot = lastprot; + lastprot = protprev[lastprot]; + protnext[lastprot] = NIL; + } + + else + slot = numprots; + + protnext[slot] = firstprot; + + if ( firstprot != NIL ) + protprev[firstprot] = slot; + + firstprot = slot; + prottbl[slot] = statenum; + protcomst[slot] = comstate; + + /* copy state into save area so it can be compared with rapidly */ + tblbase = numecs * (slot - 1); + + for ( i = 1; i <= numecs; ++i ) + protsave[tblbase + i] = state[i]; + } + + +/* mktemplate - create a template entry based on a state, and connect the state + * to it + * + * synopsis + * int state[], statenum, comstate, totaltrans; + * mktemplate( state, statenum, comstate, totaltrans ); + */ + +void mktemplate(int *state, int statenum, int comstate) +{ + int i, numdiff, tmpbase, tmp[CSIZE + 1]; + Char transset[CSIZE + 1]; + int tsptr; + + ++numtemps; + + tsptr = 0; + + /* calculate where we will temporarily store the transition table + * of the template in the tnxt[] array. The final transition table + * gets created by cmptmps() + */ + + tmpbase = numtemps * numecs; + + if ( tmpbase + numecs >= current_max_template_xpairs ) + { + current_max_template_xpairs += MAX_TEMPLATE_XPAIRS_INCREMENT; + + ++num_reallocs; + + tnxt = reallocate_integer_array( tnxt, current_max_template_xpairs ); + } + + for ( i = 1; i <= numecs; ++i ) + if ( state[i] == 0 ) + tnxt[tmpbase + i] = 0; + else + { + transset[tsptr++] = i; + tnxt[tmpbase + i] = comstate; + } + + if ( usemecs ) + mkeccl( transset, tsptr, tecfwd, tecbck, numecs, 0 ); + + mkprot( tnxt + tmpbase, -numtemps, comstate ); + + /* we rely on the fact that mkprot adds things to the beginning + * of the proto queue + */ + + numdiff = tbldiff( state, firstprot, tmp ); + mkentry( tmp, numecs, statenum, -numtemps, numdiff ); + } + + +/* mv2front - move proto queue element to front of queue + * + * synopsis + * int qelm; + * mv2front( qelm ); + */ + +void mv2front(int qelm) +{ + if ( firstprot != qelm ) + { + if ( qelm == lastprot ) + lastprot = protprev[lastprot]; + + protnext[protprev[qelm]] = protnext[qelm]; + + if ( protnext[qelm] != NIL ) + protprev[protnext[qelm]] = protprev[qelm]; + + protprev[qelm] = NIL; + protnext[qelm] = firstprot; + protprev[firstprot] = qelm; + firstprot = qelm; + } + } + + +/* place_state - place a state into full speed transition table + * + * synopsis + * int *state, statenum, transnum; + * place_state( state, statenum, transnum ); + * + * State is the statenum'th state. It is indexed by equivalence class and + * gives the number of the state to enter for a given equivalence class. + * Transnum is the number of out-transitions for the state. + */ + +void place_state(int *state, int statenum, int transnum) +{ + int i; + int *state_ptr; + int position = find_table_space( state, transnum ); + + /* base is the table of start positions */ + base[statenum] = position; + + /* put in action number marker; this non-zero number makes sure that + * find_table_space() knows that this position in chk/nxt is taken + * and should not be used for another accepting number in another state + */ + chk[position - 1] = 1; + + /* put in end-of-buffer marker; this is for the same purposes as above */ + chk[position] = 1; + + /* place the state into chk and nxt */ + state_ptr = &state[1]; + + for ( i = 1; i <= numecs; ++i, ++state_ptr ) + if ( *state_ptr != 0 ) + { + chk[position + i] = i; + nxt[position + i] = *state_ptr; + } + + if ( position + numecs > tblend ) + tblend = position + numecs; + } + + +/* stack1 - save states with only one out-transition to be processed later + * + * synopsis + * int statenum, sym, nextstate, deflink; + * stack1( statenum, sym, nextstate, deflink ); + * + * if there's room for another state one the "one-transition" stack, the + * state is pushed onto it, to be processed later by mk1tbl. If there's + * no room, we process the sucker right now. + */ + +void stack1(int statenum, int sym, int nextstate, int deflink) +{ + if ( onesp >= ONE_STACK_SIZE - 1 ) + mk1tbl( statenum, sym, nextstate, deflink ); + + else + { + ++onesp; + onestate[onesp] = statenum; + onesym[onesp] = sym; + onenext[onesp] = nextstate; + onedef[onesp] = deflink; + } + } + + +/* tbldiff - compute differences between two state tables + * + * synopsis + * int state[], pr, ext[]; + * int tbldiff, numdifferences; + * numdifferences = tbldiff( state, pr, ext ) + * + * "state" is the state array which is to be extracted from the pr'th + * proto. "pr" is both the number of the proto we are extracting from + * and an index into the save area where we can find the proto's complete + * state table. Each entry in "state" which differs from the corresponding + * entry of "pr" will appear in "ext". + * Entries which are the same in both "state" and "pr" will be marked + * as transitions to "SAME_TRANS" in "ext". The total number of differences + * between "state" and "pr" is returned as function value. Note that this + * number is "numecs" minus the number of "SAME_TRANS" entries in "ext". + */ + +int tbldiff(int *state, int pr, int *ext) +{ + int i, *sp = state, *ep = ext, *protp; + int numdiff = 0; + + protp = &protsave[numecs * (pr - 1)]; + + for ( i = numecs; i > 0; --i ) + { + if ( *++protp == *++sp ) + *++ep = SAME_TRANS; + else + { + *++ep = *sp; + ++numdiff; + } + } + + return ( numdiff ); + } diff --git a/modules/libcom/src/flex/yylex.c b/modules/libcom/src/flex/yylex.c new file mode 100644 index 000000000..28f487d6b --- /dev/null +++ b/modules/libcom/src/flex/yylex.c @@ -0,0 +1,218 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* yylex - scanner front-end for flex */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include "flexdef.h" +#include "parse.h" + +/* ANSI C does not guarantee that isascii() is defined */ +#ifndef isascii +#define isascii(c) ((c) <= 0177) +#endif + + +/* yylex - scan for a regular expression token + * + * synopsis + * + * token = yylex(); + * + * token - return token found + */ + +int yylex(void) +{ + int toktype; + static int beglin = false; + + if ( eofseen ) + toktype = EOF; + else + toktype = flexscan(); + + if ( toktype == EOF || toktype == 0 ) + { + eofseen = 1; + + if ( sectnum == 1 ) + { + synerr( "premature EOF" ); + sectnum = 2; + toktype = SECTEND; + } + + else if ( sectnum == 2 ) + { + sectnum = 3; + toktype = 0; + } + + else + toktype = 0; + } + + if ( trace ) + { + if ( beglin ) + { + fprintf( stderr, "%d\t", num_rules + 1 ); + beglin = 0; + } + + switch ( toktype ) + { + case '<': + case '>': + case '^': + case '$': + case '"': + case '[': + case ']': + case '{': + case '}': + case '|': + case '(': + case ')': + case '-': + case '/': + case '\\': + case '?': + case '.': + case '*': + case '+': + case ',': + (void) putc( toktype, stderr ); + break; + + case '\n': + (void) putc( '\n', stderr ); + + if ( sectnum == 2 ) + beglin = 1; + + break; + + case SCDECL: + fputs( "%s", stderr ); + break; + + case XSCDECL: + fputs( "%x", stderr ); + break; + + case WHITESPACE: + (void) putc( ' ', stderr ); + break; + + case SECTEND: + fputs( "%%\n", stderr ); + + /* we set beglin to be true so we'll start + * writing out numbers as we echo rules. flexscan() has + * already assigned sectnum + */ + + if ( sectnum == 2 ) + beglin = 1; + + break; + + case NAME: + fprintf( stderr, "'%s'", nmstr ); + break; + + case CHAR: + switch ( yylval ) + { + case '<': + case '>': + case '^': + case '$': + case '"': + case '[': + case ']': + case '{': + case '}': + case '|': + case '(': + case ')': + case '-': + case '/': + case '\\': + case '?': + case '.': + case '*': + case '+': + case ',': + fprintf( stderr, "\\%c", yylval ); + break; + + default: + if ( ! isascii( yylval ) || ! isprint( yylval ) ) + fprintf( stderr, "\\%.3o", yylval ); + else + (void) putc( yylval, stderr ); + break; + } + + break; + + case NUMBER: + fprintf( stderr, "%d", yylval ); + break; + + case PREVCCL: + fprintf( stderr, "[%d]", yylval ); + break; + + case EOF_OP: + fprintf( stderr, "<>" ); + break; + + case 0: + fprintf( stderr, "End Marker" ); + break; + + default: + fprintf( stderr, "*Something Weird* - tok: %d val: %d\n", + toktype, yylval ); + break; + } + } + + return ( toktype ); +} + diff --git a/modules/libcom/src/freeList/Makefile b/modules/libcom/src/freeList/Makefile new file mode 100644 index 000000000..3a1e25182 --- /dev/null +++ b/modules/libcom/src/freeList/Makefile @@ -0,0 +1,14 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/freeList + +INC += freeList.h + +Com_SRCS += freeListLib.c diff --git a/modules/libcom/src/freeList/freeList.h b/modules/libcom/src/freeList/freeList.h new file mode 100644 index 000000000..77ebfb82e --- /dev/null +++ b/modules/libcom/src/freeList/freeList.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 04-19-94 */ + +#ifndef INCfreeListh +#define INCfreeListh + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI freeListInitPvt(void **ppvt,int size,int nmalloc); +epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt); +epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt); +epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem); +epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt); +epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt); + +#ifdef __cplusplus +} +#endif + +#endif /*INCfreeListh*/ diff --git a/modules/libcom/src/freeList/freeListLib.c b/modules/libcom/src/freeList/freeListLib.c new file mode 100755 index 000000000..b489a476d --- /dev/null +++ b/modules/libcom/src/freeList/freeListLib.c @@ -0,0 +1,181 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 04-19-94 */ + +#include +#include +#include + +#include "valgrind/valgrind.h" + +#ifndef NVALGRIND +/* buffer around allocations to detect out of bounds access */ +#define REDZONE sizeof(double) +#else +#define REDZONE 0 +#endif + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsMutex.h" +#include "freeList.h" +#include "adjustment.h" + +typedef struct allocMem { + struct allocMem *next; + void *memory; +}allocMem; +typedef struct { + int size; + int nmalloc; + void *head; + allocMem *mallochead; + size_t nBlocksAvailable; + epicsMutexId lock; +}FREELISTPVT; + +epicsShareFunc void epicsShareAPI + freeListInitPvt(void **ppvt,int size,int nmalloc) +{ + FREELISTPVT *pfl; + + pfl = callocMustSucceed(1,sizeof(FREELISTPVT), "freeListInitPvt"); + pfl->size = adjustToWorstCaseAlignment(size); + pfl->nmalloc = nmalloc; + pfl->head = NULL; + pfl->mallochead = NULL; + pfl->nBlocksAvailable = 0u; + pfl->lock = epicsMutexMustCreate(); + *ppvt = (void *)pfl; + VALGRIND_CREATE_MEMPOOL(pfl, REDZONE, 0); + return; +} + +epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + return callocMustSucceed(1,pfl->size,"freeList Debug Calloc"); +# else + void *ptemp; + + ptemp = freeListMalloc(pvt); + if(ptemp) memset((char *)ptemp,0,pfl->size); + return(ptemp); +# endif +} + +epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + return callocMustSucceed(1,pfl->size,"freeList Debug Malloc"); +# else + void *ptemp; + void **ppnext; + allocMem *pallocmem; + int i; + + epicsMutexMustLock(pfl->lock); + ptemp = pfl->head; + if(ptemp==0) { + /* layout of each block. nmalloc+1 REDZONEs for nmallocs. + * The first sizeof(void*) bytes are used to store a pointer + * to the next free block. + * + * | RED | size0 ------ | RED | size1 | ... | RED | + * | | next | ----- | + */ + ptemp = (void *)malloc(pfl->nmalloc*(pfl->size+REDZONE)+REDZONE); + if(ptemp==0) { + epicsMutexUnlock(pfl->lock); + return(0); + } + pallocmem = (allocMem *)calloc(1,sizeof(allocMem)); + if(pallocmem==0) { + epicsMutexUnlock(pfl->lock); + free(ptemp); + return(0); + } + pallocmem->memory = ptemp; /* real allocation */ + ptemp = REDZONE + (char *) ptemp; /* skip first REDZONE */ + if(pfl->mallochead) + pallocmem->next = pfl->mallochead; + pfl->mallochead = pallocmem; + for(i=0; inmalloc; i++) { + ppnext = ptemp; + VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, sizeof(void*)); + *ppnext = pfl->head; + pfl->head = ptemp; + ptemp = ((char *)ptemp) + pfl->size+REDZONE; + } + ptemp = pfl->head; + pfl->nBlocksAvailable += pfl->nmalloc; + } + ppnext = pfl->head; + pfl->head = *ppnext; + pfl->nBlocksAvailable--; + epicsMutexUnlock(pfl->lock); + VALGRIND_MEMPOOL_FREE(pfl, ptemp); + VALGRIND_MEMPOOL_ALLOC(pfl, ptemp, pfl->size); + return(ptemp); +# endif +} + +epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + memset ( pmem, 0xdd, pfl->size ); + free(pmem); +# else + void **ppnext; + + VALGRIND_MEMPOOL_FREE(pvt, pmem); + VALGRIND_MEMPOOL_ALLOC(pvt, pmem, sizeof(void*)); + + epicsMutexMustLock(pfl->lock); + ppnext = pmem; + *ppnext = pfl->head; + pfl->head = pmem; + pfl->nBlocksAvailable++; + epicsMutexUnlock(pfl->lock); +# endif +} + +epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) +{ + FREELISTPVT *pfl = pvt; + allocMem *phead; + allocMem *pnext; + + VALGRIND_DESTROY_MEMPOOL(pvt); + + phead = pfl->mallochead; + while(phead) { + pnext = phead->next; + free(phead->memory); + free(phead); + phead = pnext; + } + epicsMutexDestroy(pfl->lock); + free(pvt); +} + +epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt) +{ + FREELISTPVT *pfl = pvt; + size_t nBlocksAvailable; + epicsMutexMustLock(pfl->lock); + nBlocksAvailable = pfl->nBlocksAvailable; + epicsMutexUnlock(pfl->lock); + return nBlocksAvailable; +} + diff --git a/modules/libcom/src/gpHash/Makefile b/modules/libcom/src/gpHash/Makefile new file mode 100644 index 000000000..6fcd3f8a7 --- /dev/null +++ b/modules/libcom/src/gpHash/Makefile @@ -0,0 +1,14 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/gpHash + +INC += gpHash.h + +Com_SRCS += gpHashLib.c diff --git a/modules/libcom/src/gpHash/gpHash.h b/modules/libcom/src/gpHash/gpHash.h new file mode 100644 index 000000000..d9c0dd204 --- /dev/null +++ b/modules/libcom/src/gpHash/gpHash.h @@ -0,0 +1,53 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 04-07-94 */ + +/* gph provides a general purpose directory accessed via a hash table*/ + +#ifndef INC_gpHash_H +#define INC_gpHash_H + +#include "shareLib.h" + +#include "ellLib.h" + +typedef struct{ + ELLNODE node; + const char *name; /*address of name placed in directory*/ + void *pvtid; /*private name for subsystem user*/ + void *userPvt; /*private for user*/ +} GPHENTRY; + +struct gphPvt; + +#ifdef __cplusplus +extern "C" { +#endif + +/*tableSize must be power of 2 in range 256 to 65536*/ +epicsShareFunc void epicsShareAPI + gphInitPvt(struct gphPvt **ppvt, int tableSize); +epicsShareFunc GPHENTRY * epicsShareAPI + gphFind(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc GPHENTRY * epicsShareAPI + gphFindParse(struct gphPvt *pvt, const char *name, size_t len, void *pvtid); +epicsShareFunc GPHENTRY * epicsShareAPI + gphAdd(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc void epicsShareAPI + gphDelete(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc void epicsShareAPI gphFreeMem(struct gphPvt *pvt); +epicsShareFunc void epicsShareAPI gphDump(struct gphPvt *pvt); +epicsShareFunc void epicsShareAPI gphDumpFP(FILE *fp, struct gphPvt *pvt); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_gpHash_H */ diff --git a/modules/libcom/src/gpHash/gpHashLib.c b/modules/libcom/src/gpHash/gpHashLib.c new file mode 100644 index 000000000..a56f00a0c --- /dev/null +++ b/modules/libcom/src/gpHash/gpHashLib.c @@ -0,0 +1,244 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 04-07-94 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsMutex.h" +#include "epicsStdioRedirect.h" +#include "epicsString.h" +#include "dbDefs.h" +#include "ellLib.h" +#include "epicsPrint.h" +#include "gpHash.h" + +typedef struct gphPvt { + int size; + unsigned int mask; + ELLLIST **paplist; /*pointer to array of pointers to ELLLIST */ + epicsMutexId lock; +} gphPvt; + +#define MIN_SIZE 256 +#define DEFAULT_SIZE 512 +#define MAX_SIZE 65536 + + +void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) +{ + gphPvt *pgphPvt; + + if (size & (size - 1)) { + fprintf(stderr, "gphInitPvt: %d is not a power of 2\n", size); + size = DEFAULT_SIZE; + } + + if (size < MIN_SIZE) + size = MIN_SIZE; + + if (size > MAX_SIZE) + size = MAX_SIZE; + + pgphPvt = callocMustSucceed(1, sizeof(gphPvt), "gphInitPvt"); + pgphPvt->size = size; + pgphPvt->mask = size - 1; + pgphPvt->paplist = callocMustSucceed(size, sizeof(ELLLIST *), "gphInitPvt"); + pgphPvt->lock = epicsMutexMustCreate(); + *ppvt = pgphPvt; + return; +} + +GPHENTRY * epicsShareAPI gphFindParse(gphPvt *pgphPvt, const char *name, size_t len, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *gphlist; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return NULL; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsMemHash(name, len, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + gphlist = paplist[hash]; + if (gphlist == NULL) { + pgphNode = NULL; + } else { + pgphNode = (GPHENTRY *) ellFirst(gphlist); + } + + while (pgphNode) { + if (pvtid == pgphNode->pvtid && + strlen(pgphNode->name) == len && + strncmp(name, pgphNode->name, len) == 0) break; + pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); + } + + epicsMutexUnlock(pgphPvt->lock); + return pgphNode; +} + +GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + return gphFindParse(pgphPvt, name, strlen(name), pvtid); +} + +GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *plist; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return NULL; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsStrHash(name, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + plist = paplist[hash]; + if (plist == NULL) { + plist = calloc(1, sizeof(ELLLIST)); + if(!plist){ + epicsMutexUnlock(pgphPvt->lock); + return NULL; + } + ellInit(plist); + paplist[hash] = plist; + } + + pgphNode = (GPHENTRY *) ellFirst(plist); + while (pgphNode) { + if (pvtid == pgphNode->pvtid && + strcmp(name, pgphNode->name) == 0) { + epicsMutexUnlock(pgphPvt->lock); + return NULL; + } + pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); + } + + pgphNode = calloc(1, sizeof(GPHENTRY)); + if(pgphNode) { + pgphNode->name = name; + pgphNode->pvtid = pvtid; + ellAdd(plist, (ELLNODE *)pgphNode); + } + + epicsMutexUnlock(pgphPvt->lock); + return (pgphNode); +} + +void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *plist = NULL; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsStrHash(name, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + if (paplist[hash] == NULL) { + pgphNode = NULL; + } else { + plist = paplist[hash]; + pgphNode = (GPHENTRY *) ellFirst(plist); + } + + while(pgphNode) { + if (pvtid == pgphNode->pvtid && + strcmp(name, pgphNode->name) == 0) { + ellDelete(plist, (ELLNODE*)pgphNode); + free((void *)pgphNode); + break; + } + pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + } + + epicsMutexUnlock(pgphPvt->lock); + return; +} + +void epicsShareAPI gphFreeMem(gphPvt *pgphPvt) +{ + ELLLIST **paplist; + int h; + + /* Caller must ensure that no other thread is using *pvt */ + if (pgphPvt == NULL) return; + + paplist = pgphPvt->paplist; + for (h = 0; h < pgphPvt->size; h++) { + ELLLIST *plist = paplist[h]; + GPHENTRY *pgphNode; + GPHENTRY *next; + + if (plist == NULL) continue; + pgphNode = (GPHENTRY *) ellFirst(plist); + + while (pgphNode) { + next = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + ellDelete(plist, (ELLNODE*)pgphNode); + free(pgphNode); + pgphNode = next; + } + free(paplist[h]); + } + epicsMutexDestroy(pgphPvt->lock); + free(paplist); + free(pgphPvt); +} + +void epicsShareAPI gphDump(gphPvt *pgphPvt) +{ + gphDumpFP(stdout, pgphPvt); +} + +void epicsShareAPI gphDumpFP(FILE *fp, gphPvt *pgphPvt) +{ + unsigned int empty = 0; + ELLLIST **paplist; + int h; + + if (pgphPvt == NULL) + return; + + fprintf(fp, "Hash table has %d buckets", pgphPvt->size); + + paplist = pgphPvt->paplist; + for (h = 0; h < pgphPvt->size; h++) { + ELLLIST *plist = paplist[h]; + GPHENTRY *pgphNode; + int i = 0; + + if (plist == NULL) { + empty++; + continue; + } + pgphNode = (GPHENTRY *) ellFirst(plist); + + fprintf(fp, "\n [%3d] %3d ", h, ellCount(plist)); + while (pgphNode) { + if (!(++i % 3)) + fprintf(fp, "\n "); + fprintf(fp, " %s %p", pgphNode->name, pgphNode->pvtid); + pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + } + } + fprintf(fp, "\n%u buckets empty.\n", empty); +} diff --git a/modules/libcom/src/iocsh/Makefile b/modules/libcom/src/iocsh/Makefile new file mode 100644 index 000000000..766577354 --- /dev/null +++ b/modules/libcom/src/iocsh/Makefile @@ -0,0 +1,16 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/iocsh +INC += iocsh.h +INC += registry.h +INC += libComRegister.h +Com_SRCS += iocsh.cpp +Com_SRCS += registry.c +Com_SRCS += libComRegister.c diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp new file mode 100644 index 000000000..d6626de89 --- /dev/null +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -0,0 +1,1097 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* iocsh.cpp */ +/* Author: Marty Kraimer Date: 27APR2000 */ +/* Heavily modified by Eric Norum Date: 03MAY2000 */ +/* Adapted to C++ by Eric Norum Date: 18DEC2000 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "macLib.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "epicsStdlib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "envDefs.h" +#include "registry.h" +#include "epicsReadline.h" +#include "cantProceed.h" +#include "iocsh.h" + +extern "C" { + +/* + * Global link to pdbbase + */ +epicsShareDef struct dbBase **iocshPpdbbase; + +/* + * File-local information + */ +struct iocshCommand { + iocshCmdDef def; + struct iocshCommand *next; +}; +static struct iocshCommand *iocshCommandHead; +static char iocshCmdID[] = "iocshCmd"; +struct iocshVariable { + iocshVarDef const *pVarDef; + struct iocshVariable *next; +}; +static struct iocshVariable *iocshVariableHead; +static char iocshVarID[] = "iocshVar"; +extern "C" { static void varCallFunc(const iocshArgBuf *); } +static epicsMutexId iocshTableMutex; +static epicsThreadOnceId iocshOnceId = EPICS_THREAD_ONCE_INIT; +static epicsThreadPrivateId iocshMacroHandleId; + +/* + * I/O redirection + */ +#define NREDIRECTS 5 +struct iocshRedirect { + const char *name; + const char *mode; + FILE *fp; + FILE *oldFp; + int mustRestore; +}; + +/* + * Set up module variables + */ +static void iocshOnce (void *) +{ + iocshTableMutex = epicsMutexMustCreate (); + iocshMacroHandleId = epicsThreadPrivateCreate(); +} + +static void iocshInit (void) +{ + epicsThreadOnce (&iocshOnceId, iocshOnce, NULL); +} + +/* + * Lock the table mutex + */ +static void +iocshTableLock (void) +{ + iocshInit(); + epicsMutexMustLock (iocshTableMutex); +} + +/* + * Unlock the table mutex + */ +static void +iocshTableUnlock (void) +{ + epicsMutexUnlock (iocshTableMutex); +} + +/* + * Register a command + */ +void epicsShareAPI iocshRegister (const iocshFuncDef *piocshFuncDef, + iocshCallFunc func) +{ + struct iocshCommand *l, *p, *n; + int i; + + iocshTableLock (); + for (l = NULL, p = iocshCommandHead ; p != NULL ; l = p, p = p->next) { + i = strcmp (piocshFuncDef->name, p->def.pFuncDef->name); + if (i == 0) { + p->def.pFuncDef = piocshFuncDef; + p->def.func = func; + iocshTableUnlock (); + return; + } + if (i < 0) + break; + } + n = (struct iocshCommand *) callocMustSucceed (1, sizeof *n, + "iocshRegister"); + if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) { + free (n); + iocshTableUnlock (); + errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name); + return; + } + if (l == NULL) { + n->next = iocshCommandHead; + iocshCommandHead = n; + } + else { + n->next = l->next; + l->next = n; + } + n->def.pFuncDef = piocshFuncDef; + n->def.func = func; + iocshTableUnlock (); +} + + +/* + * Retrieves a previously registered function with the given name. + */ +const iocshCmdDef * epicsShareAPI iocshFindCommand(const char *name) +{ + return (iocshCmdDef *) registryFind(iocshCmdID, name); +} + +/* + * Register the "var" command and any variable(s) + */ +static const iocshArg varCmdArg0 = { "[variable", iocshArgString}; +static const iocshArg varCmdArg1 = { "[value]]", iocshArgString}; +static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1}; +static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs}; + +void epicsShareAPI iocshRegisterVariable (const iocshVarDef *piocshVarDef) +{ + struct iocshVariable *l, *p, *n; + int i; + int found; + + iocshTableLock (); + while ((piocshVarDef != NULL) + && (piocshVarDef->name != NULL) + && (*piocshVarDef->name != '\0')) { + if (iocshVariableHead == NULL) + iocshRegister(&varFuncDef,varCallFunc); + found = 0; + for (l = NULL, p = iocshVariableHead ; p != NULL ; l = p, p = p->next) { + i = strcmp (piocshVarDef->name, p->pVarDef->name); + if (i == 0) { + errlogPrintf("Warning: iocshRegisterVariable redefining %s.\n", + piocshVarDef->name); + p->pVarDef = piocshVarDef; + found = 1; + break; + } + if (i < 0) + break; + } + if (!found) { + n = (struct iocshVariable *) callocMustSucceed(1, sizeof *n, + "iocshRegisterVariable"); + if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) { + free(n); + iocshTableUnlock(); + errlogPrintf("iocshRegisterVariable failed to add %s.\n", + piocshVarDef->name); + return; + } + if (l == NULL) { + n->next = iocshVariableHead; + iocshVariableHead = n; + } + else { + n->next = l->next; + l->next = n; + } + n->pVarDef = piocshVarDef; + } + piocshVarDef++; + } + iocshTableUnlock (); +} + +/* + * Retrieves a previously registered variable with the given name. + */ +const iocshVarDef * epicsShareAPI iocshFindVariable(const char *name) +{ + struct iocshVariable *temp = (iocshVariable *) registryFind(iocshVarID, name); + return temp->pVarDef; +} + +/* + * Free storage created by iocshRegister/iocshRegisterVariable + */ +void epicsShareAPI iocshFree(void) +{ + struct iocshCommand *pc; + struct iocshVariable *pv; + + iocshTableLock (); + for (pc = iocshCommandHead ; pc != NULL ; ) { + struct iocshCommand * nc = pc->next; + free (pc); + pc = nc; + } + for (pv = iocshVariableHead ; pv != NULL ; ) { + struct iocshVariable *nv = pv->next; + free (pv); + pv = nv; + } + iocshCommandHead = NULL; + iocshVariableHead = NULL; + iocshTableUnlock (); +} + +/* + * Report an error + */ +static void +showError (const char *filename, int lineno, const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + if (filename) + fprintf(epicsGetStderr(), "%s line %d: ", filename, lineno); + vfprintf (epicsGetStderr(), msg, ap); + fputc ('\n', epicsGetStderr()); + va_end (ap); +} + +static int +cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf, + const iocshArg *piocshArg) +{ + char *endp; + + switch (piocshArg->type) { + case iocshArgInt: + if (arg && *arg) { + errno = 0; + argBuf->ival = strtol (arg, &endp, 0); + if (errno == ERANGE) { + errno = 0; + argBuf->ival = strtoul (arg, &endp, 0); + if (errno == ERANGE) { + showError(filename, lineno, "Integer '%s' out of range", + arg); + return 0; + } + } + if (*endp) { + showError(filename, lineno, "Illegal integer '%s'", arg); + return 0; + } + } + else { + argBuf->ival = 0; + } + break; + + case iocshArgDouble: + if (arg && *arg) { + argBuf->dval = epicsStrtod (arg, &endp); + if (*endp) { + showError(filename, lineno, "Illegal double '%s'", arg); + return 0; + } + } + else { + argBuf->dval = 0.0; + } + break; + + case iocshArgString: + argBuf->sval = arg; + break; + + case iocshArgPersistentString: + argBuf->sval = (char *) malloc(strlen(arg) + 1); + if (argBuf->sval == NULL) { + showError(filename, lineno, "Out of memory"); + return 0; + } + strcpy(argBuf->sval, arg); + break; + + case iocshArgPdbbase: + /* Argument must be missing or 0 or pdbbase */ + if(!arg || !*arg || (*arg == '0') || (strcmp(arg, "pdbbase") == 0)) { + if(!iocshPpdbbase || !*iocshPpdbbase) { + showError(filename, lineno, "pdbbase not present"); + return 0; + } + argBuf->vval = *iocshPpdbbase; + break; + } + showError(filename, lineno, "Expecting 'pdbbase' got '%s'", arg); + return 0; + + default: + showError(filename, lineno, "Illegal argument type %d", + piocshArg->type); + return 0; + } + return 1; +} + +/* + * Open redirected I/O + */ +static int +openRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->name != NULL) { + redirect->fp = fopen(redirect->name, redirect->mode); + if (redirect->fp == NULL) { + showError(filename, lineno, "Can't open \"%s\": %s.", + redirect->name, strerror(errno)); + redirect->name = NULL; + while (i--) { + redirect--; + if (redirect->fp) { + fclose(redirect->fp); + redirect->fp = NULL; + } + redirect->name = NULL; + } + return -1; + } + redirect->mustRestore = 0; + } + } + return 0; +} + +/* + * Start I/O redirection + */ +static void +startRedirect(const char * /*filename*/, int /*lineno*/, + struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->fp != NULL) { + switch(i) { + case 0: + redirect->oldFp = epicsGetThreadStdin(); + epicsSetThreadStdin(redirect->fp); + redirect->mustRestore = 1; + break; + case 1: + redirect->oldFp = epicsGetThreadStdout(); + epicsSetThreadStdout(redirect->fp); + redirect->mustRestore = 1; + break; + case 2: + redirect->oldFp = epicsGetThreadStderr(); + epicsSetThreadStderr(redirect->fp); + redirect->mustRestore = 1; + break; + } + } + } +} + +/* + * Finish up I/O redirection + */ +static void +stopRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->fp != NULL) { + if (fclose(redirect->fp) != 0) + showError(filename, lineno, "Error closing \"%s\": %s.", + redirect->name, strerror(errno)); + redirect->fp = NULL; + if (redirect->mustRestore) { + switch(i) { + case 0: epicsSetThreadStdin(redirect->oldFp); break; + case 1: epicsSetThreadStdout(redirect->oldFp); break; + case 2: epicsSetThreadStderr(redirect->oldFp); break; + } + } + } + redirect->name = NULL; + } +} + +/* + * "help" command + */ +static const iocshArg helpArg0 = { "[command ...]",iocshArgArgv}; +static const iocshArg *helpArgs[1] = {&helpArg0}; +static const iocshFuncDef helpFuncDef = + {"help",1,helpArgs}; +static void helpCallFunc(const iocshArgBuf *args) +{ + int argc = args[0].aval.ac; + const char * const * argv = args[0].aval.av; + struct iocshFuncDef const *piocshFuncDef; + struct iocshCommand *pcmd; + + if (argc == 1) { + int l, col = 0; + + fprintf(epicsGetStdout(), + "Type 'help ' to see the arguments of .\n"); + iocshTableLock (); + for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { + piocshFuncDef = pcmd->def.pFuncDef; + l = strlen (piocshFuncDef->name); + if ((l + col) >= 79) { + fputc('\n', epicsGetStdout()); + col = 0; + } + fputs(piocshFuncDef->name, epicsGetStdout()); + col += l; + if (col >= 64) { + fputc('\n', epicsGetStdout()); + col = 0; + } + else { + do { + fputc(' ', epicsGetStdout()); + col++; + } while ((col % 16) != 0); + } + } + if (col) + fputc('\n', epicsGetStdout()); + iocshTableUnlock (); + } + else { + for (int iarg = 1 ; iarg < argc ; iarg++) { + for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { + piocshFuncDef = pcmd->def.pFuncDef; + if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) { + fputs(piocshFuncDef->name, epicsGetStdout()); + for (int a = 0 ; a < piocshFuncDef->nargs ; a++) { + const char *cp = piocshFuncDef->arg[a]->name; + if ((piocshFuncDef->arg[a]->type == iocshArgArgv) + || (strchr (cp, ' ') == NULL)) { + fprintf(epicsGetStdout(), " %s", cp); + } + else { + fprintf(epicsGetStdout(), " '%s'", cp); + } + } + fprintf(epicsGetStdout(),"\n");; + } + } + } + } +} + +/* + * The body of the command interpreter + */ +static int +iocshBody (const char *pathname, const char *commandLine, const char *macros) +{ + FILE *fp = NULL; + const char *filename = NULL; + int icin, icout; + char c; + int quote, inword, backslash; + const char *raw = NULL;; + char *line = NULL; + int lineno = 0; + int argc; + char **argv = NULL; + int argvCapacity = 0; + struct iocshRedirect *redirects = NULL; + struct iocshRedirect *redirect = NULL; + int sep; + const char *prompt = NULL; + const char *ifs = " \t(),\r"; + iocshArgBuf *argBuf = NULL; + int argBufCapacity = 0; + struct iocshCommand *found; + void *readlineContext = NULL; + int wasOkToBlock; + static const char * pairs[] = {"", "environ", NULL, NULL}; + MAC_HANDLE *handle; + char ** defines = NULL; + + iocshInit(); + + /* + * See if command interpreter is interactive + */ + if (commandLine == NULL) { + if ((pathname == NULL) || (strcmp (pathname, "") == 0)) { + if ((prompt = envGetConfigParamPtr(&IOCSH_PS1)) == NULL) + prompt = "epics> "; + } + else { + fp = fopen (pathname, "r"); + if (fp == NULL) { + fprintf(epicsGetStderr(), "Can't open %s: %s\n", pathname, + strerror (errno)); + return -1; + } + if ((filename = strrchr (pathname, '/')) == NULL) + filename = pathname; + else + filename++; + prompt = NULL; + } + + /* + * Create a command-line input context + */ + if ((readlineContext = epicsReadlineBegin(fp)) == NULL) { + fprintf(epicsGetStderr(), "Can't allocate command-line object.\n"); + if (fp) + fclose(fp); + return -1; + } + } + + /* + * Set up redirection + */ + redirects = (struct iocshRedirect *)calloc(NREDIRECTS, sizeof *redirects); + if (redirects == NULL) { + fprintf(epicsGetStderr(), "Out of memory!\n"); + return -1; + } + + /* + * Parse macro definitions, this check occurs before creating the + * macro handle to simplify cleanup. + */ + + if (macros) { + if (macParseDefns(NULL, macros, &defines) < 0) { + free(redirects); + return -1; + } + } + + /* + * Check for existing macro context or construct a new one. + */ + handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId); + + if (handle == NULL) { + if (macCreateHandle(&handle, pairs)) { + errlogMessage("iocsh: macCreateHandle failed."); + free(redirects); + return -1; + } + + epicsThreadPrivateSet(iocshMacroHandleId, (void *) handle); + } + + macPushScope(handle); + macInstallMacros(handle, defines); + + /* + * Read commands till EOF or exit + */ + argc = 0; + wasOkToBlock = epicsThreadIsOkToBlock(); + epicsThreadSetOkToBlock(1); + for (;;) { + /* + * Read a line + */ + if (commandLine) { + if (raw != NULL) + break; + raw = commandLine; + } + else { + if ((raw = epicsReadline(prompt, readlineContext)) == NULL) + break; + } + lineno++; + + /* + * Skip leading white-space + */ + icin = 0; + while ((c = raw[icin]) && isspace(c)) { + icin++; + } + + /* + * Ignore comment lines other than to echo + * them if they came from a script (disable echoing + * with '#-'). This avoids macLib errors from comments. + */ + if (c == '#') { + if ((prompt == NULL) && (commandLine == NULL)) + if (raw[icin + 1] != '-') + puts(raw); + continue; + } + + /* + * Expand macros + */ + free(line); + if ((line = macDefExpand(raw, handle)) == NULL) + continue; + + /* + * Skip leading white-space coming from a macro + */ + while ((c = line[icin]) && isspace(c)) { + icin++; + } + + /* + * Echo non-empty lines read from a script. + * Comments delineated with '#-' aren't echoed. + */ + if ((prompt == NULL) && *line && (commandLine == NULL)) + if ((c != '#') || (line[icin + 1] != '-')) + puts(line); + + /* + * Ignore lines that became a comment or empty after macro expansion + */ + if (!c || c == '#') + continue; + + /* + * Break line into words + */ + icout = 0; + inword = 0; + argc = 0; + quote = EOF; + backslash = 0; + redirect = NULL; + for (;;) { + if (argc >= argvCapacity) { + char **av; + argvCapacity += 50; + av = (char **)realloc (argv, argvCapacity * sizeof *argv); + if (av == NULL) { + fprintf (epicsGetStderr(), "Out of memory!\n"); + argc = -1; + break; + } + argv = av; + } + c = line[icin++]; + if (c == '\0') + break; + if ((quote == EOF) && !backslash && (strchr (ifs, c))) + sep = 1; + else + sep = 0; + if ((quote == EOF) && !backslash) { + int redirectFd = 1; + if (c == '\\') { + backslash = 1; + continue; + } + if (c == '<') { + if (redirect != NULL) { + break; + } + redirect = &redirects[0]; + sep = 1; + redirect->mode = "r"; + } + if ((c >= '1') && (c <= '9') && (line[icin] == '>')) { + redirectFd = c - '0'; + c = '>'; + icin++; + } + if (c == '>') { + if (redirect != NULL) + break; + if (redirectFd >= NREDIRECTS) { + redirect = &redirects[1]; + break; + } + redirect = &redirects[redirectFd]; + sep = 1; + if (line[icin] == '>') { + icin++; + redirect->mode = "a"; + } + else { + redirect->mode = "w"; + } + } + } + if (inword) { + if (c == quote) { + quote = EOF; + } + else { + if ((quote == EOF) && !backslash) { + if (sep) { + inword = 0; + line[icout++] = '\0'; + } + else if ((c == '"') || (c == '\'')) { + quote = c; + } + else { + line[icout++] = c; + } + } + else { + line[icout++] = c; + } + } + } + else { + if (!sep) { + if (((c == '"') || (c == '\'')) && !backslash) + quote = c; + if (redirect != NULL) { + if (redirect->name != NULL) { + argc = -1; + break; + } + redirect->name = line + icout; + redirect = NULL; + } + else { + argv[argc++] = line + icout; + } + if (quote == EOF) + line[icout++] = c; + inword = 1; + } + } + backslash = 0; + } + if (redirect != NULL) { + showError(filename, lineno, "Illegal redirection."); + continue; + } + if (argc < 0) + break; + if (quote != EOF) { + showError(filename, lineno, "Unbalanced quote."); + continue; + } + if (backslash) { + showError(filename, lineno, "Trailing backslash."); + continue; + } + if (inword) + line[icout++] = '\0'; + argv[argc] = NULL; + + /* + * Special case -- Redirected input but no command + * Treat as if 'iocsh filename'. + */ + if ((argc == 0) && (redirects[0].name != NULL)) { + const char *commandFile = redirects[0].name; + redirects[0].name = NULL; + if (openRedirect(filename, lineno, redirects) < 0) + continue; + startRedirect(filename, lineno, redirects); + iocshBody(commandFile, NULL, macros); + stopRedirect(filename, lineno, redirects); + continue; + } + + /* + * Special command? + */ + if ((argc > 0) && (strcmp(argv[0], "exit") == 0)) + break; + + /* + * Set up redirection + */ + if ((openRedirect(filename, lineno, redirects) == 0) && (argc > 0)) { + /* + * Look up command + */ + found = (iocshCommand *)registryFind (iocshCmdID, argv[0]); + if (found) { + /* + * Process arguments and call function + */ + struct iocshFuncDef const *piocshFuncDef = found->def.pFuncDef; + for (int iarg = 0 ; ; ) { + if (iarg == piocshFuncDef->nargs) { + startRedirect(filename, lineno, redirects); + (*found->def.func)(argBuf); + break; + } + if (iarg >= argBufCapacity) { + void *np; + + argBufCapacity += 20; + np = realloc (argBuf, argBufCapacity * sizeof *argBuf); + if (np == NULL) { + fprintf (epicsGetStderr(), "Out of memory!\n"); + argBufCapacity -= 20; + break; + } + argBuf = (iocshArgBuf *)np; + } + if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) { + argBuf[iarg].aval.ac = argc-iarg; + argBuf[iarg].aval.av = argv+iarg; + iarg = piocshFuncDef->nargs; + } + else { + if (!cvtArg (filename, lineno, + ((iarg < argc) ? argv[iarg+1] : NULL), + &argBuf[iarg], piocshFuncDef->arg[iarg])) + break; + iarg++; + } + } + if ((prompt != NULL) && (strcmp(argv[0], "epicsEnvSet") == 0)) { + const char *newPrompt; + if ((newPrompt = envGetConfigParamPtr(&IOCSH_PS1)) != NULL) + prompt = newPrompt; + } + } + else { + showError(filename, lineno, "Command %s not found.", argv[0]); + } + } + stopRedirect(filename, lineno, redirects); + } + macPopScope(handle); + + if (handle->level == 0) { + macDeleteHandle(handle); + epicsThreadPrivateSet(iocshMacroHandleId, NULL); + } + if (fp && (fp != stdin)) + fclose (fp); + if (redirects != NULL) { + stopRedirect(filename, lineno, redirects); + free (redirects); + } + free(line); + free (argv); + free (argBuf); + errlogFlush(); + if (readlineContext) + epicsReadlineEnd(readlineContext); + epicsThreadSetOkToBlock( wasOkToBlock); + return 0; +} + +/* + * External access to the command interpreter + */ +int epicsShareAPI +iocsh (const char *pathname) +{ + return iocshLoad(pathname, NULL); +} + +int epicsShareAPI +iocshCmd (const char *cmd) +{ + return iocshRun(cmd, NULL); +} + +int epicsShareAPI +iocshLoad(const char *pathname, const char *macros) +{ + if (pathname) + epicsEnvSet("IOCSH_STARTUP_SCRIPT", pathname); + return iocshBody(pathname, NULL, macros); +} + +int epicsShareAPI +iocshRun(const char *cmd, const char *macros) +{ + if (cmd == NULL) + return 0; + return iocshBody(NULL, cmd, macros); +} + +/* + * Needed to work around the necessary limitations of macLib and + * environment variables. In every other case of macro expansion + * it is the expected outcome that defined macros override any + * environment variables. + * + * iocshLoad/Run turn this on its head as it is very likely that + * an epicsEnvSet command may be run within the context of their + * calls. Thus, it would be expected that the new value would be + * returned in any future macro expansion. + * + * To do so, the epicsEnvSet command needs to be able to access + * and update the shared MAC_HANDLE that the iocsh uses. Which is + * what this function is provided for. + */ +void epicsShareAPI +iocshEnvClear(const char *name) +{ + MAC_HANDLE *handle; + + if (iocshMacroHandleId) { + handle = (MAC_HANDLE *) epicsThreadPrivateGet(iocshMacroHandleId); + + if (handle != NULL) { + macPutValue(handle, name, NULL); + } + } +} + +/* + * Internal commands + */ +static void varHandler(const iocshVarDef *v, const char *setString) +{ + switch(v->type) { + default: + fprintf(epicsGetStderr(), "Can't handle variable %s of type %d.\n", + v->name, v->type); + return; + case iocshArgInt: break; + case iocshArgDouble: break; + } + if(setString == NULL) { + switch(v->type) { + default: break; + case iocshArgInt: + fprintf(epicsGetStdout(), "%s = %d\n", v->name, *(int *)v->pval); + break; + case iocshArgDouble: + fprintf(epicsGetStdout(), "%s = %g\n", v->name, *(double *)v->pval); + break; + } + } + else { + switch(v->type) { + default: break; + case iocshArgInt: + { + char *endp; + long ltmp = strtol(setString, &endp, 0); + if((*setString != '\0') && (*endp == '\0')) + *(int *)v->pval = ltmp; + else + fprintf(epicsGetStderr(), + "Invalid integer value. Var %s not changed.\n", v->name); + break; + } + case iocshArgDouble: + { + char *endp; + double dtmp = epicsStrtod(setString, &endp); + if((*setString != '\0') && (*endp == '\0')) + *(double *)v->pval = dtmp; + else + fprintf(epicsGetStderr(), + "Invalid double value. Var %s not changed.\n", v->name); + break; + } + } + } +} + +static void varCallFunc(const iocshArgBuf *args) +{ + struct iocshVariable *v; + if(args[0].sval == NULL) { + for (v = iocshVariableHead ; v != NULL ; v = v->next) + varHandler(v->pVarDef, args[1].sval); + } + else { + v = (iocshVariable *)registryFind(iocshVarID, args[0].sval); + if (v == NULL) { + fprintf(epicsGetStderr(), "Var %s not found.\n", args[0].sval); + } + else { + varHandler(v->pVarDef, args[1].sval); + } + } +} + +/* iocshCmd */ +static const iocshArg iocshCmdArg0 = { "command",iocshArgString}; +static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0}; +static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs}; +static void iocshCmdCallFunc(const iocshArgBuf *args) +{ + iocshCmd(args[0].sval); +} + +/* iocshLoad */ +static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString}; +static const iocshArg iocshLoadArg1 = { "macros", iocshArgString}; +static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1}; +static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs}; +static void iocshLoadCallFunc(const iocshArgBuf *args) +{ + iocshLoad(args[0].sval, args[1].sval); +} + +/* iocshRun */ +static const iocshArg iocshRunArg0 = { "command",iocshArgString}; +static const iocshArg iocshRunArg1 = { "macros", iocshArgString}; +static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1}; +static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs}; +static void iocshRunCallFunc(const iocshArgBuf *args) +{ + iocshRun(args[0].sval, args[1].sval); +} + +/* + * Dummy internal commands -- register and install in command table + * so they show up in the help display + */ + +/* comment */ +static const iocshArg commentArg0 = { "newline-terminated comment", + iocshArgArgv}; +static const iocshArg *commentArgs[1] = {&commentArg0}; +static const iocshFuncDef commentFuncDef = {"#",1,commentArgs}; +static void commentCallFunc(const iocshArgBuf *) +{ +} + +/* exit */ +static const iocshFuncDef exitFuncDef = + {"exit",0,0}; +static void exitCallFunc(const iocshArgBuf *) +{ +} + +static void localRegister (void) +{ + iocshRegister(&commentFuncDef,commentCallFunc); + iocshRegister(&exitFuncDef,exitCallFunc); + iocshRegister(&helpFuncDef,helpCallFunc); + iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc); + iocshRegister(&iocshLoadFuncDef,iocshLoadCallFunc); + iocshRegister(&iocshRunFuncDef,iocshRunCallFunc); +} + +} /* extern "C" */ + +/* + * Register local commands on application startup + */ +class IocshRegister { + public: + IocshRegister() { localRegister(); } +}; +static IocshRegister iocshRegisterObj; diff --git a/modules/libcom/src/iocsh/iocsh.h b/modules/libcom/src/iocsh/iocsh.h new file mode 100644 index 000000000..3ef3d952b --- /dev/null +++ b/modules/libcom/src/iocsh/iocsh.h @@ -0,0 +1,95 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* iocsh.h ioc: call registered function*/ +/* Author: Marty Kraimer Date: 27APR2000 */ + +#ifndef INCiocshH +#define INCiocshH + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + iocshArgInt, + iocshArgDouble, + iocshArgString, + iocshArgPdbbase, + iocshArgArgv, + iocshArgPersistentString +}iocshArgType; + +typedef union iocshArgBuf { + int ival; + double dval; + char *sval; + void *vval; + struct { + int ac; + char **av; + } aval; +}iocshArgBuf; + +typedef struct iocshVarDef { + const char *name; + iocshArgType type; + void * pval; +}iocshVarDef; + +typedef struct iocshArg { + const char *name; + iocshArgType type; +}iocshArg; + +typedef struct iocshFuncDef { + const char *name; + int nargs; + const iocshArg * const *arg; +}iocshFuncDef; + +typedef void (*iocshCallFunc)(const iocshArgBuf *argBuf); + +typedef struct iocshCmdDef { + iocshFuncDef const *pFuncDef; + iocshCallFunc func; +}iocshCmdDef; + +epicsShareFunc void epicsShareAPI iocshRegister( + const iocshFuncDef *piocshFuncDef, iocshCallFunc func); +epicsShareFunc void epicsShareAPI iocshRegisterVariable ( + const iocshVarDef *piocshVarDef); +epicsShareFunc const iocshCmdDef * epicsShareAPI iocshFindCommand( + const char* name); +epicsShareFunc const iocshVarDef * epicsShareAPI iocshFindVariable( + const char* name); + +/* iocshFree frees storage used by iocshRegister*/ +/* This should only be called when iocsh is no longer needed*/ +epicsShareFunc void epicsShareAPI iocshFree(void); + +epicsShareFunc int epicsShareAPI iocsh(const char *pathname); +epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd); +epicsShareFunc int epicsShareAPI iocshLoad(const char *pathname, const char* macros); +epicsShareFunc int epicsShareAPI iocshRun(const char *cmd, const char* macros); + +/* Makes macros that shadow environment variables work correctly with epicsEnvSet */ +epicsShareFunc void epicsShareAPI iocshEnvClear(const char *name); + +/* 'weak' link to pdbbase */ +epicsShareExtern struct dbBase **iocshPpdbbase; + +#ifdef __cplusplus +} +#endif + +#endif /*INCiocshH*/ diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c new file mode 100644 index 000000000..d3a5cfac4 --- /dev/null +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -0,0 +1,393 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "iocsh.h" +#include "epicsStdioRedirect.h" +#include "epicsString.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "logClient.h" +#include "errlog.h" +#include "taskwd.h" +#include "registry.h" +#include "epicsGeneralTime.h" +#include "libComRegister.h" + + +void date(const char *format) +{ + epicsTimeStamp now; + char nowText[80] = {'\0'}; + + if (epicsTimeGetCurrent(&now)) { + puts("Current time not available."); + return; + } + if (format == NULL || format[0] == '\0') + format = "%Y/%m/%d %H:%M:%S.%06f"; + epicsTimeToStrftime(nowText, sizeof(nowText), format, &now); + puts(nowText); +} + +/* date */ +static const iocshArg dateArg0 = { "format",iocshArgString}; +static const iocshArg * const dateArgs[] = {&dateArg0}; +static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs}; +static void dateCallFunc (const iocshArgBuf *args) +{ + date(args[0].sval); +} + +/* echo */ +static const iocshArg echoArg0 = { "string",iocshArgString}; +static const iocshArg * const echoArgs[1] = {&echoArg0}; +static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs}; +static void echoCallFunc(const iocshArgBuf *args) +{ + char *str = args[0].sval; + + if (str) + dbTranslateEscape(str, str); /* in-place is safe */ + else + str = ""; + printf("%s\n", str); +} + +/* chdir */ +static const iocshArg chdirArg0 = { "directory name",iocshArgString}; +static const iocshArg * const chdirArgs[1] = {&chdirArg0}; +static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs}; +static void chdirCallFunc(const iocshArgBuf *args) +{ + if (args[0].sval == NULL || + chdir(args[0].sval)) { + fprintf(stderr, "Invalid directory path, ignored\n"); + } +} + +/* print current working directory */ +static const iocshFuncDef pwdFuncDef = { "pwd", 0, 0 }; +static void pwdCallFunc (const iocshArgBuf *args) +{ + char buf[256]; + char *pwd = getcwd ( buf, sizeof(buf) - 1 ); + if ( pwd ) { + printf ( "%s\n", pwd ); + } +} + +/* epicsEnvSet */ +static const iocshArg epicsEnvSetArg0 = { "name",iocshArgString}; +static const iocshArg epicsEnvSetArg1 = { "value",iocshArgString}; +static const iocshArg * const epicsEnvSetArgs[2] = {&epicsEnvSetArg0,&epicsEnvSetArg1}; +static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs}; +static void epicsEnvSetCallFunc(const iocshArgBuf *args) +{ + char *name = args[0].sval; + char *value = args[1].sval; + + if (name == NULL) { + fprintf(stderr, "Missing environment variable name argument.\n"); + return; + } + if (value == NULL) { + fprintf(stderr, "Missing environment variable value argument.\n"); + return; + } + epicsEnvSet (name, value); +} + +/* epicsParamShow */ +static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL}; +static void epicsParamShowCallFunc(const iocshArgBuf *args) +{ + epicsPrtEnvParams (); +} + +/* epicsPrtEnvParams */ +static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0}; +static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args) +{ + epicsPrtEnvParams (); +} + +/* epicsEnvShow */ +static const iocshArg epicsEnvShowArg0 = { "[name]",iocshArgString}; +static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0}; +static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs}; +static void epicsEnvShowCallFunc(const iocshArgBuf *args) +{ + epicsEnvShow (args[0].sval); +} + +/* registryDump */ +static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL}; +static void registryDumpCallFunc(const iocshArgBuf *args) +{ + registryDump (); +} + +/* iocLogInit */ +static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0}; +static void iocLogInitCallFunc(const iocshArgBuf *args) +{ + iocLogInit (); +} + +/* iocLogDisable */ +static const iocshArg iocLogDisableArg0 = {"(0,1)=>(false,true)",iocshArgInt}; +static const iocshArg * const iocLogDisableArgs[1] = {&iocLogDisableArg0}; +static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs}; +static void iocLogDisableCallFunc(const iocshArgBuf *args) +{ + iocLogDisable = args[0].ival; +} + +/* iocLogShow */ +static const iocshArg iocLogShowArg0 = {"level",iocshArgInt}; +static const iocshArg * const iocLogShowArgs[1] = {&iocLogShowArg0}; +static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs}; +static void iocLogShowCallFunc(const iocshArgBuf *args) +{ + iocLogShow (args[0].ival); +} + +/* eltc */ +static const iocshArg eltcArg0 = {"(0,1)=>(false,true)",iocshArgInt}; +static const iocshArg * const eltcArgs[1] = {&eltcArg0}; +static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs}; +static void eltcCallFunc(const iocshArgBuf *args) +{ + eltc(args[0].ival); +} + +/* errlogInit */ +static const iocshArg errlogInitArg0 = { "bufsize",iocshArgInt}; +static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0}; +static const iocshFuncDef errlogInitFuncDef = + {"errlogInit",1,errlogInitArgs}; +static void errlogInitCallFunc(const iocshArgBuf *args) +{ + errlogInit(args[0].ival); +} + +/* errlogInit2 */ +static const iocshArg errlogInit2Arg0 = { "bufSize",iocshArgInt}; +static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt}; +static const iocshArg * const errlogInit2Args[] = + {&errlogInit2Arg0, &errlogInit2Arg1}; +static const iocshFuncDef errlogInit2FuncDef = + {"errlogInit2", 2, errlogInit2Args}; +static void errlogInit2CallFunc(const iocshArgBuf *args) +{ + errlogInit2(args[0].ival, args[1].ival); +} + +/* errlog */ +static const iocshArg errlogArg0 = { "message",iocshArgString}; +static const iocshArg * const errlogArgs[1] = {&errlogArg0}; +static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; +static void errlogCallFunc(const iocshArgBuf *args) +{ + errlogPrintfNoConsole("%s\n", args[0].sval); +} + +/* iocLogPrefix */ +static const iocshArg iocLogPrefixArg0 = { "prefix",iocshArgString}; +static const iocshArg * const iocLogPrefixArgs[1] = {&iocLogPrefixArg0}; +static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs}; +static void iocLogPrefixCallFunc(const iocshArgBuf *args) +{ + iocLogPrefix(args[0].sval); +} + +/* epicsThreadShowAll */ +static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt}; +static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0}; +static const iocshFuncDef epicsThreadShowAllFuncDef = + {"epicsThreadShowAll",1,epicsThreadShowAllArgs}; +static void epicsThreadShowAllCallFunc(const iocshArgBuf *args) +{ + epicsThreadShowAll(args[0].ival); +} + +/* epicsThreadShow */ +static const iocshArg threadArg0 = { "[-level] [thread ...]", iocshArgArgv}; +static const iocshArg * const threadArgs[1] = { &threadArg0 }; +static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs}; +static void threadCallFunc(const iocshArgBuf *args) +{ + int i = 1; + int first = 1; + int level = 0; + const char *cp; + epicsThreadId tid; + unsigned long ltmp; + int argc = args[0].aval.ac; + char **argv = args[0].aval.av; + char *endp; + + if ((i < argc) && (*(cp = argv[i]) == '-')) { + level = atoi (cp + 1); + i++; + } + if (i >= argc) { + epicsThreadShowAll (level); + return; + } + for ( ; i < argc ; i++) { + cp = argv[i]; + ltmp = strtoul (cp, &endp, 0); + if (*endp) { + tid = epicsThreadGetId (cp); + if (!tid) { + fprintf(stderr, "\t'%s' is not a known thread name\n", cp); + continue; + } + } + else { + tid = (epicsThreadId)ltmp; + } + if (first) { + epicsThreadShow (0, level); + first = 0; + } + epicsThreadShow (tid, level); + } +} + +/* taskwdShow */ +static const iocshArg taskwdShowArg0 = { "level",iocshArgInt}; +static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0}; +static const iocshFuncDef taskwdShowFuncDef = + {"taskwdShow",1,taskwdShowArgs}; +static void taskwdShowCallFunc(const iocshArgBuf *args) +{ + taskwdShow(args[0].ival); +} + +/* epicsMutexShowAll */ +static const iocshArg epicsMutexShowAllArg0 = { "onlyLocked",iocshArgInt}; +static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; +static const iocshArg * const epicsMutexShowAllArgs[2] = + {&epicsMutexShowAllArg0,&epicsMutexShowAllArg1}; +static const iocshFuncDef epicsMutexShowAllFuncDef = + {"epicsMutexShowAll",1,epicsMutexShowAllArgs}; +static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) +{ + epicsMutexShowAll(args[0].ival,args[1].ival); +} + +/* epicsThreadSleep */ +static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble}; +static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0}; +static const iocshFuncDef epicsThreadSleepFuncDef = + {"epicsThreadSleep",1,epicsThreadSleepArgs}; +static void epicsThreadSleepCallFunc(const iocshArgBuf *args) +{ + epicsThreadSleep(args[0].dval); +} + +/* epicsThreadResume */ +static const iocshArg epicsThreadResumeArg0 = { "[thread ...]", iocshArgArgv}; +static const iocshArg * const epicsThreadResumeArgs[1] = { &epicsThreadResumeArg0 }; +static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs}; +static void epicsThreadResumeCallFunc(const iocshArgBuf *args) +{ + int i; + const char *cp; + epicsThreadId tid; + unsigned long ltmp; + char nameBuf[64]; + int argc = args[0].aval.ac; + char **argv = args[0].aval.av; + char *endp; + + for (i = 1 ; i < argc ; i++) { + cp = argv[i]; + ltmp = strtoul(cp, &endp, 0); + if (*endp) { + tid = epicsThreadGetId(cp); + if (!tid) { + fprintf(stderr, "'%s' is not a valid thread name\n", cp); + continue; + } + } + else { + tid =(epicsThreadId)ltmp; + epicsThreadGetName(tid, nameBuf, sizeof nameBuf); + if (nameBuf[0] == '\0') { + fprintf(stderr, "'%s' is not a valid thread id\n", cp); + continue; + } + + } + if (!epicsThreadIsSuspended(tid)) { + fprintf(stderr, "Thread %s is not suspended\n", cp); + continue; + } + epicsThreadResume(tid); + } +} + +/* generalTimeReport */ +static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 }; +static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs}; +static void generalTimeReportCallFunc(const iocshArgBuf *args) +{ + generalTimeReport(args[0].ival); +} + +/* installLastResortEventProvider */ +static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL}; +static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) +{ + installLastResortEventProvider(); +} + +void epicsShareAPI libComRegister(void) +{ + iocshRegister(&dateFuncDef, dateCallFunc); + iocshRegister(&echoFuncDef, echoCallFunc); + iocshRegister(&chdirFuncDef, chdirCallFunc); + iocshRegister(&pwdFuncDef, pwdCallFunc); + + iocshRegister(&epicsEnvSetFuncDef, epicsEnvSetCallFunc); + iocshRegister(&epicsParamShowFuncDef, epicsParamShowCallFunc); + iocshRegister(&epicsPrtEnvParamsFuncDef, epicsPrtEnvParamsCallFunc); + iocshRegister(&epicsEnvShowFuncDef, epicsEnvShowCallFunc); + iocshRegister(®istryDumpFuncDef, registryDumpCallFunc); + + iocshRegister(&iocLogInitFuncDef, iocLogInitCallFunc); + iocshRegister(&iocLogDisableFuncDef, iocLogDisableCallFunc); + iocshRegister(&iocLogShowFuncDef, iocLogShowCallFunc); + iocshRegister(&eltcFuncDef, eltcCallFunc); + iocshRegister(&errlogInitFuncDef,errlogInitCallFunc); + iocshRegister(&errlogInit2FuncDef,errlogInit2CallFunc); + iocshRegister(&errlogFuncDef, errlogCallFunc); + iocshRegister(&iocLogPrefixFuncDef, iocLogPrefixCallFunc); + + iocshRegister(&epicsThreadShowAllFuncDef,epicsThreadShowAllCallFunc); + iocshRegister(&threadFuncDef, threadCallFunc); + iocshRegister(&taskwdShowFuncDef,taskwdShowCallFunc); + iocshRegister(&epicsMutexShowAllFuncDef,epicsMutexShowAllCallFunc); + iocshRegister(&epicsThreadSleepFuncDef,epicsThreadSleepCallFunc); + iocshRegister(&epicsThreadResumeFuncDef,epicsThreadResumeCallFunc); + + iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc); + iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc); +} diff --git a/modules/libcom/src/iocsh/libComRegister.h b/modules/libcom/src/iocsh/libComRegister.h new file mode 100644 index 000000000..ef93f4a8d --- /dev/null +++ b/modules/libcom/src/iocsh/libComRegister.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_libComRegister_H +#define INC_libComRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI libComRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_libComRegister_H */ diff --git a/modules/libcom/src/iocsh/registry.c b/modules/libcom/src/iocsh/registry.c new file mode 100644 index 000000000..0b2e312e3 --- /dev/null +++ b/modules/libcom/src/iocsh/registry.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*registry.c */ + +/* Author: Marty Kraimer Date: 08JUN99 */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "cantProceed.h" +#include "epicsFindSymbol.h" +#include "gpHash.h" +#include "registry.h" + +static struct gphPvt *gphPvt = 0; + +static void registryInit(int tableSize) +{ + if(tableSize==0) tableSize = DEFAULT_TABLE_SIZE; + gphInitPvt(&gphPvt,tableSize); + if(!gphPvt) cantProceed("registry why did gphInitPvt fail\n"); +} + +epicsShareFunc int epicsShareAPI registrySetTableSize(int size) +{ + if(gphPvt) { + printf("registryInit already called\n"); + return(-1); + } + registryInit(size); + return(0); +} + + +epicsShareFunc int epicsShareAPI registryAdd( + void *registryID,const char *name,void *data) +{ + GPHENTRY *pentry; + if(!gphPvt) registryInit(0); + pentry = gphAdd(gphPvt,name,registryID); + if(!pentry) return(FALSE); + pentry->userPvt = data; + return(TRUE); +} + +epicsShareFunc int epicsShareAPI registryChange( + void *registryID,const char *name,void *data) +{ + GPHENTRY *pentry; + if(!gphPvt) registryInit(0); + pentry = gphFind(gphPvt,(char *)name,registryID); + if(!pentry) return(FALSE); + pentry->userPvt = data; + return(TRUE); +} + +epicsShareFunc void * epicsShareAPI registryFind( + void *registryID,const char *name) +{ + GPHENTRY *pentry; + if(name==0) return(0); + if(registryID==0) return(epicsFindSymbol(name)); + if(!gphPvt) registryInit(0); + pentry = gphFind(gphPvt,(char *)name,registryID); + if(!pentry) return(0); + return(pentry->userPvt); +} + +epicsShareFunc void epicsShareAPI registryFree(void) +{ + if(!gphPvt) return; + gphFreeMem(gphPvt); + gphPvt = 0; +} + +epicsShareFunc int epicsShareAPI registryDump(void) +{ + if(!gphPvt) return(0); + gphDump(gphPvt); + return(0); +} diff --git a/modules/libcom/src/iocsh/registry.h b/modules/libcom/src/iocsh/registry.h new file mode 100644 index 000000000..ea1bb7ce7 --- /dev/null +++ b/modules/libcom/src/iocsh/registry.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INCregistryh +#define INCregistryh + +#include "shareLib.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_TABLE_SIZE 1024 + +epicsShareFunc int epicsShareAPI registryAdd( + void *registryID,const char *name,void *data); +epicsShareFunc void *epicsShareAPI registryFind( + void *registryID,const char *name); +epicsShareFunc int epicsShareAPI registryChange( + void *registryID,const char *name,void *data); + +epicsShareFunc int epicsShareAPI registrySetTableSize(int size); +epicsShareFunc void epicsShareAPI registryFree(void); +epicsShareFunc int epicsShareAPI registryDump(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INCregistryh */ diff --git a/modules/libcom/src/libComVersion.h b/modules/libcom/src/libComVersion.h new file mode 100644 index 000000000..60537f9c2 --- /dev/null +++ b/modules/libcom/src/libComVersion.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef LIBCOMVERSION_H +#define LIBCOMVERSION_H + +#include +#include + +#ifndef VERSION_INT +# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) +#endif + +/* include generated headers with: + * EPICS_LIBCOM_MAJOR_VERSION + * EPICS_LIBCOM_MINOR_VERSION + * EPICS_LIBCOM_MAINTENANCE_VERSION + * EPICS_LIBCOM_DEVELOPMENT_FLAG + */ +#include "libComVersionNum.h" + +#define LIBCOM_VERSION_INT VERSION_INT(EPICS_LIBCOM_MAJOR_VERSION, EPICS_LIBCOM_MINOR_VERSION, EPICS_LIBCOM_MAINTENANCE_VERSION, 0) + +#endif // LIBCOMVERSION_H diff --git a/modules/libcom/src/libComVersionNum.h@ b/modules/libcom/src/libComVersionNum.h@ new file mode 100644 index 000000000..3b340cc8f --- /dev/null +++ b/modules/libcom/src/libComVersionNum.h@ @@ -0,0 +1,7 @@ +#ifndef LIBCOMVERSION_H +# error include libComVersion.h, not this header +#endif +#define EPICS_LIBCOM_MAJOR_VERSION @EPICS_LIBCOM_MAJOR_VERSION@ +#define EPICS_LIBCOM_MINOR_VERSION @EPICS_LIBCOM_MINOR_VERSION@ +#define EPICS_LIBCOM_MAINTENANCE_VERSION @EPICS_LIBCOM_MAINTENANCE_VERSION@ +#define EPICS_LIBCOM_DEVELOPMENT_FLAG @EPICS_LIBCOM_DEVELOPMENT_FLAG@ diff --git a/modules/libcom/src/log/Makefile b/modules/libcom/src/log/Makefile new file mode 100644 index 000000000..7d7a6b620 --- /dev/null +++ b/modules/libcom/src/log/Makefile @@ -0,0 +1,28 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/log +INC += iocLog.h +INC += logClient.h +Com_SRCS += iocLog.c +Com_SRCS += logClient.c + +PROD_HOST += iocLogServer + +iocLogServer_SRCS = iocLogServer.c +iocLogServer_LIBS = Com + +iocLogServer_SYS_LIBS_solaris += socket +iocLogServer_SYS_LIBS_WIN32 += user32 ws2_32 + +SCRIPTS_HOST = S99logServer + +EXPAND += S99logServer@ +EXPAND_VARS = INSTALL_BIN=$(abspath $(INSTALL_BIN)) + diff --git a/modules/libcom/src/log/S99logServer@ b/modules/libcom/src/log/S99logServer@ new file mode 100644 index 000000000..294b2ec01 --- /dev/null +++ b/modules/libcom/src/log/S99logServer@ @@ -0,0 +1,29 @@ +#!/bin/sh +# +# System-V init script for the EPICS IOC Log Server. +# + +INSTALL_BIN=@INSTALL_BIN@ + +# To change the default values for the EPICS Environment parameters, +# uncomment and modify the relevant lines below. + +# EPICS_IOC_LOG_PORT="6500" export EPICS_IOC_LOG_PORT +# EPICS_IOC_LOG_FILE_NAME="/path/to/iocLog" export EPICS_IOC_LOG_FILE_NAME +# EPICS_IOC_LOG_FILE_LIMIT="1000000" export EPICS_IOC_LOG_FILE_LIMIT + +if [ $1 = "start" ]; then + if [ -x $INSTALL_BIN/iocLogServer ]; then + echo "Starting EPICS Log Server " + $INSTALL_BIN/iocLogServer & + fi +else + if [ $1 = "stop" ]; then + pid=`ps -e | sed -ne '/iocLogSe/s/^ *\([1-9][0-9]*\).*$/\1/p'` + if [ "${pid}" != "" ]; then + echo "Stopping EPICS Log Server " + kill ${pid} + fi + fi +fi + diff --git a/modules/libcom/src/log/iocLog.c b/modules/libcom/src/log/iocLog.c new file mode 100644 index 000000000..e62da2050 --- /dev/null +++ b/modules/libcom/src/log/iocLog.c @@ -0,0 +1,137 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ +/* + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "logClient.h" +#include "iocLog.h" + +int iocLogDisable = 0; + +static const int iocLogSuccess = 0; +static const int iocLogError = -1; + +static logClientId iocLogClient; + +/* + * getConfig() + * Get Server Configuration + */ +static int getConfig (struct in_addr *pserver_addr, unsigned short *pserver_port) +{ + long status; + long epics_port; + + status = envGetLongConfigParam (&EPICS_IOC_LOG_PORT, &epics_port); + if (status<0) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" undefined\n", + EPICS_IOC_LOG_PORT.name); + return iocLogError; + } + + if (epics_port<0 || epics_port>USHRT_MAX) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" out of range\n", + EPICS_IOC_LOG_PORT.name); + return iocLogError; + } + *pserver_port = (unsigned short) epics_port; + + status = envGetInetAddrConfigParam (&EPICS_IOC_LOG_INET, pserver_addr); + if (status<0) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" undefined\n", + EPICS_IOC_LOG_INET.name); + return iocLogError; + } + + return iocLogSuccess; +} + +/* + * iocLogFlush () + */ +void epicsShareAPI epicsShareAPI iocLogFlush (void) +{ + if (iocLogClient!=NULL) { + logClientFlush (iocLogClient); + } +} + +/* + * iocLogClientInit() + */ +static logClientId iocLogClientInit (void) +{ + int status; + logClientId id; + struct in_addr addr; + unsigned short port; + + status = getConfig (&addr, &port); + if (status) { + return NULL; + } + id = logClientCreate (addr, port); + return id; +} + +/* + * iocLogInit() + */ +int epicsShareAPI iocLogInit (void) +{ + /* + * check for global disable + */ + if (iocLogDisable) { + return iocLogSuccess; + } + /* + * dont init twice + */ + if (iocLogClient!=NULL) { + return iocLogSuccess; + } + iocLogClient = iocLogClientInit (); + if (iocLogClient) { + return iocLogSuccess; + } + else { + return iocLogError; + } +} + +/* + * iocLogShow () + */ +void epicsShareAPI iocLogShow (unsigned level) +{ + if (iocLogClient!=NULL) { + logClientShow (iocLogClient, level); + } +} + +/* + * logClientInit(); deprecated + */ +logClientId epicsShareAPI logClientInit (void) +{ + return iocLogClientInit (); +} diff --git a/modules/libcom/src/log/iocLog.h b/modules/libcom/src/log/iocLog.h new file mode 100644 index 000000000..b6f7ddf6e --- /dev/null +++ b/modules/libcom/src/log/iocLog.h @@ -0,0 +1,38 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ +/* + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#ifndef INCiocLogh +#define INCiocLogh 1 +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ioc log client interface + */ +epicsShareExtern int iocLogDisable; +epicsShareFunc int epicsShareAPI iocLogInit (void); +epicsShareFunc void epicsShareAPI iocLogShow (unsigned level); +epicsShareFunc void epicsShareAPI iocLogFlush (void); + +#ifdef __cplusplus +} +#endif + +#endif /*INCiocLogh*/ diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c new file mode 100644 index 000000000..f5694fc0f --- /dev/null +++ b/modules/libcom/src/log/iocLogServer.c @@ -0,0 +1,999 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* iocLogServer.c */ + +/* + * archive logMsg() from several IOC's to a common rotating file + * + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#include +#include +#include +#include +#include +#include + +#ifdef UNIX +#include +#include +#endif + +#include "dbDefs.h" +#include "epicsAssert.h" +#include "fdmgr.h" +#include "envDefs.h" +#include "osiSock.h" +#include "epicsStdio.h" + +static unsigned short ioc_log_port; +static long ioc_log_file_limit; +static char ioc_log_file_name[256]; +static char ioc_log_file_command[256]; + + +struct iocLogClient { + int insock; + struct ioc_log_server *pserver; + size_t nChar; + char recvbuf[1024]; + char name[32]; + char ascii_time[32]; +}; + +struct ioc_log_server { + char outfile[256]; + long filePos; + FILE *poutfile; + void *pfdctx; + SOCKET sock; + long max_file_size; +}; + +#define IOCLS_ERROR (-1) +#define IOCLS_OK 0 + +static void acceptNewClient (void *pParam); +static void readFromClient(void *pParam); +static void logTime (struct iocLogClient *pclient); +static int getConfig(void); +static int openLogFile(struct ioc_log_server *pserver); +static void handleLogFileError(void); +static void envFailureNotify(const ENV_PARAM *pparam); +static void freeLogClient(struct iocLogClient *pclient); +static void writeMessagesToLog (struct iocLogClient *pclient); + +#ifdef UNIX +static int setupSIGHUP(struct ioc_log_server *); +static void sighupHandler(int); +static void serviceSighupRequest(void *pParam); +static int getDirectory(void); +static int sighupPipe[2]; +#endif + + + +/* + * + * main() + * + */ +int main(void) +{ + struct sockaddr_in serverAddr; /* server's address */ + struct timeval timeout; + int status; + struct ioc_log_server *pserver; + + osiSockIoctl_t optval; + + status = getConfig(); + if (status<0) { + fprintf(stderr, "iocLogServer: EPICS environment underspecified\n"); + fprintf(stderr, "iocLogServer: failed to initialize\n"); + return IOCLS_ERROR; + } + + pserver = (struct ioc_log_server *) + calloc(1, sizeof *pserver); + if (!pserver) { + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + pserver->pfdctx = (void *) fdmgr_init(); + if (!pserver->pfdctx) { + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + /* + * Open the socket. Use ARPA Internet address format and stream + * sockets. Format described in . + */ + pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if (pserver->sock == INVALID_SOCKET) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf); + free(pserver); + return IOCLS_ERROR; + } + + epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock ); + + /* Zero the sock_addr structure */ + memset((void *)&serverAddr, 0, sizeof serverAddr); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(ioc_log_port); + + /* get server's Internet address */ + status = bind ( pserver->sock, + (struct sockaddr *)&serverAddr, + sizeof (serverAddr) ); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf ); + fprintf (stderr, + "iocLogServer: a server is already installed on port %u?\n", + (unsigned)ioc_log_port); + return IOCLS_ERROR; + } + + /* listen and accept new connections */ + status = listen(pserver->sock, 10); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf); + return IOCLS_ERROR; + } + + /* + * Set non blocking IO + * to prevent dead locks + */ + optval = TRUE; + status = socket_ioctl( + pserver->sock, + FIONBIO, + &optval); + if (status < 0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf); + return IOCLS_ERROR; + } + +# ifdef UNIX + status = setupSIGHUP(pserver); + if (status < 0) { + return IOCLS_ERROR; + } +# endif + + status = openLogFile(pserver); + if (status < 0) { + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + return IOCLS_ERROR; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + pserver->sock, + fdi_read, + acceptNewClient, + pserver); + if (status < 0) { + fprintf(stderr, + "iocLogServer: failed to add read callback\n"); + return IOCLS_ERROR; + } + + + while (TRUE) { + timeout.tv_sec = 60; /* 1 min */ + timeout.tv_usec = 0; + fdmgr_pend_event(pserver->pfdctx, &timeout); + fflush(pserver->poutfile); + } +} + +/* + * seekLatestLine (struct ioc_log_server *pserver) + */ +static int seekLatestLine (struct ioc_log_server *pserver) +{ + static const time_t invalidTime = (time_t) -1; + time_t theLatestTime = invalidTime; + long latestFilePos = -1; + int status; + + /* + * start at the beginning + */ + rewind (pserver->poutfile); + + while (1) { + struct tm theDate; + int convertStatus; + char month[16]; + + /* + * find the line in the file with the latest date + * + * this assumes ctime() produces dates of the form: + * DayName MonthName dayNum 24hourHourNum:minNum:secNum yearNum + */ + convertStatus = fscanf ( + pserver->poutfile, " %*s %*s %15s %d %d:%d:%d %d %*[^\n] ", + month, &theDate.tm_mday, &theDate.tm_hour, + &theDate.tm_min, &theDate.tm_sec, &theDate.tm_year); + if (convertStatus==6) { + static const char *pMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + static const unsigned nMonths = sizeof(pMonths)/sizeof(pMonths[0]); + time_t lineTime = (time_t) -1; + unsigned iMonth; + + for (iMonth=0; iMonthtm_epoch_year) { + theDate.tm_year -= tm_epoch_year; + theDate.tm_isdst = -1; /* dont know */ + lineTime = mktime (&theDate); + if ( lineTime != invalidTime ) { + if (theLatestTime == invalidTime || + difftime(lineTime, theLatestTime)>=0) { + latestFilePos = ftell (pserver->poutfile); + theLatestTime = lineTime; + } + } + else { + char date[128]; + size_t nChar; + nChar = strftime (date, sizeof(date), "%a %b %d %H:%M:%S %Y\n", &theDate); + if (nChar>0) { + fprintf (stderr, "iocLogServer: strange date in log file: %s\n", date); + } + else { + fprintf (stderr, "iocLogServer: strange date in log file\n"); + } + } + } + else { + fprintf (stderr, "iocLogServer: strange year in log file: %d\n", theDate.tm_year); + } + } + else { + fprintf (stderr, "iocLogServer: strange month in log file: %s\n", month); + } + } + else { + int c = fgetc (pserver->poutfile); + + /* + * bypass the line if it does not match the expected format + */ + while ( c!=EOF && c!='\n' ) { + c = fgetc (pserver->poutfile); + } + + if (c==EOF) { + break; + } + } + } + + /* + * move to the proper location in the file + */ + if (latestFilePos != -1) { + status = fseek (pserver->poutfile, latestFilePos, SEEK_SET); + if (status!=IOCLS_OK) { + fclose (pserver->poutfile); + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + } + else { + status = fseek (pserver->poutfile, 0L, SEEK_END); + if (status!=IOCLS_OK) { + fclose (pserver->poutfile); + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + } + + pserver->filePos = ftell (pserver->poutfile); + + if (theLatestTime==invalidTime) { + if (pserver->filePos!=0) { + fprintf (stderr, "iocLogServer: **** Warning ****\n"); + fprintf (stderr, "iocLogServer: no recognizable dates in \"%s\"\n", + ioc_log_file_name); + fprintf (stderr, "iocLogServer: logging at end of file\n"); + } + } + + return IOCLS_OK; +} + + +/* + * openLogFile() + * + */ +static int openLogFile (struct ioc_log_server *pserver) +{ + enum TF_RETURN ret; + + if (ioc_log_file_limit==0u) { + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + + if (pserver->poutfile && pserver->poutfile != stderr){ + fclose (pserver->poutfile); + pserver->poutfile = NULL; + } + + pserver->poutfile = fopen(ioc_log_file_name, "r+"); + if (pserver->poutfile) { + fclose (pserver->poutfile); + pserver->poutfile = NULL; + ret = truncateFile (ioc_log_file_name, ioc_log_file_limit); + if (ret==TF_ERROR) { + return IOCLS_ERROR; + } + pserver->poutfile = fopen(ioc_log_file_name, "r+"); + } + else { + pserver->poutfile = fopen(ioc_log_file_name, "w"); + } + + if (!pserver->poutfile) { + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + strcpy (pserver->outfile, ioc_log_file_name); + pserver->max_file_size = ioc_log_file_limit; + + return seekLatestLine (pserver); +} + + +/* + * handleLogFileError() + * + */ +static void handleLogFileError(void) +{ + fprintf(stderr, + "iocLogServer: log file access problem (errno=%s)\n", + strerror(errno)); + exit(IOCLS_ERROR); +} + + + +/* + * acceptNewClient() + * + */ +static void acceptNewClient ( void *pParam ) +{ + struct ioc_log_server *pserver = (struct ioc_log_server *) pParam; + struct iocLogClient *pclient; + osiSocklen_t addrSize; + struct sockaddr_in addr; + int status; + osiSockIoctl_t optval; + + pclient = ( struct iocLogClient * ) malloc ( sizeof ( *pclient ) ); + if ( ! pclient ) { + return; + } + + addrSize = sizeof ( addr ); + pclient->insock = epicsSocketAccept ( pserver->sock, (struct sockaddr *)&addr, &addrSize ); + if ( pclient->insock==INVALID_SOCKET || addrSize < sizeof (addr) ) { + static unsigned acceptErrCount; + static int lastErrno; + int thisErrno; + + free ( pclient ); + if ( SOCKERRNO == SOCK_EWOULDBLOCK || SOCKERRNO == SOCK_EINTR ) { + return; + } + + thisErrno = SOCKERRNO; + if ( acceptErrCount % 1000 || lastErrno != thisErrno ) { + fprintf ( stderr, "Accept Error %d\n", SOCKERRNO ); + } + acceptErrCount++; + lastErrno = thisErrno; + + return; + } + + /* + * Set non blocking IO + * to prevent dead locks + */ + optval = TRUE; + status = socket_ioctl( + pclient->insock, + FIONBIO, + &optval); + if(status<0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "%s:%d ioctl FBIO client er %s\n", + __FILE__, __LINE__, sockErrBuf); + epicsSocketDestroy ( pclient->insock ); + free(pclient); + return; + } + + pclient->pserver = pserver; + pclient->nChar = 0u; + + ipAddrToA (&addr, pclient->name, sizeof(pclient->name)); + + logTime(pclient); + +#if 0 + status = fprintf( + pclient->pserver->poutfile, + "%s %s ----- Client Connect -----\n", + pclient->name, + pclient->ascii_time); + if(status<0){ + handleLogFileError(); + } +#endif + + /* + * turn on KEEPALIVE so if the client crashes + * this task will find out and exit + */ + { + long true = 1; + + status = setsockopt( + pclient->insock, + SOL_SOCKET, + SO_KEEPALIVE, + (char *)&true, + sizeof(true) ); + if(status<0){ + fprintf(stderr, "Keepalive option set failed\n"); + } + } + + status = shutdown(pclient->insock, SHUT_WR); + if(status<0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "%s:%d shutdown err %s\n", __FILE__, __LINE__, + sockErrBuf); + epicsSocketDestroy ( pclient->insock ); + free(pclient); + + return; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + pclient->insock, + fdi_read, + readFromClient, + pclient); + if (status<0) { + epicsSocketDestroy ( pclient->insock ); + free(pclient); + fprintf(stderr, "%s:%d client fdmgr_add_callback() failed\n", + __FILE__, __LINE__); + return; + } +} + + + +/* + * readFromClient() + * + */ +#define NITEMS 1 + +static void readFromClient(void *pParam) +{ + struct iocLogClient *pclient = (struct iocLogClient *)pParam; + int recvLength; + int size; + + logTime(pclient); + + size = (int) (sizeof(pclient->recvbuf) - pclient->nChar); + recvLength = recv(pclient->insock, + &pclient->recvbuf[pclient->nChar], + size, + 0); + if (recvLength <= 0) { + if (recvLength<0) { + int errnoCpy = SOCKERRNO; + if (errnoCpy==SOCK_EWOULDBLOCK || errnoCpy==SOCK_EINTR) { + return; + } + if (errnoCpy != SOCK_ECONNRESET && + errnoCpy != SOCK_ECONNABORTED && + errnoCpy != SOCK_EPIPE && + errnoCpy != SOCK_ETIMEDOUT + ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, + "%s:%d socket=%d size=%d read error=%s\n", + __FILE__, __LINE__, pclient->insock, + size, sockErrBuf); + } + } + /* + * disconnect + */ + freeLogClient (pclient); + return; + } + + pclient->nChar += (size_t) recvLength; + + writeMessagesToLog (pclient); +} + +/* + * writeMessagesToLog() + */ +static void writeMessagesToLog (struct iocLogClient *pclient) +{ + int status; + size_t lineIndex = 0; + + while (TRUE) { + size_t nchar; + size_t nTotChar; + size_t crIndex; + int ntci; + + if ( lineIndex >= pclient->nChar ) { + pclient->nChar = 0u; + break; + } + + /* + * find the first carrage return and create + * an entry in the log for the message associated + * with it. If a carrage return does not exist and + * the buffer isnt full then move the partial message + * to the front of the buffer and wait for a carrage + * return to arrive. If the buffer is full and there + * is no carrage return then force the message out and + * insert an artificial carrage return. + */ + nchar = pclient->nChar - lineIndex; + for ( crIndex = lineIndex; crIndex < pclient->nChar; crIndex++ ) { + if ( pclient->recvbuf[crIndex] == '\n' ) { + break; + } + } + if ( crIndex < pclient->nChar ) { + nchar = crIndex - lineIndex; + } + else { + nchar = pclient->nChar - lineIndex; + if ( nchar < sizeof ( pclient->recvbuf ) ) { + if ( lineIndex != 0 ) { + pclient->nChar = nchar; + memmove ( pclient->recvbuf, + & pclient->recvbuf[lineIndex], nchar); + } + break; + } + } + + /* + * reset the file pointer if we hit the end of the file + */ + nTotChar = strlen(pclient->name) + + strlen(pclient->ascii_time) + nchar + 3u; + assert (nTotChar <= INT_MAX); + ntci = (int) nTotChar; + if ( pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) { + if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) { + unsigned nPadChar; + /* + * this gets rid of leftover junk at the end of the file + */ + nPadChar = pclient->pserver->max_file_size - pclient->pserver->filePos; + while (nPadChar--) { + status = putc ( ' ', pclient->pserver->poutfile ); + if ( status == EOF ) { + handleLogFileError(); + } + } + } + +# ifdef DEBUG + fprintf ( stderr, + "ioc log server: resetting the file pointer\n" ); +# endif + fflush ( pclient->pserver->poutfile ); + rewind ( pclient->pserver->poutfile ); + pclient->pserver->filePos = ftell ( pclient->pserver->poutfile ); + } + + /* + * NOTE: !! change format string here then must + * change nTotChar calc above !! + */ + assert (ncharpserver->poutfile, + "%s %s %.*s\n", + pclient->name, + pclient->ascii_time, + (int) nchar, + &pclient->recvbuf[lineIndex]); + if (status<0) { + handleLogFileError(); + } + else { + if (status != ntci) { + fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n"); + } + pclient->pserver->filePos += status; + } + lineIndex += nchar+1u; + } +} + + +/* + * freeLogClient () + */ +static void freeLogClient(struct iocLogClient *pclient) +{ + int status; + +# ifdef DEBUG + if(length == 0){ + fprintf(stderr, "iocLogServer: nil message disconnect\n"); + } +# endif + + /* + * flush any left overs + */ + if (pclient->nChar) { + /* + * this forces a flush + */ + if (pclient->nCharrecvbuf)) { + pclient->recvbuf[pclient->nChar] = '\n'; + } + writeMessagesToLog (pclient); + } + + status = fdmgr_clear_callback( + pclient->pserver->pfdctx, + pclient->insock, + fdi_read); + if (status!=IOCLS_OK) { + fprintf(stderr, "%s:%d fdmgr_clear_callback() failed\n", + __FILE__, __LINE__); + } + + epicsSocketDestroy ( pclient->insock ); + + free (pclient); + + return; +} + + +/* + * + * logTime() + * + */ +static void logTime(struct iocLogClient *pclient) +{ + time_t sec; + char *pcr; + char *pTimeString; + + sec = time (NULL); + pTimeString = ctime (&sec); + strncpy (pclient->ascii_time, + pTimeString, + sizeof (pclient->ascii_time) ); + pclient->ascii_time[sizeof(pclient->ascii_time)-1] = '\0'; + pcr = strchr(pclient->ascii_time, '\n'); + if (pcr) { + *pcr = '\0'; + } +} + + +/* + * + * getConfig() + * Get Server Configuration + * + * + */ +static int getConfig(void) +{ + int status; + char *pstring; + long param; + + status = envGetLongConfigParam( + &EPICS_IOC_LOG_PORT, + ¶m); + if(status>=0){ + ioc_log_port = (unsigned short) param; + } + else { + ioc_log_port = 7004U; + } + + status = envGetLongConfigParam( + &EPICS_IOC_LOG_FILE_LIMIT, + &ioc_log_file_limit); + if(status>=0){ + if (ioc_log_file_limit<=0) { + envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT); + return IOCLS_ERROR; + } + } + else { + ioc_log_file_limit = 10000; + } + + pstring = envGetConfigParam( + &EPICS_IOC_LOG_FILE_NAME, + sizeof ioc_log_file_name, + ioc_log_file_name); + if(pstring == NULL){ + envFailureNotify(&EPICS_IOC_LOG_FILE_NAME); + return IOCLS_ERROR; + } + + /* + * its ok to not specify the IOC_LOG_FILE_COMMAND + */ + pstring = envGetConfigParam( + &EPICS_IOC_LOG_FILE_COMMAND, + sizeof ioc_log_file_command, + ioc_log_file_command); + return IOCLS_OK; +} + + + +/* + * + * failureNotify() + * + * + */ +static void envFailureNotify(const ENV_PARAM *pparam) +{ + fprintf(stderr, + "iocLogServer: EPICS environment variable `%s' undefined\n", + pparam->name); +} + + + +#ifdef UNIX +static int setupSIGHUP(struct ioc_log_server *pserver) +{ + int status; + struct sigaction sigact; + + status = getDirectory(); + if (status<0){ + fprintf(stderr, "iocLogServer: failed to determine log file " + "directory\n"); + return IOCLS_ERROR; + } + + /* + * Set up SIGHUP handler. SIGHUP will cause the log file to be + * closed and re-opened, possibly with a different name. + */ + sigact.sa_handler = sighupHandler; + sigemptyset (&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGHUP, &sigact, NULL)){ + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + status = pipe(sighupPipe); + if(status<0){ + fprintf(stderr, + "iocLogServer: failed to create pipe because `%s'\n", + strerror(errno)); + return IOCLS_ERROR; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + sighupPipe[0], + fdi_read, + serviceSighupRequest, + pserver); + if(status<0){ + fprintf(stderr, + "iocLogServer: failed to add SIGHUP callback\n"); + return IOCLS_ERROR; + } + return IOCLS_OK; +} + +/* + * + * sighupHandler() + * + * + */ +static void sighupHandler(int signo) +{ + (void) write(sighupPipe[1], "SIGHUP\n", 7); +} + + + +/* + * serviceSighupRequest() + * + */ +static void serviceSighupRequest(void *pParam) +{ + struct ioc_log_server *pserver = (struct ioc_log_server *)pParam; + char buff[256]; + int status; + + /* + * Read and discard message from pipe. + */ + (void) read(sighupPipe[0], buff, sizeof buff); + + /* + * Determine new log file name. + */ + status = getDirectory(); + if (status<0){ + fprintf(stderr, "iocLogServer: failed to determine new log " + "file name\n"); + return; + } + + /* + * Try (re)opening the file. + */ + status = openLogFile(pserver); + if(status<0){ + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + /* Revert to old filename */ + strcpy(ioc_log_file_name, pserver->outfile); + status = openLogFile(pserver); + if(status<0){ + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + return; + } + else { + fprintf(stderr, + "iocLogServer: re-opened old log file %s\n", + ioc_log_file_name); + } + } + else { + fprintf(stderr, + "iocLogServer: opened new log file %s\n", + ioc_log_file_name); + } +} + + + +/* + * + * getDirectory() + * + * + */ +static int getDirectory(void) +{ + FILE *pipe; + char dir[256]; + int i; + + if (ioc_log_file_command[0] != '\0') { + + /* + * Use popen() to execute command and grab output. + */ + pipe = popen(ioc_log_file_command, "r"); + if (pipe == NULL) { + fprintf(stderr, + "Problem executing `%s' because `%s'\n", + ioc_log_file_command, + strerror(errno)); + return IOCLS_ERROR; + } + if (fgets(dir, sizeof(dir), pipe) == NULL) { + fprintf(stderr, + "Problem reading o/p from `%s' because `%s'\n", + ioc_log_file_command, + strerror(errno)); + (void) pclose(pipe); + return IOCLS_ERROR; + } + (void) pclose(pipe); + + /* + * Terminate output at first newline and discard trailing + * slash character if present.. + */ + for (i=0; dir[i] != '\n' && dir[i] != '\0'; i++) + ; + dir[i] = '\0'; + + i = strlen(dir); + if (i > 1 && dir[i-1] == '/') dir[i-1] = '\0'; + + /* + * Use output as directory part of file name. + */ + if (dir[0] != '\0') { + char *name = ioc_log_file_name; + char *slash = strrchr(ioc_log_file_name, '/'); + char temp[256]; + + if (slash != NULL) name = slash + 1; + strcpy(temp,name); + sprintf(ioc_log_file_name,"%s/%s",dir,temp); + } + } + return IOCLS_OK; +} +#endif diff --git a/modules/libcom/src/log/logClient.c b/modules/libcom/src/log/logClient.c new file mode 100644 index 000000000..99ee671d9 --- /dev/null +++ b/modules/libcom/src/log/logClient.c @@ -0,0 +1,618 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ +/* + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +/* + * ANSI C + */ +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsEvent.h" +#include "iocLog.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "osiSock.h" +#include "epicsAssert.h" +#include "epicsExit.h" +#include "epicsSignal.h" + +#include "logClient.h" + +typedef struct { + char msgBuf[0x4000]; + struct sockaddr_in addr; + char name[64]; + epicsMutexId mutex; + SOCKET sock; + epicsThreadId restartThreadId; + epicsEventId stateChangeNotify; + unsigned connectCount; + unsigned nextMsgIndex; + unsigned connected; + unsigned shutdown; + unsigned shutdownConfirm; + int connFailStatus; +} logClient; + +static const double LOG_RESTART_DELAY = 5.0; /* sec */ +static const double LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT = 5.0; /* sec */ +static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */ + +/* + * If set using iocLogPrefix() this string is prepended to all log messages: + */ +static char* logClientPrefix = NULL; + +/* + * logClientClose () + */ +static void logClientClose ( logClient *pClient ) +{ +# ifdef DEBUG + fprintf (stderr, "log client: lingering for connection close..."); + fflush (stderr); +# endif + + /* + * mutex on + */ + epicsMutexMustLock (pClient->mutex); + + /* + * close any preexisting connection to the log server + */ + if ( pClient->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( pClient->sock ); + pClient->sock = INVALID_SOCKET; + } + + pClient->nextMsgIndex = 0u; + memset ( pClient->msgBuf, '\0', sizeof ( pClient->msgBuf ) ); + pClient->connected = 0u; + + /* + * mutex off + */ + epicsMutexUnlock (pClient->mutex); + +# ifdef DEBUG + fprintf (stderr, "done\n"); +# endif +} + +/* + * logClientDestroy + */ +static void logClientDestroy (logClientId id) +{ + enum epicsSocketSystemCallInterruptMechanismQueryInfo interruptInfo; + logClient *pClient = (logClient *) id; + epicsTimeStamp begin, current; + double diff; + + /* command log client thread to shutdown - taking mutex here */ + /* forces cache flush on SMP machines */ + epicsMutexMustLock ( pClient->mutex ); + pClient->shutdown = 1u; + epicsMutexUnlock ( pClient->mutex ); + + /* unblock log client thread blocking in send() or connect() */ + interruptInfo = + epicsSocketSystemCallInterruptMechanismQuery (); + switch ( interruptInfo ) { + case esscimqi_socketCloseRequired: + logClientClose ( pClient ); + break; + case esscimqi_socketBothShutdownRequired: + shutdown ( pClient->sock, SHUT_WR ); + break; + case esscimqi_socketSigAlarmRequired: + epicsSignalRaiseSigAlarm ( pClient->restartThreadId ); + break; + default: + break; + }; + + /* wait for confirmation that the thread exited - taking */ + /* mutex here forces cache update on SMP machines */ + epicsTimeGetCurrent ( & begin ); + epicsMutexMustLock ( pClient->mutex ); + do { + epicsMutexUnlock ( pClient->mutex ); + epicsEventWaitWithTimeout ( + pClient->stateChangeNotify, + LOG_SERVER_SHUTDOWN_TIMEOUT / 10.0 ); + epicsTimeGetCurrent ( & current ); + diff = epicsTimeDiffInSeconds ( & current, & begin ); + epicsMutexMustLock ( pClient->mutex ); + } + while ( ! pClient->shutdownConfirm && diff < LOG_SERVER_SHUTDOWN_TIMEOUT ); + epicsMutexUnlock ( pClient->mutex ); + + if ( ! pClient->shutdownConfirm ) { + fprintf ( stderr, "log client shutdown: timed out stopping" + " reconnect thread for \"%s\" after %.1f seconds - cleanup aborted\n", + pClient->name, LOG_SERVER_SHUTDOWN_TIMEOUT ); + return; + } + + errlogRemoveListeners ( logClientSendMessage, (void *) pClient ); + + logClientClose ( pClient ); + + epicsMutexDestroy ( pClient->mutex ); + + epicsEventDestroy ( pClient->stateChangeNotify ); + + free ( pClient ); +} + +/* + * This method requires the pClient->mutex be owned already. + */ +static void sendMessageChunk(logClient * pClient, const char * message) { + unsigned strSize; + + strSize = strlen ( message ); + while ( strSize ) { + unsigned msgBufBytesLeft = + sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; + + if ( strSize > msgBufBytesLeft ) { + int status; + + if ( ! pClient->connected ) { + break; + } + + if ( msgBufBytesLeft > 0u ) { + memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], + message, msgBufBytesLeft ); + pClient->nextMsgIndex += msgBufBytesLeft; + strSize -= msgBufBytesLeft; + message += msgBufBytesLeft; + } + + status = send ( pClient->sock, pClient->msgBuf, + pClient->nextMsgIndex, 0 ); + if ( status > 0 ) { + unsigned nSent = (unsigned) status; + if ( nSent < pClient->nextMsgIndex ) { + unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + newNextMsgIndex ); + pClient->nextMsgIndex = newNextMsgIndex; + } + else { + pClient->nextMsgIndex = 0u; + } + } + else { + if ( ! pClient->shutdown ) { + char sockErrBuf[64]; + if ( status ) { + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + } + else { + strcpy ( sockErrBuf, "server initiated disconnect" ); + } + fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", + pClient->name, sockErrBuf ); + } + logClientClose ( pClient ); + break; + } + } + else { + memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], + message, strSize ); + pClient->nextMsgIndex += strSize; + break; + } + } +} + + +/* + * logClientSend () + */ +void epicsShareAPI logClientSend ( logClientId id, const char * message ) +{ + logClient * pClient = ( logClient * ) id; + + if ( ! pClient || ! message ) { + return; + } + + epicsMutexMustLock ( pClient->mutex ); + + if (logClientPrefix) { + sendMessageChunk(pClient, logClientPrefix); + } + sendMessageChunk(pClient, message); + + epicsMutexUnlock (pClient->mutex); +} + + +void epicsShareAPI logClientFlush ( logClientId id ) +{ + logClient * pClient = ( logClient * ) id; + + if ( ! pClient ) { + return; + } + + epicsMutexMustLock ( pClient->mutex ); + + while ( pClient->nextMsgIndex && pClient->connected ) { + int status = send ( pClient->sock, pClient->msgBuf, + pClient->nextMsgIndex, 0 ); + if ( status > 0 ) { + unsigned nSent = (unsigned) status; + if ( nSent < pClient->nextMsgIndex ) { + unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + newNextMsgIndex ); + pClient->nextMsgIndex = newNextMsgIndex; + } + else { + pClient->nextMsgIndex = 0u; + } + } + else { + if ( ! pClient->shutdown ) { + char sockErrBuf[64]; + if ( status ) { + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + } + else { + strcpy ( sockErrBuf, "server initiated disconnect" ); + } + fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", + pClient->name, sockErrBuf ); + } + logClientClose ( pClient ); + break; + } + } + epicsMutexUnlock ( pClient->mutex ); +} + +/* + * logClientMakeSock () + */ +static void logClientMakeSock (logClient *pClient) +{ + +# ifdef DEBUG + fprintf (stderr, "log client: creating socket..."); +# endif + + epicsMutexMustLock (pClient->mutex); + + /* + * allocate a socket + */ + pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 ); + if ( pClient->sock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "log client: no socket error %s\n", + sockErrBuf ); + } + + epicsMutexUnlock (pClient->mutex); + +# ifdef DEBUG + fprintf (stderr, "done\n"); +# endif + +} + +/* + * logClientConnect() + */ +static void logClientConnect (logClient *pClient) +{ + osiSockIoctl_t optval; + int errnoCpy; + int status; + + if ( pClient->sock == INVALID_SOCKET ) { + logClientMakeSock ( pClient ); + if ( pClient->sock == INVALID_SOCKET ) { + return; + } + } + + while ( 1 ) { + status = connect (pClient->sock, + (struct sockaddr *)&pClient->addr, sizeof(pClient->addr)); + if ( status >= 0 ) { + break; + } + else { + errnoCpy = SOCKERRNO; + if ( errnoCpy == SOCK_EINTR ) { + continue; + } + else if ( + errnoCpy==SOCK_EINPROGRESS || + errnoCpy==SOCK_EWOULDBLOCK ) { + return; + } + else if ( errnoCpy==SOCK_EALREADY ) { + break; + } + else { + if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, + "log client: failed to connect to \"%s\" because %d=\"%s\"\n", + pClient->name, errnoCpy, sockErrBuf); + pClient->connFailStatus = errnoCpy; + } + logClientClose ( pClient ); + return; + } + } + } + + epicsMutexMustLock (pClient->mutex); + + pClient->connected = 1u; + pClient->connFailStatus = 0; + + /* + * discover that the connection has expired + * (after a long delay) + */ + optval = TRUE; + status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "log client: unable to enable keepalive option because \"%s\"\n", sockErrBuf); + } + + /* + * we don't need full-duplex (clients only write), so we shutdown + * the read end of our socket + */ + status = shutdown (pClient->sock, SHUT_RD); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "%s:%d shutdown(%d,SHUT_RD) error was \"%s\"\n", + __FILE__, __LINE__, pClient->sock, sockErrBuf); + /* not fatal (although it shouldn't happen) */ + } + + /* + * set how long we will wait for the TCP state machine + * to clean up when we issue a close(). This + * guarantees that messages are serialized when we + * switch connections. + */ + { + struct linger lingerval; + + lingerval.l_onoff = TRUE; + lingerval.l_linger = 60*5; + status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER, (char *) &lingerval, sizeof(lingerval)); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "log client: unable to set linger options because \"%s\"\n", sockErrBuf); + } + } + + pClient->connectCount++; + + epicsMutexUnlock ( pClient->mutex ); + + epicsEventSignal ( pClient->stateChangeNotify ); + + fprintf ( stderr, "log client: connected to log server at \"%s\"\n", pClient->name ); +} + +/* + * logClientRestart () + */ +static void logClientRestart ( logClientId id ) +{ + logClient *pClient = (logClient *)id; + + /* SMP safe state inspection */ + epicsMutexMustLock ( pClient->mutex ); + while ( ! pClient->shutdown ) { + unsigned isConn; + + isConn = pClient->connected; + + epicsMutexUnlock ( pClient->mutex ); + + if ( isConn ) { + logClientFlush ( pClient ); + } + else { + logClientConnect ( pClient ); + } + + epicsThreadSleep ( LOG_RESTART_DELAY ); + + epicsMutexMustLock ( pClient->mutex ); + } + epicsMutexUnlock ( pClient->mutex ); + + pClient->shutdownConfirm = 1u; + epicsEventSignal ( pClient->stateChangeNotify ); +} + +/* + * logClientCreate() + */ +logClientId epicsShareAPI logClientCreate ( + struct in_addr server_addr, unsigned short server_port) +{ + epicsTimeStamp begin, current; + logClient *pClient; + double diff; + + pClient = calloc (1, sizeof (*pClient)); + if (pClient==NULL) { + return NULL; + } + + pClient->addr.sin_family = AF_INET; + pClient->addr.sin_addr = server_addr; + pClient->addr.sin_port = htons(server_port); + ipAddrToDottedIP (&pClient->addr, pClient->name, sizeof(pClient->name)); + + pClient->mutex = epicsMutexCreate (); + if ( ! pClient->mutex ) { + free ( pClient ); + return NULL; + } + + pClient->sock = INVALID_SOCKET; + pClient->connected = 0u; + pClient->connFailStatus = 0; + pClient->shutdown = 0; + pClient->shutdownConfirm = 0; + + epicsAtExit (logClientDestroy, (void*) pClient); + + pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty); + if ( ! pClient->stateChangeNotify ) { + epicsMutexDestroy ( pClient->mutex ); + free ( pClient ); + return NULL; + } + + pClient->restartThreadId = epicsThreadCreate ( + "logRestart", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + logClientRestart, pClient ); + if ( pClient->restartThreadId == NULL ) { + epicsMutexDestroy ( pClient->mutex ); + epicsEventDestroy ( pClient->stateChangeNotify ); + free (pClient); + fprintf(stderr, "log client: unable to start log client connection watch dog thread\n"); + return NULL; + } + + /* + * attempt to synchronize with circuit connect + */ + epicsTimeGetCurrent ( & begin ); + epicsMutexMustLock ( pClient->mutex ); + do { + epicsMutexUnlock ( pClient->mutex ); + epicsEventWaitWithTimeout ( + pClient->stateChangeNotify, + LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 ); + epicsTimeGetCurrent ( & current ); + diff = epicsTimeDiffInSeconds ( & current, & begin ); + epicsMutexMustLock ( pClient->mutex ); + } + while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); + epicsMutexUnlock ( pClient->mutex ); + + if ( ! pClient->connected ) { + fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n", + pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); + } + + errlogAddListener ( logClientSendMessage, (void *) pClient ); + + return (void *) pClient; +} + +/* + * logClientShow () + */ +void epicsShareAPI logClientShow (logClientId id, unsigned level) +{ + logClient *pClient = (logClient *) id; + + if ( pClient->connected ) { + printf ("log client: connected to log server at \"%s\"\n", pClient->name); + } + else { + printf ("log client: disconnected from log server at \"%s\"\n", pClient->name); + } + + if (level>1) { + printf ("log client: sock=%s, connect cycles = %u\n", + pClient->sock==INVALID_SOCKET?"INVALID":"OK", + pClient->connectCount); + } + + if (logClientPrefix) { + printf ("log client: prefix is \"%s\"\n", logClientPrefix); + } +} + +/* + * logClientSendMessage (); deprecated + */ +void logClientSendMessage ( logClientId id, const char * message ) +{ + if ( !iocLogDisable ) { + logClientSend (id, message); + } +} + +/* + * iocLogPrefix() + */ +void epicsShareAPI iocLogPrefix(const char * prefix) +{ + + /* If we have already established a log prefix, don't let the user change + * it. The iocLogPrefix command is expected to be run from the IOC startup + * script during initialization; the prefix can't be changed once it has + * been set. + */ + + if (logClientPrefix) { + printf ("iocLogPrefix: The prefix was already set to \"%s\" " + "and can't be changed.\n", logClientPrefix); + return; + } + + if (prefix) { + unsigned prefixLen = strlen(prefix); + if (prefixLen > 0) { + char * localCopy = malloc(prefixLen+1); + strcpy(localCopy, prefix); + logClientPrefix = localCopy; + } + } +} diff --git a/modules/libcom/src/log/logClient.h b/modules/libcom/src/log/logClient.h new file mode 100644 index 000000000..1797bbb20 --- /dev/null +++ b/modules/libcom/src/log/logClient.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ +/* + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#ifndef INClogClienth +#define INClogClienth 1 +#include "shareLib.h" +#include "osiSock.h" /* for 'struct in_addr' */ + +/* include default log client interface for backward compatibility */ +#include "iocLog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *logClientId; +epicsShareFunc logClientId epicsShareAPI logClientCreate ( + struct in_addr server_addr, unsigned short server_port); +epicsShareFunc void epicsShareAPI logClientSend (logClientId id, const char *message); +epicsShareFunc void epicsShareAPI logClientShow (logClientId id, unsigned level); +epicsShareFunc void epicsShareAPI logClientFlush (logClientId id); +epicsShareFunc void epicsShareAPI iocLogPrefix(const char* prefix); + +/* deprecated interface; retained for backward compatibility */ +/* note: implementations are in iocLog.c, not logClient.c */ +epicsShareFunc logClientId epicsShareAPI logClientInit (void); +epicsShareFunc void logClientSendMessage (logClientId id, const char *message); + +#ifdef __cplusplus +} +#endif + +#endif /*INClogClienth*/ diff --git a/modules/libcom/src/macLib/EPICS/macLib.pm b/modules/libcom/src/macLib/EPICS/macLib.pm new file mode 100644 index 000000000..412c6d709 --- /dev/null +++ b/modules/libcom/src/macLib/EPICS/macLib.pm @@ -0,0 +1,250 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +package EPICS::macLib::entry; + +sub new ($$) { + my $class = shift; + my $this = { + name => shift, + type => shift, + raw => '', + val => '', + visited => 0, + error => 0, + }; + bless $this, $class; + return $this; +} + +sub report ($) { + my ($this) = @_; + return unless defined $this->{raw}; + printf "%1s %-16s %-16s %s\n", + ($this->{error} ? '*' : ' '), $this->{name}, $this->{raw}, $this->{val}; +} + + +package EPICS::macLib; + +use Carp; + +sub new ($@) { + my $proto = shift; + my $class = ref($proto) || $proto; + my $this = { + dirty => 0, + noWarn => 0, + macros => [{}], # [0] is current scope, [1] is parent etc. + }; + bless $this, $class; + $this->installList(@_); + return $this; +} + +sub installList ($@) { + # Argument is a list of strings which are arguments to installMacros + my $this = shift; + while (@_) { + $this->installMacros(shift); + } +} + +sub installMacros ($$) { + # Argument is a string: a=1,b="2",c,d='hello' + my $this = shift; + $_ = shift; + until (defined pos($_) and pos($_) == length($_)) { + m/\G \s* /xgc; # Skip whitespace + if (m/\G ( [A-Za-z0-9_-]+ ) \s* /xgc) { + my ($name, $val) = ($1); + if (m/\G = \s* /xgc) { + # The value follows, handle quotes and escapes + until (pos($_) == length($_)) { + if (m/\G , /xgc) { last; } + elsif (m/\G ' ( ( [^'] | \\ ' )* ) ' /xgc) { $val .= $1; } + elsif (m/\G " ( ( [^"] | \\ " )* ) " /xgc) { $val .= $1; } + elsif (m/\G \\ ( . ) /xgc) { $val .= $1; } + elsif (m/\G ( . ) /xgc) { $val .= $1; } + else { die "How did I get here?"; } + } + $this->putValue($name, $val); + } elsif (m/\G , /xgc or (pos($_) == length($_))) { + $this->putValue($name, undef); + } else { + warn "How did I get here?"; + } + } elsif (m/\G ( .* )/xgc) { + croak "Can't find a macro definition in '$1'"; + } else { + last; + } + } +} + +sub putValue ($$$) { + my ($this, $name, $raw) = @_; + if (exists $this->{macros}[0]{$name}) { + if (!defined $raw) { + delete $this->{macros}[0]{$name}; + } else { + $this->{macros}[0]{$name}{raw} = $raw; + } + } else { + my $entry = EPICS::macLib::entry->new($name, 'macro'); + $entry->{raw} = $raw; + $this->{macros}[0]{$name} = $entry; + } + $this->{dirty} = 1; +} + +sub pushScope ($) { + my ($this) = @_; + unshift @{$this->{macros}}, {}; +} + +sub popScope ($) { + my ($this) = @_; + shift @{$this->{macros}}; +} + +sub suppressWarning($$) { + my ($this, $suppress) = @_; + $this->{noWarn} = $suppress; +} + +sub expandString($$) { + my ($this, $src) = @_; + $this->_expand; + (my $name = $src) =~ s/^ (.{20}) .* $/$1.../xs; + my $entry = EPICS::macLib::entry->new($name, 'string'); + my $result = $this->_translate($entry, 0, $src); + return $result unless $entry->{error}; + return $this->{noWarn} ? $result : undef; +} + +sub reportMacros ($) { + my ($this) = @_; + $this->_expand; + print "Macro report\n============\n"; + foreach my $scope (@{$this->{macros}}) { + foreach my $name (keys %{$scope}) { + my $entry = $scope->{$name}; + $entry->report; + } + } continue { + print " -- scope ends --\n"; + } +} + + +# Private routines, not intended for public use + +sub _expand ($) { + my ($this) = @_; + return unless $this->{dirty}; + foreach my $scope (@{$this->{macros}}) { + foreach my $name (keys %{$scope}) { + my $entry = $scope->{$name}; + $entry->{val} = $this->_translate($entry, 1, $entry->{raw}); + } + } + $this->{dirty} = 0; +} + +sub _lookup ($$$$$) { + my ($this, $name) = @_; + foreach my $scope (@{$this->{macros}}) { + if (exists $scope->{$name}) { + return undef # Macro marked as deleted + unless defined $scope->{$name}{raw}; + return $scope->{$name}; + } + } + return undef; +} + +sub _translate ($$$$) { + my ($this, $entry, $level, $str) = @_; + return $this->_trans($entry, $level, '', \$str); +} + +sub _trans ($$$$$) { + my ($this, $entry, $level, $term, $R) = @_; + return $$R + if (!defined $$R or + $$R =~ m/\A [^\$]* \Z/x); # Short-circuit if no macros + my $quote = 0; + my $val; + until (defined pos($$R) and pos($$R) == length($$R)) { + if ($term and ($$R =~ m/\G (?= [$term] ) /xgc)) { + last; + } + if ($$R =~ m/\G \$ ( [({] ) /xgc) { + my $macEnd = $1; + $macEnd =~ tr/({/)}/; + my $name2 = $this->_trans($entry, $level+1, "=$macEnd", $R); + my $entry2 = $this->_lookup($name2); + if (!defined $entry2) { # Macro not found + if ($$R =~ m/\G = /xgc) { # Use default value given + $val .= $this->_trans($entry, $level+1, $macEnd, $R); + } else { + unless ($this->{noWarn}) { + $entry->{error} = 1; + printf STDERR "macLib: macro '%s' is undefined (expanding %s '%s')\n", + $name2, $entry->{type}, $entry->{name}; + } + $val .= "\$($name2)"; + } + $$R =~ m/\G [$macEnd] /xgc; # Discard close bracket + } else { # Macro found + if ($entry2->{visited}) { + $entry->{error} = 1; + printf STDERR "macLib: %s '%s' is recursive (expanding %s '%s')\n", + $entry->{type}, $entry->{name}, $entry2->{type}, $entry2->{name}; + $val .= "\$($name)"; + } else { + if ($$R =~ m/\G = /xgc) { # Discard default value + local $this->{noWarn} = 1; # Temporarily kill warnings + $this->_trans($entry, $level+1, $macEnd, $R); + } + $$R =~ m/\G [$macEnd] /xgc; # Discard close bracket + if ($this->{dirty}) { # Translate raw value + $entry2->{visited} = 1; + $val .= $this->_trans($entry, $level+1, '', \$entry2->{raw}); + $entry2->{visited} = 0; + } else { + $val .= $entry2->{val}; # Here's one I made earlier... + } + } + } + } elsif ($level > 0) { # Discard quotes and escapes + if ($quote and $$R =~ m/\G $quote /xgc) { + $quote = 0; + } elsif ($$R =~ m/\G ( ['"] ) /xgc) { + $quote = $1; + } elsif ($$R =~ m/\G \\? ( . ) /xgc) { + $val .= $1; + } else { + warn "How did I get here? level=$level"; + } + } else { # Level 0 + if ($$R =~ m/\G \\ ( . ) /xgc) { + $val .= "\\$1"; + } elsif ($$R =~ m/\G ( [^\\\$'")}]* ) /xgc) { + $val .= $1; + } elsif ($$R =~ m/\G ( . ) /xgc) { + $val .= $1; + } else { + warn "How did I get here? level=$level"; + } + } + } + return $val; +} + +1; diff --git a/modules/libcom/src/macLib/Makefile b/modules/libcom/src/macLib/Makefile new file mode 100644 index 000000000..08c47bd40 --- /dev/null +++ b/modules/libcom/src/macLib/Makefile @@ -0,0 +1,16 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/macLib +INC += macLib.h +Com_SRCS += macCore.c +Com_SRCS += macEnv.c +Com_SRCS += macUtil.c + +PERL_MODULES += EPICS/macLib.pm diff --git a/modules/libcom/src/macLib/macCore.c b/modules/libcom/src/macLib/macCore.c new file mode 100644 index 000000000..55d31719e --- /dev/null +++ b/modules/libcom/src/macLib/macCore.c @@ -0,0 +1,950 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Implementation of core macro substitution library (macLib) + * + * The implementation is fairly unsophisticated and linked lists are + * used to store macro values. Typically there will will be only a + * small number of macros and performance won't be a problem. Special + * measures are taken to avoid unnecessary expansion of macros whose + * definitions reference other macros. Whenever a macro is created, + * modified or deleted, a "dirty" flag is set; this causes a full + * expansion of all macros the next time a macro value is read + * + * Original Author: William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#include "dbmf.h" +#include "macLib.h" + + +/*** Local structure definitions ***/ + +/* + * Entry in linked list of macro definitions + */ +typedef struct mac_entry { + ELLNODE node; /* prev and next pointers */ + char *name; /* entry name */ + char *type; /* entry type */ + char *rawval; /* raw (unexpanded) value */ + char *value; /* expanded macro value */ + size_t length; /* length of value */ + int error; /* error expanding value? */ + int visited; /* ever been visited? */ + int special; /* special (internal) entry? */ + int level; /* scoping level */ +} MAC_ENTRY; + + +/*** Local function prototypes ***/ + +/* + * These static functions peform low-level operations on macro entries + */ +static MAC_ENTRY *first ( MAC_HANDLE *handle ); +static MAC_ENTRY *last ( MAC_HANDLE *handle ); +static MAC_ENTRY *next ( MAC_ENTRY *entry ); +static MAC_ENTRY *previous( MAC_ENTRY *entry ); + +static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ); +static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ); +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ); +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ); +static long expand( MAC_HANDLE *handle ); +static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char *term, const char **rawval, char **value, + char *valend ); +static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char **rawval, char **value, char *valend ); + +static void cpy2val( const char *src, char **value, char *valend ); +static char *Strdup( const char *string ); + + +/*** Constants ***/ + +/* + * Magic number for validating context. + */ +#define MAC_MAGIC 0xbadcafe /* ...sells sub-standard coffee? */ + +/* + * Flag bits + */ +#define FLAG_SUPPRESS_WARNINGS 0x1 +#define FLAG_USE_ENVIRONMENT 0x80 + + +/*** Library routines ***/ + +/* + * Create a new macro substitution context and return an opaque handle + * associated with the new context. Also optionally load an initial set + * of macro definitions + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macCreateHandle( + MAC_HANDLE **pHandle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + const char * pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + MAC_HANDLE *handle; /* pointer to macro substitution context */ + + /* ensure NULL handle pointer is returned on error */ + *pHandle = NULL; + + /* allocate macro substitution context */ + handle = ( MAC_HANDLE * ) dbmfMalloc( sizeof( MAC_HANDLE ) ); + if ( handle == NULL ) { + errlogPrintf( "macCreateHandle: failed to allocate context\n" ); + return -1; + } + + /* initialize context */ + handle->magic = MAC_MAGIC; + handle->dirty = FALSE; + handle->level = 0; + handle->debug = 0; + handle->flags = 0; + ellInit( &handle->list ); + + /* use environment variables if so specified */ + if (pairs && pairs[0] && !strcmp(pairs[0],"") && pairs[1] && !strcmp(pairs[1],"environ") && !pairs[3]) { + handle->flags |= FLAG_USE_ENVIRONMENT; + } + else { + /* if supplied, load macro definitions */ + for ( ; pairs && pairs[0]; pairs += 2 ) { + if ( macPutValue( handle, pairs[0], pairs[1] ) < 0 ) { + dbmfFree( handle ); + return -1; + } + } + } + + /* set returned handle pointer */ + *pHandle = handle; + + return 0; +} + +/* + * Turn on/off suppression of printed warnings from macLib calls + * for the given handle + */ +void +epicsShareAPI macSuppressWarning( + MAC_HANDLE *handle, /* opaque handle */ + int suppress /* 0 means issue, 1 means suppress */ +) +{ + if ( handle && handle->magic == MAC_MAGIC ) { + handle->flags = (handle->flags & ~FLAG_SUPPRESS_WARNINGS) | + (suppress ? FLAG_SUPPRESS_WARNINGS : 0); + } +} + +/* + * Expand a string that may contain macro references and return the + * expanded string + * + * This is a very basic and powerful routine. It's basically a wrapper + * around the the translation "engine" trans() + */ +long /* strlen(dest), <0 if any macros are */ + /* undefined */ +epicsShareAPI macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + const char *src, /* source string */ + + char *dest, /* destination string */ + + long capacity ) /* capacity of destination buffer (dest) */ +{ + MAC_ENTRY entry; + const char *s; + char *d; + long length; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macExpandString: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString( %s, capacity = %ld )\n", src, capacity ); + + /* Check size */ + if (capacity <= 1) + return -1; + + /* expand raw values if necessary */ + if ( expand( handle ) < 0 ) + errlogPrintf( "macExpandString: failed to expand raw values\n" ); + + /* fill in necessary fields in fake macro entry structure */ + entry.name = (char *) src; + entry.type = "string"; + entry.error = FALSE; + + /* expand the string */ + s = src; + d = dest; + *d = '\0'; + trans( handle, &entry, 0, "", &s, &d, d + capacity - 1 ); + + /* return +/- #chars copied depending on successful expansion */ + length = d - dest; + length = ( entry.error ) ? -length : length; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString() -> %ld\n", length ); + + return length; +} + +/* + * Define the value of a macro. A NULL value deletes the macro if it + * already existed + */ +long /* strlen(value) */ +epicsShareAPI macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name */ + + const char *value ) /* macro value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPutValue: NULL or invalid handle\n" ); + return -1; + } + + if ( handle->debug & 1 ) + printf( "macPutValue( %s, %s )\n", name, value ? value : "NULL" ); + + /* handle NULL value case: if name was found, delete entry (may be + several entries at different scoping levels) */ + if ( value == NULL ) { + /* + * FIXME: shouldn't be able to delete entries from lower scopes + * NOTE: when this is changed, this functionality of removing + * a macro from all scopes will still be needed by iocshEnvClear + */ + while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) { + int done = strcmp(entry->type, "environment variable") == 0; + delete( handle, entry ); + + if (done) + break; + } + + return 0; + } + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* new entry must be created if macro doesn't exist or if it only + exists at a lower scoping level */ + if ( entry == NULL || entry->level < handle->level ) { + entry = create( handle, name, FALSE ); + if ( entry == NULL ) { + errlogPrintf( "macPutValue: failed to create macro %s = %s\n", + name, value ); + return -1; + } else { + entry->type = "macro"; + } + } + + /* copy raw value */ + if ( rawval( handle, entry, value ) == NULL ) { + errlogPrintf( "macPutValue: failed to copy macro %s = %s\n", + name, value ) ; + return -1; + } + + /* return length of value */ + return strlen( value ); +} + +/* + * Return the value of a macro + */ +long /* strlen(value), <0 if undefined */ +epicsShareAPI macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long capacity ) /* capacity of destination buffer (value) */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + long length; /* number of characters returned */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macGetValue: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macGetValue( %s )\n", name ); + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* if capacity <= 1 or VALUE == NULL just return -1 / 0 for undefined / + defined macro */ + if ( capacity <= 1 || value == NULL ) { + return ( entry == NULL ) ? -1 : 0; + } + + /* if not found, copy name to value and return minus #chars copied */ + if ( entry == NULL ) { + strncpy( value, name, capacity ); + return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; + } + + /* expand raw values if necessary; if fail (can only fail because of + memory allocation failure), return same as if not found */ + if ( expand( handle ) < 0 ) { + errlogPrintf( "macGetValue: failed to expand raw values\n" ); + strncpy( value, name, capacity ); + return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; + } + + /* copy value and return +/- #chars copied depending on successful + expansion */ + strncpy( value, entry->value, capacity ); + length = ( value[capacity-1] == '\0' ) ? entry->length : capacity; + + return ( entry->error ) ? -length : length; +} + +/* + * Free up all storage associated with and delete a macro substitution + * context + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macDeleteHandle( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macDeleteHandle: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macDeleteHandle()\n" ); + + /* delete all entries */ + for ( entry = first( handle ); entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* clear magic field and free context structure */ + handle->magic = 0; + dbmfFree( handle ); + + return 0; +} + +/* + * Mark the start of a new scoping level + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPushScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPushScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPushScope()\n" ); + + /* increment scoping level */ + handle->level++; + + /* create new "special" entry of name "" */ + entry = create( handle, "", TRUE ); + if ( entry == NULL ) { + handle->level--; + errlogPrintf( "macPushScope: failed to push scope\n" ); + return -1; + } else { + entry->type = "scope marker"; + } + + return 0; +} + +/* + * Pop all macros defined since the last call to macPushScope() + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPopScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPopScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPopScope()\n" ); + + /* check scoping level isn't already zero */ + if ( handle->level == 0 ) { + errlogPrintf( "macPopScope: no scope to pop\n" ); + return -1; + } + + /* look up most recent scope entry */ + entry = lookup( handle, "", TRUE ); + if ( entry == NULL ) { + errlogPrintf( "macPopScope: no scope to pop\n" ); + return -1; + } + + /* delete scope entry and all macros defined since it */ + for ( ; entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* decrement scoping level */ + handle->level--; + + return 0; +} + +/* + * Report macro details to standard output + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macReportMacros( + MAC_HANDLE *handle ) /* opaque handle */ +{ + const char *format = "%-1s %-16s %-16s %s\n"; + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macReportMacros: NULL or invalid handle\n" ); + return -1; + } + + /* expand raw values if necessary; report but ignore failure */ + if ( expand( handle ) < 0 ) + errlogPrintf( "macGetValue: failed to expand raw values\n" ); + + /* loop through macros, reporting names and values */ + printf( format, "e", "name", "rawval", "value" ); + printf( format, "-", "----", "------", "-----" ); + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + /* differentiate between "special" (scope marker) and ordinary + entries */ + if ( entry->special ) + printf( format, "s", "----", "------", "-----" ); + else + printf( format, entry->error ? "*" : " ", entry->name, + entry->rawval ? entry->rawval : "", + entry->value ? entry->value : ""); + } + + return 0; +} + +/******************** beginning of static functions ********************/ + +/* + * Return pointer to first macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *first( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellFirst( &handle->list ); +} + +/* + * Return pointer to last macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *last( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellLast( &handle->list ); +} + +/* + * Return pointer to next macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *next( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellNext( ( ELLNODE * ) entry ); +} + +/* + * Return pointer to previous macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *previous( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellPrevious( ( ELLNODE * ) entry ); +} + +/* + * Create new macro entry (can assume it doesn't exist) + */ +static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ) +{ + ELLLIST *list = &handle->list; + MAC_ENTRY *entry = ( MAC_ENTRY * ) dbmfMalloc( sizeof( MAC_ENTRY ) ); + + if ( entry != NULL ) { + entry->name = Strdup( name ); + if ( entry->name == NULL ) { + dbmfFree( entry ); + entry = NULL; + } + else { + entry->type = ""; + entry->rawval = NULL; + entry->value = NULL; + entry->length = 0; + entry->error = FALSE; + entry->visited = FALSE; + entry->special = special; + entry->level = handle->level; + + ellAdd( list, ( ELLNODE * ) entry ); + } + } + + return entry; +} + +/* + * Look up macro entry with matching "special" attribute by name + */ +static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ) +{ + MAC_ENTRY *entry; + + if ( handle->debug & 2 ) + printf( "lookup-> level = %d, name = %s, special = %d\n", + handle->level, name, special ); + + /* search backwards so scoping works */ + for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) { + if ( entry->special != special ) + continue; + if ( strcmp( name, entry->name ) == 0 ) + break; + } + if ( (special == FALSE) && (entry == NULL) && + (handle->flags & FLAG_USE_ENVIRONMENT) ) { + char *value = getenv(name); + if (value) { + entry = create( handle, name, FALSE ); + if ( entry ) { + entry->type = "environment variable"; + if ( rawval( handle, entry, value ) == NULL ) + entry = NULL; + } + } + } + + if ( handle->debug & 2 ) + printf( "<-lookup level = %d, name = %s, result = %p\n", + handle->level, name, entry ); + + return entry; +} + +/* + * Copy raw value to macro entry + */ +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ) +{ + if ( entry->rawval != NULL ) + dbmfFree( entry->rawval ); + entry->rawval = Strdup( value ); + + handle->dirty = TRUE; + + return entry->rawval; +} + +/* + * Delete a macro entry; requires re-expansion of macro values since this + * macro may be referenced by another one + */ +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ) +{ + ELLLIST *list = &handle->list; + + ellDelete( list, ( ELLNODE * ) entry ); + + dbmfFree( entry->name ); + if ( entry->rawval != NULL ) + dbmfFree( entry->rawval ); + if ( entry->value != NULL ) + free( entry->value ); + dbmfFree( entry ); + + handle->dirty = TRUE; +} + +/* + * Expand macro definitions (expensive but done very infrequently) + */ +static long expand( MAC_HANDLE *handle ) +{ + MAC_ENTRY *entry; + const char *rawval; + char *value; + + if ( !handle->dirty ) + return 0; + + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + if ( handle->debug & 2 ) + printf( "\nexpand %s = %s\n", entry->name, + entry->rawval ? entry->rawval : "" ); + + if ( entry->value == NULL ) { + if ( ( entry->value = malloc( MAC_SIZE + 1 ) ) == NULL ) { + return -1; + } + } + + /* start at level 1 so quotes and escapes will be removed from + expanded value */ + rawval = entry->rawval; + value = entry->value; + *value = '\0'; + entry->error = FALSE; + trans( handle, entry, 1, "", &rawval, &value, entry->value + MAC_SIZE ); + entry->length = value - entry->value; + entry->value[MAC_SIZE] = '\0'; + } + + handle->dirty = FALSE; + + return 0; +} + +/* + * Translate raw macro value (recursive). This is by far the most complicated + * of the macro routines and calls itself recursively both to translate any + * macros referenced in the name and to translate the resulting name + */ +static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char *term, const char **rawval, char **value, + char *valend ) +{ + char quote; + const char *r; + char *v; + int discard; + int macRef; + + /* return immediately if raw value is NULL */ + if ( *rawval == NULL ) return; + + /* discard quotes and escapes if level is > 0 (i.e. if these aren't + the user's quotes and escapes) */ + discard = ( level > 0 ); + + /* debug output */ + if ( handle->debug & 2 ) + printf( "trans-> entry = %p, level = %d, capacity = %u, discard = %s, " + "rawval = %s\n", entry, level, (unsigned int)(valend - *value), discard ? "T" : "F", *rawval ); + + /* initially not in quotes */ + quote = 0; + + /* scan characters until hit terminator or end of string */ + for ( r = *rawval, v = *value; strchr( term, *r ) == NULL; r++ ) { + + /* handle quoted characters (quotes are discarded if in name) */ + if ( quote ) { + if ( *r == quote ) { + quote = 0; + if ( discard ) continue; + } + } + else if ( *r == '"' || *r == '\'' ) { + quote = *r; + if ( discard ) continue; + } + + /* macro reference if '$' followed by '(' or '{' */ + macRef = ( *r == '$' && + *( r + 1 ) != '\0' && + strchr( "({", *( r + 1 ) ) != NULL ); + + /* macros are not expanded in single quotes */ + if ( macRef && quote != '\'' ) { + /* Handle macro reference */ + refer ( handle, entry, level, &r, &v, valend ); + } + + else { + /* handle escaped characters (escape is discarded if in name) */ + if ( *r == '\\' && *( r + 1 ) != '\0' ) { + if ( v < valend && !discard ) *v++ = '\\'; + if ( v < valend ) *v++ = *++r; + } + + /* copy character to output */ + else { + if ( v < valend ) *v++ = *r; + } + + /* ensure string remains properly terminated */ + if ( v <= valend ) *v = '\0'; + } + } + + /* debug output */ + if ( handle->debug & 2 ) + printf( "<-trans level = %d, length = %4u, value = %s\n", + level, (unsigned int)(v - *value), *value ); + + /* update pointers to next characters to scan in raw value and to fill + in in output value (if at end of input, step back so terminator is + still there to be seen) */ + *rawval = ( *r == '\0' ) ? r - 1 : r; + *value = v; + + return; +} + +/* + * Expand a macro reference, handling default values and defining scoped + * macros as encountered. This code used to part of trans(), but was + * pulled out for ease of understanding. + */ +static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char **rawval, char **value, char *valend ) +{ + const char *r = *rawval; + char *v = *value; + char refname[MAC_SIZE + 1] = {'\0'}; + char *rn = refname; + MAC_ENTRY *refentry; + const char *defval = NULL; + const char *macEnd; + const char *errval = NULL; + int pop = FALSE; + + /* debug output */ + if ( handle->debug & 2 ) + printf( "refer-> entry = %p, level = %d, capacity = %u, rawval = %s\n", + entry, level, (unsigned int)(valend - *value), *rawval ); + + /* step over '$(' or '${' */ + r++; + macEnd = ( *r == '(' ) ? "=,)" : "=,}"; + r++; + + /* translate name (may contain macro references); truncated + quietly if too long but always guaranteed zero-terminated */ + trans( handle, entry, level + 1, macEnd, &r, &rn, rn + MAC_SIZE ); + refname[MAC_SIZE] = '\0'; + + /* Is there a default value? */ + if ( *r == '=' ) { + MAC_ENTRY dflt; + int flags = handle->flags; + handle->flags |= FLAG_SUPPRESS_WARNINGS; + + /* store its location in case we need it */ + defval = ++r; + + /* Find the end, discarding its value */ + dflt.name = refname; + dflt.type = "default value"; + dflt.error = FALSE; + trans( handle, &dflt, level + 1, macEnd+1, &r, &v, v); + + handle->flags = flags; + } + + /* extract and set values for any scoped macros */ + if ( *r == ',' ) { + MAC_ENTRY subs; + int flags = handle->flags; + handle->flags |= FLAG_SUPPRESS_WARNINGS; + + subs.type = "scoped macro"; + subs.error = FALSE; + + macPushScope( handle ); + pop = TRUE; + + while ( *r == ',' ) { + char subname[MAC_SIZE + 1] = {'\0'}; + char subval[MAC_SIZE + 1] = {'\0'}; + char *sn = subname; + char *sv = subval; + + /* translate the macro name */ + ++r; + subs.name = refname; + + trans( handle, &subs, level + 1, macEnd, &r, &sn, sn + MAC_SIZE ); + subname[MAC_SIZE] = '\0'; + + /* If it has a value, translate that and assign it */ + if ( *r == '=' ) { + ++r; + subs.name = subname; + + trans( handle, &subs, level + 1, macEnd+1, &r, &sv, + sv + MAC_SIZE); + subval[MAC_SIZE] = '\0'; + + macPutValue( handle, subname, subval ); + handle->dirty = TRUE; /* re-expand with new macro values */ + } + } + + handle->flags = flags; + } + + /* Now we can look up the translated name */ + refentry = lookup( handle, refname, FALSE ); + + if ( refentry ) { + if ( !refentry->visited ) { + /* reference is good, use it */ + if ( !handle->dirty ) { + /* copy the already-expanded value, merge any error status */ + cpy2val( refentry->value, &v, valend ); + entry->error = entry->error || refentry->error; + } else { + /* translate raw value */ + const char *rv = refentry->rawval; + refentry->visited = TRUE; + trans( handle, entry, level + 1, "", &rv, &v, valend ); + refentry->visited = FALSE; + } + goto cleanup; + } + /* reference is recursive */ + entry->error = TRUE; + errval = ",recursive)"; + if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { + errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n", + entry->type, entry->name, + refentry->type, refentry->name ); + } + } else { + /* no macro found by this name */ + if ( defval ) { + /* there was a default value, translate that instead */ + trans( handle, entry, level + 1, macEnd+1, &defval, &v, valend ); + goto cleanup; + } + entry->error = TRUE; + errval = ",undefined)"; + if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { + errlogPrintf( "macLib: macro %s is undefined (expanding %s %s)\n", + refname, entry->type, entry->name ); + } + } + + /* Bad reference, insert $(name,errval) */ + if ( v < valend ) *v++ = '$'; + if ( v < valend ) *v++ = '('; + cpy2val( refname, &v, valend ); + cpy2val( errval, &v, valend ); + +cleanup: + if (pop) { + macPopScope( handle ); + } + + /* debug output */ + if ( handle->debug & 2 ) + printf( "<-refer level = %d, length = %4u, value = %s\n", + level, (unsigned int)(v - *value), *value ); + + *rawval = r; + *value = v; + return; +} + +/* + * Copy a string, honoring the 'end of destination string' pointer + * Returns with **value pointing to the '\0' terminator + */ +static void cpy2val(const char *src, char **value, char *valend) +{ + char *v = *value; + while ((v < valend) && (*v = *src++)) { v++; } + *v = '\0'; + *value = v; +} + +/* + * strdup() implementation which uses our own memory allocator + */ +static char *Strdup(const char *string ) +{ + char *copy = dbmfMalloc( strlen( string ) + 1 ); + + if ( copy != NULL ) + strcpy( copy, string ); + + return copy; +} + diff --git a/modules/libcom/src/macLib/macEnv.c b/modules/libcom/src/macLib/macEnv.c new file mode 100644 index 000000000..f102748a8 --- /dev/null +++ b/modules/libcom/src/macLib/macEnv.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Macro expansion of environment variables + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "epicsString.h" +#include "macLib.h" + +char * epicsShareAPI +macEnvExpand(const char *str) +{ + return macDefExpand(str, NULL); +} + +char * epicsShareAPI +macDefExpand(const char *str, MAC_HANDLE *macros) +{ + MAC_HANDLE *handle; + static const char * pairs[] = { "", "environ", NULL, NULL }; + long destCapacity = 128; + char *dest = NULL; + int n; + + if (macros) { + handle = macros; + } else { + if (macCreateHandle(&handle, pairs)){ + errlogMessage("macDefExpand: macCreateHandle failed."); + return NULL; + } + } + + do { + destCapacity *= 2; + /* + * Use free/malloc rather than realloc since there's no need to + * keep the original contents. + */ + free(dest); + dest = malloc(destCapacity); + if(!dest) + goto done; + + n = macExpandString(handle, str, dest, destCapacity); + } while (n >= (destCapacity - 1)); + + if (n < 0) { + free(dest); + dest = NULL; + } else { + size_t unused = destCapacity - ++n; + + if (unused >= 20) + dest = realloc(dest, n); + } + +done: + if (macros == NULL) { + if (macDeleteHandle(handle)) { + errlogMessage("macDefExpand: macDeleteHandle failed."); + } + } + return dest; +} diff --git a/modules/libcom/src/macLib/macLib.h b/modules/libcom/src/macLib/macLib.h new file mode 100644 index 000000000..14ecdaa3f --- /dev/null +++ b/modules/libcom/src/macLib/macLib.h @@ -0,0 +1,164 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Definitions for macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#ifndef INCmacLibH +#define INCmacLibH + +/* + * EPICS include files needed by this file + */ +#include "ellLib.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Maximum size of macro name or value string (simpler to make fixed) + */ +#define MAC_SIZE 256 + +/* + * Macro substitution context. One of these contexts is allocated each time + * macCreateHandle() is called + */ +typedef struct { + long magic; /* magic number (used for authentication) */ + int dirty; /* values need expanding from raw values? */ + int level; /* scoping level */ + int debug; /* debugging level */ + ELLLIST list; /* macro name / value list */ + int flags; /* operating mode flags */ +} MAC_HANDLE; + +/* + * Function prototypes (core library) + */ +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macCreateHandle( + MAC_HANDLE **handle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + const char * pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +epicsShareFunc void +epicsShareAPI macSuppressWarning( + MAC_HANDLE *handle, /* opaque handle */ + + int falseTrue /*0 means issue, 1 means suppress*/ +); + +epicsShareFunc long /* strlen(dest), <0 if any macros are */ + /* undefined */ +epicsShareAPI macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + const char *src, /* source string */ + + char *dest, /* destination string */ + + long capacity /* capacity of destination buffer (dest) */ +); + + +epicsShareFunc long /* strlen(value) */ +epicsShareAPI macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name */ + + const char *value /* macro value */ +); + +epicsShareFunc long /* strlen(value), <0 if undefined */ +epicsShareAPI macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long capacity /* capacity of destination buffer (value) */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macDeleteHandle( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPushScope( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPopScope( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macReportMacros( + MAC_HANDLE *handle /* opaque handle */ +); + +/* + * Function prototypes (utility library) + */ +epicsShareFunc long /* #defns encountered; <0 = ERROR */ +epicsShareAPI macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + const char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +); + +epicsShareFunc long /* #macros defined; <0 = ERROR */ +epicsShareAPI macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +epicsShareFunc char * /* expanded string; NULL if any undefined macros */ +epicsShareAPI macEnvExpand( + const char *str /* string to be expanded */ +); + +epicsShareFunc char * /* expanded string; NULL if any undefined macros */ +epicsShareAPI macDefExpand( + const char *str, /* string to be expanded */ + MAC_HANDLE *macros /* opaque handle; can be NULL if default */ + /* special characters are to be used */ +); + +#ifdef __cplusplus +} +#endif + +#endif /*INCmacLibH*/ diff --git a/modules/libcom/src/macLib/macLibNOTES b/modules/libcom/src/macLib/macLibNOTES new file mode 100644 index 000000000..fc3606254 --- /dev/null +++ b/modules/libcom/src/macLib/macLibNOTES @@ -0,0 +1,158 @@ +# Test input file for macTest filter, doubling as notes on usage of the +# macro library. Some special strings at start of line are supported: +# +# 1. '#' indicates a comment and is ignored +# +# 2. '%set' is followed by "a=b, c=d" style macro definitions which are +# passed through macParseDefs() and macInstallMacros() +# +# 3. '%push' pushes a scoping level by calling macPushScope() +# +# 4. '%pop' pops a scoping level by calling macPopScope() +# +# 5. '%report' reports macro definitions by calling macReportMacros() +# +# 6. all other lines are expanded by callins macExpandString() +# +introduction +------------ + +See the README file for the library specification and see the header +comments of macCore.c (the core library) for notes on what has been +implemented. + +This file contains tutorial information and examples. It's the +best documentation that there is. + + +simple examples +--------------- + +To define the a, b, c and d macros to be 1, 2, 3 and 4 (note optional +white space is ignored around '=' and ',' characters): + +%set a=1, b=2, c = 3 , d = 4 + +These macros can be dereferenced using '$(xxx)' or '${xxx}' notation: +a = $(a), b = $(b), c = ${c}, d = ${d}. + +Macro values can reference other macros in an arbitrarily complex way. +The only current restrictions are that a macro name or value cannot +exceed 256 characters in length. This restriction could fairly easily +be lifted. + +Here's an example: + +%set x = ${$(y$(z))} + +If this is expanded now: $(x), it won't work because the other macros +aren't defined yet. So: + +%set cash=lucre, ywork=cash, z=work + +Now expansion yields "$(x)" (work -> ywork -> cash -> lucre). + +One can inadvertently set up circular references. For example: + +%set mac1=$(mac2), mac2=$(mac3), mac3=$(mac1) + +An attempt to dereference mac1 gives $(mac1). When a macro expansion +fails, the translation that failed is replaced with the text that could +not be expanded. + +You can get a report of current macro definitions by doing +%report + +The '*' character in the first column indicates a problem expanding that +macro. We'll get rid of these problem macros: +%set mac1, mac2, mac3 + +You can also push a new scoping level by doing +%push + +and pop back to it by doing +%pop + +For example: +%set level = 0 +%push +%set level = 1 +%push +%set level = 2 +%push +%set level = 3 +%report +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +(That last error was deliberate) + + +quote and escape handling +------------------------- + +Both single and double quotes are supported, as are escapes. Some of the +implications are quite subtle and some of the design choices made may not +meet with universal approval. The basic idea is that a string in which +macro references have been expanded should look like the source string +apart from the where macros have been expanded. This implies that quote +and escape characters should not be removed from the string. + +Single and double quotes are different in that (as in most shells), +macros are substituted within double quotes but not within single +quotes. Back quotes are not special characters. Missing quotes at the +end of a string are automatically and quietly supplied. + +We've already seen some examples but what happens here: $(x)? +Should not that have been expanded? Why wasn't it? The answer is given +later on. As a clue, this is: $(x). This isn't: $(x)! + +Characters may be escaped by preceding them with a \ (back slash). +Escapes work even within quotes, which is more like C than most shells. +Thus, '\'' works, and it doesn't with the C shell. + +Quotes and escapes can also be used in "a=b, c=d" style assignments +but they are not part of macro names. For example: + +%set \= = equal, \' = quote + +defines macros called "=" and "'", not "\=" and "\'". To reference +these macros, "$(=)" ('$(=)') works because "=" is not special in +this context. However the quote must be escaped because quotes are +always special, as in "$(\')" ('$(\')'). + + +miscellaneous notes +------------------- + +In the "a=b, c=d" strings that are parsed by macParseDefns(), a macro +can be set to an empty value by putting nothing after the equals +sign, as in + +%set empty = +resulting in empty = "$(empty)". Omitting the equal sign gives the +macro a NULL rather than empty value (with the result that the macro +is deleted since this is how macPutValue() interprets a NULL value), so + +%set empty +deletes the empty macro, as can be seen when we try to de-reference +it: $(empty). + +The only special characters in these "a=b, c=d" strings are "=", "," +and white space surrounding these characters (plus quotes and escapes +of course). This means that + +%set fred 2 +actually sets a macro called "fred 2" to a NULL value (i.e. it deletes +it if it existed). This is probably wrong and the space should be +taken as an implicit '='. However, to do this and still to support +ignored white space around '=' and ',' characters is a bigger change +than I am prepared to make at present. + +What was the problem expanding "$(x)" before? It was single quotes +from words like "wasn't"! Is this a problem or a feature? diff --git a/modules/libcom/src/macLib/macLibREADME b/modules/libcom/src/macLib/macLibREADME new file mode 100644 index 000000000..f3cda53c2 --- /dev/null +++ b/modules/libcom/src/macLib/macLibREADME @@ -0,0 +1,177 @@ +1. Macro substitution library +----------------------------- + +This library could be used directly by applications which need to +support macro substitution. It will be implemented on all platforms. + +1.1 Core library +---------------- + +The core library provides a minimal set of basic operations. Some +utility routines, described later, use core routines to provide a more +convenient interface for some purposes. + +a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] ); + void macSuppressWarning(MAC_HANDLE *handle,int falseTrue); + + Creates a new macro substitution context and returns an opaque handle + to that context. An application can, if it desires, have several + active contexts, although most will not. + + If desired, an initial set of macro definitions may be loaded + ("pairs" is set to NULL to avoid this). The definitions are in the + "standard" pairs format, as described under macParseDefns(). + + Note that MAC_HANDLE is a typedef for the context structure. The + opaque handle is of type "MAC_HANDLE *", which is a pointer to the + context structure. The memory for this context is allocated by this + routine. + + macSuppressWarning can be called to suppress the marning message + when macExpandString cant expand a macro. A non zero value will + suppress the messages. + + +c) long macGetValue ( MAC_HANDLE *handle, char *name, + char *value, long maxlen ); + + Returns up to maxlen characters of the value of macro "name". "value" + will be zero-terminated if the length of the value is less than + maxlen. The function value will be the number of characters copied to + "value" (see below for behavior if the macro is undefined). If maxlen + is zero, no characters will be copied to "value" (which can be NULL) + and the call can be used to check whether the macro is defined. Note + that truncation of the value is therefore not reported. Is this a + problem? + + If the macro is undefined, the macro reference will be returned in + the value string (if permitted by maxlen) and the function value will + be _minus_ the number of characters copied. Note that treatment of + maxlen is intended to be consistent with what people are used to with + strncpy(). + + If the value contains macro references then the references will be + expanded recursively. This expansion will detect a direct or indirect + self reference. + + Macro references begin with a "$" immediately followed by either a + "(" or a "{" character. The macro name comes next, and may optionally + be succeeded by an "=" and a default value, which will be returned if + the named macro is undefined at the moment of expansion. The reference + is terminated by the matching ")" or "}" character. + +d) long macPutValue( MAC_HANDLE *handle, char *name, char *value ); + + Sets the value of the macro "name". If "value" is NULL, it undefines + all instances of "name" at all scoping levels (it's not an error if + "name" is not defined in this case). Macros referenced by "value" + need not be defined at this point. + + The function returns the length of the value string. + +e) long macDeleteHandle( MAC_HANDLE *handle ); + + Marks a handle invalid, and frees all storage associated with it. + + Note that this does not free any strings into which macro values have + been returned. Macro values are always returned into strings which + were pre-allocated by the caller. + +f) long macPushScope( MAC_HANDLE *handle ); + + Marks the start of a new scoping level such that all definitions made + up until the next macPopScope() call will be lost on macPopScope() + and those current at macPushScope() will be re-instated. + +g) long macPopScope( MAC_HANDLE *handle ); + + See macPushScope. + +h) Error handling + + These routines conform to 0 (=OK) for success, -1 (=ERROR) for + failure, and small positive values for extra info. I contravened this + for macGetValue() and macExpandString() because I felt that it was + worth returning information both on success / failure and on value + length. + + Errors and warnings are reported using errlogPrintf(). + + + +1.2 Utility library +------------------- + +These are convenience functions. If other useful functions emerge during +implementation, the list may grow. + +a) macParseDefns( MAC_HANDLE *handle, char *defns, char **pairs[] ); + + This takes a set of macro definitions in "a=xxx,b=yyy" format and + converts them into an array of pointers to character strings which + are, in order, "first name", "first value", "second name", "second + value" etc. The array is terminated with two NULL pointers and all + storage is allocated contiguously so that it can be freed with a + single call to free(). + + This routine is independent of any handle and provides a generally + useful service which may be used elsewhere. Any macro references in + values are not expanded at this point since the referenced macros may + be defined or redefined before the macro actually has to be + translated. + + Shell-style escapes and quotes are supported, as are things like + "A=B,B=$(C$(A)),CA=CA,CB=CB" (sets B to "CB"). White space is + significant within values but ignored elsewhere (i.e. surrounding "=" + and "," characters). + + Probably noone will ever want to, but the special meanings of "$", + "{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx() + calls. This routine does not have a handle argument, so they must be + changed globally for it to use the new definitions. Should it have a + handle argument? This makes it more of a pain to use but guarantees + that there will be no conflicts. I think it should really. + + The function value is the number of definitions encountered, or -1 if + the supplied string is invalid. + +b) long macInstallMacros( MAC_HANDLE *handle, char *pairs[] ); + + This takes an array of pairs as defined above and installs them as + definitions by calling macPutValue(). The pairs array is terminated + by a NULL pointer. + + The function value is the number of macros defined. + +c) long macExpandString( MAC_HANDLE *handle, char *src, + char *dest, long maxlen ); + + This operates on a string which may contain macro references. It + parses the string looking for such references and passes them to + macGetValue() for translation. It returns the expanded string in the + supplied argument. + + The function value is similar to that of macGetValue(). Its absolute + value is the number of characters copied. If negative, at least one + undefined macro has been left unexpanded. + +d) char *macEnvExpand( char *src ); + + This operates on a string which may contain macros defined by + environment variables. It parses the string looking for such + references and passes them to macGetValue() for translation. It uses + malloc() to allocate space for the expanded string and returns a + pointer to this null-terminated string. It returns NULL if the source + string contains any undefined references. + +e) char *macDefExpand( char *src, MAC_HANDLE *macros ); + + This operates in the same manner as macEnvExpand, but takes an + optional macro handle that can contain a set of macro definitions. + These macros are appended to the set of macros from environment + variables when expanding the string. + +f) long macReportMacros( MAC_HANDLE *handle ); + + This reports details of current definitions to standard output, and is + intended purely for debugging purposes. diff --git a/modules/libcom/src/macLib/macUtil.c b/modules/libcom/src/macLib/macUtil.c new file mode 100644 index 000000000..7bf73542e --- /dev/null +++ b/modules/libcom/src/macLib/macUtil.c @@ -0,0 +1,284 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Implementation of utility macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#include "macLib.h" + +/* + * Parse macros definitions in "a=xxx,b=yyy" format and convert them to + * a contiguously allocated array pointers to names and values, and the + * name and value strings, terminated with two NULL pointers. Quotes + * and escapes are honored but only removed from macro names (not + * values) + */ +long /* #defns encountered; <0 = ERROR */ +epicsShareAPI macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + const char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] ) /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +{ + static const size_t altNumMax = 4; + size_t numMax; + int i; + int num; + int quote; + int escape; + size_t nbytes; + const char **ptr; + const char **end; + int *del; + char *memCp, **memCpp; + const char *c; + char *s, *d, **p; + enum { preName, inName, preValue, inValue } state; + + /* debug output */ + if ( handle && (handle->debug & 1) ) + printf( "macParseDefns( %s )\n", defns ); + + /* allocate temporary pointer arrays; in worst case they need to have + as many entries as the length of the defns string */ + numMax = strlen( defns ); + if ( numMax < altNumMax ) + numMax = altNumMax; + ptr = (const char **) calloc( numMax, sizeof( char * ) ); + end = (const char **) calloc( numMax, sizeof( char * ) ); + del = (int *) calloc( numMax, sizeof( int ) ); + if ( ptr == NULL || end == NULL || del == NULL ) goto error; + + /* go through definitions, noting pointers to starts and ends of macro + names and values; honor quotes and escapes; ignore white space + around assignment and separator characters */ + num = 0; + del[0] = FALSE; + quote = 0; + state = preName; + for ( c = (const char *) defns; *c != '\0'; c++ ) { + + /* handle quotes */ + if ( quote ) + quote = ( *c == quote ) ? 0 : quote; + else if ( *c == '\'' || *c == '"' ) + quote = *c; + + /* handle escapes (pointer incremented below) */ + escape = ( *c == '\\' && *( c + 1 ) != '\0' ); + + switch ( state ) { + case preName: + if ( !quote && !escape && ( isspace( (int) *c ) || *c == ',' ) ) break; + ptr[num] = c; + state = inName; + /* fall through (may be empty name) */ + + case inName: + if ( quote || escape || ( *c != '=' && *c != ',' ) ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preValue; + if ( *c != ',' ) break; + del[num] = TRUE; + /* fall through (','; will delete) */ + + case preValue: + if ( !quote && !escape && isspace( (int) *c ) ) break; + ptr[num] = c; + state = inValue; + /* fall through (may be empty value) */ + + case inValue: + if ( quote || escape || *c != ',' ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preName; + break; + } + + /* if this was escape, increment pointer now (couldn't do + before because could have ignored escape at start of name + or value) */ + if ( escape ) c++; + } + + /* tidy up from state at end of string */ + switch ( state ) { + case preName: + break; + case inName: + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = TRUE; + case preValue: + ptr[num] = c; + case inValue: + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + } + + /* debug output */ + if ( handle != NULL && handle->debug & 4 ) + for ( i = 0; i < num; i += 2 ) + printf( "[%ld] %.*s = [%ld] %.*s (%s) (%s)\n", + (long) (end[i+0] - ptr[i+0]), (int) (end[i+0] - ptr[i+0]), ptr[i+0], + (long) (end[i+1] - ptr[i+1]), (int) (end[i+1] - ptr[i+1]), ptr[i+1], + del[i+0] ? "del" : "nodel", + del[i+1] ? "del" : "nodel" ); + + /* calculate how much memory to allocate: pointers followed by + strings */ + nbytes = ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) + nbytes += end[i] - ptr[i] + 1; + + /* allocate memory and set returned pairs pointer */ + memCp = malloc( nbytes ); + if ( memCp == NULL ) goto error; + memCpp = ( char ** ) memCp; + *pairs = memCpp; + + /* copy pointers and strings (memCpp accesses the pointer section + and memCp accesses the string section) */ + memCp += ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) { + + /* if no '=' followed the name, macro will be deleted */ + if ( del[i] ) + *memCpp++ = NULL; + else + *memCpp++ = memCp; + + /* copy value regardless of the above */ + strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] ); + memCp += end[i] - ptr[i]; + *memCp++ = '\0'; + } + + /* add two NULL pointers */ + *memCpp++ = NULL; + *memCpp++ = NULL; + + /* remove quotes and escapes from names in place (unlike values, they + will not be re-parsed) */ + for ( p = *pairs; *p != NULL; p += 2 ) { + quote = 0; + for ( s = d = *p; *s != '\0'; s++ ) { + + /* quotes are not copied */ + if ( quote ) { + if ( *s == quote ) { + quote = 0; + continue; + } + } + else if ( *s == '\'' || *s == '"' ) { + quote = *s; + continue; + } + + /* escapes are not copied but next character is */ + if ( *s == '\\' && *( s + 1 ) != '\0' ) + s++; + + /* others are copied */ + *d++ = *s; + } + + /* need to terminate destination */ + *d++ = '\0'; + } + + /* free workspace */ + free( ( void * ) ptr ); + free( ( void * ) end ); + free( ( char * ) del ); + + /* debug output */ + if ( handle != NULL && handle->debug & 1 ) + printf( "macParseDefns() -> %d\n", num / 2 ); + + /* success exit; return number of definitions */ + return num / 2; + + /* error exit */ +error: + errlogPrintf( "macParseDefns: failed to allocate memory\n" ); + if ( ptr != NULL ) free( ( void * ) ptr ); + if ( end != NULL ) free( ( void * ) end ); + if ( del != NULL ) free( ( char * ) del ); + *pairs = NULL; + return -1; +} + +/* + * Install an array of name / value pairs as macro definitions. The + * array should have an even number of elements followed by at least + * one (preferably two) NULL pointers + */ +long /* #macros defined; <0 = ERROR */ +epicsShareAPI macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + int n; + char **p; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros( %s, %s, ... )\n", + pairs && pairs[0] ? pairs[0] : "NULL", + pairs && pairs[1] ? pairs[1] : "NULL" ); + + /* go through array defining macros */ + for ( n = 0, p = pairs; p != NULL && p[0] != NULL; n++, p += 2 ) { + if ( macPutValue( handle, p[0], p[1] ) < 0 ) + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros() -> %d\n", n ); + + /* return number of macros defined */ + return n; +} + diff --git a/modules/libcom/src/misc/Makefile b/modules/libcom/src/misc/Makefile new file mode 100644 index 000000000..3d5412dc3 --- /dev/null +++ b/modules/libcom/src/misc/Makefile @@ -0,0 +1,44 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/misc + +INC += alarm.h +INC += alarmString.h +INC += adjustment.h +INC += cantProceed.h +INC += dbDefs.h +INC += epicsConvert.h +INC += epicsExit.h +INC += epicsStdlib.h +INC += epicsString.h +INC += epicsTypes.h +INC += shareLib.h +INC += epicsExport.h +INC += unixFileName.h +INC += locationException.h +INC += ipAddrToAsciiAsynchronous.h +INC += compilerDependencies.h +INC += epicsUnitTest.h +INC += testMain.h + +# epicsVersion.h is created by this Makefile +INC += epicsVersion.h + +Com_SRCS += alarmString.c +Com_SRCS += aToIPAddr.c +Com_SRCS += adjustment.c +Com_SRCS += cantProceed.c +Com_SRCS += epicsConvert.c +Com_SRCS += epicsExit.c +Com_SRCS += epicsStdlib.c +Com_SRCS += epicsString.c +Com_SRCS += truncateFile.c +Com_SRCS += ipAddrToAsciiAsynchronous.cpp +Com_SRCS += epicsUnitTest.c diff --git a/modules/libcom/src/misc/RULES b/modules/libcom/src/misc/RULES new file mode 100644 index 000000000..2ba358675 --- /dev/null +++ b/modules/libcom/src/misc/RULES @@ -0,0 +1,18 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +MAKEVERSION = $(LIBCOM)/misc/makeEpicsVersion.pl +MAKEVERSION_FLAGS = -o $(notdir $@) $(QUIET_FLAG) +MAKEVERSION_FLAGS += $(if $(EPICS_SITE_VERSION),-v "$(EPICS_SITE_VERSION)") + +$(COMMON_DIR)/epicsVersion.h: $(CONFIG)/CONFIG_BASE_VERSION \ + $(CONFIG)/CONFIG_SITE $(MAKEVERSION) + @$(RM) $(notdir $@) + $(PERL) $(MAKEVERSION) $(MAKEVERSION_FLAGS) $< + @$(MV) $(notdir $@) $@ diff --git a/modules/libcom/src/misc/aToIPAddr.c b/modules/libcom/src/misc/aToIPAddr.c new file mode 100644 index 000000000..c21b574f6 --- /dev/null +++ b/modules/libcom/src/misc/aToIPAddr.c @@ -0,0 +1,193 @@ +/*************************************************************************\ +* Copyright (c) 2013 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * rational replacement for inet_addr() + * + * author: Jeff Hill + */ +#include +#include + +#define epicsExportSharedSymbols +#include "epicsTypes.h" +#include "osiSock.h" + +#ifndef NELEMENTS +#define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#endif /*NELEMENTS*/ + +/* + * addrArrayToUL () + */ +static int addrArrayToUL ( const unsigned *pAddr, + unsigned nElements, struct in_addr *pIpAddr ) +{ + unsigned i; + epicsUInt32 addr = 0ul; + + for ( i=0u; i < nElements; i++ ) { + if ( pAddr[i] > 0xff ) { + return -1; + } + addr <<= 8; + addr |= ( epicsUInt32 ) pAddr[i]; + } + pIpAddr->s_addr = htonl ( addr ); + + return 0; +} + +/* + * initIPAddr() + * !! ipAddr should be passed in in network byte order !! + * !! port is passed in in host byte order !! + */ +static int initIPAddr ( struct in_addr ipAddr, unsigned port, + struct sockaddr_in *pIP ) +{ + if ( port > 0xffff ) { + return -1; + } + { + epicsUInt16 port_16 = (epicsUInt16) port; + memset (pIP, '\0', sizeof(*pIP)); + pIP->sin_family = AF_INET; + pIP->sin_port = htons(port_16); + pIP->sin_addr = ipAddr; + } + return 0; +} + +/* + * rational replacement for inet_addr() + * which allows the limited broadcast address + * 255.255.255.255, allows the user + * to specify a port number, and allows also a + * named host to be specified. + * + * Sets the port number to "defaultPort" only if + * "pAddrString" does not contain an address of the form + * "n.n.n.n:p or host:p" + */ +epicsShareFunc int epicsShareAPI +aToIPAddr( const char *pAddrString, unsigned short defaultPort, + struct sockaddr_in *pIP ) +{ + int status; + unsigned addr[4]; + unsigned long rawAddr; + /* + * !! change n elements here requires change in format below !! + */ + char hostName[512]; + char dummy[8]; + unsigned port; + struct in_addr ina; + + /* + * dotted ip addresses + */ + status = sscanf ( pAddrString, " %u . %u . %u . %u %7s ", + addr, addr+1u, addr+2u, addr+3u, dummy ); + if ( status == 4 ) { + if ( addrArrayToUL ( addr, NELEMENTS ( addr ), & ina ) < 0 ) { + return -1; + } + port = defaultPort; + return initIPAddr ( ina, port, pIP ); + } + + /* + * dotted ip addresses and port + */ + status = sscanf ( pAddrString, " %u . %u . %u . %u : %u %7s", + addr, addr+1u, addr+2u, addr+3u, &port, dummy ); + if ( status >= 5 ) { + if ( status > 5 ) { + /* + * valid at the start but detritus on the end + */ + return -1; + } + if ( addrArrayToUL ( addr, NELEMENTS ( addr ), &ina ) < 0 ) { + return -1; + } + return initIPAddr ( ina, port, pIP ); + } + + /* + * IP address as a raw number + */ + status = sscanf ( pAddrString, " %lu %7s ", &rawAddr, dummy ); + if ( status == 1 ) { + if ( rawAddr > 0xffffffff ) { + return -1; + } + port = defaultPort; + { + epicsUInt32 rawAddr_32 = ( epicsUInt32 ) rawAddr; + ina.s_addr = htonl ( rawAddr_32 ); + return initIPAddr ( ina, port, pIP ); + } + } + + /* + * IP address as a raw number, and port + */ + status = sscanf ( pAddrString, " %lu : %u %7s ", &rawAddr, &port, dummy ); + if ( status >= 2 ) { + if ( status > 2 ) { + /* + * valid at the start but detritus on the end + */ + return -1; + } + if ( rawAddr > 0xffffffff ) { + return -1; + } + { + epicsUInt32 rawAddr_32 = ( epicsUInt32 ) rawAddr; + ina.s_addr = htonl ( rawAddr_32 ); + return initIPAddr ( ina, port, pIP ); + } + } + + + /* + * host name string + */ + status = sscanf ( pAddrString, " %511[^:] %s ", hostName, dummy ); + if ( status == 1 ) { + port = defaultPort; + status = hostToIPAddr ( hostName, &ina ); + if ( status == 0 ) { + return initIPAddr ( ina, port, pIP ); + } + } + + /* + * host name string, and port + */ + status = sscanf ( pAddrString, " %511[^:] : %u %s ", hostName, + &port, dummy ); + if ( status >= 2 ) { + if ( status > 2 ) { + /* + * valid at the start but detritus on the end + */ + return -1; + } + status = hostToIPAddr ( hostName, &ina ); + if ( status == 0 ) { + return initIPAddr ( ina, port, pIP ); + } + } + + return -1; +} diff --git a/modules/libcom/src/misc/adjustment.c b/modules/libcom/src/misc/adjustment.c new file mode 100644 index 000000000..ac10e28c9 --- /dev/null +++ b/modules/libcom/src/misc/adjustment.c @@ -0,0 +1,55 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* src/libCom/adjustment.c */ + +/* Author: Peregrine McGehee */ + +#include +#include +#include + +/* Up to now epicsShareThings have been declared as imports + * (Appropriate for other stuff) + * After setting the following they will be declared as exports + * (Appropriate for what we implenment) + */ +#define epicsExportSharedSymbols +#include "adjustment.h" + +epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size) +{ + int align_size, adjust; + struct test_long_word { char c; long lw; }; + struct test_double { char c; double d; }; + struct test_ptr { char c; void *p; }; + int test_long_size = sizeof(struct test_long_word) - sizeof(long); + int test_double_size = sizeof(struct test_double) - sizeof(double); + int test_ptr_size = sizeof(struct test_ptr) - sizeof(void *); + size_t adjusted_size = size; + + /* + * Use Jeff's alignment tests to determine worst case of long, + * double or pointer alignment requirements. + */ + align_size = test_long_size > test_ptr_size ? + test_long_size : test_ptr_size; + + align_size = align_size > test_double_size ? + align_size : test_double_size; + + /* + * Increase the size to fit worst case alignment if not already + * properly aligned. + */ + adjust = align_size - size%align_size; + if (adjust != align_size) adjusted_size += adjust; + + return (adjusted_size); +} diff --git a/modules/libcom/src/misc/adjustment.h b/modules/libcom/src/misc/adjustment.h new file mode 100644 index 000000000..3f152039f --- /dev/null +++ b/modules/libcom/src/misc/adjustment.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* src/libCom/adjustment.h */ + +#ifndef INCadjustmenth +#define INCadjustmenth +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size); + +#ifdef __cplusplus +} +#endif + + +#endif /*INCadjustmenth*/ + diff --git a/modules/libcom/src/misc/alarm.h b/modules/libcom/src/misc/alarm.h new file mode 100644 index 000000000..58e2b7313 --- /dev/null +++ b/modules/libcom/src/misc/alarm.h @@ -0,0 +1,108 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Alarm definitions, must match menuAlarmSevr.dbd and menuAlarmStat.dbd */ + +/* + * Authors: Bob Dalesio and Marty Kraimer + * Date: 11-7-90 + */ + +#ifndef INC_alarm_H +#define INC_alarm_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define NO_ALARM 0 + +/* ALARM SEVERITIES - must match menuAlarmSevr.dbd */ + +typedef enum { + epicsSevNone = NO_ALARM, + epicsSevMinor, + epicsSevMajor, + epicsSevInvalid, + ALARM_NSEV +} epicsAlarmSeverity; + +#define firstEpicsAlarmSev epicsSevNone +#define MINOR_ALARM epicsSevMinor +#define MAJOR_ALARM epicsSevMajor +#define INVALID_ALARM epicsSevInvalid +#define lastEpicsAlarmSev epicsSevInvalid + + +/* ALARM STATUS - must match menuAlarmStat.dbd */ + +typedef enum { + epicsAlarmNone = NO_ALARM, + epicsAlarmRead, + epicsAlarmWrite, + epicsAlarmHiHi, + epicsAlarmHigh, + epicsAlarmLoLo, + epicsAlarmLow, + epicsAlarmState, + epicsAlarmCos, + epicsAlarmComm, + epicsAlarmTimeout, + epicsAlarmHwLimit, + epicsAlarmCalc, + epicsAlarmScan, + epicsAlarmLink, + epicsAlarmSoft, + epicsAlarmBadSub, + epicsAlarmUDF, + epicsAlarmDisable, + epicsAlarmSimm, + epicsAlarmReadAccess, + epicsAlarmWriteAccess, + ALARM_NSTATUS +} epicsAlarmCondition; + +#define firstEpicsAlarmCond epicsAlarmNone +#define READ_ALARM epicsAlarmRead +#define WRITE_ALARM epicsAlarmWrite +#define HIHI_ALARM epicsAlarmHiHi +#define HIGH_ALARM epicsAlarmHigh +#define LOLO_ALARM epicsAlarmLoLo +#define LOW_ALARM epicsAlarmLow +#define STATE_ALARM epicsAlarmState +#define COS_ALARM epicsAlarmCos +#define COMM_ALARM epicsAlarmComm +#define TIMEOUT_ALARM epicsAlarmTimeout +#define HW_LIMIT_ALARM epicsAlarmHwLimit +#define CALC_ALARM epicsAlarmCalc +#define SCAN_ALARM epicsAlarmScan +#define LINK_ALARM epicsAlarmLink +#define SOFT_ALARM epicsAlarmSoft +#define BAD_SUB_ALARM epicsAlarmBadSub +#define UDF_ALARM epicsAlarmUDF +#define DISABLE_ALARM epicsAlarmDisable +#define SIMM_ALARM epicsAlarmSimm +#define READ_ACCESS_ALARM epicsAlarmReadAccess +#define WRITE_ACCESS_ALARM epicsAlarmWriteAccess +#define lastEpicsAlarmCond epicsAlarmWriteAccess + + +/* Name string arrays */ + +epicsShareExtern const char *epicsAlarmSeverityStrings [ALARM_NSEV]; +epicsShareExtern const char *epicsAlarmConditionStrings [ALARM_NSTATUS]; + + +#ifdef __cplusplus +} +#endif + +#endif /* INC_alarm_H */ diff --git a/modules/libcom/src/misc/alarmString.c b/modules/libcom/src/misc/alarmString.c new file mode 100644 index 000000000..4db179dcb --- /dev/null +++ b/modules/libcom/src/misc/alarmString.c @@ -0,0 +1,50 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* String names for alarm status and severity values */ + +#define epicsExportSharedSymbols +#include "alarm.h" + +/* ALARM SEVERITIES - must match menuAlarmSevr.dbd and alarm.h */ + +epicsShareDef const char * epicsAlarmSeverityStrings[ALARM_NSEV] = { + "NO_ALARM", + "MINOR", + "MAJOR", + "INVALID" +}; + + +/* ALARM STATUS - must match menuAlarmStat.dbd and alarm.h */ + +epicsShareDef const char * epicsAlarmConditionStrings[ALARM_NSTATUS] = { + "NO_ALARM", + "READ", + "WRITE", + "HIHI", + "HIGH", + "LOLO", + "LOW", + "STATE", + "COS", + "COMM", + "TIMEOUT", + "HWLIMIT", + "CALC", + "SCAN", + "LINK", + "SOFT", + "BAD_SUB", + "UDF", + "DISABLE", + "SIMM", + "READ_ACCESS", + "WRITE_ACCESS" +}; diff --git a/modules/libcom/src/misc/alarmString.h b/modules/libcom/src/misc/alarmString.h new file mode 100644 index 000000000..ab320f058 --- /dev/null +++ b/modules/libcom/src/misc/alarmString.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This file is deprecated, use alarm.h instead. + * + * Old string names for alarm status and severity values + */ + +#ifndef INC_alarmString_H +#define INC_alarmString_H + +#include "alarm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Old versions of alarmString.h defined these names: */ + +#define alarmSeverityString epicsAlarmSeverityStrings +#define alarmStatusString epicsAlarmConditionStrings + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/misc/cantProceed.c b/modules/libcom/src/misc/cantProceed.c new file mode 100644 index 000000000..6c35796fd --- /dev/null +++ b/modules/libcom/src/misc/cantProceed.c @@ -0,0 +1,73 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 04JAN99 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "epicsStackTrace.h" + +epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *msg) +{ + void * mem = NULL; + if (count > 0 && size > 0) { + while ((mem = calloc(count, size)) == NULL) { + errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n", + msg, (unsigned long)count, (unsigned long)size); + errlogPrintf("Thread %s (%p) suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + errlogFlush(); + epicsThreadSuspendSelf(); + } + } + return mem; +} + +epicsShareFunc void * mallocMustSucceed(size_t size, const char *msg) +{ + void * mem = NULL; + if (size > 0) { + while ((mem = malloc(size)) == NULL) { + errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n", + msg, (unsigned long)size); + errlogPrintf("Thread %s (%p) suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + errlogFlush(); + epicsThreadSuspendSelf(); + } + } + return mem; +} + +epicsShareFunc void cantProceed(const char *msg, ...) +{ + va_list pvar; + va_start(pvar, msg); + if (msg) + errlogVprintf(msg, pvar); + va_end(pvar); + + errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + + epicsStackTrace(); + + errlogFlush(); + + epicsThreadSleep(1.0); + while (1) + epicsThreadSuspendSelf(); +} diff --git a/modules/libcom/src/misc/cantProceed.h b/modules/libcom/src/misc/cantProceed.h new file mode 100644 index 000000000..437f96802 --- /dev/null +++ b/modules/libcom/src/misc/cantProceed.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INCcantProceedh +#define INCcantProceedh + +#include + +#include "compilerDependencies.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void cantProceed(const char *errorMessage, ...) EPICS_PRINTF_STYLE(1,2); +epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *errorMessage); +epicsShareFunc void * mallocMustSucceed(size_t size, const char *errorMessage); + +#ifdef __cplusplus +} +#endif + +#endif /* cantProceedh */ diff --git a/modules/libcom/src/misc/dbDefs.h b/modules/libcom/src/misc/dbDefs.h new file mode 100644 index 000000000..e1de3f200 --- /dev/null +++ b/modules/libcom/src/misc/dbDefs.h @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INC_dbDefs_H +#define INC_dbDefs_H + +#include + +#ifdef TRUE +# undef TRUE +#endif +#define TRUE 1 + +#ifdef FALSE +# undef FALSE +#endif +#define FALSE 0 + +/* deprecated, use static */ +#ifndef LOCAL +# define LOCAL static +#endif + +/* number of elements in an array */ +#ifndef NELEMENTS +# define NELEMENTS(array) (sizeof (array) / sizeof ((array) [0])) +#endif + +/* byte offset of member in structure - deprecated, use offsetof */ +#ifndef OFFSET +# define OFFSET(structure, member) offsetof(structure, member) +#endif + +/* Subtract member byte offset, returning pointer to parent object */ +#ifndef CONTAINER +# ifdef __GNUC__ +# define CONTAINER(ptr, structure, member) ({ \ + const __typeof(((structure*)0)->member) *_ptr = (ptr); \ + (structure*)((char*)_ptr - offsetof(structure, member)); \ + }) +# else +# define CONTAINER(ptr, structure, member) \ + ((structure*)((char*)(ptr) - offsetof(structure, member))) +# endif +#endif + +/*Process Variable Name Size */ +/* PVNAME_STRINGSZ includes the nil terminator */ +#define PVNAME_STRINGSZ 61 +#define PVNAME_SZ (PVNAME_STRINGSZ - 1) + +/* Buffer size for the string representation of a DBF_*LINK field */ +#define PVLINK_STRINGSZ 1024 + +/* dbAccess enums/menus can have up to this many choices */ +#define DB_MAX_CHOICES 30 + +#endif /* INC_dbDefs_H */ diff --git a/modules/libcom/src/misc/epicsConvert.c b/modules/libcom/src/misc/epicsConvert.c new file mode 100644 index 000000000..9318ceb7e --- /dev/null +++ b/modules/libcom/src/misc/epicsConvert.c @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsConvert.c*/ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "epicsConvert.h" +#include "cantProceed.h" + +epicsShareFunc float epicsConvertDoubleToFloat(double value) +{ + double abs; + + if (value == 0 || !finite(value)) + return (float) value; + + abs = fabs(value); + + if (abs >= FLT_MAX) + return (value > 0) ? FLT_MAX : -FLT_MAX; + + if (abs <= FLT_MIN) + return (value > 0) ? FLT_MIN : -FLT_MIN; + + return (float) value; +} diff --git a/modules/libcom/src/misc/epicsConvert.h b/modules/libcom/src/misc/epicsConvert.h new file mode 100644 index 000000000..1c38bc30a --- /dev/null +++ b/modules/libcom/src/misc/epicsConvert.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsConvert.h*/ + +#ifndef INC_epicsConvert_H +#define INC_epicsConvert_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc float epicsConvertDoubleToFloat(double value); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsConvert_H */ diff --git a/modules/libcom/src/misc/epicsExit.c b/modules/libcom/src/misc/epicsExit.c new file mode 100644 index 000000000..266835eaa --- /dev/null +++ b/modules/libcom/src/misc/epicsExit.c @@ -0,0 +1,212 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsExit.c*/ +/* + * Author: Marty Kraimer + * Date: 23AUG2004 + * Thread exit revisions: Jeff Hill + * Date: 06Dec2006 + * + * Note that epicsExitCallAtThreadExits is currently called directly from the + * thread entry wrapper in OS dependent code. That approach might not work + * correctly if the thread exits indirectly instead of just returning from + * the function specified to epicsThreadCreate. For example the thread might + * exit via the exit() call. There might be OS dependent solutions for that + * weakness. + * + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "ellLib.h" +#include "errlog.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "cantProceed.h" +#include "epicsExit.h" + +void epicsMutexCleanup(void); + +typedef struct exitNode { + ELLNODE node; + epicsExitFunc func; + void *arg; + char name[1]; +}exitNode; + +typedef struct exitPvt { + ELLLIST list; +} exitPvt; + +int atExitDebug = 0; + +static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; +static epicsThreadOnceId exitLaterOnce = EPICS_THREAD_ONCE_INIT; +static exitPvt * pExitPvtPerProcess = 0; +static epicsMutexId exitPvtLock = 0; +static epicsThreadPrivateId exitPvtPerThread = 0; + +static int exitLaterStatus; + +static void destroyExitPvt(exitPvt * pep) +{ + ellFree ( &pep->list ); + free ( pep ); +} + +static exitPvt * createExitPvt(void) +{ + exitPvt * pep = calloc ( 1, sizeof ( * pep ) ); + if ( pep ) { + ellInit ( &pep->list ); + } + return pep; +} + +static void exitPvtOnceFunc(void *pParm) +{ + exitPvtPerThread = epicsThreadPrivateCreate (); + assert ( exitPvtPerThread ); + exitPvtLock = epicsMutexMustCreate (); +} + +static void epicsExitInit(void) +{ + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); +} + +static void epicsExitCallAtExitsPvt(exitPvt *pep) +{ + exitNode *pexitNode; + + while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { + if (atExitDebug && pexitNode->name[0]) + fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); + else if(atExitDebug) + fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg); + pexitNode->func ( pexitNode->arg ); + ellDelete ( & pep->list, & pexitNode->node ); + free ( pexitNode ); + } +} + +epicsShareFunc void epicsExitCallAtExits(void) +{ + exitPvt * pep = 0; + + epicsExitInit (); + epicsMutexMustLock ( exitPvtLock ); + if ( pExitPvtPerProcess ) { + pep = pExitPvtPerProcess; + pExitPvtPerProcess = 0; + } + epicsMutexUnlock ( exitPvtLock ); + if ( pep ) { + epicsExitCallAtExitsPvt ( pep ); + destroyExitPvt ( pep ); + } + /* Handle specially to avoid circular reference */ + epicsMutexCleanup(); +} + +epicsShareFunc void epicsExitCallAtThreadExits(void) +{ + exitPvt * pep; + + epicsExitInit (); + pep = epicsThreadPrivateGet ( exitPvtPerThread ); + if ( pep ) { + epicsExitCallAtExitsPvt ( pep ); + destroyExitPvt ( pep ); + epicsThreadPrivateSet ( exitPvtPerThread, 0 ); + } +} + +static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name) +{ + int status = -1; + exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); + + if ( pExitNode ) { + pExitNode->func = func; + pExitNode->arg = arg; + if(name) + strcpy(pExitNode->name, name); + ellAdd ( & pep->list, & pExitNode->node ); + status = 0; + } + return status; +} + +epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) +{ + exitPvt * pep; + + epicsExitInit (); + pep = epicsThreadPrivateGet ( exitPvtPerThread ); + if ( ! pep ) { + pep = createExitPvt (); + if ( ! pep ) { + return -1; + } + epicsThreadPrivateSet ( exitPvtPerThread, pep ); + } + return epicsAtExitPvt ( pep, func, arg, NULL ); +} + +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) +{ + int status = -1; + + epicsExitInit (); + epicsMutexMustLock ( exitPvtLock ); + if ( !pExitPvtPerProcess ) { + pExitPvtPerProcess = createExitPvt (); + } + if ( pExitPvtPerProcess ) { + status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); + } + epicsMutexUnlock ( exitPvtLock ); + return status; +} + +epicsShareFunc void epicsExit(int status) +{ + epicsExitCallAtExits(); + epicsThreadSleep(0.1); + exit(status); +} + +static void exitNow(void *junk) +{ + epicsExit(exitLaterStatus); +} + +static void exitLaterOnceFunc(void *raw) +{ + int *status = raw; + exitLaterStatus = *status; + epicsThreadMustCreate("exitLater", + epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + &exitNow, NULL); +} + +epicsShareFunc void epicsExitLater(int status) +{ + epicsThreadOnce(&exitLaterOnce, &exitLaterOnceFunc, &status); +} + +#include "epicsExport.h" + +epicsExportAddress(int,atExitDebug); diff --git a/modules/libcom/src/misc/epicsExit.h b/modules/libcom/src/misc/epicsExit.h new file mode 100644 index 000000000..3e961cbc1 --- /dev/null +++ b/modules/libcom/src/misc/epicsExit.h @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsExit.h*/ +#ifndef epicsExith +#define epicsExith +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*epicsExitFunc)(void *arg); + +epicsShareFunc void epicsExit(int status); +epicsShareFunc void epicsExitLater(int status); +epicsShareFunc void epicsExitCallAtExits(void); +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name); +#define epicsAtExit(F,A) epicsAtExit3(F,A,#F) + +epicsShareFunc void epicsExitCallAtThreadExits(void); +epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg); + + +#ifdef __cplusplus +} +#endif + +#endif /*epicsExith*/ diff --git a/modules/libcom/src/misc/epicsExport.h b/modules/libcom/src/misc/epicsExport.h new file mode 100644 index 000000000..dea8dd67a --- /dev/null +++ b/modules/libcom/src/misc/epicsExport.h @@ -0,0 +1,43 @@ +/*epicsExport.h */ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INCepicsExporth +#define INCepicsExporth + +#ifdef __cplusplus +extern "C" { +#endif + +#define epicsExportSharedSymbols +#include + +typedef void (*REGISTRAR)(void); + +#define EPICS_EXPORT_POBJ(typ,obj) pvar_ ## typ ## _ ## obj +#define EPICS_EXPORT_PFUNC(obj) pvar_func_ ## obj + +#define epicsExportAddress(typ,obj) \ +epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \ +epicsShareDef typ *EPICS_EXPORT_POBJ(typ,obj) = (typ *)(char *)&obj + +#define epicsExportRegistrar(func) \ +epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(func) = (REGISTRAR)(void*)&func + +#define epicsRegisterFunction(func) \ +static void register_func_ ## func(void) { \ + registryFunctionAdd(#func,(REGISTRYFUNCTION)func);} \ +epicsExportRegistrar(register_func_ ## func) + +#ifdef __cplusplus +} +#endif + +#endif /* epicsExporth */ diff --git a/modules/libcom/src/misc/epicsStdlib.c b/modules/libcom/src/misc/epicsStdlib.c new file mode 100644 index 000000000..f4348981b --- /dev/null +++ b/modules/libcom/src/misc/epicsStdlib.c @@ -0,0 +1,401 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonna LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Authors: Eric Norum & Andrew Johnson */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "epicsStdlib.h" +#include "epicsString.h" +#include "epicsConvert.h" + + +/* These are the conversion primitives */ + +epicsShareFunc int +epicsParseLong(const char *str, long *to, int base, char **units) +{ + int c; + char *endp; + long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtol(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +epicsShareFunc int +epicsParseULong(const char *str, unsigned long *to, int base, char **units) +{ + int c; + char *endp; + unsigned long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoul(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +epicsShareFunc int +epicsParseLLong(const char *str, long long *to, int base, char **units) +{ + int c; + char *endp; + long long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoll(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +epicsShareFunc int +epicsParseULLong(const char *str, unsigned long long *to, int base, char **units) +{ + int c; + char *endp; + unsigned long long value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtoull(str, &endp, base); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == EINVAL) /* Not universally supported */ + return S_stdlib_badBase; + if (errno == ERANGE) + return S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + +epicsShareFunc int +epicsParseDouble(const char *str, double *to, char **units) +{ + int c; + char *endp; + double value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = epicsStrtod(str, &endp); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == ERANGE) + return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} + + +/* These call the primitives */ + +epicsShareFunc int +epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + + if (value < -0x80 || value > 0x7f) + return S_stdlib_overflow; + + *to = (epicsInt8) value; + return 0; +} + +epicsShareFunc int +epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + + if (value > 0xff && value <= ~0xffUL) + return S_stdlib_overflow; + + *to = (epicsUInt8) value; + return 0; +} + +epicsShareFunc int +epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + + if (value < -0x8000 || value > 0x7fff) + return S_stdlib_overflow; + + *to = (epicsInt16) value; + return 0; +} + +epicsShareFunc int +epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + + if (value > 0xffff && value <= ~0xffffUL) + return S_stdlib_overflow; + + *to = (epicsUInt16) value; + return 0; +} + +epicsShareFunc int +epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units) +{ + long value; + int status = epicsParseLong(str, &value, base, units); + + if (status) + return status; + +#if (LONG_MAX > 0x7fffffffLL) + if (value < -0x80000000L || value > 0x7fffffffL) + return S_stdlib_overflow; +#endif + + *to = (epicsInt32) value; + return 0; +} + +epicsShareFunc int +epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units) +{ + unsigned long value; + int status = epicsParseULong(str, &value, base, units); + + if (status) + return status; + +#if (ULONG_MAX > 0xffffffffULL) + if (value > 0xffffffffUL && value <= ~0xffffffffUL) + return S_stdlib_overflow; +#endif + + *to = (epicsUInt32) value; + return 0; +} + +epicsShareFunc int +epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units) +{ +#if (LONG_MAX == 0x7fffffffffffffffLL) + long value; + int status = epicsParseLong(str, &value, base, units); +#else + long long value; + int status = epicsParseLLong(str, &value, base, units); +#endif + + if (status) + return status; + + *to = (epicsInt64) value; + return 0; +} + +epicsShareFunc int +epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units) +{ +#if (ULONG_MAX == 0xffffffffffffffffULL) + unsigned long value; + int status = epicsParseULong(str, &value, base, units); +#else + unsigned long long value; + int status = epicsParseULLong(str, &value, base, units); +#endif + + if (status) + return status; + + *to = (epicsUInt64) value; + return 0; +} + + +epicsShareFunc int +epicsParseFloat(const char *str, float *to, char **units) +{ + double value, abs; + int status = epicsParseDouble(str, &value, units); + + if (status) + return status; + + abs = fabs(value); + if (value > 0 && abs <= FLT_MIN) + return S_stdlib_underflow; + if (finite(value) && abs >= FLT_MAX) + return S_stdlib_overflow; + + *to = (float) value; + return 0; +} + + +/* If strtod() works properly, the OS-specific osdStrtod.h does: + * #define epicsStrtod strtod + * + * If strtod() is broken, osdStrtod.h defines this prototype: + * epicsShareFunc double epicsStrtod(const char *str, char **endp); + */ + +#ifndef epicsStrtod +epicsShareFunc double +epicsStrtod(const char *str, char **endp) +{ + const char *cp = str; + int negative = 0; + double res; + + while (isspace((int)*cp)) + cp++; + + if (*cp == '+') { + cp++; + } else if (*cp == '-') { + negative = 1; + cp++; + } + + if (epicsStrnCaseCmp("0x", cp, 2) == 0) { + if (negative) + return strtol(str, endp, 16); + else + return strtoul(str, endp, 16); + } + if (!isalpha((int)*cp)) { + res = strtod(str, endp); + if (isinf(res)) + errno = ERANGE; + return res; + } + + if (epicsStrnCaseCmp("NAN", cp, 3) == 0) { + res = epicsNAN; + cp += 3; + if (*cp == '(') { + cp++; + while (*cp && (*cp++ != ')')) + continue; + } + } + else if (epicsStrnCaseCmp("INF", cp, 3) == 0) { + res = negative ? -epicsINF : epicsINF; + cp += 3; + if (epicsStrnCaseCmp("INITY", cp, 5) == 0) { + cp += 5; + } + } else { + cp = str; + res = 0; + } + + if (endp) + *endp = (char *)cp; + + return res; +} +#endif diff --git a/modules/libcom/src/misc/epicsStdlib.h b/modules/libcom/src/misc/epicsStdlib.h new file mode 100644 index 000000000..20b7cd26f --- /dev/null +++ b/modules/libcom/src/misc/epicsStdlib.h @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsStdlib.h */ +/* Author: Eric Norum */ + +#ifndef INC_epicsStdlib_H +#define INC_epicsStdlib_H + +#include +#include + +#include "shareLib.h" +#include "osdStrtod.h" +#include "epicsTypes.h" +#include "errMdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */ +#define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */ +#define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */ +#define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */ +#define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */ + + +epicsShareFunc int + epicsParseLong(const char *str, long *to, int base, char **units); +epicsShareFunc int + epicsParseULong(const char *str, unsigned long *to, int base, char **units); +epicsShareFunc int + epicsParseLLong(const char *str, long long *to, int base, char **units); +epicsShareFunc int + epicsParseULLong(const char *str, unsigned long long *to, int base, char **units); +epicsShareFunc int + epicsParseDouble(const char *str, double *to, char **units); + +epicsShareFunc int + epicsParseFloat(const char *str, float *to, char **units); + +epicsShareFunc int + epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units); +epicsShareFunc int + epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units); +epicsShareFunc int + epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units); +epicsShareFunc int + epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units); + +epicsShareFunc int + epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units); +epicsShareFunc int + epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units); + +epicsShareFunc int + epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units); +epicsShareFunc int + epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units); + +#define epicsParseFloat32(str, to, units) epicsParseFloat(str, to, units) +#define epicsParseFloat64(str, to, units) epicsParseDouble(str, to, units) + +/* These macros return 1 if successful, 0 on failure. + * This is analagous to the return value from sscanf() + */ +#define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL)) +#define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL)) +#define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL)) +#define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL)) +#define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL)) +#define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL)) + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsStdlib_H */ diff --git a/modules/libcom/src/misc/epicsString.c b/modules/libcom/src/misc/epicsString.c new file mode 100644 index 000000000..e41e21b72 --- /dev/null +++ b/modules/libcom/src/misc/epicsString.c @@ -0,0 +1,380 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, + * Mark Rivers, Andrew Johnson, Ralph Lange + * + * Routines in this file should have corresponding test code in + * libCom/test/epicsStringTest.c + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "cantProceed.h" +#include "epicsString.h" + +/* Deprecated, use epicsStrnRawFromEscaped() instead */ +int dbTranslateEscape(char *dst, const char *src) +{ + size_t big_enough = strlen(src) + 1; + + return epicsStrnRawFromEscaped(dst, big_enough, src, big_enough); +} + +int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src, + size_t srclen) +{ + int rem = dstlen; + int ndst = 0; + + while (srclen--) { + int c = *src++; + #define OUT(chr) if (--rem > 0) ndst++, *dst++ = chr + + if (!c) break; + + input: + if (c != '\\') { + OUT(c); + continue; + } + + if (!srclen-- || !(c = *src++)) break; + + switch (c) { + case 'a': OUT('\a'); break; + case 'b': OUT('\b'); break; + case 'f': OUT('\f'); break; + case 'n': OUT('\n'); break; + case 'r': OUT('\r'); break; + case 't': OUT('\t'); break; + case 'v': OUT('\v'); break; + case '\\': OUT('\\'); break; + case '\'': OUT('\''); break; + case '\"': OUT('\"'); break; + + case '0' :case '1' :case '2' :case '3' : + case '4' :case '5' :case '6' :case '7' : + { /* \ooo */ + unsigned int u = c - '0'; + + if (!srclen-- || !(c = *src++)) { + OUT(u); goto done; + } + if (c < '0' || c > '7') { + OUT(u); goto input; + } + u = u << 3 | (c - '0'); + + if (!srclen-- || !(c = *src++)) { + OUT(u); goto done; + } + if (c < '0' || c > '7') { + OUT(u); goto input; + } + u = u << 3 | (c - '0'); + + if (u > 0377) { + /* Undefined behaviour! */ + } + OUT(u); + } + break; + + case 'x' : + { /* \xXXX... */ + unsigned int u = 0; + + if (!srclen-- || !(c = *src++ & 0xff)) + goto done; + + while (isxdigit(c)) { + u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0'); + if (u > 0xff) { + /* Undefined behaviour! */ + } + if (!srclen-- || !(c = *src++ & 0xff)) { + OUT(u); + goto done; + } + } + OUT(u); + goto input; + } + + default: + OUT(c); + } + #undef OUT + } +done: + if (dstlen) + *dst = '\0'; + return ndst; +} + +int epicsStrnEscapedFromRaw(char *dst, size_t dstlen, const char *src, + size_t srclen) +{ + int rem = dstlen; + int ndst = 0; + + if (dst == src) + return -1; + + while (srclen--) { + int c = *src++; + #define OUT(chr) ndst++; if (--rem > 0) *dst++ = chr + + switch (c) { + case '\a': OUT('\\'); OUT('a'); break; + case '\b': OUT('\\'); OUT('b'); break; + case '\f': OUT('\\'); OUT('f'); break; + case '\n': OUT('\\'); OUT('n'); break; + case '\r': OUT('\\'); OUT('r'); break; + case '\t': OUT('\\'); OUT('t'); break; + case '\v': OUT('\\'); OUT('v'); break; + case '\\': OUT('\\'); OUT('\\'); break; + case '\'': OUT('\\'); OUT('\''); break; + case '\"': OUT('\\'); OUT('\"'); break; + default: + if (isprint(c & 0xff)) { + OUT(c); + break; + } + OUT('\\'); + OUT('0' + ((c & 0300) >> 6)); + OUT('0' + ((c & 0070) >> 3)); + OUT('0' + (c & 0007)); + } + #undef OUT + } + if (dstlen) + *dst = '\0'; + return ndst; +} + +size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen) +{ + size_t ndst = srclen; + + while (srclen--) { + int c = *src++; + + switch (c) { + case '\a': case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': case '\\': + case '\'': case '\"': + ndst++; + break; + default: + if (!isprint(c & 0xff)) + ndst += 3; + } + } + return ndst; +} + +int epicsStrCaseCmp(const char *s1, const char *s2) +{ + while (1) { + int ch1 = toupper((int) *s1); + int ch2 = toupper((int) *s2); + + if (ch2 == 0) return (ch1 != 0); + if (ch1 == 0) return -1; + if (ch1 < ch2) return -1; + if (ch1 > ch2) return 1; + s1++; + s2++; + } +} + +int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len) +{ + size_t i = 0; + + while (i++ < len) { + int ch1 = toupper((int) *s1); + int ch2 = toupper((int) *s2); + + if (ch2 == 0) return (ch1 != 0); + if (ch1 == 0) return -1; + if (ch1 < ch2) return -1; + if (ch1 > ch2) return 1; + s1++; + s2++; + } + return 0; +} + +char * epicsStrnDup(const char *s, size_t len) +{ + char *buf = mallocMustSucceed(len + 1, "epicsStrnDup"); + + strncpy(buf, s, len); + buf[len] = '\0'; + return buf; +} + +char * epicsStrDup(const char *s) +{ + return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s); +} + +int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) +{ + int nout = 0; + + while (len--) { + char c = *s++; + + switch (c) { + case '\a': nout += fprintf(fp, "\\a"); break; + case '\b': nout += fprintf(fp, "\\b"); break; + case '\f': nout += fprintf(fp, "\\f"); break; + case '\n': nout += fprintf(fp, "\\n"); break; + case '\r': nout += fprintf(fp, "\\r"); break; + case '\t': nout += fprintf(fp, "\\t"); break; + case '\v': nout += fprintf(fp, "\\v"); break; + case '\\': nout += fprintf(fp, "\\\\"); break; + case '\'': nout += fprintf(fp, "\\'"); break; + case '\"': nout += fprintf(fp, "\\\""); break; + default: + if (isprint(0xff & (int)c)) + nout += fprintf(fp, "%c", c); + else + nout += fprintf(fp, "\\%03o", (unsigned char)c); + break; + } + } + return nout; +} + +/* Until Base requires POSIX 2008 we must provide our own implementation */ +size_t epicsStrnLen(const char *s, size_t maxlen) +{ + size_t i; + + for (i=0; i> 5)); + if (!(c = *str++)) break; + hash ^= (hash << 7) ^ c ^ (hash >> 3); + } + return hash; +} + +unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed) +{ + unsigned int hash = seed; + + while (length--) { + hash ^= ~((hash << 11) ^ *str++ ^ (hash >> 5)); + if (!length--) break; + hash ^= (hash << 7) ^ *str++ ^ (hash >> 3); + } + return hash; +} diff --git a/modules/libcom/src/misc/epicsString.h b/modules/libcom/src/misc/epicsString.h new file mode 100644 index 000000000..093c73df4 --- /dev/null +++ b/modules/libcom/src/misc/epicsString.h @@ -0,0 +1,51 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, + * Mark Rivers, Andrew Johnson, Ralph Lange + */ + +#ifndef INC_epicsString_H +#define INC_epicsString_H + +#include +#include "epicsTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsStrnRawFromEscaped(char *outbuf, size_t outsize, + const char *inbuf, size_t inlen); +epicsShareFunc int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, + const char *inbuf, size_t inlen); +epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len); +epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2); +epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len); +epicsShareFunc char * epicsStrDup(const char *s); +epicsShareFunc char * epicsStrnDup(const char *s, size_t len); +epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n); +#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw +epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen); +epicsShareFunc int epicsStrGlobMatch(const char *str, const char *pattern); +epicsShareFunc char * epicsStrtok_r(char *s, const char *delim, char **lasts); +epicsShareFunc unsigned int epicsStrHash(const char *str, unsigned int seed); +epicsShareFunc unsigned int epicsMemHash(const char *str, size_t length, + unsigned int seed); + +/* dbTranslateEscape is deprecated, use epicsStrnRawFromEscaped instead */ +epicsShareFunc int dbTranslateEscape(char *s, const char *ct); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsString_H */ diff --git a/modules/libcom/src/misc/epicsTypes.h b/modules/libcom/src/misc/epicsTypes.h new file mode 100644 index 000000000..efc6a91cb --- /dev/null +++ b/modules/libcom/src/misc/epicsTypes.h @@ -0,0 +1,231 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + * Date: 5-95 + */ + +#ifndef INC_epicsTypes_H +#define INC_epicsTypes_H + +#include "shareLib.h" +#include "compilerDependencies.h" + +#ifndef stringOf +# if defined (__STDC__ ) || defined (__cplusplus) +# define stringOf(TOKEN) #TOKEN +# else +# define stringOf(TOKEN) "TOKEN" +# endif +#endif + +typedef enum { + epicsFalse = 0, + epicsTrue = 1 +} epicsBoolean EPICS_DEPRECATED; + +/* + * Architecture Independent Data Types + * These are sufficient for all our current archs + */ +typedef char epicsInt8; +typedef unsigned char epicsUInt8; +typedef short epicsInt16; +typedef unsigned short epicsUInt16; +typedef int epicsInt32; +typedef unsigned int epicsUInt32; +typedef long long epicsInt64; +typedef unsigned long long epicsUInt64; + +typedef epicsUInt16 epicsEnum16; +typedef float epicsFloat32; +typedef double epicsFloat64; +typedef epicsInt32 epicsStatus; + + +typedef struct { + unsigned length; + char *pString; +} epicsString; + +/* + * !! Dont use this - it may vanish in the future !! + * + * Provided only for backwards compatibility with + * db_access.h + * + */ +#define MAX_STRING_SIZE 40 +typedef char epicsOldString[MAX_STRING_SIZE]; + +/* + * union of all types + * + * Strings included here as pointers only so that we support + * large string types. + * + * Arrays included here as pointers because large arrays will + * not fit in this union. + */ +typedef union epics_any { + epicsInt8 int8; + epicsUInt8 uInt8; + epicsInt16 int16; + epicsUInt16 uInt16; + epicsEnum16 enum16; + epicsInt32 int32; + epicsUInt32 uInt32; + epicsInt64 int64; + epicsUInt64 uInt64; + epicsFloat32 float32; + epicsFloat64 float64; + epicsString string; +} epicsAny; + +/* + * Corresponding Type Codes + * (this enum must start at zero) + * + * !! Update epicsTypeToDBR_XXXX[] and DBR_XXXXToEpicsType + * in db_access.h if you edit this enum !! + */ +typedef enum { + epicsInt8T, + epicsUInt8T, + epicsInt16T, + epicsUInt16T, + epicsEnum16T, + epicsInt32T, + epicsUInt32T, + epicsFloat32T, + epicsFloat64T, + epicsStringT, + epicsOldStringT +} epicsType; +#define firstEpicsType epicsInt8T +#define lastEpicsType epicsOldStringT +#define validEpicsType(x) ((x>=firstEpicsType) && (x<=lastEpicsType)) +#define invalidEpicsType(x) ((xlastEpicsType)) + + +/* + * The enumeration "epicsType" is an index to this array + * of type name strings. + */ +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeNames [lastEpicsType+1] = { + "epicsInt8", + "epicsUInt8", + "epicsInt16", + "epicsUInt16", + "epicsEnum16", + "epicsInt32", + "epicsUInt32", + "epicsFloat32", + "epicsFloat64", + "epicsString", + "epicsOldString", +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeNames [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +/* + * The enumeration "epicsType" is an index to this array + * of type code name strings. + */ +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeCodeNames [lastEpicsType+1] = { + "epicsInt8T", + "epicsUInt8T", + "epicsInt16T", + "epicsUInt16T", + "epicsEnum16T", + "epicsInt32T", + "epicsUInt32T", + "epicsFloat32T", + "epicsFloat64T", + "epicsStringT", + "epicsOldStringT", +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeCodeNames [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +#ifdef epicsTypesGLOBAL +epicsShareDef const unsigned epicsTypeSizes [lastEpicsType+1] = { + sizeof (epicsInt8), + sizeof (epicsUInt8), + sizeof (epicsInt16), + sizeof (epicsUInt16), + sizeof (epicsEnum16), + sizeof (epicsInt32), + sizeof (epicsUInt32), + sizeof (epicsFloat32), + sizeof (epicsFloat64), + sizeof (epicsString), + sizeof (epicsOldString), +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const unsigned epicsTypeSizes [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +/* + * The enumeration "epicsType" is an index to this array + * of type class identifiers. + */ +typedef enum { + epicsIntC, + epicsUIntC, + epicsEnumC, + epicsFloatC, + epicsStringC, + epicsOldStringC +} epicsTypeClass; + +#ifdef epicsTypesGLOBAL +epicsShareDef const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = { + epicsIntC, + epicsUIntC, + epicsIntC, + epicsUIntC, + epicsEnumC, + epicsIntC, + epicsUIntC, + epicsFloatC, + epicsFloatC, + epicsStringC, + epicsOldStringC +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const epicsTypeClass epicsTypeClasses [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + + +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeAnyFieldName [lastEpicsType+1] = { + "int8", + "uInt8", + "int16", + "uInt16", + "enum16", + "int32", + "uInt32", + "float32", + "float64", + "string", + "", /* Old Style Strings will not be in epicsAny type */ +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeAnyFieldName [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +#endif /* INC_epicsTypes_H */ + diff --git a/modules/libcom/src/misc/epicsUnitTest.c b/modules/libcom/src/misc/epicsUnitTest.c new file mode 100644 index 000000000..4ac18ea1c --- /dev/null +++ b/modules/libcom/src/misc/epicsUnitTest.c @@ -0,0 +1,264 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Andrew Johnson + * + * Unit test module which generates output in the Test Anything Protocol + * format. See perldoc Test::Harness for details of output format. + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsUnitTest.h" +#include "epicsExit.h" +#include "epicsTime.h" +#include "ellLib.h" +#include "errlog.h" +#include "cantProceed.h" + +typedef struct { + ELLNODE node; + const char *name; + int tests; + int failures; + int skips; +} testFailure; + +static epicsMutexId testLock = 0; +static int perlHarness; +static int planned; +static int tested; +static int passed; +static int failed; +static int skipped; +static int bonus; +static const char *todo; + +epicsTimeStamp started; +static int Harness; +static int Programs; +static int Tests; + +ELLLIST faults; +const char *testing = NULL; + +static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; + +static void testOnce(void *dummy) { + testLock = epicsMutexMustCreate(); + perlHarness = (getenv("HARNESS_ACTIVE") != NULL); +} + +void testPlan(int plan) { + epicsThreadOnce(&onceFlag, testOnce, NULL); + epicsMutexMustLock(testLock); + planned = plan; + tested = passed = failed = skipped = bonus = 0; + todo = NULL; + if (plan) printf("1..%d\n", plan); + epicsMutexUnlock(testLock); +} + +int testOkV(int pass, const char *fmt, va_list pvar) { + const char *result = "not ok"; + epicsMutexMustLock(testLock); + tested++; + if (pass) { + result += 4; /* skip "not " */ + passed++; + if (todo) + bonus++; + } else { + if (todo) + passed++; + else + failed++; + } + printf("%s %2d - ", result, tested); + vprintf(fmt, pvar); + if (todo) + printf(" # TODO %s", todo); + putchar('\n'); + fflush(stdout); + epicsMutexUnlock(testLock); + return pass; +} + +int testOk(int pass, const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(pass, fmt, pvar); + va_end(pvar); + return pass; +} + +void testPass(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(1, fmt, pvar); + va_end(pvar); +} + +void testFail(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(0, fmt, pvar); + va_end(pvar); +} + +void testSkip(int skip, const char *why) { + epicsMutexMustLock(testLock); + while (skip-- > 0) { + tested++; + passed++; + skipped++; + printf("ok %2d # SKIP %s\n", tested, why); + } + fflush(stdout); + epicsMutexUnlock(testLock); +} + +void testTodoBegin(const char *why) { + epicsMutexMustLock(testLock); + todo = why; + epicsMutexUnlock(testLock); +} + +void testTodoEnd(void) { + todo = NULL; +} + +int testDiag(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + epicsMutexMustLock(testLock); + printf("# "); + vprintf(fmt, pvar); + putchar('\n'); + fflush(stdout); + epicsMutexUnlock(testLock); + va_end(pvar); + return 0; +} + +void testAbort(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + printf("Bail out! "); + vprintf(fmt, pvar); + putchar('\n'); + fflush(stdout); + va_end(pvar); + abort(); +} + +static void testResult(const char *result, int count) { + printf("%12.12s: %3d = %5.2f%%\n", result, count, 100.0 * count / tested); +} + +int testDone(void) { + int status = 0; + + epicsMutexMustLock(testLock); + if (perlHarness) { + if (!planned) printf("1..%d\n", tested); + } else { + if (planned && tested > planned) { + printf("\nRan %d tests but only planned for %d!\n", tested, planned); + status = 2; + } else if (planned && tested < planned) { + printf("\nPlanned %d tests but only ran %d\n", planned, tested); + status = 2; + } + printf("\n Results\n =======\n Tests: %-3d\n", tested); + if (tested) { + testResult("Passed", passed); + if (bonus) testResult("Todo Passes", bonus); + if (failed) { + testResult("Failed", failed); + status = 1; + } + if (skipped) testResult("Skipped", skipped); + } + } + if (Harness) { + if (failed) { + testFailure *fault = callocMustSucceed(1, sizeof(testFailure), + "testDone calloc"); + fault->name = testing; + fault->tests = tested; + fault->failures = failed; + fault->skips = skipped; + ellAdd(&faults, &fault->node); + } + Programs++; + Tests += tested; + } + epicsMutexUnlock(testLock); + return (status); +} + + +/* Our test harness, for RTEMS and vxWorks */ + +void testHarnessExit(void *dummy) { + epicsTimeStamp ended; + int Faulty; + + if (!Harness) return; + + epicsTimeGetCurrent(&ended); + + printf("\n\n EPICS Test Harness Results" + "\n ==========================\n\n"); + + Faulty = ellCount(&faults); + if (!Faulty) + printf("All tests successful.\n"); + else { + int Failures = 0; + testFailure *f; + + printf("Failing Program Tests Faults\n" + "---------------------------------------\n"); + while ((f = (testFailure *)ellGet(&faults))) { + Failures += f->failures; + printf("%-25s %5d %5d\n", f->name, f->tests, f->failures); + if (f->skips) + printf("%d subtests skipped\n", f->skips); + free(f); + } + printf("\nFailed %d/%d test programs. %d/%d subtests failed.\n", + Faulty, Programs, Failures, Tests); + } + + printf("Programs=%d, Tests=%d, %.0f wallclock secs\n\n", + Programs, Tests, epicsTimeDiffInSeconds(&ended, &started)); +} + +void testHarness(void) { + epicsThreadOnce(&onceFlag, testOnce, NULL); + epicsAtExit(testHarnessExit, NULL); + Harness = 1; + Programs = 0; + Tests = 0; + ellInit(&faults); + epicsTimeGetCurrent(&started); +} + +void runTestFunc(const char *name, TESTFUNC func) { + printf("\n***** %s *****\n", name); + testing = name; + func(); /* May not return */ + epicsThreadSleep(1.0); +} diff --git a/modules/libcom/src/misc/epicsUnitTest.h b/modules/libcom/src/misc/epicsUnitTest.h new file mode 100644 index 000000000..9a119ad22 --- /dev/null +++ b/modules/libcom/src/misc/epicsUnitTest.h @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Andrew Johnson + */ + +#ifndef INC_epicsUnitTest_H +#define INC_epicsUnitTest_H + +#include + +#include "compilerDependencies.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void testPlan(int tests); +epicsShareFunc int testOkV(int pass, const char *fmt, va_list pvar); +epicsShareFunc int testOk(int pass, const char *fmt, ...) + EPICS_PRINTF_STYLE(2, 3); +epicsShareFunc void testPass(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testFail(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testSkip(int skip, const char *why); +epicsShareFunc void testTodoBegin(const char *why); +epicsShareFunc void testTodoEnd(void); +epicsShareFunc int testDiag(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testAbort(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc int testDone(void); + +#define testOk1(cond) testOk(cond, "%s", #cond) + + +typedef int (*TESTFUNC)(void); +epicsShareFunc void testHarness(void); +epicsShareFunc void testHarnessExit(void *dummy); +epicsShareFunc void runTestFunc(const char *name, TESTFUNC func); + +#define runTest(func) runTestFunc(#func, func) +#define testHarnessDone() testHarnessExit(0) + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsUnitTest_H */ diff --git a/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp new file mode 100644 index 000000000..4301612c9 --- /dev/null +++ b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.cpp @@ -0,0 +1,473 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include +#include +#include +#include + +//#define EPICS_FREELIST_DEBUG +#define EPICS_PRIVATE_API + +#define epicsExportSharedSymbols +#include "ipAddrToAsciiAsynchronous.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsGuard.h" +#include "epicsExit.h" +#include "tsDLList.h" +#include "tsFreeList.h" +#include "errlog.h" + +// - this class implements the asynchronous DNS query +// - it completes early with the host name in dotted IP address form +// if the ipAddrToAsciiEngine is destroyed before IO completion +// or if there are too many items already in the engine's queue. +class ipAddrToAsciiTransactionPrivate : + public ipAddrToAsciiTransaction, + public tsDLNode < ipAddrToAsciiTransactionPrivate > { +public: + ipAddrToAsciiTransactionPrivate ( class ipAddrToAsciiEnginePrivate & engineIn ); + virtual ~ipAddrToAsciiTransactionPrivate (); + osiSockAddr address () const; + void show ( unsigned level ) const; + void * operator new ( size_t size, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & )) + osiSockAddr addr; + ipAddrToAsciiEnginePrivate & engine; + ipAddrToAsciiCallBack * pCB; + bool pending; + void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ); + void release (); + void operator delete ( void * ); +private: + ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & ); + ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & ); +}; + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +extern "C" { +static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ); +} + +namespace { +struct ipAddrToAsciiGlobal : public epicsThreadRunable { + ipAddrToAsciiGlobal(); + virtual ~ipAddrToAsciiGlobal() {} + + virtual void run (); + + char nameTmp [1024]; + tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > + transactionFreeList; + tsDLList < ipAddrToAsciiTransactionPrivate > labor; + mutable epicsMutex mutex; + epicsEvent laborEvent; + epicsEvent destructorBlockEvent; + epicsThread thread; + // pCurrent may be changed by any thread (worker or other) + ipAddrToAsciiTransactionPrivate * pCurrent; + // pActive may only be changed by the worker + ipAddrToAsciiTransactionPrivate * pActive; + unsigned cancelPendingCount; + bool exitFlag; + bool callbackInProgress; +}; +} + +// - this class executes the synchronous DNS query +// - it creates one thread +class ipAddrToAsciiEnginePrivate : + public ipAddrToAsciiEngine { +public: + ipAddrToAsciiEnginePrivate() :refcount(1u), released(false) {} + virtual ~ipAddrToAsciiEnginePrivate () {} + void show ( unsigned level ) const; + + unsigned refcount; + bool released; + + static ipAddrToAsciiGlobal * pEngine; + ipAddrToAsciiTransaction & createTransaction (); + void release (); + +private: + ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & ); + ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & ); +}; + +ipAddrToAsciiGlobal * ipAddrToAsciiEnginePrivate :: pEngine = 0; +static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT; + +// the users are not required to supply a show routine +// for there transaction callback class +void ipAddrToAsciiCallBack::show ( unsigned /* level */ ) const {} + +// some noop pure virtual destructures +ipAddrToAsciiCallBack::~ipAddrToAsciiCallBack () {} +ipAddrToAsciiTransaction::~ipAddrToAsciiTransaction () {} +ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {} + +static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ) +{ + try { + ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiGlobal (); + } catch (std::exception& e) { + errlogPrintf("ipAddrToAsciiEnginePrivate ctor fails with: %s\n", e.what()); + } +} + +void ipAddrToAsciiEngine::cleanup() +{ + { + epicsGuard G(ipAddrToAsciiEnginePrivate::pEngine->mutex); + ipAddrToAsciiEnginePrivate::pEngine->exitFlag = true; + } + ipAddrToAsciiEnginePrivate::pEngine->laborEvent.signal(); + ipAddrToAsciiEnginePrivate::pEngine->thread.exitWait(); + delete ipAddrToAsciiEnginePrivate::pEngine; + ipAddrToAsciiEnginePrivate::pEngine = 0; +} + +// for now its probably sufficent to allocate one +// DNS transaction thread for all codes sharing +// the same process that need DNS services but we +// leave our options open for the future +ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate () +{ + epicsThreadOnce ( + & ipAddrToAsciiEngineGlobalMutexOnceFlag, + ipAddrToAsciiEngineGlobalMutexConstruct, 0 ); + if(!ipAddrToAsciiEnginePrivate::pEngine) + throw std::runtime_error("ipAddrToAsciiEngine::allocate fails"); + return * new ipAddrToAsciiEnginePrivate(); +} + +ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () : + thread ( *this, "ipToAsciiProxy", + epicsThreadGetStackSize(epicsThreadStackBig), + epicsThreadPriorityLow ), + pCurrent ( 0 ), pActive ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ), + callbackInProgress ( false ) +{ + this->thread.start (); // start the thread +} + + +void ipAddrToAsciiEnginePrivate::release () +{ + bool last; + { + epicsGuard < epicsMutex > guard ( this->pEngine->mutex ); + if(released) + throw std::logic_error("Engine release() called again!"); + + // released==true prevents new transactions + released = true; + + { + // cancel any pending transactions + tsDLIter < ipAddrToAsciiTransactionPrivate > it(pEngine->labor.firstIter()); + while(it.valid()) { + ipAddrToAsciiTransactionPrivate *trn = it.pointer(); + ++it; + + if(this==&trn->engine) { + trn->pending = false; + pEngine->labor.remove(*trn); + } + } + + // cancel transaction in lookup or callback + if (pEngine->pCurrent && this==&pEngine->pCurrent->engine) { + pEngine->pCurrent->pending = false; + pEngine->pCurrent = 0; + } + + // wait for completion of in-progress callback + pEngine->cancelPendingCount++; + while(pEngine->pActive && this==&pEngine->pActive->engine + && ! pEngine->thread.isCurrentThread()) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + pEngine->destructorBlockEvent.wait(); + } + pEngine->cancelPendingCount--; + if(pEngine->cancelPendingCount) + pEngine->destructorBlockEvent.signal(); + } + + assert(refcount>0); + last = 0==--refcount; + } + if(last) { + delete this; + } +} + +void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->pEngine->mutex ); + printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n", + static_cast (this), this->pEngine->labor.count () ); + if ( level > 0u ) { + tsDLIter < ipAddrToAsciiTransactionPrivate > + pItem = this->pEngine->labor.firstIter (); + while ( pItem.valid () ) { + pItem->show ( level - 1u ); + pItem++; + } + } + if ( level > 1u ) { + printf ( "mutex:\n" ); + this->pEngine->mutex.show ( level - 2u ); + printf ( "laborEvent:\n" ); + this->pEngine->laborEvent.show ( level - 2u ); + printf ( "exitFlag boolean = %u\n", this->pEngine->exitFlag ); + printf ( "exit event:\n" ); + } +} + +inline void * ipAddrToAsciiTransactionPrivate::operator new ( size_t size, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void ipAddrToAsciiTransactionPrivate::operator delete ( void * pTrans, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) +{ + freeList.release ( pTrans ); +} +#endif + +void ipAddrToAsciiTransactionPrivate::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + +ipAddrToAsciiTransaction & ipAddrToAsciiEnginePrivate::createTransaction () +{ + epicsGuard G(this->pEngine->mutex); + if(this->released) + throw std::logic_error("createTransaction() on release()'d ipAddrToAsciiEngine"); + + assert(this->refcount>0); + + ipAddrToAsciiTransactionPrivate *ret = new ( this->pEngine->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this ); + + this->refcount++; + + return * ret; +} + +void ipAddrToAsciiGlobal::run () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( ! this->exitFlag ) { + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->laborEvent.wait (); + } + while ( true ) { + ipAddrToAsciiTransactionPrivate * pItem = this->labor.get (); + if ( ! pItem ) { + break; + } + osiSockAddr addr = pItem->addr; + this->pCurrent = pItem; + + if ( this->exitFlag ) + { + sockAddrToDottedIP ( & addr.sa, this->nameTmp, + sizeof ( this->nameTmp ) ); + } + else { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // depending on DNS configuration, this could take a very long time + // so we release the lock + sockAddrToA ( &addr.sa, this->nameTmp, sizeof ( this->nameTmp ) ); + } + + // the ipAddrToAsciiTransactionPrivate destructor is allowed to + // set pCurrent to nill and avoid blocking on a slow DNS + // operation + if ( ! this->pCurrent ) { + continue; + } + + // fix for lp:1580623 + // a destructing cac sets pCurrent to NULL, so + // make local copy to avoid race when releasing the guard + ipAddrToAsciiTransactionPrivate *pCur = pActive = pCurrent; + this->callbackInProgress = true; + + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // dont call callback with lock applied + pCur->pCB->transactionComplete ( this->nameTmp ); + } + + this->callbackInProgress = false; + pActive = 0; + + if ( this->pCurrent ) { + this->pCurrent->pending = false; + this->pCurrent = 0; + } + if ( this->cancelPendingCount ) { + this->destructorBlockEvent.signal (); + } + } + } +} + +ipAddrToAsciiTransactionPrivate::ipAddrToAsciiTransactionPrivate + ( ipAddrToAsciiEnginePrivate & engineIn ) : + engine ( engineIn ), pCB ( 0 ), pending ( false ) +{ + memset ( & this->addr, '\0', sizeof ( this->addr ) ); + this->addr.sa.sa_family = AF_UNSPEC; +} + +void ipAddrToAsciiTransactionPrivate::release () +{ + this->~ipAddrToAsciiTransactionPrivate (); + this->engine.pEngine->transactionFreeList.release ( this ); +} + +ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate () +{ + ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine; + bool last; + { + epicsGuard < epicsMutex > guard ( pGlobal->mutex ); + while ( this->pending ) { + if ( pGlobal->pCurrent == this && + pGlobal->callbackInProgress && + ! pGlobal->thread.isCurrentThread() ) { + // cancel from another thread while callback in progress + // waits for callback to complete + assert ( pGlobal->cancelPendingCount < UINT_MAX ); + pGlobal->cancelPendingCount++; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + pGlobal->destructorBlockEvent.wait (); + } + assert ( pGlobal->cancelPendingCount > 0u ); + pGlobal->cancelPendingCount--; + if ( ! this->pending ) { + if ( pGlobal->cancelPendingCount ) { + pGlobal->destructorBlockEvent.signal (); + } + break; + } + } + else { + if ( pGlobal->pCurrent == this ) { + // cancel from callback, or while lookup in progress + pGlobal->pCurrent = 0; + } + else { + // cancel before lookup starts + pGlobal->labor.remove ( *this ); + } + this->pending = false; + } + } + assert(this->engine.refcount>0); + last = 0==--this->engine.refcount; + } + if(last) { + delete &this->engine; + } +} + +void ipAddrToAsciiTransactionPrivate::ipAddrToAscii ( + const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn ) +{ + bool success; + ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine; + + { + epicsGuard < epicsMutex > guard ( pGlobal->mutex ); + + if (this->engine.released) { + errlogPrintf("Warning: ipAddrToAscii on transaction with release()'d ipAddrToAsciiEngine"); + success = false; + + } else if ( !this->pending && pGlobal->labor.count () < 16u ) { + // put some reasonable limit on queue expansion + this->addr = addrIn; + this->pCB = & cbIn; + this->pending = true; + pGlobal->labor.add ( *this ); + success = true; + } + else { + success = false; + } + } + + if ( success ) { + pGlobal->laborEvent.signal (); + } + else { + char autoNameTmp[256]; + sockAddrToDottedIP ( & addrIn.sa, autoNameTmp, + sizeof ( autoNameTmp ) ); + cbIn.transactionComplete ( autoNameTmp ); + } +} + +osiSockAddr ipAddrToAsciiTransactionPrivate::address () const +{ + return this->addr; +} + +void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->engine.pEngine->mutex ); + char ipAddr [64]; + sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) ); + printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr ); + if ( level > 0u ) { + printf ( "\tengine %p\n", + static_cast ( & this->engine ) ); + this->pCB->show ( level - 1u ); + } +} diff --git a/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.h b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.h new file mode 100644 index 000000000..9aefca44e --- /dev/null +++ b/modules/libcom/src/misc/ipAddrToAsciiAsynchronous.h @@ -0,0 +1,53 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef ipAddrToAsciiAsynchronous_h +#define ipAddrToAsciiAsynchronous_h + +#include "osiSock.h" +#include "shareLib.h" + +class epicsShareClass ipAddrToAsciiCallBack { +public: + virtual void transactionComplete ( const char * pHostName ) = 0; + virtual void show ( unsigned level ) const; + virtual ~ipAddrToAsciiCallBack () = 0; +}; + +class epicsShareClass ipAddrToAsciiTransaction { +public: + virtual void release () = 0; + virtual void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ) = 0; + virtual osiSockAddr address () const = 0; + virtual void show ( unsigned level ) const = 0; +protected: + virtual ~ipAddrToAsciiTransaction () = 0; +}; + +class epicsShareClass ipAddrToAsciiEngine { +public: + virtual void release () = 0; + virtual ipAddrToAsciiTransaction & createTransaction () = 0; + virtual void show ( unsigned level ) const = 0; + static ipAddrToAsciiEngine & allocate (); +protected: + virtual ~ipAddrToAsciiEngine () = 0; +public: +#ifdef EPICS_PRIVATE_API + static void cleanup(); +#endif +}; + +#endif // ifdef ipAddrToAsciiAsynchronous_h diff --git a/modules/libcom/src/misc/locationException.h b/modules/libcom/src/misc/locationException.h new file mode 100644 index 000000000..30b187208 --- /dev/null +++ b/modules/libcom/src/misc/locationException.h @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Author: Jeff Hill +// + +#ifndef locationException_h +#define locationException_h + +#include + +#include "cantProceed.h" +#include "errlog.h" + +template +class sourceFileLocation : public T { +public: + sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber); + sourceFileLocation ( const sourceFileLocation & ); + sourceFileLocation & operator = ( const sourceFileLocation & ); + const char *fileName () const; + unsigned lineNumber () const; +private: + const char *pFileName; + unsigned lineNumberCopy; +}; + +template +inline sourceFileLocation::sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber) : + T ( parm ), pFileName ( fileName ) , lineNumberCopy ( lineNumber ) {} + +template +inline sourceFileLocation::sourceFileLocation ( const sourceFileLocation &in ) : + T ( in ), pFileName ( in.pFileName ), lineNumberCopy ( in.lineNumberCopy ) +{ +} + +template < class T > +inline sourceFileLocation & sourceFileLocation::operator = ( const sourceFileLocation &in ) +{ + this->pFileName = in.pFileName; + this->lineNumberCopy = in.lineNumberCopy; + return *this; +} + +template < class T > +inline unsigned sourceFileLocation::lineNumber () const +{ + return this->lineNumberCopy; +} + +template +inline const char * sourceFileLocation::fileName () const +{ + return this->pFileName; +} + +#define throwWithLocation(parm) throwExceptionWithLocation (parm, __FILE__, __LINE__); + +template +inline void throwExceptionWithLocation (const T &parm, const char *pFileName, unsigned lineNo) +{ + throw sourceFileLocation (parm, pFileName, lineNo); +} + +#endif // ifdef locationException_h + diff --git a/modules/libcom/src/misc/makeEpicsVersion.pl b/modules/libcom/src/misc/makeEpicsVersion.pl new file mode 100644 index 000000000..42fe37449 --- /dev/null +++ b/modules/libcom/src/misc/makeEpicsVersion.pl @@ -0,0 +1,99 @@ +#!/usr/bin/env perl + +#************************************************************************* +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +use strict; + +use Getopt::Std; +use File::Basename; + +my $tool = basename($0); + +our ($opt_h, $opt_q, $opt_v); +our $opt_o = 'epicsVersion.h'; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +HELP_MESSAGE() unless getopts('ho:qv:') && @ARGV == 1; +HELP_MESSAGE() if $opt_h; + +my ($infile) = @ARGV; + +print "Building $opt_o from $infile\n" unless $opt_q; + +open my $VARS, '<', $infile + or die "$tool: Can't open $infile: $!\n"; + +my ($ver, $rev, $mod, $patch, $snapshot); +while (<$VARS>) { + chomp; + next if m/^\s*#/; # Skip comments + if (m/^EPICS_VERSION\s*=\s*(\d+)/) { $ver = $1; } + if (m/^EPICS_REVISION\s*=\s*(\d+)/) { $rev = $1; } + if (m/^EPICS_MODIFICATION\s*=\s*(\d+)/) { $mod = $1; } + if (m/^EPICS_PATCH_LEVEL\s*=\s*(\d+)/) { $patch = $1; } + if (m/^EPICS_DEV_SNAPSHOT\s*=\s*([-\w]*)/) { $snapshot = $1; } +} +close $VARS; + +map { + die "$tool: Variable missing from $infile" unless defined $_; +} $ver, $rev, $mod, $patch, $snapshot; + +my $ver_str = "$ver.$rev.$mod"; +$ver_str .= ".$patch" if $patch > 0; +my $ver_short = $ver_str; +$ver_str .= $snapshot if $snapshot ne ''; +$ver_str .= "-$opt_v" if $opt_v; + +print "Found EPICS Version $ver_str\n" unless $opt_q; + +open my $OUT, '>', $opt_o + or die "$tool: Can't create $opt_o: $!\n"; + +my $obase = basename($opt_o, '.h'); + +print $OUT <<"END"; +/* Generated file $opt_o */ + +#ifndef INC_${obase}_H +#define INC_${obase}_H + +#define EPICS_VERSION $ver +#define EPICS_REVISION $rev +#define EPICS_MODIFICATION $mod +#define EPICS_PATCH_LEVEL $patch +#define EPICS_DEV_SNAPSHOT "$snapshot" +#define EPICS_SITE_VERSION "$opt_v" + +#define EPICS_VERSION_SHORT "$ver_short" +#define EPICS_VERSION_FULL "$ver_str" +#define EPICS_VERSION_STRING "EPICS $ver_str" +#define epicsReleaseVersion "EPICS R$ver_str" + +#ifndef VERSION_INT +# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P)) +#endif +#define EPICS_VERSION_INT VERSION_INT($ver, $rev, $mod, $patch) + +#endif /* INC_${obase}_H */ +END + +close $OUT; + +sub HELP_MESSAGE { + print STDERR "Usage: $tool [options] CONFIG_BASE_VERSION\n", + " -h Help: Print this message\n", + " -q Quiet: Only print errors\n", + " -o file Output filename, default is $opt_o\n", + " -v vers Site-specific version string\n", + "\n"; + exit 1; +} diff --git a/modules/libcom/src/misc/shareLib.h b/modules/libcom/src/misc/shareLib.h new file mode 100644 index 000000000..0d376d64a --- /dev/null +++ b/modules/libcom/src/misc/shareLib.h @@ -0,0 +1,214 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Compiler specific key words to set up external symbols and entry points + * + * USAGE: + * There are two distinct classes of keywords in this file: + * + * 1) epicsShareAPI - specifies a multi-language calling mechanism. On windows + * this is the pascal calling convention which is used by visual basic and other + * high level tools. This is only necessary if a C/C++ function needs to be called + * from other languages or from high level tools. The epicsShareAPI keyword + * must be present between the function's returned data type and the function's + * name. All compilers targeting windows accept the __stdcall keyword in this + * location. Functions with variable argument lists should not use the epicsShareAPI + * keyword because __stdcall (pascal) calling convention cannot support variable + * length ed argument lists. + * + * int epicsShareAPI myExtFunc ( int arg ); + * int epicsShareAPI myExtFunc ( int arg ) {} + * + * ** NOTE ** The epicsShareAPI attribute is deprecated and has been removed + * from all IOC-specific APIs. Most libCom APIs still use it, but + * it may get removed from these at some point in the future. + * + * 2) epicsShare{Func,Class,Extern,Def} - specifies shareable library (DLL) + * export/import related information in the source code. On windows these keywords + * allow faster dispatching of calls to DLLs because more is known at compile time. + * It is also not necessary to maintain a linker input files specifying the DLL + * entry points. This maintenance can be more laborious with C++ decorated symbol + * names. These keywords are only necessary if the address of a function or data + * internal to a shareable library (DLL) needs to be visible outside of this shareable + * library (DLL). All compilers targeting windows accept the __declspec(dllexport) + * and __declspec(dllimport) keywords. For GCC version 4 and above the first three + * keywords specify a visibility attribute of "default", which marks the symbol as + * exported even when gcc is given the option -fvisibility=hidden. Doing this can + * significantly reduce the number of symbols exported to a shared library. See the + * URL below for more information. + * + * In header files declare references to externally visible variables, classes and + * functions like this: + * + * #include "shareLib.h" + * epicsShareFunc int myExtFunc ( int arg ); + * epicsShareExtern int myExtVar; + * class epicsShareClass myClass { int func ( void ); }; + * + * In the implementation file, however, you write: + * + * #include + * #define epicsExportSharedSymbols + * #include + * + * epicsShareDef int myExtVar = 4; + * int myExtFunc ( int arg ) {} + * int myClass::func ( void ) {} + * + * By default shareLib.h sets the DLL import / export keywords to import from + * a DLL so that, for DLL consumers (users), nothing special must be done. However, + * DLL implementors must set epicsExportSharedSymbols as above to specify + * which functions are exported from the DLL and which of them are imported + * from other DLLs. + * + * You must first #include what you import and then define epicsExportSharedSymbols + * only right before you #include the prototypes for what you implement! You must + * include shareLib.h again each time that the state of the import/ export keywords + * changes, but this usually occurs as a side effect of including the shareable + * libraries header file(s). + * + * Frequently a header file for a shareable library exported interface will + * have some preprocessor switches like this if this header file must also + * include header files describing interfaces to other shareable libraries. + * + * #ifdef epicsExportSharedSymbols + * # define interfacePDQ_epicsExportSharedSymbols + * # undef epicsExportSharedSymbols + * #endif + * + * #include "epicsTypes.h" + * #include "epicsTime.h" + * + * #ifdef interfacePDQ_epicsExportSharedSymbols + * # define epicsExportSharedSymbols + * # include "shareLib.h" + * #endif + * + * epicsShareFunc int myExtFunc ( int arg ); + * epicsShareExtern int myExtVar; + * class epicsShareClass myClass {}; + * + * Fortunately, the above is only the concern of library authors and will have no + * impact on persons using functions and or external data from a library. + */ + +#undef epicsShareExtern +#undef epicsShareDef +#undef epicsShareClass +#undef epicsShareFunc +#undef epicsShareAPI +#undef READONLY + +#if defined(_WIN32) || defined(__CYGWIN__) +/* + * Check if EPICS_BUILD_DLL or EPICS_CALL_DLL defined and use the dllimport/ + * dllexport keywords if this is a shared library build of base under WIN32. + */ + +# if defined(epicsExportSharedSymbols) +# if defined(EPICS_BUILD_DLL) +# define epicsShareExtern __declspec(dllexport) extern +# define epicsShareClass __declspec(dllexport) +# define epicsShareFunc __declspec(dllexport) +# else +# define epicsShareExtern extern +# define epicsShareClass +# define epicsShareFunc +# endif +# else +# if defined(EPICS_CALL_DLL) +# define epicsShareExtern __declspec(dllimport) extern +# define epicsShareClass __declspec(dllimport) +# define epicsShareFunc __declspec(dllimport) +# else +# define epicsShareExtern extern +# define epicsShareClass +# define epicsShareFunc +# endif +# endif +# define epicsShareDef +# define epicsShareAPI __stdcall /* function removes arguments */ +# define READONLY const + +#elif __GNUC__ >= 4 +/* + * See http://gcc.gnu.org/wiki/Visibility + * For these to work, gcc must be given the flag + * -fvisibility=hidden + * and g++ the flags + * -fvisibility=hidden -fvisibility-inlines-hidden + */ + +# define epicsShareExtern __attribute__ ((visibility("default"))) extern +# define epicsShareClass __attribute__ ((visibility("default"))) +# define epicsShareFunc __attribute__ ((visibility("default"))) + +# define epicsShareDef +# define epicsShareAPI +# if defined(__STDC__) || defined (__cplusplus) +# define READONLY const +# else +# define READONLY +# endif + +/* + * if its the old VAX C Compiler (not DEC C) + */ +#elif defined(VAXC) + + /* + * VAXC creates FORTRAN common blocks when + * we use "extern int fred"/"int fred=4". Therefore, + * the initialization is not loaded unless we + * call a function in that object module. + * + * DEC CXX does not have this problem. + * We suspect (but do not know) that DEC C + * also does not have this problem. + */ +# define epicsShareExtern globalref +# define epicsShareDef globaldef +# define READONLY const +# define epicsShareClass +# define epicsShareFunc +# define epicsShareAPI + +#else + +/* else => no import/export specifiers */ + +# define epicsShareExtern extern +# define epicsShareAPI +# define epicsShareClass +# define epicsShareDef + +# define epicsShareFunc +# if defined(__STDC__) || defined (__cplusplus) +# define READONLY const +# else +# define READONLY +# endif + +#endif + +#ifndef INLINE_defs_EPICS +#define INLINE_defs_EPICS +# ifndef __cplusplus +# if defined (__GNUC__) +# define INLINE static __inline__ +# elif defined (_MSC_VER) +# define INLINE __inline +# elif defined (_SUNPRO_C) +# pragma error_messages (off, E_EXTERN_PRIOR_REDECL_STATIC) +# define INLINE static +# else +# define INLINE static +# endif +# endif /* ifndef __cplusplus */ +#endif /* ifdef INLINE_defs_EPICS */ diff --git a/modules/libcom/src/misc/testMain.h b/modules/libcom/src/misc/testMain.h new file mode 100644 index 000000000..4db72c39e --- /dev/null +++ b/modules/libcom/src/misc/testMain.h @@ -0,0 +1,45 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_testMain_H +#define INC_testMain_H + +/* This header defines a convenience macro for use by pure test programs. + * A pure test program cannot take any arguments since it must be fully + * automatable. If your program needs to use argv/argc, it may be doing + * measurements not unit and/or regression testing. On Host architectures + * these programs needs to be named main and take dummy argc/argv args, + * but on vxWorks and RTEMS they must be named as the test program. + * + * Use this macro as follows: + * + * #include "testMain.h" + * #include "epicsUnitTest.h" + * + * MAIN(myProgTest) { + * testPlan(...); + * testOk(...) + * return testDone(); + * } + */ + +#if defined(vxWorks) || defined(__rtems__) + #ifdef __cplusplus + #define MAIN(prog) extern "C" int prog(void) + #else + #define MAIN(prog) int prog() + #endif +#else + #ifdef __cplusplus + #define MAIN(prog) int main(int /*argc*/, char * /*argv*/ [] ) + #else + #define MAIN(prog) int main(int argc, char *argv[] ) + #endif +#endif + + +#endif /* INC_testMain_H */ diff --git a/modules/libcom/src/misc/truncateFile.c b/modules/libcom/src/misc/truncateFile.c new file mode 100644 index 000000000..ccb1450c2 --- /dev/null +++ b/modules/libcom/src/misc/truncateFile.c @@ -0,0 +1,136 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +/* + * truncate to specified size (we dont use truncate() + * because it is not portable) + */ +epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned long size) +{ + char tmpName[256>L_tmpnam?256:L_tmpnam]; + long filePos; + FILE *pFile; + FILE *ptmp; + int status; + int c; + unsigned charNo; + + /* + * see cast of size to long below + */ + if (size>LONG_MAX) { + return TF_ERROR; + } + + pFile = fopen(pFileName, "r"); + if (!pFile) { + fprintf (stderr, + "File access problems to `%s' because `%s'\n", + pFileName, + strerror(errno)); + return TF_ERROR; + } + + /* + * This is not required under UNIX but does appear + * to be required under WIN32. + */ + status = fseek (pFile, 0L, SEEK_END); + if (status!=TF_OK) { + fclose (pFile); + return TF_ERROR; + } + + filePos = ftell(pFile); + if (filePos <= (long) size) { + fclose (pFile); + return TF_OK; + } + + epicsTempName ( tmpName, sizeof (tmpName) ); + if ( tmpName[0] == '\0' ) { + fprintf (stderr,"Unable to create tmp file name?\n"); + fclose (pFile); + return TF_ERROR; + } + + ptmp = fopen (tmpName, "w"); + if (!ptmp) { + fprintf (stderr, + "File access problems to `%s' because `%s'\n", + tmpName, + strerror(errno)); + fclose (pFile); + return TF_ERROR; + } + rewind (pFile); + charNo = 0u; + while (charNo 2 +# define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline)) +#else +# define EPICS_ALWAYS_INLINE __inline__ +#endif + +/* Expands to a 'const char*' which describes the name of the current function scope */ +#define EPICS_FUNCTION __PRETTY_FUNCTION__ + +#ifdef __cplusplus + +/* + * in general we dont like ifdefs but they do allow us to check the + * compiler version and make the optimistic assumption that + * standards incompliance issues will be fixed by future compiler + * releases + */ + +/* + * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete + * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification + */ + +#if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 95 ) +# define CXX_THROW_SPECIFICATION +#endif + +#if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 96 ) +# define CXX_PLACEMENT_DELETE +#endif + +#endif /* __cplusplus */ + +/* + * Enable format-string checking if possible + */ +#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) + +/* + * Deprecation marker if possible + */ +#if (__GNUC__ > 2) +# define EPICS_DEPRECATED __attribute__((deprecated)) +#endif + +/* + * Unused marker + */ +#define EPICS_UNUSED __attribute__((unused)) + +#endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h new file mode 100644 index 000000000..15b9a6c9a --- /dev/null +++ b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h @@ -0,0 +1,194 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicCD_h +#define epicsAtomicCD_h + +#ifndef __GNUC__ +# error this header is only for use with the gnu compiler +#endif + +#define EPICS_ATOMIC_CMPLR_NAME "GCC" + +#define GCC_ATOMIC_CONCAT( A, B ) GCC_ATOMIC_CONCATR(A,B) +#define GCC_ATOMIC_CONCATR( A, B ) ( A ## B ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_INT__ ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ + GCC_ATOMIC_CONCAT ( \ + __GCC_HAVE_SYNC_COMPARE_AND_SWAP_, \ + __SIZEOF_SIZE_T__ ) + +#define GCC_ATOMIC_INTRINSICS_MIN_X86 \ + ( defined ( __i486 ) || defined ( __pentium ) || \ + defined ( __pentiumpro ) || defined ( __MMX__ ) ) + +#define GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER \ + ( ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 ) + +#define GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER \ + ( GCC_ATOMIC_INTRINSICS_MIN_X86 && \ + GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * We are optimistic that __sync_synchronize is implemented + * in all version four gcc invariant of target. The gnu doc + * seems to say that when not supported by architecture a call + * to an external function is generated but in practice + * this isn`t the case for some of the atomic intrinsics, and + * so there is an undefined symbol. So far we have not seen + * that with __sync_synchronize, but we can only guess based + * on experimental evidence. + * + * For example we know that when generating object code for + * 386 most of the atomic intrinsics are not present and + * we see undefined symbols with mingw, but we don`t have + * troubles with __sync_synchronize. + */ +#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + __sync_synchronize (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + __sync_synchronize (); +} +#endif + +#else + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#if GCC_ATOMIC_INTRINSICS_MIN_X86 +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + asm("mfence;"); +} +#endif +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#if GCC_ATOMIC_INTRINSICS_MIN_X86 +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + asm("mfence;"); +} +#endif +#endif + +#endif /* if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER */ + +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T \ + || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER + +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + return __sync_add_and_fetch ( pTarget, 1 ); +} + +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + return __sync_sub_and_fetch ( pTarget, 1 ); +} + +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + return __sync_add_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T */ + +#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T \ + || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER + +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + return __sync_add_and_fetch ( pTarget, 1u ); +} + +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + return __sync_sub_and_fetch ( pTarget, 1u ); +} + +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) +{ + return __sync_add_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + return __sync_sub_and_fetch ( pTarget, delta ); +} + +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + return __sync_val_compare_and_swap ( pTarget, oldVal, newVal); +} + +#endif /* if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +/* + * if currently unavailable as gcc intrinsics we + * will try for an os specific inline solution + */ +#include "epicsAtomicOSD.h" + +#endif /* epicsAtomicCD_h */ diff --git a/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h new file mode 100644 index 000000000..75bfa83fb --- /dev/null +++ b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: + * Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef compilerSpecific_h +#define compilerSpecific_h + +#ifndef _MSC_VER +# error compiler/msvc/compilerSpecific.h is only for use with the Microsoft compiler +#endif + +#if _MSC_VER >= 1200 +#define EPICS_ALWAYS_INLINE __forceinline +#else +#define EPICS_ALWAYS_INLINE __inline +#endif + +/* Expands to a 'const char*' which describes the name of the current function scope */ +#define EPICS_FUNCTION __FUNCTION__ + +#ifdef __cplusplus + +/* + * in general we dont like ifdefs but they do allow us to check the + * compiler version and make the optimistic assumption that + * standards incompliance issues will be fixed by future compiler + * releases + */ + +/* + * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete + * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification + */ +#if _MSC_VER >= 1200 /* visual studio 6.0 or later */ +# define CXX_PLACEMENT_DELETE +#endif + +#if _MSC_VER > 1300 /* some release after visual studio 7 we hope */ +# define CXX_THROW_SPECIFICATION +#endif + +#endif /* __cplusplus */ + + +#endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h new file mode 100644 index 000000000..e7d1c4b77 --- /dev/null +++ b/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h @@ -0,0 +1,118 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicCD_h +#define epicsAtomicCD_h + +#include "epicsAssert.h" + +#ifndef _MSC_VER +# error this header file is only for use with with the Microsoft Compiler +#endif + +#ifdef _MSC_EXTENSIONS + +#define EPICS_ATOMIC_CMPLR_NAME "MSVC-INTRINSIC" + +#include + +#if defined ( _M_IX86 ) +# pragma warning( push ) +# pragma warning( disable : 4793 ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) + { + long fence; + __asm { xchg fence, eax } + } +# pragma warning( pop ) +#elif defined ( _M_X64 ) +# define MS_ATOMIC_64 +# pragma intrinsic ( __faststorefence ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) + { + __faststorefence (); + } +#elif defined ( _M_IA64 ) +# define MS_ATOMIC_64 +# pragma intrinsic ( __mf ) + EPICS_ATOMIC_INLINE void epicsAtomicMemoryBarrier (void) + { + __mf (); + } +#else +# error unexpected target architecture, msvc version of epicsAtomicCD.h +#endif + +/* + * The windows doc appears to recommend defining InterlockedExchange + * to be _InterlockedExchange to cause it to be an intrinsic, but that + * creates issues when later, in a windows os specific header, we include + * windows.h. Therefore, we except some code duplication between the msvc + * csAtomic.h and win32 osdAtomic.h to avoid problems, and to keep the + * os specific windows.h header file out of the msvc cdAtomic.h + */ +#define MS_LONG long +#define MS_InterlockedExchange _InterlockedExchange +#define MS_InterlockedCompareExchange _InterlockedCompareExchange +#define MS_InterlockedIncrement _InterlockedIncrement +#define MS_InterlockedDecrement _InterlockedDecrement +#define MS_InterlockedExchange _InterlockedExchange +#define MS_InterlockedExchangeAdd _InterlockedExchangeAdd +#if defined ( MS_ATOMIC_64 ) +# define MS_LONGLONG long long +# define MS_InterlockedIncrement64 _InterlockedIncrement64 +# define MS_InterlockedDecrement64 _InterlockedDecrement64 +# define MS_InterlockedExchange64 _InterlockedExchange64 +# define MS_InterlockedExchangeAdd64 _InterlockedExchangeAdd64 +# define MS_InterlockedCompareExchange64 _InterlockedCompareExchange64 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + epicsAtomicMemoryBarrier (); +} + +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + epicsAtomicMemoryBarrier (); +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicMS.h" +#include "epicsAtomicDefault.h" + +#else /* ifdef _MSC_EXTENSIONS */ + +#define EPICS_ATOMIC_CMPLR_NAME "MSVC-DIRECT" + +/* + * if unavailable as an intrinsic we will try + * for os specific inline solution + */ +#include "epicsAtomicOSD.h" + +#endif /* ifdef _MSC_EXTENSIONS */ + +#endif /* epicsAtomicCD_h */ + diff --git a/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h b/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h new file mode 100644 index 000000000..a9d59a5bf --- /dev/null +++ b/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h @@ -0,0 +1,44 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: + * Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef compilerSpecific_h +#define compilerSpecific_h + +#if !defined(__SUNPRO_C) && !defined (__SUNPRO_CC) +# error Not Solaris Studio +#endif + +#if (defined(__SUNPRO_C) && __SUNPRO_C < 0x590) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define EPICS_ALWAYS_INLINE inline +#else +# define EPICS_ALWAYS_INLINE inline __attribute__((always_inline)) +#endif + +#ifdef __cplusplus + +/* + * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete + * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification + * + * (our default guess is that the compiler implements the C++ 97 standard) + */ +#define CXX_THROW_SPECIFICATION +#define CXX_PLACEMENT_DELETE + +#endif /* __cplusplus */ + + +#endif /* ifndef compilerSpecific_h */ diff --git a/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h new file mode 100644 index 000000000..8a733a558 --- /dev/null +++ b/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h @@ -0,0 +1,23 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicCD_h +#define epicsAtomicCD_h + +#define EPICS_ATOMIC_CMPLR_NAME "SOLSTUDIO" + +#include "epicsAtomicOSD.h" + +#endif /* epicsAtomicCD_h */ diff --git a/modules/libcom/src/osi/compilerDependencies.h b/modules/libcom/src/osi/compilerDependencies.h new file mode 100644 index 000000000..3bd835c91 --- /dev/null +++ b/modules/libcom/src/osi/compilerDependencies.h @@ -0,0 +1,74 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: + * Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef compilerDependencies_h +#define compilerDependencies_h + +#include "compilerSpecific.h" + +#ifdef __cplusplus + +/* + * usage: void func () epicsThrows (( std::bad_alloc, std::logic_error )) + * + * Note: now a widely accepted concensus (ref Meyers and C++ faq) is that + * one should avoid using throw specifications in C++ code + */ +#if defined ( CXX_THROW_SPECIFICATION ) +# define epicsThrows(X) throw X +#else +# define epicsThrows(X) +#endif + +/* + * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) + */ +#if defined ( CXX_PLACEMENT_DELETE ) +# define epicsPlacementDeleteOperator(X) void operator delete X; +#else +# define epicsPlacementDeleteOperator(X) +#endif + +#endif /* __cplusplus */ + + +#ifndef EPICS_PRINTF_STYLE +/* + * No format-string checking + */ +# define EPICS_PRINTF_STYLE(f,a) +#endif + +#ifndef EPICS_DEPRECATED +/* + * No deprecation markers + */ +#define EPICS_DEPRECATED +#endif + +#ifndef EPICS_UNUSED +# define EPICS_UNUSED +#endif + +#ifndef EPICS_FUNCTION +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)) || (defined(__cplusplus) && __cplusplus>=201103L) +# define EPICS_FUNCTION __func__ +#else +/* Expands to a 'const char*' which describes the name of the current function scope */ +# define EPICS_FUNCTION ("") +#endif +#endif + +#endif /* ifndef compilerDependencies_h */ diff --git a/modules/libcom/src/osi/devLib.h b/modules/libcom/src/osi/devLib.h new file mode 100644 index 000000000..729f9b932 --- /dev/null +++ b/modules/libcom/src/osi/devLib.h @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.h */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + */ +#ifndef EPICSDEVLIB_H +#define EPICSDEVLIB_H + +/* + * Support macros + */ + +/* + * Normalize a digital value and convert it to type TYPE + * + * Ex: + * float f; + * int d; + * f = devNormalizeDigital(d,12) + * + */ +#define devCreateMask(NBITS) ((1<<(NBITS))-1) +#define devDigToNml(DIGITAL,NBITS) \ + (((double)(DIGITAL))/devCreateMask(NBITS)) +#define devNmlToDig(NORMAL,NBITS) \ + (((long)(NORMAL)) * devCreateMask(NBITS)) + +/* + * + * Alignment mask + * (for use when testing to see if the proper number of least + * significant bits are zero) + * + */ +#define devCreateAlignmentMask(CTYPE)\ +(sizeof(CTYPE)>sizeof(double)?sizeof(double)-1:sizeof(CTYPE)-1) + +/* + * pointer aligned test + * (returns true if the pointer is on the worst case alignemnt + * boundary for its type) + */ +#define devPtrAlignTest(PTR) (!(devCreateAlignmentMask(*PTR)&(long)(PTR))) + +/* + * error codes (and messages) associated with devLib.c + */ +#define S_dev_success 0 +#define S_dev_vectorInUse (M_devLib| 1) /*interrupt vector in use*/ +#define S_dev_vecInstlFail (M_devLib| 2) /*interrupt vector install failed*/ +#define S_dev_uknIntType (M_devLib| 3) /*Unrecognized interrupt type*/ +#define S_dev_vectorNotInUse (M_devLib| 4) /*Interrupt vector not in use by caller*/ +#define S_dev_badA16 (M_devLib| 5) /*Invalid VME A16 address*/ +#define S_dev_badA24 (M_devLib| 6) /*Invalid VME A24 address*/ +#define S_dev_badA32 (M_devLib| 7) /*Invalid VME A32 address*/ +#define S_dev_uknAddrType (M_devLib| 8) /*Unrecognized address space type*/ +#define S_dev_addressOverlap (M_devLib| 9) /*Specified device address overlaps another device*/ +#define S_dev_identifyOverlap (M_devLib| 10) /*This device already owns the address range*/ +#define S_dev_addrMapFail (M_devLib| 11) /*unable to map address*/ +#define S_dev_intDisconnect (M_devLib| 12) /*Interrupt at vector disconnected from an EPICS device*/ +#define S_dev_internal (M_devLib| 13) /*Internal failure*/ +#define S_dev_intEnFail (M_devLib| 14) /*unable to enable interrupt level*/ +#define S_dev_intDissFail (M_devLib| 15) /*unable to disable interrupt level*/ +#define S_dev_noMemory (M_devLib| 16) /*Memory allocation failed*/ +#define S_dev_addressNotFound (M_devLib| 17) /*Specified device address unregistered*/ +#define S_dev_noDevice (M_devLib| 18) /*No device at specified address*/ +#define S_dev_wrongDevice (M_devLib| 19) /*Wrong device type found at specified address*/ +#define S_dev_badSignalNumber (M_devLib| 20) /*Signal number (offset) to large*/ +#define S_dev_badSignalCount (M_devLib| 21) /*Signal count to large*/ +#define S_dev_badRequest (M_devLib| 22) /*Device does not support requested operation*/ +#define S_dev_highValue (M_devLib| 23) /*Parameter to high*/ +#define S_dev_lowValue (M_devLib| 24) /*Parameter to low*/ +#define S_dev_multDevice (M_devLib| 25) /*Specified address is ambiguous (more than one device responds)*/ +#define S_dev_badSelfTest (M_devLib| 26) /*Device self test failed*/ +#define S_dev_badInit (M_devLib| 27) /*Device failed during initialization*/ +#define S_dev_hdwLimit (M_devLib| 28) /*Input exceeds Hardware Limit*/ +#define S_dev_deviceDoesNotFit (M_devLib| 29) /*Unable to locate address space for device*/ +#define S_dev_deviceTMO (M_devLib| 30) /*device timed out*/ +#define S_dev_badFunction (M_devLib| 31) /*bad function pointer*/ +#define S_dev_badVector (M_devLib| 32) /*bad interrupt vector*/ +#define S_dev_badArgument (M_devLib| 33) /*bad function argument*/ +#define S_dev_badISA (M_devLib| 34) /*Invalid ISA address*/ +#define S_dev_badCRCSR (M_devLib| 35) /*Invalid VME CR/CSR address*/ +#define S_dev_vxWorksIntEnFail S_dev_intEnFail + + +#endif /* EPICSDEVLIB_H */ + +/* + * Retain compatibility by including VME by default + */ +#ifndef NO_DEVLIB_COMPAT +# include "devLibVME.h" +#endif diff --git a/modules/libcom/src/osi/devLibVME.c b/modules/libcom/src/osi/devLibVME.c new file mode 100644 index 000000000..6c7d93f99 --- /dev/null +++ b/modules/libcom/src/osi/devLibVME.c @@ -0,0 +1,1156 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.c - support for allocation of common device resources */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + * + * NOTES: + * .01 06-14-93 joh needs devAllocInterruptVector() routine + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "ellLib.h" + +#define NO_DEVLIB_COMPAT +#include "devLibVME.h" +#include "devLibVMEImpl.h" + +static ELLLIST addrAlloc[atLast]; +static ELLLIST addrFree[atLast]; + +static size_t addrLast[atLast] = { + 0xffff, + 0xffffff, + 0xffffffff, + 0xffffff, + 0xffffff, + }; + +static unsigned addrHexDig[atLast] = { + 4, + 6, + 8, + 6, + 6 + }; + +static long addrFail[atLast] = { + S_dev_badA16, + S_dev_badA24, + S_dev_badA32, + S_dev_badISA, + S_dev_badCRCSR + }; + +static epicsMutexId addrListLock; +static char devLibInitFlag; + +const char *epicsAddressTypeName[] + = { + "VME A16", + "VME A24", + "VME A32", + "ISA", + "VME CR/CSR" + }; + +typedef struct{ + ELLNODE node; + const char *pOwnerName; + volatile void *pPhysical; + /* + * first, last is used here instead of base, size + * so that we can store a block that is the maximum size + * available in type size_t + */ + size_t begin; + size_t end; +}rangeItem; + +/* + * These routines are not exported + */ + +static long devLibInit(void); + +static long addrVerify( + epicsAddressType addrType, + size_t base, + size_t size); + +static long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pFirst); + +static void report_conflict( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName); + +static void report_conflict_device( + epicsAddressType addrType, + const rangeItem *pRange); + +static void devInsertAddress( + ELLLIST *pRangeList, + rangeItem *pNewRange); + +static long devListAddressMap( + ELLLIST *pRangeList); + +static long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange); + +static long devInstallAddr( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress); + +#define SUCCESS 0 + +/* + * devBusToLocalAddr() + */ +long devBusToLocalAddr( + epicsAddressType addrType, + size_t busAddr, + volatile void **ppLocalAddress) +{ + long status; + volatile void *localAddress; + + /* + * Make sure that devLib has been intialized + */ + if (!devLibInitFlag) { + status = devLibInit(); + if(status){ + return status; + } + } + + /* + * Make sure we have a valid bus address + */ + status = addrVerify (addrType, busAddr, 4); + if (status) { + return status; + } + + /* + * Call the virtual os routine to map the bus address to a CPU address + */ + status = (*pdevLibVME->pDevMapAddr) (addrType, 0, busAddr, 4, &localAddress); + if (status) { + errPrintf (status, __FILE__, __LINE__, "%s bus address =0X%X\n", + epicsAddressTypeName[addrType], (unsigned int)busAddr); + return status; + } + + /* + * Return the local CPU address if the pointer is supplied + */ + if (ppLocalAddress) { + *ppLocalAddress = localAddress; + } + + return SUCCESS; + +}/*end devBusToLocalAddr()*/ + + +/* + * devRegisterAddress() + */ +long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + rangeItem *pRange; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, base, size); + if (s) { + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + +#ifdef DEBUG + printf ("Req Addr 0X%X Size 0X%X\n", base, size); +#endif + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrFree[addrType]); + while (TRUE) { + if (pRange->begin > base) { + pRange = NULL; +# ifdef DEBUG + printf ("Unable to locate a free block\n"); + devListAddressMap (&addrFree[addrType]); +# endif + break; + } + else if (base + (size - 1) <= pRange->end) { +# ifdef DEBUG + printf ("Found free block Begin 0X%X End 0X%X\n", + pRange->begin, pRange->end); +# endif + break; + } + + pRange = (rangeItem *) ellNext (&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (pRange==NULL) { + report_conflict (addrType, base, size, pOwnerName); + return S_dev_addressOverlap; + } + + s = devInstallAddr( + pRange, /* item on the free list to be split */ + pOwnerName, + addrType, + base, + size, + ppPhysicalAddress); + + return s; +} + +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevReadProbe) (wordSize, ptr, pValue); +} + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevWriteProbe) (wordSize, ptr, pValue); +} + +/* + * devInstallAddr() + */ +static long devInstallAddr ( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + volatile void *pPhysicalAddress; + rangeItem *pNewRange; + size_t reqEnd = base + (size-1); + long status; + + /* + * does it start below the specified block + */ + if (base < pRange->begin) { + return S_dev_badArgument; + } + + /* + * does it end above the specified block + */ + if (reqEnd > pRange->end) { + return S_dev_badArgument; + } + + /* + * always map through the virtual os in case the memory + * management is set up there + */ + status = (*pdevLibVME->pDevMapAddr) (addrType, 0, base, + size, &pPhysicalAddress); + if (status) { + errPrintf (status, __FILE__, __LINE__, "%s base=0X%X size = 0X%X", + epicsAddressTypeName[addrType], (unsigned int)base, (unsigned int)size); + return status; + } + + /* + * set the callers variable if the pointer is supplied + */ + if (ppPhysicalAddress) { + *ppPhysicalAddress = pPhysicalAddress; + } + + /* + * does it start at the beginning of the block + */ + if (pRange->begin == base) { + if (pRange->end == reqEnd) { + epicsMutexMustLock(addrListLock); + ellDelete(&addrFree[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + free ((void *)pRange); + } + else { + pRange->begin = base + size; + } + } + /* + * does it end at the end of the block + */ + else if (pRange->end == reqEnd) { + pRange->end = base-1; + } + /* + * otherwise split the item on the free list + */ + else { + + pNewRange = (rangeItem *) calloc (1, sizeof(*pRange)); + if(!pNewRange){ + return S_dev_noMemory; + } + + pNewRange->begin = base + size; + pNewRange->end = pRange->end; + pNewRange->pOwnerName = ""; + pNewRange->pPhysical = NULL; + pRange->end = base - 1; + + /* + * add the node after the old item on the free list + * (blocks end up ordered by address) + */ + epicsMutexMustLock(addrListLock); + ellInsert(&addrFree[addrType], &pRange->node, &pNewRange->node); + epicsMutexUnlock(addrListLock); + } + + /* + * allocate a new address range entry and add it to + * the list + */ + pNewRange = (rangeItem *)calloc (1, sizeof(*pRange)); + if (!pNewRange) { + return S_dev_noMemory; + } + + pNewRange->begin = base; + pNewRange->end = reqEnd; + pNewRange->pOwnerName = pOwnerName; + pNewRange->pPhysical = pPhysicalAddress; + + devInsertAddress (&addrAlloc[addrType], pNewRange); + + return SUCCESS; +} + +/* + * report_conflict() + */ +static void report_conflict ( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName +) +{ + const rangeItem *pRange; + + errPrintf ( + S_dev_addressOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - OX%08X Requested by %s", + epicsAddressTypeName[addrType], + (unsigned int)base, + (unsigned int)(base+size-1), + pOwnerName); + + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + + if (pRange->begin <= base + (size-1) && pRange->end >= base) { + report_conflict_device (addrType, pRange); + } + + pRange = (rangeItem *) pRange->node.next; + } +} + +/* + * report_conflict_device() + */ +static void report_conflict_device(epicsAddressType addrType, const rangeItem *pRange) +{ + errPrintf ( + S_dev_identifyOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - 0X%08X Owned by %s", + epicsAddressTypeName[addrType], + (unsigned int)pRange->begin, + (unsigned int)pRange->end, + pRange->pOwnerName); +} + +/* + * devUnregisterAddress() + */ +long devUnregisterAddress( + epicsAddressType addrType, + size_t baseAddress, + const char *pOwnerName) +{ + rangeItem *pRange; + int s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s) { + return s; + } + } + + s = addrVerify (addrType, baseAddress, 1); + if (s != SUCCESS) { + return s; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + if (pRange->begin == baseAddress) { + break; + } + if (pRange->begin > baseAddress) { + pRange = NULL; + break; + } + pRange = (rangeItem *) ellNext(&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (!pRange) { + return S_dev_addressNotFound; + } + + if (strcmp(pOwnerName,pRange->pOwnerName)) { + s = S_dev_addressOverlap; + errPrintf ( + s, + __FILE__, + __LINE__, + "unregister address for %s at 0X%X failed because %s owns it", + pOwnerName, + (unsigned int)baseAddress, + pRange->pOwnerName); + return s; + } + + epicsMutexMustLock(addrListLock); + ellDelete (&addrAlloc[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + + pRange->pOwnerName = ""; + devInsertAddress (&addrFree[addrType], pRange); + s = devCombineAdjacentBlocks (&addrFree[addrType], pRange); + if(s){ + errMessage (s, "devCombineAdjacentBlocks error"); + return s; + } + + return SUCCESS; +} + +/* + * devCombineAdjacentBlocks() + */ +static long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + pBefore = (rangeItem *) ellPrevious (&pRange->node); + pAfter = (rangeItem *) ellNext (&pRange->node); + + /* + * combine adjacent blocks + */ + if (pBefore) { + if (pBefore->end == pRange->begin-1) { + epicsMutexMustLock(addrListLock); + pRange->begin = pBefore->begin; + ellDelete (pRangeList, &pBefore->node); + epicsMutexUnlock(addrListLock); + free ((void *)pBefore); + } + } + + if (pAfter) { + if (pAfter->begin == pRange->end+1) { + epicsMutexMustLock(addrListLock); + pRange->end = pAfter->end; + ellDelete (pRangeList, &pAfter->node); + epicsMutexUnlock(addrListLock); + free((void *)pAfter); + } + } + + return SUCCESS; +} + +/* + * devInsertAddress() + */ +static void devInsertAddress( +ELLLIST *pRangeList, +rangeItem *pNewRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + epicsMutexMustLock(addrListLock); + pAfter = (rangeItem *) ellFirst (pRangeList); + while (pAfter) { + if (pNewRange->end < pAfter->begin) { + break; + } + pAfter = (rangeItem *) ellNext (&pAfter->node); + } + + if (pAfter) { + pBefore = (rangeItem *) ellPrevious (&pAfter->node); + ellInsert (pRangeList, &pBefore->node, &pNewRange->node); + } + else { + ellAdd (pRangeList, &pNewRange->node); + } + epicsMutexUnlock(addrListLock); +} + +/* + * devAllocAddress() + */ +long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /* n ls bits zero in base addr*/ + volatile void ** pLocalAddress ) +{ + int s; + rangeItem *pRange; + size_t base = 0; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, 0, size); + if(s){ + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst (&addrFree[addrType]); + while (pRange) { + if ((pRange->end - pRange->begin) + 1 >= size){ + s = blockFind ( + addrType, + pRange, + size, + alignment, + &base); + if (s==SUCCESS) { + break; + } + } + pRange = (rangeItem *) pRange->node.next; + } + epicsMutexUnlock(addrListLock); + + if(!pRange){ + s = S_dev_deviceDoesNotFit; + errMessage(s, epicsAddressTypeName[addrType]); + return s; + } + + s = devInstallAddr (pRange, pOwnerName, addrType, base, + size, pLocalAddress); + + return s; +} + +/* + * addrVerify() + * + * care has been taken here not to overflow type size_t + */ +static long addrVerify(epicsAddressType addrType, size_t base, size_t size) +{ + if (addrType>=atLast) { + return S_dev_uknAddrType; + } + + if (size == 0) { + return addrFail[addrType]; + } + + if (size-1 > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (base > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (size - 1 > addrLast[addrType] - base) { + return addrFail[addrType]; + } + + return SUCCESS; +} + +/* + * devLibInit() + */ +static long devLibInit (void) +{ + rangeItem *pRange; + int i; + + + if(devLibInitFlag) return(SUCCESS); + if(!pdevLibVME) { + epicsPrintf ("pdevLibVME is NULL\n"); + return S_dev_internal; + } + + if (NELEMENTS(addrAlloc) != NELEMENTS(addrFree)) { + return S_dev_internal; + } + + addrListLock = epicsMutexMustCreate(); + + epicsMutexMustLock(addrListLock); + for (i=0; ipOwnerName = ""; + pRange->pPhysical = NULL; + pRange->begin = 0; + pRange->end = addrLast[i]; + ellAdd (&addrFree[i], &pRange->node); + } + epicsMutexUnlock(addrListLock); + devLibInitFlag = TRUE; + return pdevLibVME->pDevInit(); +} + +/* + * devAddressMap() + */ +long devAddressMap(void) +{ + return devListAddressMap(addrAlloc); +} + +/* + * devListAddressMap() + */ +static long devListAddressMap(ELLLIST *pRangeList) +{ + rangeItem *pri; + int i; + long s; + + if (!devLibInitFlag) { + s = devLibInit (); + if (s) { + return s; + } + } + + epicsMutexMustLock(addrListLock); + for (i=0; ibegin, + addrHexDig[i], + (unsigned long) pri->end, + pri->pPhysical, + pri->pOwnerName); + pri = (rangeItem *) ellNext (&pri->node); + } + } + epicsMutexUnlock(addrListLock); + + return SUCCESS; +} + + +/* + * + * blockFind() + * + * Find unoccupied block in a large block + * + */ +static long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pBase) +{ + int s = SUCCESS; + size_t bb; + size_t mask; + size_t newBase; + size_t newSize; + + /* + * align the block base + */ + mask = devCreateMask (alignment); + newBase = pRange->begin; + if ( mask & newBase ) { + newBase |= mask; + newBase++; + } + + if ( requestSize == 0) { + return S_dev_badRequest; + } + + /* + * align size of block + */ + newSize = requestSize; + if (mask & newSize) { + newSize |= mask; + newSize++; + } + + if (pRange->end - pRange->begin + 1 < newSize) { + return S_dev_badRequest; + } + + bb = pRange->begin; + while (bb <= (pRange->end + 1) - newSize) { + s = devNoResponseProbe (addrType, bb, newSize); + if (s==SUCCESS) { + *pBase = bb; + return SUCCESS; + } + bb += newSize; + } + + return s; +} + +/* + * devNoResponseProbe() + */ +long devNoResponseProbe (epicsAddressType addrType, + size_t base, size_t size) +{ + volatile void *pPhysical; + size_t probe; + size_t byteNo; + unsigned wordSize; + union { + char charWord; + short shortWord; + int intWord; + long longWord; + }allWordSizes; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if (s) { + return s; + } + } + + byteNo = 0; + while (byteNo < size) { + + probe = base + byteNo; + + /* + * for all word sizes + */ + for (wordSize=1; wordSize<=sizeof(allWordSizes); wordSize <<= 1) { + /* + * only check naturally aligned words + */ + if ( (probe&(wordSize-1)) != 0 ) { + break; + } + + if (byteNo+wordSize>size) { + break; + } + + /* + * every byte in the block must + * map to a physical address + */ + s = (*pdevLibVME->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical); + if (s!=SUCCESS) { + return s; + } + + /* + * verify that no device is present + */ + s = (*pdevLibVME->pDevReadProbe)(wordSize, pPhysical, &allWordSizes); + if (s==SUCCESS) { + return S_dev_addressOverlap; + } + } + byteNo++; + } + return SUCCESS; +} + +long devConnectInterruptVME( +unsigned vectorNumber, +void (*pFunction)(void *), +void *parameter ) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, + pFunction, parameter); +} + +long devDisconnectInterruptVME( +unsigned vectorNumber, +void (*pFunction)(void *) ) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, pFunction); +} + +int devInterruptInUseVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevInterruptInUseVME) (level); +} + +long devEnableInterruptLevelVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); +} + +long devDisableInterruptLevelVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); +} + +/* + * devConnectInterrupt () + * + * !! DEPRECATED !! + */ +long devConnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)(void *), +void *parameter) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, + pFunction, parameter); + default: + return S_dev_uknIntType; + } +} + +/* + * + * devDisconnectInterrupt() + * + * !! DEPRECATED !! + */ +long devDisconnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)(void *) +) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, + pFunction); + default: + return S_dev_uknIntType; + } +} + +/* + * devEnableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devEnableInterruptLevel( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * devDisableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devDisableInterruptLevel ( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * locationProbe + * + * !! DEPRECATED !! + */ +long locationProbe (epicsAddressType addrType, char *pLocation) +{ + return devNoResponseProbe (addrType, (size_t) pLocation, sizeof(long)); +} + +/****************************************************************************** + * + * The follwing may, or may not be present in the BSP for the CPU in use. + * + */ +/****************************************************************************** + * + * Routines to use to allocate and free memory present in the A24 region. + * + ******************************************************************************/ + +int devLibA24Debug = 0; /* Debugging flag */ + +void *devLibA24Calloc(size_t size) +{ + void *ret; + + ret = devLibA24Malloc(size); + + if (ret == NULL) + return (NULL); + + memset(ret, 0x00, size); + return(ret); +} + +void *devLibA24Malloc(size_t size) +{ + void *ret; + + if (devLibA24Debug) + epicsPrintf ("devLibA24Malloc(%u) entered\n", (unsigned int)size); + + ret = pdevLibVME->pDevA24Malloc(size); + return(ret); +} + +void devLibA24Free(void *pBlock) +{ + if (devLibA24Debug) + epicsPrintf("devLibA24Free(%p) entered\n", pBlock); + + pdevLibVME->pDevA24Free(pBlock); +} diff --git a/modules/libcom/src/osi/devLibVME.h b/modules/libcom/src/osi/devLibVME.h new file mode 100644 index 000000000..4fb17f0c2 --- /dev/null +++ b/modules/libcom/src/osi/devLibVME.h @@ -0,0 +1,306 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.h */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + */ + +#ifndef INCdevLibh +#define INCdevLibh 1 + +#include "dbDefs.h" +#include "osdVME.h" +#include "errMdef.h" +#include "shareLib.h" +#include "devLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epdevAddressType & EPICStovxWorksAddrType + * devLib.c must change in unison + */ +typedef enum { + atVMEA16, + atVMEA24, + atVMEA32, + atISA, /* memory mapped ISA access (until now only on PC) */ + atVMECSR, /* VME-64 CR/CSR address space */ + atLast /* atLast must be the last enum in this list */ + } epicsAddressType; + +/* + * pointer to an array of strings for each of + * the above address types + */ +epicsShareExtern const char *epicsAddressTypeName[]; + +#ifdef __cplusplus +} +#endif + +/* + * To retain compatibility include everything by default + */ +#ifndef NO_DEVLIB_COMPAT +# include "devLibVMEImpl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * General API + * + * This section applies to all bus types + */ + +epicsShareFunc long devAddressMap(void); /* print an address map */ + +/* + * devBusToLocalAddr() + * + * OSI routine to translate bus addresses their local CPU address mapping + */ +epicsShareFunc long devBusToLocalAddr ( + epicsAddressType addrType, + size_t busAddr, + volatile void **ppLocalAddr); +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +epicsShareFunc long devReadProbe ( + unsigned wordSize, volatile const void *ptr, void *pValueRead); + +/* + * devNoResponseProbe() + * + * Verifies that no devices respond at naturally aligned words + * within the specified address range. Return success if no devices + * respond. Returns an error if a device does respond or if + * a physical address for a naturally aligned word cant be mapped. + * Checks all naturally aligned word sizes between char and long for + * the entire specified range of bytes. + */ +epicsShareFunc long devNoResponseProbe( + epicsAddressType addrType, + size_t base, + size_t size +); + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +epicsShareFunc long devWriteProbe ( + unsigned wordSize, volatile void *ptr, const void *pValueWritten); + +epicsShareFunc long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t logicalBaseAddress, + size_t size, /* bytes */ + volatile void **pPhysicalAddress); + +epicsShareFunc long devUnregisterAddress( + epicsAddressType addrType, + size_t logicalBaseAddress, + const char *pOwnerName); + +/* + * allocate and register an unoccupied address block + */ +epicsShareFunc long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /*n ls bits zero in addr*/ + volatile void **pLocalAddress); + +/* + * VME API + * + * Functions in this section apply only to the VME bus type + */ + +/* + * connect ISR to a VME interrupt vector + */ +epicsShareFunc long devConnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *), + void *parameter); + +/* + * disconnect ISR from a VME interrupt vector + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +epicsShareFunc long devDisconnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *)); + +/* + * determine if a VME interrupt vector is in use + * + * returns boolean + */ +epicsShareFunc int devInterruptInUseVME (unsigned vectorNumber); + +/* + * enable VME interrupt level + */ +epicsShareFunc long devEnableInterruptLevelVME (unsigned level); + +/* + * disable VME interrupt level + */ +epicsShareFunc long devDisableInterruptLevelVME (unsigned level); + +/* + * Routines to allocate and free memory in the A24 memory region. + * + */ +epicsShareFunc void *devLibA24Malloc(size_t); +epicsShareFunc void *devLibA24Calloc(size_t); +epicsShareFunc void devLibA24Free(void *pBlock); + +/* + * ISA API + * + * Functions in this section apply only to the ISA bus type + */ + +/* + * connect ISR to an ISA interrupt level + * (not implemented) + * (API should be reviewed) + */ +epicsShareFunc long devConnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *), + void *parameter); + +/* + * disconnect ISR from an ISA interrupt level + * (not implemented) + * (API should be reviewed) + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +epicsShareFunc long devDisconnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *)); + +/* + * determine if an ISA interrupt level is in use + * (not implemented) + * + * returns boolean + */ +epicsShareFunc int devInterruptLevelInUseISA (unsigned interruptLevel); + +/* + * enable ISA interrupt level + */ +epicsShareFunc long devEnableInterruptLevelISA (unsigned level); + +/* + * disable ISA interrupt level + */ +epicsShareFunc long devDisableInterruptLevelISA (unsigned level); + +/* + * Deprecated interface + */ + +#ifndef NO_DEVLIB_OLD_INTERFACE + +typedef enum {intVME, intVXI, intISA} epicsInterruptType; + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devConnectInterruptVME, devConnectInterruptPCI, + * devConnectInterruptISA etc. devConnectInterrupt will be removed + * in a future release. + */ +epicsShareFunc long devConnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)(void *), + void *parameter); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisconnectInterruptVME, devDisconnectInterruptPCI, + * devDisconnectInterruptISA etc. devDisconnectInterrupt will be removed + * in a future release. + */ +epicsShareFunc long devDisconnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)(void *)); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devEnableInterruptLevelVME, devEnableInterruptLevelPCI, + * devEnableInterruptLevelISA etc. devEnableInterruptLevel will be removed + * in a future release. + */ +epicsShareFunc long devEnableInterruptLevel( + epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisableInterruptLevelVME, devDisableInterruptLevelISA, + * devDisableInterruptLevelPCI etc. devDisableInterruptLevel will be removed + * in a future release. + */ +epicsShareFunc long devDisableInterruptLevel ( + epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use devNoResponseProbe(). locationProbe() will be removed + * in a future release. + */ +epicsShareFunc long locationProbe (epicsAddressType addrType, char *pLocation); + +#endif /* NO_DEVLIB_OLD_INTERFACE */ + +#ifdef __cplusplus +} +#endif + +#endif /* INCdevLibh.h*/ diff --git a/modules/libcom/src/osi/devLibVMEImpl.h b/modules/libcom/src/osi/devLibVMEImpl.h new file mode 100644 index 000000000..e479d869c --- /dev/null +++ b/modules/libcom/src/osi/devLibVMEImpl.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLibImpl.h */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + */ + +#ifndef INCdevLibImplh +#define INCdevLibImplh 1 + +#include "dbDefs.h" +#include "shareLib.h" +#include "devLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * virtual OS layer for devLib.c + * + * The global virtual OS table pdevLibVME controls + * the behaviour of the functions defined in devLib.h. + * All of which call into the functions found in this table + * to perform system specific tasks. + */ +typedef struct devLibVME { + /* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ + long (*pDevMapAddr) (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + + /* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevReadProbe) (unsigned wordSize, volatile const void *ptr, void *pValueRead); + + /* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevWriteProbe) (unsigned wordSize, volatile void *ptr, const void *pValueWritten); + + /* + * connect ISR to a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevConnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(void *), void *parameter); + + /* + * disconnect ISR from a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevDisconnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(void *)); + + /* + * enable VME interrupt level + */ + long (*pDevEnableInterruptLevelVME) (unsigned level); + + /* + * disable VME interrupt level + */ + long (*pDevDisableInterruptLevelVME) (unsigned level); + /* malloc/free A24 address space */ + void *(*pDevA24Malloc)(size_t nbytes); + void (*pDevA24Free)(void *pBlock); + long (*pDevInit)(void); + + /* + * test if VME interrupt has an ISR connected + */ + int (*pDevInterruptInUseVME) (unsigned vectorNumber); +}devLibVME; + +epicsShareExtern devLibVME *pdevLibVME; + +#ifndef NO_DEVLIB_COMPAT +# define pdevLibVirtualOS pdevLibVME +typedef devLibVME devLibVirtualOS; +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INCdevLibImplh */ diff --git a/modules/libcom/src/osi/epicsAssert.h b/modules/libcom/src/osi/epicsAssert.h new file mode 100644 index 000000000..6f83d3a82 --- /dev/null +++ b/modules/libcom/src/osi/epicsAssert.h @@ -0,0 +1,61 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * EPICS assert + * + * Author: Jeffrey O. Hill + * Date: 022795 + */ + +#ifndef INC_epicsAssert_H +#define INC_epicsAssert_H + +#include "shareLib.h" +#include "compilerDependencies.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef epicsAssertAuthor +# define epicsAssertAuthor 0 +#endif + +#undef assert + +#ifdef NDEBUG +# define assert(ignore) ((void) 0) +#else /* NDEBUG */ + +epicsShareFunc void epicsAssert (const char *pFile, const unsigned line, + const char *pExp, const char *pAuthorName); + +# define assert(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +#endif /* NDEBUG */ + + +/* Compile-time checks */ +#if __cplusplus>=201103L +#define STATIC_ASSERT(expr) static_assert(expr, #expr) +#else +#define STATIC_JOIN(x, y) STATIC_JOIN2(x, y) +#define STATIC_JOIN2(x, y) x ## y +#define STATIC_ASSERT(expr) \ + typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \ + [ (expr) ? 1 : -1 ] EPICS_UNUSED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsAssert_H */ diff --git a/modules/libcom/src/osi/epicsAtomic.h b/modules/libcom/src/osi/epicsAtomic.h new file mode 100644 index 000000000..b3aad5c9c --- /dev/null +++ b/modules/libcom/src/osi/epicsAtomic.h @@ -0,0 +1,217 @@ +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomic_h +#define epicsAtomic_h + +#include /* define size_t */ + +#include "compilerSpecific.h" + +#define EPICS_ATOMIC_INLINE static EPICS_ALWAYS_INLINE + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void * EpicsAtomicPtrT; + +/* load target into cache */ +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void); + +/* push cache version of target into target */ +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void); + +/* + * lock out other smp processors from accessing the target, + * load target into cache, add one to target, flush cache + * to target, allow other smp processors to access the target, + * return new value of target as modified by this operation + */ +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ); +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ); + +/* + * lock out other smp processors from accessing the target, + * load target into cache, subtract one from target, flush cache + * to target, allow out other smp processors to access the target, + * return new value of target as modified by this operation + */ +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ); +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ); + +/* + * lock out other smp processors from accessing the target, + * load target into cache, add/sub delta to/from target, flush cache + * to target, allow other smp processors to access the target, + * return new value of target as modified by this operation + */ +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ); +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ); +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ); + +/* + * set cache version of target, flush cache to target + */ +EPICS_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newValue ); +EPICS_ATOMIC_INLINE void epicsAtomicSetIntT ( int * pTarget, int newValue ); +EPICS_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, EpicsAtomicPtrT newValue ); + +/* + * fetch target into cache, return new value of target + */ +EPICS_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ); +EPICS_ATOMIC_INLINE int epicsAtomicGetIntT ( const int * pTarget ); +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ); + +/* + * lock out other smp processors from accessing the target, + * load target into cache, if target is equal to oldVal set target + * to newVal, flush cache to target, allow other smp processors + * to access the target, return the original value stored in the + * target + */ +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldVal, size_t newVal ); +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ); +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, + EpicsAtomicPtrT newVal ); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +/* + * options for in-line compiler intrinsic or OS specific + * implementations of the above function prototypes + * + * for some of the compilers we must define the + * in-line functions before they get used in the c++ + * in-line functions below + */ +#include "epicsAtomicCD.h" + +#ifdef __cplusplus + +namespace epics { +namespace atomic { + +/* + * overloaded c++ interface + */ + +/************* incr ***************/ +EPICS_ATOMIC_INLINE size_t increment ( size_t & v ) +{ + return epicsAtomicIncrSizeT ( & v ); +} + +EPICS_ATOMIC_INLINE int increment ( int & v ) +{ + return epicsAtomicIncrIntT ( & v ); +} + +/************* decr ***************/ +EPICS_ATOMIC_INLINE size_t decrement ( size_t & v ) +{ + return epicsAtomicDecrSizeT ( & v ); +} + +EPICS_ATOMIC_INLINE int decrement ( int & v ) +{ + return epicsAtomicDecrIntT ( & v ); +} + +/************* add ***************/ +EPICS_ATOMIC_INLINE size_t add ( size_t & v, size_t delta ) +{ + return epicsAtomicAddSizeT ( & v, delta ); +} + +EPICS_ATOMIC_INLINE int add ( int & v, int delta ) +{ + return epicsAtomicAddIntT ( & v, delta ); +} + +/************* sub ***************/ +EPICS_ATOMIC_INLINE size_t subtract ( size_t & v, size_t delta ) +{ + return epicsAtomicSubSizeT ( & v, delta ); +} + +EPICS_ATOMIC_INLINE int subtract ( int & v, int delta ) +{ + return epicsAtomicAddIntT ( & v, -delta ); +} + +/************* set ***************/ +EPICS_ATOMIC_INLINE void set ( size_t & v , size_t newValue ) +{ + epicsAtomicSetSizeT ( & v, newValue ); +} + +EPICS_ATOMIC_INLINE void set ( int & v, int newValue ) +{ + epicsAtomicSetIntT ( & v, newValue ); +} + +EPICS_ATOMIC_INLINE void set ( EpicsAtomicPtrT & v, EpicsAtomicPtrT newValue ) +{ + epicsAtomicSetPtrT ( & v, newValue ); +} + +/************* get ***************/ +EPICS_ATOMIC_INLINE size_t get ( const size_t & v ) +{ + return epicsAtomicGetSizeT ( & v ); +} + +EPICS_ATOMIC_INLINE int get ( const int & v ) +{ + return epicsAtomicGetIntT ( & v ); +} + +EPICS_ATOMIC_INLINE EpicsAtomicPtrT get ( const EpicsAtomicPtrT & v ) +{ + return epicsAtomicGetPtrT ( & v ); +} + +/************* cas ***************/ +EPICS_ATOMIC_INLINE size_t compareAndSwap ( size_t & v, + size_t oldVal, size_t newVal ) +{ + return epicsAtomicCmpAndSwapSizeT ( & v, oldVal, newVal ); +} + +EPICS_ATOMIC_INLINE int compareAndSwap ( int & v, int oldVal, int newVal ) +{ + return epicsAtomicCmpAndSwapIntT ( & v, oldVal, newVal ); +} + +EPICS_ATOMIC_INLINE EpicsAtomicPtrT compareAndSwap ( EpicsAtomicPtrT & v, + EpicsAtomicPtrT oldVal, + EpicsAtomicPtrT newVal ) +{ + return epicsAtomicCmpAndSwapPtrT ( & v, oldVal, newVal ); +} + +} /* end of name space atomic */ +} /* end of name space epics */ + +#endif /* ifdef __cplusplus */ + +#endif /* epicsAtomic_h */ diff --git a/modules/libcom/src/osi/epicsAtomicDefault.h b/modules/libcom/src/osi/epicsAtomicDefault.h new file mode 100644 index 000000000..26115ce63 --- /dev/null +++ b/modules/libcom/src/osi/epicsAtomicDefault.h @@ -0,0 +1,247 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicDefault_h +#define epicsAtomicDefault_h + +#ifdef __cpluplus +extern "C" { +#endif + +/* + * struct EpicsAtomicLockKey; + * epicsShareFunc void epicsAtomicReadMemoryBarrier (); + * epicsShareFunc void epicsAtomicWriteMemoryBarrier (); + * epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); + * epicsShareFunc void epicsAtomicUnock ( struct EpicsAtomicLockKey * ); + */ + +/* + * incr + */ +#ifndef EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + EpicsAtomicLockKey key; + int result; + + epicsAtomicLock ( & key ); + result = ++(*pTarget); + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +#ifndef EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + EpicsAtomicLockKey key; + size_t result; + + epicsAtomicLock ( & key ); + result = ++(*pTarget); + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +/* + * decr + */ +#ifndef EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + EpicsAtomicLockKey key; + int result; + + epicsAtomicLock ( & key ); + result = --(*pTarget); + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + EpicsAtomicLockKey key; + size_t result; + + epicsAtomicLock ( & key ); + result = --(*pTarget); + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +/* + * add/sub + */ +#ifndef EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + EpicsAtomicLockKey key; + int result; + + epicsAtomicLock ( & key ); + result = *pTarget += delta; + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) +{ + EpicsAtomicLockKey key; + size_t result; + + epicsAtomicLock ( & key ); + result = *pTarget += delta; + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +#ifndef EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + EpicsAtomicLockKey key; + size_t result; + + epicsAtomicLock ( & key ); + result = *pTarget -= delta; + epicsAtomicUnlock ( & key ); + return result; +} +#endif + +/* + * set + */ +#ifndef EPICS_ATOMIC_SET_INTT +EPICS_ATOMIC_INLINE void epicsAtomicSetIntT ( int * pTarget, int newVal ) +{ + *pTarget = newVal; + epicsAtomicWriteMemoryBarrier (); +} +#endif + +#ifndef EPICS_ATOMIC_SET_SIZET +EPICS_ATOMIC_INLINE void epicsAtomicSetSizeT ( size_t * pTarget, size_t newVal ) +{ + *pTarget = newVal; + epicsAtomicWriteMemoryBarrier (); +} +#endif + +#ifndef EPICS_ATOMIC_SET_PTRT +EPICS_ATOMIC_INLINE void epicsAtomicSetPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT newVal ) +{ + *pTarget = newVal; + epicsAtomicWriteMemoryBarrier (); +} +#endif + +/* + * get + */ +#ifndef EPICS_ATOMIC_GET_INTT +EPICS_ATOMIC_INLINE int epicsAtomicGetIntT ( const int * pTarget ) +{ + epicsAtomicReadMemoryBarrier (); + return *pTarget; +} +#endif + +#ifndef EPICS_ATOMIC_GET_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicGetSizeT ( const size_t * pTarget ) +{ + epicsAtomicReadMemoryBarrier (); + return *pTarget; +} +#endif + +#ifndef EPICS_ATOMIC_GET_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT + epicsAtomicGetPtrT ( const EpicsAtomicPtrT * pTarget ) +{ + epicsAtomicReadMemoryBarrier (); + return *pTarget; +} +#endif + +/* + * cmp and swap + */ +#ifndef EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, int oldval, int newval ) +{ + EpicsAtomicLockKey key; + int cur; + + epicsAtomicLock ( & key ); + cur = *pTarget; + if ( cur == oldval ) { + *pTarget = newval; + } + epicsAtomicUnlock ( & key ); + return cur; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldval, size_t newval ) +{ + EpicsAtomicLockKey key; + size_t cur; + + epicsAtomicLock ( & key ); + cur = *pTarget; + if ( cur == oldval ) { + *pTarget = newval; + } + epicsAtomicUnlock ( & key ); + return cur; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldval, EpicsAtomicPtrT newval ) +{ + EpicsAtomicLockKey key; + EpicsAtomicPtrT cur; + + epicsAtomicLock ( & key ); + cur = *pTarget; + if ( cur == oldval ) { + *pTarget = newval; + } + epicsAtomicUnlock ( & key ); + return cur; +} +#endif + +#ifdef __cpluplus +} /* end of extern "C" */ +#endif + +#endif /* epicsAtomicDefault_h */ + + diff --git a/modules/libcom/src/osi/epicsEndian.h b/modules/libcom/src/osi/epicsEndian.h new file mode 100644 index 000000000..fd662822f --- /dev/null +++ b/modules/libcom/src/osi/epicsEndian.h @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsEndian_H +#define INC_epicsEndian_H + +/* This file must be usable from both C and C++ */ + +#define EPICS_ENDIAN_LITTLE 1234 +#define EPICS_ENDIAN_BIG 4321 + + +/* The following OS Dependent file defines the macros + * EPICS_BYTE_ORDER and EPICS_FLOAT_WORD_ORDER to be + * one of the above EPICS_ENDIAN_ values. + */ + +#include "osdWireConfig.h" + +#ifndef EPICS_BYTE_ORDER +#error osdWireConfig.h didnt define EPICS_BYTE_ORDER +#endif + +#ifndef EPICS_FLOAT_WORD_ORDER +#error osdWireConfig.h didnt define EPICS_FLOAT_WORD_ORDER +#endif + +#endif /* INC_epicsEndian_H */ diff --git a/modules/libcom/src/osi/epicsEvent.cpp b/modules/libcom/src/osi/epicsEvent.cpp new file mode 100644 index 000000000..237f7d268 --- /dev/null +++ b/modules/libcom/src/osi/epicsEvent.cpp @@ -0,0 +1,132 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsMutex.c */ +/* Author: Jeff Hill */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsStdio.h" +#include "cantProceed.h" + +// vxWorks 5.4 gcc fails during compile when I use std::exception +using namespace std; + +// exception payload +class epicsEvent::invalidSemaphore : public exception +{ + const char * what () const throw (); +}; + +const char * epicsEvent::invalidSemaphore::what () const throw () +{ + return "epicsEvent::invalidSemaphore()"; +} + +// +// Its probably preferable to not make these inline because they are in +// the sharable library interface. The use of inline or not here is probably +// not an issue because all of this ends up in the operating system in system +// calls +// + +epicsEvent::epicsEvent ( epicsEventInitialState initial ) : + id ( epicsEventCreate ( initial ) ) +{ + if ( this->id == 0 ) { + throw std::bad_alloc (); + } +} + +epicsEvent::~epicsEvent () +{ + epicsEventDestroy ( this->id ); +} + +void epicsEvent::trigger () +{ + epicsEventStatus status = epicsEventTrigger (this->id); + + if (status != epicsEventOK) { + throw invalidSemaphore (); + } +} + +void epicsEvent::wait () +{ + epicsEventStatus status = epicsEventWait (this->id); + + if (status != epicsEventOK) { + throw invalidSemaphore (); + } +} + +bool epicsEvent::wait (double timeOut) +{ + epicsEventStatus status = epicsEventWaitWithTimeout (this->id, timeOut); + + if (status == epicsEventOK) { + return true; + } else if (status == epicsEventWaitTimeout) { + return false; + } + throw invalidSemaphore (); +} + +bool epicsEvent::tryWait () +{ + epicsEventStatus status = epicsEventTryWait (this->id); + + if (status == epicsEventOK) { + return true; + } else if (status == epicsEventWaitTimeout) { + return false; + } + throw invalidSemaphore (); +} + +void epicsEvent::show ( unsigned level ) const +{ + epicsEventShow ( this->id, level ); +} + + +// epicsEventMust... convenience routines for C code + +extern "C" { + +epicsShareFunc epicsEventId epicsEventMustCreate ( + epicsEventInitialState initialState) +{ + epicsEventId id = epicsEventCreate (initialState); + + if (!id) + cantProceed ("epicsEventMustCreate"); + return id; +} + +epicsShareFunc void epicsEventMustTrigger (epicsEventId id) { + epicsEventStatus status = epicsEventTrigger (id); + + if (status != epicsEventOK) + cantProceed ("epicsEventMustTrigger"); +} + +epicsShareFunc void epicsEventMustWait (epicsEventId id) { + epicsEventStatus status = epicsEventWait (id); + + if (status != epicsEventOK) + cantProceed ("epicsEventMustWait"); +} + +} // extern "C" + diff --git a/modules/libcom/src/osi/epicsEvent.h b/modules/libcom/src/osi/epicsEvent.h new file mode 100644 index 000000000..e77d9a5ce --- /dev/null +++ b/modules/libcom/src/osi/epicsEvent.h @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsEventh +#define epicsEventh + +#include "shareLib.h" + +typedef struct epicsEventOSD *epicsEventId; + +typedef enum { + epicsEventOK = 0, + epicsEventWaitTimeout, + epicsEventError +} epicsEventStatus; + +/* Backwards compatibility */ +#define epicsEventWaitStatus epicsEventStatus +#define epicsEventWaitOK epicsEventOK +#define epicsEventWaitError epicsEventError + +typedef enum { + epicsEventEmpty, + epicsEventFull +} epicsEventInitialState; + +#ifdef __cplusplus + +class epicsShareClass epicsEvent { +public: + epicsEvent ( epicsEventInitialState initial = epicsEventEmpty ); + ~epicsEvent (); + void trigger (); + void signal () { this->trigger(); } + void wait (); /* blocks until full */ + bool wait ( double timeOut ); /* false if still empty at time out */ + bool tryWait (); /* false if empty */ + void show ( unsigned level ) const; + + class invalidSemaphore; /* exception payload */ +private: + epicsEvent ( const epicsEvent & ); + epicsEvent & operator = ( const epicsEvent & ); + epicsEventId id; +}; + +extern "C" { +#endif /*__cplusplus */ + +epicsShareFunc epicsEventId epicsEventCreate( + epicsEventInitialState initialState); +epicsShareFunc epicsEventId epicsEventMustCreate ( + epicsEventInitialState initialState); +epicsShareFunc void epicsEventDestroy(epicsEventId id); +epicsShareFunc epicsEventStatus epicsEventTrigger( + epicsEventId id); +epicsShareFunc void epicsEventMustTrigger(epicsEventId id); +#define epicsEventSignal(ID) epicsEventMustTrigger(ID) +epicsShareFunc epicsEventStatus epicsEventWait( + epicsEventId id); +epicsShareFunc void epicsEventMustWait(epicsEventId id); +epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout( + epicsEventId id, double timeOut); +epicsShareFunc epicsEventStatus epicsEventTryWait( + epicsEventId id); +epicsShareFunc void epicsEventShow( + epicsEventId id, unsigned int level); + +#ifdef __cplusplus +} +#endif /*__cplusplus */ + +#include "osdEvent.h" + +#endif /* epicsEventh */ diff --git a/modules/libcom/src/osi/epicsFindSymbol.h b/modules/libcom/src/osi/epicsFindSymbol.h new file mode 100644 index 000000000..9935834da --- /dev/null +++ b/modules/libcom/src/osi/epicsFindSymbol.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsFindSymbolh +#define epicsFindSymbolh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name); +epicsShareFunc const char *epicsLoadError(void); +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsFindSymbolh */ diff --git a/modules/libcom/src/osi/epicsGeneralTime.c b/modules/libcom/src/osi/epicsGeneralTime.c new file mode 100644 index 000000000..3054175ef --- /dev/null +++ b/modules/libcom/src/osi/epicsGeneralTime.c @@ -0,0 +1,635 @@ +/*************************************************************************\ +* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Original Authors: David H. Thompson & Sheng Peng (ORNL) */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsTypes.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsMessageQueue.h" +#include "epicsString.h" +#include "epicsStdioRedirect.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "epicsTimer.h" +#include "epicsInterrupt.h" +#include "osiSock.h" +#include "ellLib.h" +#include "errlog.h" +#include "cantProceed.h" +#include "envDefs.h" +#include "generalTimeSup.h" +#include "epicsGeneralTime.h" + +/* Change 'undef' to 'define' to turn on debug statements: */ +#undef DEBUG_GENERAL_TIME + +#ifdef DEBUG_GENERAL_TIME + int generalTimeDebug = 10; +# define IFDEBUG(n) \ + if (generalTimeDebug >= n) /* block or statement */ +#else +# define IFDEBUG(n) \ + if(0) /* Compiler will elide the block or statement */ +#endif + +/* Declarations */ + +typedef struct { + ELLNODE node; + char *name; + int priority; + union { + TIMECURRENTFUN Time; + TIMEEVENTFUN Event; + } get; + union { + TIMECURRENTFUN Time; + TIMEEVENTFUN Event; + } getInt; +} gtProvider; + +static struct { + epicsMutexId timeListLock; + ELLLIST timeProviders; + gtProvider *lastTimeProvider; + epicsTimeStamp lastProvidedTime; + + epicsMutexId eventListLock; + ELLLIST eventProviders; + gtProvider *lastEventProvider; + epicsTimeStamp eventTime[NUM_TIME_EVENTS]; + epicsTimeStamp lastProvidedBestTime; + + int ErrorCounts; +} gtPvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + +static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f"; + +/* Implementation */ + +static void generalTime_InitOnce(void *dummy) +{ + ellInit(>Pvt.timeProviders); + gtPvt.timeListLock = epicsMutexMustCreate(); + + ellInit(>Pvt.eventProviders); + gtPvt.eventListLock = epicsMutexMustCreate(); + + IFDEBUG(1) + printf("General Time Initialized\n"); +} + +void generalTime_Init(void) +{ + epicsThreadOnce(&onceId, generalTime_InitOnce, NULL); +} + + +int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) +{ + gtProvider *ptp; + int status = S_time_noProvider; + + generalTime_Init(); + + IFDEBUG(2) + printf("generalTimeGetExceptPriority(ignore=%d)\n", ignore); + + epicsMutexMustLock(gtPvt.timeListLock); + for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + if ((ignore > 0 && ptp->priority == ignore) || + (ignore < 0 && ptp->priority != -ignore)) + continue; + + status = ptp->get.Time(pDest); + if (status == epicsTimeOK) { + /* No ratchet, time from this routine may go backwards */ + if (pPrio) + *pPrio = ptp->priority; + break; + } + else IFDEBUG(2) + printf("gTGExP provider '%s' returned error\n", ptp->name); + } + epicsMutexUnlock(gtPvt.timeListLock); + + IFDEBUG(2) { + if (ptp && status == epicsTimeOK) { + char buff[40]; + + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, pDest); + printf("gTGExP returning %s from provider '%s'\n", + buff, ptp->name); + } + else + printf("gTGExP returning error\n"); + } + + return status; +} + +int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest) +{ + gtProvider *ptp; + int status = S_time_noProvider; + epicsTimeStamp ts; + + generalTime_Init(); + + IFDEBUG(20) + printf("epicsTimeGetCurrent()\n"); + + epicsMutexMustLock(gtPvt.timeListLock); + for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + + status = ptp->get.Time(&ts); + if (status == epicsTimeOK) { + /* check time is monotonic */ + if (epicsTimeGreaterThanEqual(&ts, >Pvt.lastProvidedTime)) { + *pDest = ts; + gtPvt.lastProvidedTime = ts; + gtPvt.lastTimeProvider = ptp; + } else { + int key; + + *pDest = gtPvt.lastProvidedTime; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + + IFDEBUG(10) { + char last[40], buff[40]; + + epicsTimeToStrftime(last, sizeof(last), tsfmt, + >Pvt.lastProvidedTime); + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); + printf("eTGC provider '%s' returned older time\n" + " %s, using %s instead\n", ptp->name, buff, last); + } + } + break; + } + } + if (status) + gtPvt.lastTimeProvider = NULL; + epicsMutexUnlock(gtPvt.timeListLock); + + IFDEBUG(20) { + if (ptp && status == epicsTimeOK) { + char buff[40]; + + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); + printf("eTGC returning %s from provider '%s'\n", + buff, ptp->name); + } + else + printf("eTGC returning error\n"); + } + + return status; +} + +int epicsTimeGetCurrentInt(epicsTimeStamp *pDest) +{ + gtProvider *ptp = gtPvt.lastTimeProvider; + + if (ptp == NULL || + ptp->getInt.Time == NULL) { + IFDEBUG(20) + epicsInterruptContextMessage("eTGCInt: No support\n"); + return S_time_noProvider; + } + + return ptp->getInt.Time(pDest); +} + + +static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, + int *pPrio) +{ + gtProvider *ptp; + int status = S_time_noProvider; + epicsTimeStamp ts; + + generalTime_Init(); + + IFDEBUG(2) + printf("generalTimeGetEventPriority(eventNum=%d)\n", eventNumber); + + if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) && + (eventNumber != epicsTimeEventBestTime)) + return S_time_badEvent; + + epicsMutexMustLock(gtPvt.eventListLock); + for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + + status = ptp->get.Event(&ts, eventNumber); + if (status == epicsTimeOK) { + gtPvt.lastEventProvider = ptp; + if (pPrio) + *pPrio = ptp->priority; + + if (eventNumber == epicsTimeEventBestTime) { + if (epicsTimeGreaterThanEqual(&ts, + >Pvt.lastProvidedBestTime)) { + *pDest = ts; + gtPvt.lastProvidedBestTime = ts; + } else { + int key; + + *pDest = gtPvt.lastProvidedBestTime; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + + IFDEBUG(10) { + char last[40], buff[40]; + + epicsTimeToStrftime(last, sizeof(last), tsfmt, + >Pvt.lastProvidedBestTime); + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); + printf("gTGEvP provider '%s' returned older time\n" + " %s, using %s instead\n", + ptp->name, buff, last); + } + } + } else { + if (epicsTimeGreaterThanEqual(&ts, + >Pvt.eventTime[eventNumber])) { + *pDest = ts; + gtPvt.eventTime[eventNumber] = ts; + } else { + int key; + + *pDest = gtPvt.eventTime[eventNumber]; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + + IFDEBUG(10) { + char last[40], buff[40]; + + epicsTimeToStrftime(last, sizeof(last), tsfmt, + >Pvt.lastProvidedBestTime); + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); + printf("gTGEvP provider '%s' returned older time\n" + " %s, using %s instead\n", + ptp->name, buff, last); + } + } + } + break; + } + else IFDEBUG(2) + printf("gTGEvP provider '%s' returned error\n", ptp->name); + } + if (status) + gtPvt.lastEventProvider = NULL; + epicsMutexUnlock(gtPvt.eventListLock); + + IFDEBUG(10) { + if (ptp && status == epicsTimeOK) { + char buff[40]; + + epicsTimeToStrftime(buff, sizeof(buff), tsfmt, &ts); + printf("gTGEvP returning %s from provider '%s'\n", + buff, ptp->name); + } + else + printf("gTGEvP returning error\n"); + } + + return status; +} + +int epicsShareAPI epicsTimeGetEvent(epicsTimeStamp *pDest, int eventNumber) +{ + if (eventNumber == epicsTimeEventCurrentTime) { + return epicsTimeGetCurrent(pDest); + } else { + return generalTimeGetEventPriority(pDest, eventNumber, NULL); + } +} + +int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber) +{ + if (eventNumber == epicsTimeEventCurrentTime) { + return epicsTimeGetCurrentInt(pDest); + } else { + gtProvider *ptp = gtPvt.lastEventProvider; + + if (ptp == NULL || + ptp->getInt.Event == NULL) { + IFDEBUG(20) + epicsInterruptContextMessage("eTGEvInt: No support\n"); + return S_time_noProvider; + } + + return ptp->getInt.Event(pDest, eventNumber); + } +} + + +/* Provider Registration */ + +static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock) +{ + gtProvider *ptpref; + + epicsMutexMustLock(lock); + + for (ptpref = (gtProvider *)ellFirst(plist); + ptpref; ptpref = (gtProvider *)ellNext(&ptpref->node)) { + if (ptpref->priority > ptp->priority) + break; + } + + if (ptpref) { + /* Found a provider below the new one */ + ptpref = (gtProvider *)ellPrevious(&ptpref->node); + ellInsert(plist, &ptpref->node, &ptp->node); + } else { + ellAdd(plist, &ptp->node); + } + + epicsMutexUnlock(lock); +} + +static gtProvider * findProvider(ELLLIST *plist, epicsMutexId lock, + const char *name, int priority) +{ + gtProvider *ptp; + + epicsMutexMustLock(lock); + + for (ptp = (gtProvider *)ellFirst(plist); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + if (ptp->priority == priority && + !strcmp(ptp->name, name)) + break; + } + + epicsMutexUnlock(lock); + return ptp; +} + +int generalTimeRegisterEventProvider(const char *name, int priority, + TIMEEVENTFUN getEvent) +{ + gtProvider *ptp; + + generalTime_Init(); + + if (name == NULL || getEvent == NULL) + return S_time_badArgs; + + ptp = (gtProvider *)malloc(sizeof(gtProvider)); + if (ptp == NULL) + return S_time_noMemory; + + ptp->name = epicsStrDup(name); + ptp->priority = priority; + ptp->get.Event = getEvent; + ptp->getInt.Event = NULL; + + insertProvider(ptp, >Pvt.eventProviders, gtPvt.eventListLock); + + IFDEBUG(1) + printf("Registered event provider '%s' at %d\n", name, priority); + + return epicsTimeOK; +} + +int generalTimeAddIntEventProvider(const char *name, int priority, + TIMEEVENTFUN getEvent) +{ + gtProvider *ptp = findProvider(>Pvt.eventProviders, gtPvt.eventListLock, + name, priority); + if (ptp == NULL) + return S_time_noProvider; + + ptp->getInt.Event = getEvent; + + IFDEBUG(1) + printf("Event provider '%s' is interrupt-callable\n", name); + + return epicsTimeOK; +} + +int generalTimeRegisterCurrentProvider(const char *name, int priority, + TIMECURRENTFUN getTime) +{ + gtProvider *ptp; + + generalTime_Init(); + + if (name == NULL || getTime == NULL) + return S_time_badArgs; + + ptp = (gtProvider *)malloc(sizeof(gtProvider)); + if (ptp == NULL) + return S_time_noMemory; + + ptp->name = epicsStrDup(name); + ptp->priority = priority; + ptp->get.Time = getTime; + ptp->getInt.Time = NULL; + + insertProvider(ptp, >Pvt.timeProviders, gtPvt.timeListLock); + + IFDEBUG(1) + printf("Registered time provider '%s' at %d\n", name, priority); + + return epicsTimeOK; +} + +int generalTimeAddIntCurrentProvider(const char *name, int priority, + TIMECURRENTFUN getTime) +{ + gtProvider *ptp = findProvider(>Pvt.timeProviders, gtPvt.timeListLock, + name, priority); + if (ptp == NULL) + return S_time_noProvider; + + ptp->getInt.Time = getTime; + + IFDEBUG(1) + printf("Time provider '%s' is interrupt-callable\n", name); + + return epicsTimeOK; +} + +/* + * Provide an optional "last resort" provider for Event Time. + * + * This is deliberately optional, as it represents site policy. + * It is intended to be installed as an EventTime provider at the lowest + * priority, to return the current time for an event if there is no + * better time provider for event times. + * + * Typically, this will only be used during startup, or a time-provider + * resynchronisation, where events are being generated by the event system + * but the time provided by the system is not yet valid. + */ +static int lastResortGetEvent(epicsTimeStamp *timeStamp, int eventNumber) +{ + return epicsTimeGetCurrent(timeStamp); +} + +int installLastResortEventProvider(void) +{ + return generalTimeEventTpRegister("Last Resort Event", + LAST_RESORT_PRIORITY, lastResortGetEvent); +} + + +/* Status Report */ + +long generalTimeReport(int level) +{ + int items; + + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("General time framework not yet initialized.\n"); + return epicsTimeOK; + } + + printf("Backwards time errors prevented %u times.\n\n", + generalTimeGetErrorCounts()); + + /* Use an output buffer to avoid holding mutexes during printing */ + + printf("Current Time Providers:\n"); + epicsMutexMustLock(gtPvt.timeListLock); + if ((items = ellCount(>Pvt.timeProviders))) { + gtProvider *ptp; + char *message; /* Temporary output buffer */ + char *pout; + + message = calloc(items, 80 * 2); /* Each provider needs 2 lines */ + if (!message) { + epicsMutexUnlock(gtPvt.timeListLock); + printf("Out of memory\n"); + return S_time_noMemory; + } + + pout = message; + + for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + pout += sprintf(pout, " \"%s\", priority = %d\n", + ptp->name, ptp->priority); + if (level) { + epicsTimeStamp tempTS; + if (ptp->get.Time(&tempTS) == epicsTimeOK) { + char tempTSText[40]; + epicsTimeToStrftime(tempTSText, sizeof(tempTSText), + "%Y-%m-%d %H:%M:%S.%06f", &tempTS); + pout += sprintf(pout, "\tCurrent Time is %s.\n", + tempTSText); + } else { + pout += sprintf(pout, "\tCurrent Time not available\n"); + } + } + } + epicsMutexUnlock(gtPvt.timeListLock); + puts(message); + free(message); + } else { + epicsMutexUnlock(gtPvt.timeListLock); + printf("\tNo Providers registered.\n"); + } + + printf("Event Time Providers:\n"); + epicsMutexMustLock(gtPvt.eventListLock); + if ((items = ellCount(>Pvt.eventProviders))) + { + gtProvider *ptp; + char *message; /* Temporary output buffer */ + char *pout; + + message = calloc(items, 80); /* Each provider needs 1 line, */ + if (!message) { + epicsMutexUnlock(gtPvt.eventListLock); + printf("Out of memory\n"); + return S_time_noMemory; + } + + pout = message; + + for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + pout += sprintf(pout, " \"%s\", priority = %d\n", + ptp->name, ptp->priority); + } + epicsMutexUnlock(gtPvt.eventListLock); + puts(message); + free(message); + } + else + { + epicsMutexUnlock(gtPvt.eventListLock); + printf("\tNo Providers registered.\n"); + } + + return epicsTimeOK; +} + +/* + * Access to internal status values. + */ + +void generalTimeResetErrorCounts(void) +{ + int key = epicsInterruptLock(); + gtPvt.ErrorCounts = 0; + epicsInterruptUnlock(key); +} + +int generalTimeGetErrorCounts(void) +{ + int key = epicsInterruptLock(); + int errors = gtPvt.ErrorCounts; + epicsInterruptUnlock(key); + return errors; +} + +const char * generalTimeCurrentProviderName(void) +{ + if (gtPvt.lastTimeProvider) + return gtPvt.lastTimeProvider->name; + return NULL; +} + +const char * generalTimeEventProviderName(void) +{ + if (gtPvt.lastEventProvider) + return gtPvt.lastEventProvider->name; + return NULL; +} + +const char * generalTimeHighestCurrentName(void) +{ + gtProvider *ptp; + + epicsMutexMustLock(gtPvt.timeListLock); + ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + epicsMutexUnlock(gtPvt.timeListLock); + return ptp ? ptp->name : NULL; +} diff --git a/modules/libcom/src/osi/epicsGeneralTime.h b/modules/libcom/src/osi/epicsGeneralTime.h new file mode 100644 index 000000000..3adc23705 --- /dev/null +++ b/modules/libcom/src/osi/epicsGeneralTime.h @@ -0,0 +1,43 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsGeneralTime_H +#define INC_epicsGeneralTime_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NUM_TIME_EVENTS 256 +/* Time Events are numbered 0 through (NUM_TIME_EVENTS-1) */ + +epicsShareFunc void generalTime_Init(void); + +epicsShareFunc int installLastResortEventProvider(void); + +epicsShareFunc void generalTimeResetErrorCounts(void); +epicsShareFunc int generalTimeGetErrorCounts(void); + +epicsShareFunc const char * generalTimeCurrentProviderName(void); +epicsShareFunc const char * generalTimeEventProviderName(void); +epicsShareFunc const char * generalTimeHighestCurrentName(void); + +/* Original names, for compatibility */ +#define generalTimeCurrentTpName generalTimeCurrentProviderName +#define generalTimeEventTpName generalTimeEventProviderName + +epicsShareFunc long generalTimeReport(int interest); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsGeneralTime_H */ diff --git a/modules/libcom/src/osi/epicsInterrupt.h b/modules/libcom/src/osi/epicsInterrupt.h new file mode 100644 index 000000000..93b443810 --- /dev/null +++ b/modules/libcom/src/osi/epicsInterrupt.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsInterrupth +#define epicsInterrupth + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +epicsShareFunc int epicsInterruptLock(void); +epicsShareFunc void epicsInterruptUnlock(int key); +epicsShareFunc int epicsInterruptIsInterruptContext(void); +epicsShareFunc void epicsInterruptContextMessage(const char *message); + +#ifdef __cplusplus +} +#endif + +#include "osdInterrupt.h" + +#endif /* epicsInterrupth */ diff --git a/modules/libcom/src/osi/epicsMath.cpp b/modules/libcom/src/osi/epicsMath.cpp new file mode 100644 index 000000000..1388e658a --- /dev/null +++ b/modules/libcom/src/osi/epicsMath.cpp @@ -0,0 +1,43 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonna LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMath.cpp */ + +#define epicsExportSharedSymbols +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4723) +#endif + +#ifndef NAN +static float makeNAN ( void ) +{ + float a = 0, b = 0; + return a / b; +} +#define NAN makeNAN() +#endif + +#ifndef INFINITY +static float makeINF ( void ) +{ + float a = 1, b = 0; + return a / b; +} +#define INFINITY makeINF() +#endif + +extern "C" { +epicsShareDef float epicsNAN = NAN; +epicsShareDef float epicsINF = INFINITY; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/modules/libcom/src/osi/epicsMessageQueue.cpp b/modules/libcom/src/osi/epicsMessageQueue.cpp new file mode 100644 index 000000000..1527b3d3a --- /dev/null +++ b/modules/libcom/src/osi/epicsMessageQueue.cpp @@ -0,0 +1,81 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsMessageQueue.h" +#include "epicsStdio.h" + +epicsMessageQueue::epicsMessageQueue(unsigned int aCapacity, + unsigned int aMaxMessageSize) + : id ( epicsMessageQueueCreate(aCapacity, aMaxMessageSize) ) +{ + if (id == NULL) + throw std::bad_alloc (); +} + +epicsMessageQueue::~epicsMessageQueue() +{ + epicsMessageQueueDestroy(id); +} + +int +epicsMessageQueue::trySend(void *message, unsigned int size) +{ + return epicsMessageQueueTrySend(id, message, size); +} + +int +epicsMessageQueue::send(void *message, unsigned int size) +{ + return epicsMessageQueueSend(id, message, size); +} + +int +epicsMessageQueue::send(void *message, unsigned int size, double timeout) +{ + return epicsMessageQueueSendWithTimeout(id, message, size, timeout); +} + +int +epicsMessageQueue::tryReceive(void *message, unsigned int size ) +{ + return epicsMessageQueueTryReceive(id, message, size); +} + +int +epicsMessageQueue::receive(void *message, unsigned int size ) +{ + return epicsMessageQueueReceive(id, message, size); +} + +int +epicsMessageQueue::receive(void *message, unsigned int size, double timeout) +{ + return epicsMessageQueueReceiveWithTimeout(id, message, size, timeout); +} + +unsigned int +epicsMessageQueue::pending() +{ + return epicsMessageQueuePending(id); +} + +void +epicsMessageQueue::show(unsigned int level) +{ + epicsMessageQueueShow(id, level); +} diff --git a/modules/libcom/src/osi/epicsMessageQueue.h b/modules/libcom/src/osi/epicsMessageQueue.h new file mode 100644 index 000000000..c6a98fd47 --- /dev/null +++ b/modules/libcom/src/osi/epicsMessageQueue.h @@ -0,0 +1,98 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Interthread message passing + */ +#ifndef epicsMessageQueueh +#define epicsMessageQueueh + +#include "epicsAssert.h" +#include "shareLib.h" + +typedef struct epicsMessageQueueOSD *epicsMessageQueueId; + +#ifdef __cplusplus + +class epicsShareClass epicsMessageQueue { +public: + epicsMessageQueue ( unsigned int capacity, + unsigned int maximumMessageSize ); + ~epicsMessageQueue (); + int trySend ( void *message, unsigned int messageSize ); + int send ( void *message, unsigned int messageSize); + int send ( void *message, unsigned int messageSize, double timeout ); + int tryReceive ( void *message, unsigned int size ); + int receive ( void *message, unsigned int size ); + int receive ( void *message, unsigned int size, double timeout ); + void show ( unsigned int level = 0 ); + unsigned int pending (); + +private: /* Prevent compiler-generated member functions */ + /* default constructor, copy constructor, assignment operator */ + epicsMessageQueue(); + epicsMessageQueue(const epicsMessageQueue &); + epicsMessageQueue& operator=(const epicsMessageQueue &); + + epicsMessageQueueId id; +}; + +extern "C" { +#endif /*__cplusplus */ + +epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( + unsigned int capacity, + unsigned int maximumMessageSize); +epicsShareFunc void epicsShareAPI epicsMessageQueueDestroy( + epicsMessageQueueId id); +epicsShareFunc int epicsShareAPI epicsMessageQueueTrySend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize); +epicsShareFunc int epicsShareAPI epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize); +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout); +epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( + epicsMessageQueueId id, + void *message, + unsigned int size); +epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int size); +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout); +epicsShareFunc int epicsShareAPI epicsMessageQueuePending( + epicsMessageQueueId id); +epicsShareFunc void epicsShareAPI epicsMessageQueueShow( + epicsMessageQueueId id, + int level); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include "osdMessageQueue.h" + +#endif /* epicsMessageQueueh */ diff --git a/modules/libcom/src/osi/epicsMutex.cpp b/modules/libcom/src/osi/epicsMutex.cpp new file mode 100644 index 000000000..7c8d05056 --- /dev/null +++ b/modules/libcom/src/osi/epicsMutex.cpp @@ -0,0 +1,363 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMutex.cpp */ +/* Author: Marty Kraimer and Jeff Hill Date: 03APR01 */ + +/* + * NOTES: + * 1) LOG_LAST_OWNER feature is normally commented out because + * it slows down the system at run time, anfd because its not + * currently safe to convert a thread id to a thread name because + * the thread may have exited making the thread id invalid. + */ + +#include + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "epicsThread.h" +#include "valgrind/valgrind.h" +#include "ellLib.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsThread.h" + +static epicsThreadOnceId epicsMutexOsiOnce = EPICS_THREAD_ONCE_INIT; +static ELLLIST mutexList; +static ELLLIST freeList; + +struct epicsMutexParm { + ELLNODE node; + epicsMutexOSD * id; +# ifdef LOG_LAST_OWNER + epicsThreadId lastOwner; +# endif + const char *pFileName; + int lineno; +}; + +static epicsMutexOSD * epicsMutexGlobalLock; + + +// vxWorks 5.4 gcc fails during compile when I use std::exception +using namespace std; + +// exception payload +class epicsMutex::mutexCreateFailed : public exception +{ + const char * what () const throw (); +}; + +const char * epicsMutex::mutexCreateFailed::what () const throw () +{ + return "epicsMutex::mutexCreateFailed()"; +} + +// exception payload +class epicsMutex::invalidMutex : public exception +{ + const char * what () const throw (); +}; + +const char * epicsMutex::invalidMutex::what () const throw () +{ + return "epicsMutex::invalidMutex()"; +} + +static void epicsMutexOsiInit(void *) { + ellInit(&mutexList); + ellInit(&freeList); + VALGRIND_CREATE_MEMPOOL(&freeList, 0, 0); + epicsMutexGlobalLock = epicsMutexOsdCreate(); +} + +epicsMutexId epicsShareAPI epicsMutexOsiCreate( + const char *pFileName,int lineno) +{ + epicsMutexOSD * id; + + epicsThreadOnce(&epicsMutexOsiOnce, epicsMutexOsiInit, NULL); + + id = epicsMutexOsdCreate(); + if(!id) { + return 0; + } + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + epicsMutexParm *pmutexNode = + reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) ); + if(pmutexNode) { + ellDelete(&freeList,&pmutexNode->node); + VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); + } else { + pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) ); + } + VALGRIND_MEMPOOL_ALLOC(&freeList, pmutexNode, sizeof(epicsMutexParm)); + pmutexNode->id = id; +# ifdef LOG_LAST_OWNER + pmutexNode->lastOwner = 0; +# endif + pmutexNode->pFileName = pFileName; + pmutexNode->lineno = lineno; + ellAdd(&mutexList,&pmutexNode->node); + epicsMutexOsdUnlock(epicsMutexGlobalLock); + return(pmutexNode); +} + +epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( + const char *pFileName,int lineno) +{ + epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno); + assert(id); + return(id ); +} + +void epicsShareAPI epicsMutexDestroy(epicsMutexId pmutexNode) +{ + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + ellDelete(&mutexList,&pmutexNode->node); + epicsMutexOsdDestroy(pmutexNode->id); + VALGRIND_MEMPOOL_FREE(&freeList, pmutexNode); + VALGRIND_MEMPOOL_ALLOC(&freeList, &pmutexNode->node, sizeof(pmutexNode->node)); + ellAdd(&freeList,&pmutexNode->node); + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + +void epicsShareAPI epicsMutexUnlock(epicsMutexId pmutexNode) +{ + epicsMutexOsdUnlock(pmutexNode->id); +} + +epicsMutexLockStatus epicsShareAPI epicsMutexLock( + epicsMutexId pmutexNode) +{ + epicsMutexLockStatus status = + epicsMutexOsdLock(pmutexNode->id); +# ifdef LOG_LAST_OWNER + if ( status == epicsMutexLockOK ) { + pmutexNode->lastOwner = epicsThreadGetIdSelf(); + } +# endif + return status; +} + +epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( + epicsMutexId pmutexNode) +{ + epicsMutexLockStatus status = + epicsMutexOsdTryLock(pmutexNode->id); +# ifdef LOG_LAST_OWNER + if ( status == epicsMutexLockOK ) { + pmutexNode->lastOwner = epicsThreadGetIdSelf(); + } +# endif + return status; +} + +/* Empty the freeList. + * Called from epicsExit.c, but not via epicsAtExit() + * to avoid the possibility of a circular reference. + */ +extern "C" +void epicsMutexCleanup(void) +{ + ELLNODE *cur; + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + + while((cur=ellGet(&freeList))!=NULL) { + VALGRIND_MEMPOOL_FREE(&freeList, cur); + free(cur); + } + + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + +void epicsShareAPI epicsMutexShow( + epicsMutexId pmutexNode, unsigned int level) +{ +# ifdef LOG_LAST_OWNER + char threadName [255]; + if ( pmutexNode->lastOwner ) { +# error currently not safe to fetch name for stale thread + epicsThreadGetName ( pmutexNode->lastOwner, + threadName, sizeof ( threadName ) ); + } + else { + strcpy ( threadName, "" ); + } + printf("epicsMutexId %p last owner \"%s\" source %s line %d\n", + (void *)pmutexNode, threadName, + pmutexNode->pFileName, pmutexNode->lineno); +# else + printf("epicsMutexId %p source %s line %d\n", + (void *)pmutexNode, pmutexNode->pFileName, + pmutexNode->lineno); +# endif + if ( level > 0 ) { + epicsMutexOsdShow(pmutexNode->id,level-1); + } +} + +void epicsShareAPI epicsMutexShowAll(int onlyLocked,unsigned int level) +{ + epicsMutexParm *pmutexNode; + + if (epicsMutexOsiOnce == EPICS_THREAD_ONCE_INIT) + return; + + printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n", + ellCount(&mutexList),ellCount(&freeList)); + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) ); + while(pmutexNode) { + if(onlyLocked) { + epicsMutexLockStatus status; + status = epicsMutexOsdTryLock(pmutexNode->id); + if(status==epicsMutexLockOK) { + epicsMutexOsdUnlock(pmutexNode->id); + pmutexNode = + reinterpret_cast < epicsMutexParm * > + ( ellNext(&pmutexNode->node) ); + continue; + } + } + epicsMutexShow(pmutexNode, level); + pmutexNode = + reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) ); + } + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + +epicsMutex :: epicsMutex () : + id ( epicsMutexCreate () ) +{ + if ( this->id == 0 ) { + throw mutexCreateFailed (); + } +} + +epicsMutex :: epicsMutex ( const char *pFileName, int lineno ) : + id ( epicsMutexOsiCreate (pFileName, lineno) ) +{ + if ( this->id == 0 ) { + throw mutexCreateFailed (); + } +} + +epicsMutex ::~epicsMutex () +{ + epicsMutexDestroy ( this->id ); +} + +void epicsMutex::lock () +{ + epicsMutexLockStatus status = epicsMutexLock ( this->id ); + if ( status != epicsMutexLockOK ) { + throw invalidMutex (); + } +} + +bool epicsMutex::tryLock () +{ + epicsMutexLockStatus status = epicsMutexTryLock ( this->id ); + if ( status == epicsMutexLockOK ) { + return true; + } + else if ( status != epicsMutexLockTimeout ) { + throw invalidMutex (); + } + return false; +} + +void epicsMutex::unlock () +{ + epicsMutexUnlock ( this->id ); +} + +void epicsMutex :: show ( unsigned level ) const +{ + epicsMutexShow ( this->id, level ); +} + +static epicsThreadPrivate < epicsDeadlockDetectMutex > + * pCurrentMutexLevel = 0; + +static epicsThreadOnceId epicsDeadlockDetectMutexInit = EPICS_THREAD_ONCE_INIT; + +extern "C" +void epicsDeadlockDetectMutexInitFunc ( void * ) +{ + pCurrentMutexLevel = new epicsThreadPrivate < epicsDeadlockDetectMutex > (); +} + +epicsDeadlockDetectMutex:: + epicsDeadlockDetectMutex ( hierarchyLevel_t level ) : + hierarchyLevel ( level ), pPreviousLevel ( 0 ) +{ + epicsThreadOnce ( & epicsDeadlockDetectMutexInit, + epicsDeadlockDetectMutexInitFunc, 0 ); +} + +epicsDeadlockDetectMutex::~epicsDeadlockDetectMutex () +{ +} + +void epicsDeadlockDetectMutex::show ( unsigned level ) const +{ + this->mutex.show ( level ); +} + +void epicsDeadlockDetectMutex::lock () +{ + epicsDeadlockDetectMutex * pPrev = pCurrentMutexLevel->get(); + if ( pPrev && pPrev != this ) { + if ( pPrev->hierarchyLevel >= this->hierarchyLevel ) { + errlogPrintf ( "!!!! Deadlock Vulnerability Detected !!!! " + "at level %u and moving to level %u\n", + pPrev->hierarchyLevel, + this->hierarchyLevel ); + } + } + this->mutex.lock (); + if ( pPrev && pPrev != this ) { + pCurrentMutexLevel->set ( this ); + this->pPreviousLevel = pPrev; + } +} + +void epicsDeadlockDetectMutex::unlock () +{ + pCurrentMutexLevel->set ( this->pPreviousLevel ); + this->mutex.unlock (); +} + +bool epicsDeadlockDetectMutex::tryLock () +{ + bool success = this->mutex.tryLock (); + if ( success ) { + this->pPreviousLevel = pCurrentMutexLevel->get(); + pCurrentMutexLevel->set ( this ); + } + return success; +} + + diff --git a/modules/libcom/src/osi/epicsMutex.h b/modules/libcom/src/osi/epicsMutex.h new file mode 100644 index 000000000..6d74f9599 --- /dev/null +++ b/modules/libcom/src/osi/epicsMutex.h @@ -0,0 +1,117 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsMutexh +#define epicsMutexh + +#include "epicsAssert.h" + +#include "shareLib.h" + +typedef struct epicsMutexParm *epicsMutexId; +typedef enum { + epicsMutexLockOK,epicsMutexLockTimeout,epicsMutexLockError +} epicsMutexLockStatus; + +#ifdef __cplusplus + +#include "compilerDependencies.h" +#include "epicsGuard.h" + +#define newEpicsMutex new epicsMutex(__FILE__,__LINE__) + +class epicsShareClass epicsMutex { +public: + typedef epicsGuard guard_t; + typedef epicsGuard release_t; + class mutexCreateFailed; /* exception payload */ + class invalidMutex; /* exception payload */ + epicsMutex (); + epicsMutex ( const char *pFileName, int lineno ); + ~epicsMutex (); + void show ( unsigned level ) const; + void lock (); /* blocks until success */ + void unlock (); + bool tryLock (); /* true if successful */ +private: + epicsMutexId id; + epicsMutex ( const epicsMutex & ); + epicsMutex & operator = ( const epicsMutex & ); +}; + +class epicsShareClass epicsDeadlockDetectMutex { +public: + typedef epicsGuard guard_t; + typedef epicsGuard release_t; + typedef unsigned hierarchyLevel_t; + epicsDeadlockDetectMutex ( unsigned hierarchyLevel_t ); + ~epicsDeadlockDetectMutex (); + void show ( unsigned level ) const; + void lock (); /* blocks until success */ + void unlock (); + bool tryLock (); /* true if successful */ +private: + epicsMutex mutex; + const hierarchyLevel_t hierarchyLevel; + class epicsDeadlockDetectMutex * pPreviousLevel; + epicsDeadlockDetectMutex ( const epicsDeadlockDetectMutex & ); + epicsDeadlockDetectMutex & operator = ( const epicsDeadlockDetectMutex & ); +}; + +#endif /*__cplusplus*/ + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define epicsMutexCreate() epicsMutexOsiCreate(__FILE__,__LINE__) +epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiCreate( + const char *pFileName,int lineno); +#define epicsMutexMustCreate() epicsMutexOsiMustCreate(__FILE__,__LINE__) +epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( + const char *pFileName,int lineno); +epicsShareFunc void epicsShareAPI epicsMutexDestroy(epicsMutexId id); +epicsShareFunc void epicsShareAPI epicsMutexUnlock(epicsMutexId id); +epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexLock( + epicsMutexId id); +#define epicsMutexMustLock(ID) { \ + epicsMutexLockStatus status = epicsMutexLock(ID); \ + assert(status == epicsMutexLockOK); \ +} +epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( + epicsMutexId id); +epicsShareFunc void epicsShareAPI epicsMutexShow( + epicsMutexId id,unsigned int level); +epicsShareFunc void epicsShareAPI epicsMutexShowAll( + int onlyLocked,unsigned int level); + +/*NOTES: + epicsMutex MUST implement recursive locking + epicsMutex should implement priority inheritance and deletion safe +*/ + +/* + * The following is the interface to the OS dependent + * implementation and should NOT be called directly by + * user code + */ +struct epicsMutexOSD * epicsMutexOsdCreate(void); +void epicsMutexOsdDestroy(struct epicsMutexOSD *); +void epicsMutexOsdUnlock(struct epicsMutexOSD *); +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *); +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *); +void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned int level); + +#ifdef __cplusplus +} +#endif + +#include "osdMutex.h" + +#endif /* epicsMutexh */ diff --git a/modules/libcom/src/osi/epicsReadline.c b/modules/libcom/src/osi/epicsReadline.c new file mode 100644 index 000000000..0caa50fc2 --- /dev/null +++ b/modules/libcom/src/osi/epicsReadline.c @@ -0,0 +1,154 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Eric Norum */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "epicsReadline.h" + +/* Basic command-line input, no editing or history: */ +#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 + +/* OS-specific command-line editing and/or history: */ +#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 +#define EPICS_COMMANDLINE_LIBRARY_LEDLIB 1 +#define EPICS_COMMANDLINE_LIBRARY_OTHER 1 + +/* GNU readline, or Apple's libedit wrapper: */ +#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 + +#ifndef EPICS_COMMANDLINE_LIBRARY +# define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS +#endif + +struct osdContext; +struct readlineContext { + FILE *in; + char *line; + struct osdContext *osd; +}; + +static void osdReadlineBegin(struct readlineContext *); +static char * osdReadline(const char *prompt, struct readlineContext *); +static void osdReadlineEnd(struct readlineContext *); + +#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS + +static void osdReadlineBegin(struct readlineContext *rc) {} +static char * osdReadline(const char *prompt, struct readlineContext *rc) +{ + return NULL; +} +static void osdReadlineEnd(struct readlineContext *rc) {} + +#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE +# include "gnuReadline.c" +#else +# include "osdReadline.c" +#endif + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *rc = calloc(1, sizeof(*rc)); + + if (rc) { + rc->in = in; + rc->line = NULL; + if (!envGetConfigParamPtr(&IOCSH_HISTEDIT_DISABLE)) + osdReadlineBegin(rc); + } + return rc; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *rc = context; + FILE *in; + char *line; + int c; /* char is unsigned on some archs, EOF is -ve */ + int linelen = 0; + int linesize = 50; + + if (rc->osd) + return osdReadline(prompt, rc); + + free(rc->line); + rc->line = NULL; + if ((in = rc->in) == NULL) { + in = stdin; + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + } + line = (char *)malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + while ((c = getc(in)) != '\n') { + if (c == EOF) { + if (ferror(in)) { + if ((errno == EINTR) || (errno == EPIPE)) { + clearerr(in); + continue; + } + } + free (line); + return NULL; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf("Out of memory!\n"); + free(line); + return NULL; + } + line = cp; + } + line[linelen++] = c; + } + line[linelen] = '\0'; + rc->line = line; + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + if (context) { + struct readlineContext *rc = context; + + if (rc->osd) + osdReadlineEnd(rc); + else + free(rc->line); + free(rc); + } +} + diff --git a/modules/libcom/src/osi/epicsReadline.h b/modules/libcom/src/osi/epicsReadline.h new file mode 100644 index 000000000..1d0e8b761 --- /dev/null +++ b/modules/libcom/src/osi/epicsReadline.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INC_epicsReadline_H +#define INC_epicsReadline_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +epicsShareFunc void * epicsShareAPI epicsReadlineBegin (FILE *in); +epicsShareFunc char * epicsShareAPI epicsReadline (const char *prompt, void *context); +epicsShareFunc void epicsShareAPI epicsReadlineEnd (void *context); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsReadline_H */ diff --git a/modules/libcom/src/osi/epicsSignal.h b/modules/libcom/src/osi/epicsSignal.h new file mode 100644 index 000000000..79adf71d1 --- /dev/null +++ b/modules/libcom/src/osi/epicsSignal.h @@ -0,0 +1,49 @@ +/*************************************************************************\ + * Copyright (c) 2002 The University of Chicago, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsSignal_H +#define INC_epicsSignal_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +/* + * The requests in this interface are typically ignored on OS that do not implement + * POSIX signals. + */ + +struct epicsThreadOSD; + +/* + * Required to avoid problems with soft IOCs getting SIGHUPs when a + * Channel Access client disconnects + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ); + +/* + * Required to avoid terminating a process which is blocking in a + * socket send() call when the SIGPIPE signal is generated by the OS. + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ); + +/* + * required only if shutdown() and close() do not interrupt a thread blocking in + * a socket system call + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ); +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * ); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsSignal_H */ diff --git a/modules/libcom/src/osi/epicsSpin.h b/modules/libcom/src/osi/epicsSpin.h new file mode 100644 index 000000000..22ce8ec8f --- /dev/null +++ b/modules/libcom/src/osi/epicsSpin.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsSpinh +#define epicsSpinh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct epicsSpin *epicsSpinId; + +epicsShareFunc epicsSpinId epicsSpinCreate(void); +epicsShareFunc epicsSpinId epicsSpinMustCreate(void); +epicsShareFunc void epicsSpinDestroy(epicsSpinId); + +epicsShareFunc void epicsSpinLock(epicsSpinId); +epicsShareFunc int epicsSpinTryLock(epicsSpinId); +epicsShareFunc void epicsSpinUnlock(epicsSpinId); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsSpinh */ diff --git a/modules/libcom/src/osi/epicsStackTrace.c b/modules/libcom/src/osi/epicsStackTrace.c new file mode 100644 index 000000000..0fe6928c4 --- /dev/null +++ b/modules/libcom/src/osi/epicsStackTrace.c @@ -0,0 +1,123 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "epicsStackTracePvt.h" +#include "epicsStackTrace.h" + +/* How many stack frames to capture */ +#define MAXDEPTH 100 + +static epicsThreadOnceId stackTraceInitId = EPICS_THREAD_ONCE_INIT; +static epicsMutexId stackTraceMtx; + +static void stackTraceInit(void *unused) +{ + stackTraceMtx = epicsMutexMustCreate(); +} + +static void stackTraceLock(void) +{ + epicsThreadOnce( &stackTraceInitId, stackTraceInit, 0 ); + epicsMutexLock( stackTraceMtx ); +} + +static void stackTraceUnlock(void) +{ + epicsMutexUnlock( stackTraceMtx ); +} + +static int +dumpInfo(void *addr, epicsSymbol *sym_p) +{ +int rval = 0; + + rval += errlogPrintf("[%*p]", (int)(sizeof(addr)*2 + 2), addr); + if ( sym_p ) { + if ( sym_p->f_nam ) { + rval += errlogPrintf(": %s", sym_p->f_nam); + } + if ( sym_p->s_nam ) { + rval += errlogPrintf("(%s+0x%lx)", sym_p->s_nam, (unsigned long)((char*)addr - (char*)sym_p->s_val)); + } else { + rval += errlogPrintf("()"); + } + } + rval += errlogPrintf("\n"); + errlogFlush(); + + return rval; +} + +void epicsStackTrace(void) +{ +void **buf; +int i,n; +epicsSymbol sym; + + if ( 0 == epicsStackTraceGetFeatures() ) { + /* unsupported on this platform */ + return; + } + + if ( ! (buf = malloc(sizeof(*buf) * MAXDEPTH))) { + free(buf); + errlogPrintf("epicsStackTrace(): not enough memory for backtrace\n"); + return; + } + + n = epicsBackTrace(buf, MAXDEPTH); + + if ( n > 0 ) { + + stackTraceLock(); + + errlogPrintf("Dumping a stack trace of thread '%s':\n", epicsThreadGetNameSelf()); + + errlogFlush(); + + for ( i=0; i, 2011, 2014 + */ + +#ifndef INC_epicsStackTrace_H +#define INC_epicsStackTrace_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Dump a stack trace to the errlog */ +epicsShareFunc void epicsStackTrace(void); + +/* Inquire about functionality implemented on your system */ + +/* StackTrace provides numerical addresses */ +#define EPICS_STACKTRACE_ADDRESSES (1<<0) + +/* StackTrace is able to lookup dynamic symbols */ +#define EPICS_STACKTRACE_DYN_SYMBOLS (1<<1) + +/* StackTrace is able to lookup global symbols */ +#define EPICS_STACKTRACE_GBL_SYMBOLS (1<<2) + +/* StackTrace is able to lookup local symbols */ +#define EPICS_STACKTRACE_LCL_SYMBOLS (1<<3) + +/* returns ORed bitset of supported features */ +epicsShareFunc int epicsStackTraceGetFeatures(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/osi/epicsStackTracePvt.h b/modules/libcom/src/osi/epicsStackTracePvt.h new file mode 100644 index 000000000..de26044fa --- /dev/null +++ b/modules/libcom/src/osi/epicsStackTracePvt.h @@ -0,0 +1,48 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#ifndef INC_epicsStackTracePvt_H +#define INC_epicsStackTracePvt_H + +#include "shareLib.h" + +typedef struct epicsSymbol { + const char *f_nam; /* file where the symbol is defined */ + const char *s_nam; /* symbol name */ + void *s_val; /* symbol value */ +} epicsSymbol; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Take a snapshot of the stack into 'buf' (limited to buf_sz entries) + * RETURNS: actual number of entries in 'buf' + */ +epicsShareFunc int epicsBackTrace(void **buf, int buf_sz); + +/* Find symbol closest to 'addr'. + * + * If successful the routine fills in the members of *sym_p but + * note that 'f_nam' and/or 's_nam' may be NULL if the address + * cannot be resolved. + * + * RETURNS: 0 on success, nonzero on failure (not finding an address + * is not considered an error). + */ +epicsShareFunc int epicsFindAddr(void *addr, epicsSymbol *sym_p); + +/* report supported features (as reported by epicsStackTraceGetFeatures) */ +epicsShareFunc int epicsFindAddrGetFeatures(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/osi/epicsStdio.c b/modules/libcom/src/osi/epicsStdio.c new file mode 100644 index 000000000..52b65bbe4 --- /dev/null +++ b/modules/libcom/src/osi/epicsStdio.c @@ -0,0 +1,129 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdio.c */ + +/* Author: Marty Kraimer */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define epicsStdioStdStreams +#include "shareLib.h" +#include "epicsThread.h" +#include "epicsStdio.h" + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; +static epicsThreadPrivateId stdinThreadPrivateId; +static epicsThreadPrivateId stdoutThreadPrivateId; +static epicsThreadPrivateId stderrThreadPrivateId = 0; + +static void once(void *junk) +{ + stdinThreadPrivateId = epicsThreadPrivateCreate(); + stdoutThreadPrivateId = epicsThreadPrivateCreate(); + stderrThreadPrivateId = epicsThreadPrivateCreate(); +} + +FILE * epicsShareAPI epicsGetStdin(void) +{ + FILE *fp = epicsGetThreadStdin(); + + if (!fp) + fp = stdin; + return fp; +} + +FILE * epicsShareAPI epicsGetStdout(void) +{ + FILE *fp = epicsGetThreadStdout(); + + if (!fp) + fp = stdout; + return fp; +} + +FILE * epicsShareAPI epicsGetStderr(void) +{ + FILE *fp = epicsGetThreadStderr(); + + if (!fp) + fp = stderr; + return fp; +} + +FILE * epicsShareAPI epicsGetThreadStdin(void) +{ + epicsThreadOnce(&onceId,once,0); + return epicsThreadPrivateGet(stdinThreadPrivateId); +} + +FILE * epicsShareAPI epicsGetThreadStdout(void) +{ + epicsThreadOnce(&onceId,once,0); + return epicsThreadPrivateGet(stdoutThreadPrivateId); +} + +FILE * epicsShareAPI epicsGetThreadStderr(void) +{ + /* Deliberately don't do the epicsThreadOnce() here; epicsThreadInit() + * is allowed to use stderr inside its once() routine, in which case we + * must return the OS's stderr instead. There may be a tiny chance of a + * race happening here during initialization for some architectures, so + * we only use it for stderr to reduce the chance of that happening. + */ + if (!stderrThreadPrivateId) + return NULL; + return epicsThreadPrivateGet(stderrThreadPrivateId); +} + +void epicsShareAPI epicsSetThreadStdin(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stdinThreadPrivateId,fp); +} + +void epicsShareAPI epicsSetThreadStdout(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stdoutThreadPrivateId,fp); +} + +void epicsShareAPI epicsSetThreadStderr(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stderrThreadPrivateId,fp); +} + +int epicsShareAPI epicsStdoutPrintf(const char *pFormat, ...) +{ + va_list pvar; + int nchar; + FILE *stream = epicsGetStdout(); + + va_start(pvar, pFormat); + nchar = vfprintf(stream, pFormat, pvar); + va_end(pvar); + return nchar; +} + +int epicsShareAPI epicsStdoutPuts(const char *str) +{ + return fprintf(epicsGetStdout(), "%s\n", str); +} + +int epicsShareAPI epicsStdoutPutchar(int c) +{ + return putc(c, epicsGetStdout()); +} diff --git a/modules/libcom/src/osi/epicsStdio.h b/modules/libcom/src/osi/epicsStdio.h new file mode 100644 index 000000000..172d5cf87 --- /dev/null +++ b/modules/libcom/src/osi/epicsStdio.h @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdio.h */ + +#ifndef epicsStdioh +#define epicsStdioh + +#include +#include + +#include "shareLib.h" +#include "compilerDependencies.h" +#include "epicsTempFile.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef epicsStdioStdStreams +# undef stdin +# define stdin epicsGetStdin() +# undef stdout +# define stdout epicsGetStdout() +# undef stderr +# define stderr epicsGetStderr() +#endif + +/* Make printf, puts and putchar use *our* version of stdout */ + +#ifdef printf +# undef printf +#endif /* printf */ +#define printf epicsStdoutPrintf + +#ifdef puts +# undef puts +#endif /* puts */ +#define puts epicsStdoutPuts + +#ifdef putchar +# undef putchar +#endif /* putchar */ +#define putchar epicsStdoutPutchar + +epicsShareFunc int epicsShareAPI epicsSnprintf( + char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4); +epicsShareFunc int epicsShareAPI epicsVsnprintf( + char *str, size_t size, const char *format, va_list ap); + +/* + * truncate to specified size (we dont use truncate() + * because it is not portable) + * + * pFileName - name (and optionally path) of file + * size - the new file size (if file is curretly larger) + * + * returns TF_OK if the file is less than size bytes + * or if it was successfully truncated. Returns + * TF_ERROR if the file could not be truncated. + */ +enum TF_RETURN {TF_OK=0, TF_ERROR=1}; +epicsShareFunc enum TF_RETURN truncateFile ( const char *pFileName, unsigned long size ); + +/* The following are for redirecting stdin,stdout,stderr */ +epicsShareFunc FILE * epicsShareAPI epicsGetStdin(void); +epicsShareFunc FILE * epicsShareAPI epicsGetStdout(void); +epicsShareFunc FILE * epicsShareAPI epicsGetStderr(void); +/* These are intended for iocsh only */ +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdin(void); +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdout(void); +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStderr(void); +epicsShareFunc void epicsShareAPI epicsSetThreadStdin(FILE *); +epicsShareFunc void epicsShareAPI epicsSetThreadStdout(FILE *); +epicsShareFunc void epicsShareAPI epicsSetThreadStderr(FILE *); + +epicsShareFunc int epicsShareAPI epicsStdoutPrintf( + const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int epicsShareAPI epicsStdoutPuts(const char *str); +epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsStdioh */ diff --git a/modules/libcom/src/osi/epicsStdioRedirect.h b/modules/libcom/src/osi/epicsStdioRedirect.h new file mode 100644 index 000000000..6f99cc9a5 --- /dev/null +++ b/modules/libcom/src/osi/epicsStdioRedirect.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdioRedirect.h */ + +/* This file is now obselete, and is likely to be + * deleted in a future release of EPICS Base. + * + * Use the epicsStdio.h header file instead. + */ + +#ifndef epicsStdioRedirecth +#define epicsStdioRedirecth + +#include + +#endif /* epicsStdioRedirecth */ diff --git a/modules/libcom/src/osi/epicsTempFile.h b/modules/libcom/src/osi/epicsTempFile.h new file mode 100644 index 000000000..c48e336ec --- /dev/null +++ b/modules/libcom/src/osi/epicsTempFile.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsTempFile.h */ + +#ifndef INC_epicsTempFile_H +#define INC_epicsTempFile_H + +#include + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI epicsTempName(char *pbuf, size_t bufLen); +epicsShareFunc FILE * epicsShareAPI epicsTempFile(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsTempFile_H */ diff --git a/modules/libcom/src/osi/epicsThread.cpp b/modules/libcom/src/osi/epicsThread.cpp new file mode 100644 index 000000000..892d73de0 --- /dev/null +++ b/modules/libcom/src/osi/epicsThread.cpp @@ -0,0 +1,354 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// Author: Jeff Hill +// + +#include +#include + +#include +#include +#include +#include + +// The following is required for Solaris builds +#undef __EXTENSIONS__ + +#define epicsExportSharedSymbols +#include "epicsAlgorithm.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsAssert.h" +#include "epicsGuard.h" +#include "errlog.h" + +using namespace std; + +epicsThreadRunable::~epicsThreadRunable () {} +void epicsThreadRunable::run () {} +void epicsThreadRunable::show ( unsigned int ) const {} + +class epicsThread :: unableToCreateThread : + public std :: exception { +public: + const char * what () const throw (); +}; + +const char * epicsThread :: + unableToCreateThread :: what () const throw () +{ + return "unable to create thread"; +} + +void epicsThread :: printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ) +{ + char date[64]; + try { + epicsTime cur = epicsTime :: getCurrent (); + cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + } + catch ( ... ) { + strcpy ( date, "" ); + } + char name [128]; + epicsThreadGetName ( this->id, name, sizeof ( name ) ); + errlogPrintf ( + "epicsThread: Unexpected C++ exception \"%s\" " + "with type \"%s\" in thread \"%s\" at %s\n", + pExceptionContext, pExceptionTypeName, name, date ); + errlogFlush (); + // This behavior matches the C++ implementation when an exception + // isn't handled by the thread code. Users can install their own + // application-specific unexpected handler if preferred. + std::unexpected (); +} + +extern "C" void epicsThreadCallEntryPoint ( void * pPvt ) +{ + epicsThread * pThread = + static_cast ( pPvt ); + bool threadDestroyed = false; + try { + pThread->pThreadDestroyed = & threadDestroyed; + if ( pThread->beginWait () ) { + pThread->runable.run (); + // The run() routine may have destroyed the epicsThread + // object by now; pThread can only be used below here + // when the threadDestroyed flag is false. + } + } + catch ( const epicsThread::exitException & ) { + } + catch ( std :: exception & except ) { + if ( ! threadDestroyed ) { + pThread->printLastChanceExceptionMessage ( + typeid ( except ).name (), except.what () ); + } + } + catch ( ... ) { + if ( ! threadDestroyed ) { + pThread->printLastChanceExceptionMessage ( + "catch ( ... )", "Non-standard C++ exception" ); + } + } + if ( ! threadDestroyed ) { + epicsGuard < epicsMutex > guard ( pThread->mutex ); + pThread->pThreadDestroyed = NULL; + pThread->terminated = true; + pThread->exitEvent.signal (); + // After the terminated flag is set and guard's destructor + // releases the lock, pThread must never be used again. + } +} + +bool epicsThread::beginWait () throw () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( ! this->begin && ! this->cancel ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->event.wait (); + } + return this->begin && ! this->cancel; +} + +void epicsThread::exit () +{ + throw exitException (); +} + +void epicsThread::exitWait () throw () +{ + bool success = this->exitWait ( DBL_MAX ); + assert ( success ); +} + +bool epicsThread::exitWait ( const double delay ) throw () +{ + try { + // When called (usually by a destructor) in the context of + // the managed thread we can't wait for the thread to exit. + // Set the threadDestroyed flag and return success. + if ( this->isCurrentThread() ) { + if ( this->pThreadDestroyed ) { + *this->pThreadDestroyed = true; + } + return true; + } + epicsTime exitWaitBegin = epicsTime::getCurrent (); + double exitWaitElapsed = 0.0; + epicsGuard < epicsMutex > guard ( this->mutex ); + this->cancel = true; + while ( ! this->terminated && exitWaitElapsed < delay ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->event.signal (); + this->exitEvent.wait ( delay - exitWaitElapsed ); + epicsTime current = epicsTime::getCurrent (); + exitWaitElapsed = current - exitWaitBegin; + } + } + catch ( std :: exception & except ) { + errlogPrintf ( + "epicsThread::exitWait(): Unexpected exception " + " \"%s\"\n", + except.what () ); + epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); + } + catch ( ... ) { + errlogPrintf ( + "Non-standard unexpected exception in " + "epicsThread::exitWait()\n" ); + epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); + } + // the event mechanism is used for other purposes + this->event.signal (); + return this->terminated; +} + +epicsThread::epicsThread ( + epicsThreadRunable & runableIn, const char * pName, + unsigned stackSize, unsigned priority ) : + runable ( runableIn ), id ( 0 ), pThreadDestroyed ( 0 ), + begin ( false ), cancel ( false ), terminated ( false ) +{ + this->id = epicsThreadCreate ( + pName, priority, stackSize, epicsThreadCallEntryPoint, + static_cast < void * > ( this ) ); + if ( ! this->id ) { + throw unableToCreateThread (); + } +} + +epicsThread::~epicsThread () throw () +{ + while ( ! this->exitWait ( 10.0 ) ) { + char nameBuf [256]; + this->getName ( nameBuf, sizeof ( nameBuf ) ); + fprintf ( stderr, + "epicsThread::~epicsThread(): " + "blocking for thread \"%s\" to exit\n", + nameBuf ); + fprintf ( stderr, + "was epicsThread object destroyed before thread exit ?\n"); + } +} + +void epicsThread::start () throw () +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->begin = true; + } + this->event.signal (); +} + +bool epicsThread::isCurrentThread () const throw () +{ + return ( epicsThreadGetIdSelf () == this->id ); +} + +void epicsThread::resume () throw () +{ + epicsThreadResume ( this->id ); +} + +void epicsThread::getName ( char *name, size_t size ) const throw () +{ + epicsThreadGetName ( this->id, name, size ); +} + +epicsThreadId epicsThread::getId () const throw () +{ + return this->id; +} + +unsigned int epicsThread::getPriority () const throw () +{ + return epicsThreadGetPriority (this->id); +} + +void epicsThread::setPriority (unsigned int priority) throw () +{ + epicsThreadSetPriority (this->id, priority); +} + +bool epicsThread::priorityIsEqual (const epicsThread &otherThread) const throw () +{ + if ( epicsThreadIsEqual (this->id, otherThread.id) ) { + return true; + } + return false; +} + +bool epicsThread::isSuspended () const throw () +{ + if ( epicsThreadIsSuspended (this->id) ) { + return true; + } + return false; +} + +bool epicsThread::operator == (const epicsThread &rhs) const throw () +{ + return (this->id == rhs.id); +} + +void epicsThread::suspendSelf () throw () +{ + epicsThreadSuspendSelf (); +} + +void epicsThread::sleep (double seconds) throw () +{ + epicsThreadSleep (seconds); +} + +const char *epicsThread::getNameSelf () throw () +{ + return epicsThreadGetNameSelf (); +} + +bool epicsThread::isOkToBlock () throw () +{ + return epicsThreadIsOkToBlock() != 0; +} + +void epicsThread::setOkToBlock(bool isOkToBlock) throw () +{ + epicsThreadSetOkToBlock(static_cast(isOkToBlock)); +} + +void epicsThreadPrivateBase::throwUnableToCreateThreadPrivate () +{ + throw epicsThreadPrivateBase::unableToCreateThreadPrivate (); +} + +void epicsThread :: show ( unsigned level ) const throw () +{ + ::printf ( "epicsThread at %p\n", this->id ); + if ( level > 0u ) { + epicsThreadShow ( this->id, level - 1 ); + if ( level > 1u ) { + ::printf ( "pThreadDestroyed = %p\n", this->pThreadDestroyed ); + ::printf ( "begin = %c, cancel = %c, terminated = %c\n", + this->begin ? 'T' : 'F', + this->cancel ? 'T' : 'F', + this->terminated ? 'T' : 'F' ); + this->runable.show ( level - 2u ); + this->mutex.show ( level - 2u ); + ::printf ( "general purpose event\n" ); + this->event.show ( level - 2u ); + ::printf ( "exit event\n" ); + this->exitEvent.show ( level - 2u ); + } + } +} + +extern "C" { + static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT; + epicsThreadPrivateId okToBlockPrivate; + static const int okToBlockNo = 0; + static const int okToBlockYes = 1; + + static void epicsThreadOnceIdInit(void *) + { + okToBlockPrivate = epicsThreadPrivateCreate(); + } + + int epicsShareAPI epicsThreadIsOkToBlock(void) + { + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate); + return (pokToBlock ? *pokToBlock : 0); + } + + void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock) + { + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo; + epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock); + } + + epicsThreadId epicsShareAPI epicsThreadMustCreate ( + const char *name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) + { + epicsThreadId id = epicsThreadCreate ( + name, priority, stackSize, funptr, parm ); + assert ( id ); + return id; + } +} // extern "C" + +// Ensure the main thread gets a unique ID +epicsThreadId epicsThreadMainId = epicsThreadGetIdSelf(); diff --git a/modules/libcom/src/osi/epicsThread.h b/modules/libcom/src/osi/epicsThread.h new file mode 100644 index 000000000..84b2c4788 --- /dev/null +++ b/modules/libcom/src/osi/epicsThread.h @@ -0,0 +1,247 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsThreadh +#define epicsThreadh + +#include + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*EPICSTHREADFUNC)(void *parm); + +#define epicsThreadPriorityMax 99 +#define epicsThreadPriorityMin 0 + +/* some generic values */ +#define epicsThreadPriorityLow 10 +#define epicsThreadPriorityMedium 50 +#define epicsThreadPriorityHigh 90 + +/* some iocCore specific values */ +#define epicsThreadPriorityCAServerLow 20 +#define epicsThreadPriorityCAServerHigh 40 +#define epicsThreadPriorityScanLow 60 +#define epicsThreadPriorityScanHigh 70 +#define epicsThreadPriorityIocsh 91 +#define epicsThreadPriorityBaseMax 91 + +/* stack sizes for each stackSizeClass are implementation and CPU dependent */ +typedef enum { + epicsThreadStackSmall, epicsThreadStackMedium, epicsThreadStackBig +} epicsThreadStackSizeClass; + +typedef enum { + epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess +} epicsThreadBooleanStatus; + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize( + epicsThreadStackSizeClass size); + +/* (epicsThreadId)0 is guaranteed to be an invalid thread id */ +typedef struct epicsThreadOSD *epicsThreadId; + +typedef epicsThreadId epicsThreadOnceId; +#define EPICS_THREAD_ONCE_INIT 0 + +epicsShareFunc void epicsShareAPI epicsThreadOnce( + epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); + +/* When real-time scheduling is active, attempt any post-init operations + * that preserve real-time performance. For POSIX targets this locks the + * process into RAM, preventing swap-related VM faults. + */ +epicsShareFunc void epicsThreadRealtimeLock(void); + +epicsShareFunc void epicsShareAPI epicsThreadExitMain(void); + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate ( + const char * name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void * parm ); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate ( + const char * name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void * parm ); +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void); +epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id); +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority( + epicsThreadId id); +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void); +epicsShareFunc void epicsShareAPI epicsThreadSetPriority( + epicsThreadId id,unsigned int priority); +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI + epicsThreadHighestPriorityLevelBelow ( + unsigned int priority, unsigned *pPriorityJustBelow); +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI + epicsThreadLowestPriorityLevelAbove ( + unsigned int priority, unsigned *pPriorityJustAbove); +epicsShareFunc int epicsShareAPI epicsThreadIsEqual( + epicsThreadId id1, epicsThreadId id2); +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId id); +epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds); +epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum(void); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name); +epicsShareFunc int epicsThreadGetCPUs(void); + +epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf(void); + +/* For epicsThreadGetName name is guaranteed to be null terminated */ +/* size is size of buffer to hold name (including terminator) */ +/* Failure results in an empty string stored in name */ +epicsShareFunc void epicsShareAPI epicsThreadGetName( + epicsThreadId id, char *name, size_t size); + +epicsShareFunc int epicsShareAPI epicsThreadIsOkToBlock(void); +epicsShareFunc void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock); + +epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level); +epicsShareFunc void epicsShareAPI epicsThreadShow( + epicsThreadId id,unsigned int level); + +/* Hooks called when a thread starts, map function called once for every thread */ +typedef void (*EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id); +epicsShareFunc int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook); +epicsShareFunc int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook); +epicsShareFunc void epicsThreadHooksShow(void); +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func); + +typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void); +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id); +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet(epicsThreadPrivateId,void *); +epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet(epicsThreadPrivateId); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +#include "epicsEvent.h" +#include "epicsMutex.h" + +class epicsShareClass epicsThreadRunable { +public: + virtual ~epicsThreadRunable () = 0; + virtual void run () = 0; + virtual void show ( unsigned int level ) const; +}; + +extern "C" void epicsThreadCallEntryPoint ( void * ); + +class epicsShareClass epicsThread { +public: + epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize, + unsigned int priority=epicsThreadPriorityLow ); + ~epicsThread () throw (); + void start () throw (); + void exitWait () throw (); + bool exitWait ( const double delay ) throw (); + static void exit (); + void resume () throw (); + void getName ( char * name, size_t size ) const throw (); + epicsThreadId getId () const throw (); + unsigned int getPriority () const throw (); + void setPriority ( unsigned int ) throw (); + bool priorityIsEqual ( const epicsThread & ) const throw (); + bool isSuspended () const throw (); + bool isCurrentThread () const throw (); + bool operator == ( const epicsThread & ) const throw (); + void show ( unsigned level ) const throw (); + + /* these operate on the current thread */ + static void suspendSelf () throw (); + static void sleep (double seconds) throw (); + static const char * getNameSelf () throw (); + static bool isOkToBlock () throw (); + static void setOkToBlock ( bool isOkToBlock ) throw (); + + /* exceptions */ + class unableToCreateThread; +private: + epicsThreadRunable & runable; + epicsThreadId id; + epicsMutex mutex; + epicsEvent event; + epicsEvent exitEvent; + bool * pThreadDestroyed; + bool begin; + bool cancel; + bool terminated; + + bool beginWait () throw (); + epicsThread ( const epicsThread & ); + epicsThread & operator = ( const epicsThread & ); + friend void epicsThreadCallEntryPoint ( void * ); + void printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ); + /* exceptions */ + class exitException {}; +}; + +class epicsShareClass epicsThreadPrivateBase { +public: + class unableToCreateThreadPrivate {}; /* exception */ +protected: + static void throwUnableToCreateThreadPrivate (); +}; + +template < class T > +class epicsThreadPrivate : + private epicsThreadPrivateBase { +public: + epicsThreadPrivate (); + ~epicsThreadPrivate () throw (); + T * get () const throw (); + void set (T *) throw (); +private: + epicsThreadPrivateId id; +}; + +#endif /* __cplusplus */ + +#include "osdThread.h" + +#ifdef __cplusplus + +template +inline epicsThreadPrivate::epicsThreadPrivate () +{ + this->id = epicsThreadPrivateCreate (); + if ( this->id == 0 ) { + epicsThreadPrivateBase::throwUnableToCreateThreadPrivate (); + } +} + +template +inline epicsThreadPrivate::~epicsThreadPrivate () throw () +{ + epicsThreadPrivateDelete ( this->id ); +} + +template +inline T *epicsThreadPrivate::get () const throw () +{ + return static_cast ( epicsThreadPrivateGet (this->id) ); +} + +template +inline void epicsThreadPrivate::set (T *pIn) throw () +{ + epicsThreadPrivateSet ( this->id, static_cast (pIn) ); +} + +#endif /* ifdef __cplusplus */ + +#endif /* epicsThreadh */ diff --git a/modules/libcom/src/osi/epicsTime.cpp b/modules/libcom/src/osi/epicsTime.cpp new file mode 100644 index 000000000..6dd1d3f97 --- /dev/null +++ b/modules/libcom/src/osi/epicsTime.cpp @@ -0,0 +1,1133 @@ +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTime.cpp */ +/* Author Jeffrey O. Hill */ + +// Notes: +// 1) The epicsTime::nSec field is not public and so it could be +// changed to work more like the fractional seconds field in the NTP time +// stamp. That would significantly improve the precision of epicsTime on +// 64 bit architectures. +// + +#include + +#include +#include +#include +#include +#include +#include +#include // vxWorks 6.0 requires this include + +#define epicsExportSharedSymbols +#include "locationException.h" +#include "epicsAssert.h" +#include "epicsVersion.h" +#include "envDefs.h" +#include "epicsTime.h" +#include "osiSock.h" /* pull in struct timeval */ +#include "epicsStdio.h" + +static const char pEpicsTimeVersion[] = + "@(#) " EPICS_VERSION_STRING ", Common Utilities Library " __DATE__; + +// +// useful public constants +// +static const unsigned mSecPerSec = 1000u; +static const unsigned uSecPerMSec = 1000u; +static const unsigned uSecPerSec = uSecPerMSec * mSecPerSec; +static const unsigned nSecPerUSec = 1000u; +static const unsigned nSecPerSec = nSecPerUSec * uSecPerSec; +static const unsigned nSecFracDigits = 9u; + + +// Timescale conversion data + +static const unsigned long NTP_TIME_AT_POSIX_EPOCH = 2208988800ul; +static const unsigned long NTP_TIME_AT_EPICS_EPOCH = + NTP_TIME_AT_POSIX_EPOCH + POSIX_TIME_AT_EPICS_EPOCH; + +// +// epicsTime (const unsigned long secIn, const unsigned long nSecIn) +// +inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : + secPastEpoch ( secIn ), nSec ( nSecIn ) +{ + if (nSecIn >= nSecPerSec) { + this->secPastEpoch += nSecIn / nSecPerSec; + this->nSec = nSecIn % nSecPerSec; + } +} + +// +// epicsTimeLoadTimeInit +// +class epicsTimeLoadTimeInit { +public: + epicsTimeLoadTimeInit (); + double epicsEpochOffset; // seconds + double time_tSecPerTick; // seconds (both NTP and EPICS use int sec) + unsigned long epicsEpochOffsetAsAnUnsignedLong; + bool useDiffTimeOptimization; +}; + +// +// epicsTimeLoadTimeInit () +// +epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () +{ + // All we know about time_t is that it is an arithmetic type. + time_t t_zero = static_cast (0); + time_t t_one = static_cast (1); + this->time_tSecPerTick = difftime (t_one, t_zero); + + /* The EPICS epoch (1/1/1990 00:00:00UTC) was 631152000 seconds after + * the ANSI epoch (1/1/1970 00:00:00UTC) + * Convert this offset into time_t units, however this must not be + * calculated using local time (i.e. using mktime() or similar), since + * in the UK the ANSI Epoch had daylight saving time in effect, and + * the value calculated would be 3600 seconds wrong.*/ + this->epicsEpochOffset = + (double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick; + + if (this->time_tSecPerTick == 1.0 && + this->epicsEpochOffset <= ULONG_MAX && + this->epicsEpochOffset >= 0) { + // We can use simpler code on Posix-compliant systems + this->useDiffTimeOptimization = true; + this->epicsEpochOffsetAsAnUnsignedLong = + static_cast(this->epicsEpochOffset); + } else { + // Forced to use the slower but correct code + this->useDiffTimeOptimization = false; + this->epicsEpochOffsetAsAnUnsignedLong = 0; + } +} + +// +// private epicsTime::addNanoSec () +// +// Most formats keep the nSec value as an unsigned long, so are +ve. +// struct timeval's tv_usec may be -1, but I think that means error, +// so this private method never needs to handle -ve offsets. +// +void epicsTime :: addNanoSec ( long nSecAdj ) +{ + if (nSecAdj <= 0) + return; + + if (static_cast(nSecAdj) >= nSecPerSec) { + this->secPastEpoch += nSecAdj / nSecPerSec; + nSecAdj %= nSecPerSec; + } + + this->nSec += nSecAdj; // Can't overflow + if (this->nSec >= nSecPerSec) { + this->secPastEpoch++; + this->nSec -= nSecPerSec; + } +} + +// +// epicsTime (const time_t_wrapper &tv) +// +epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) +{ + // avoid c++ static initialization order issues + static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); + + // + // try to directly map time_t into an unsigned long integer because this is + // faster on systems w/o hardware floating point and a simple integer type time_t. + // + if ( lti.useDiffTimeOptimization ) { + // LONG_MAX is used here and not ULONG_MAX because some systems (linux) + // still store time_t as a long. + if ( ansiTimeTicks.ts > 0 && ansiTimeTicks.ts <= LONG_MAX ) { + unsigned long ticks = static_cast < unsigned long > ( ansiTimeTicks.ts ); + if ( ticks >= lti.epicsEpochOffsetAsAnUnsignedLong ) { + this->secPastEpoch = ticks - lti.epicsEpochOffsetAsAnUnsignedLong; + } + else { + this->secPastEpoch = ( ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) + ticks; + } + this->nSec = 0; + return; + } + } + + // + // otherwise map time_t, which ANSI C and POSIX define as any arithmetic type, + // into type double + // + double sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset; + + // + // map into the the EPICS time stamp range (which allows rollover) + // + static double uLongMax = static_cast (ULONG_MAX); + if ( sec < 0.0 ) { + if ( sec < -uLongMax ) { + sec = sec + static_cast ( -sec / uLongMax ) * uLongMax; + } + sec += uLongMax; + } + else if ( sec > uLongMax ) { + sec = sec - static_cast ( sec / uLongMax ) * uLongMax; + } + + this->secPastEpoch = static_cast ( sec ); + this->nSec = static_cast ( ( sec-this->secPastEpoch ) * nSecPerSec ); +} + +epicsTime::epicsTime (const epicsTimeStamp &ts) +{ + if ( ts.nsec < nSecPerSec ) { + this->secPastEpoch = ts.secPastEpoch; + this->nSec = ts.nsec; + } + else { + throw std::logic_error ( + "epicsTimeStamp has overflow in nano-seconds field" ); + } +} + +epicsTime::epicsTime () : + secPastEpoch(0u), nSec(0u) {} + +epicsTime::epicsTime (const epicsTime &t) : + secPastEpoch (t.secPastEpoch), nSec (t.nSec) {} + +epicsTime epicsTime::getCurrent () +{ + epicsTimeStamp current; + int status = epicsTimeGetCurrent (¤t); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + return epicsTime ( current ); +} + +epicsTime epicsTime::getEvent (const epicsTimeEvent &event) +{ + epicsTimeStamp current; + int status = epicsTimeGetEvent (¤t, event); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + return epicsTime ( current ); +} + +// +// operator time_t_wrapper () +// +epicsTime::operator time_t_wrapper () const +{ + // avoid c++ static initialization order issues + static epicsTimeLoadTimeInit & lti = * new epicsTimeLoadTimeInit (); + time_t_wrapper wrap; + + if ( lti.useDiffTimeOptimization ) { + if ( this->secPastEpoch < ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) { + wrap.ts = static_cast ( this->secPastEpoch + lti.epicsEpochOffsetAsAnUnsignedLong ); + return wrap; + } + } + + // + // map type double into time_t which ansi C defines as some arithmetic type + // + double tmp = (this->secPastEpoch + lti.epicsEpochOffset) / lti.time_tSecPerTick; + tmp += (this->nSec / lti.time_tSecPerTick) / nSecPerSec; + + wrap.ts = static_cast ( tmp ); + + return wrap; +} + +// +// convert to ANSI C struct tm (with nano seconds) adjusted for the local time zone +// +epicsTime::operator local_tm_nano_sec () const +{ + time_t_wrapper ansiTimeTicks = *this; + + local_tm_nano_sec tm; + + int status = epicsTime_localtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); + if ( status ) { + throw std::logic_error ( "epicsTime_localtime failed" ); + } + + tm.nSec = this->nSec; + + return tm; +} + +// +// convert to ANSI C struct tm (with nano seconds) adjusted for UTC +// +epicsTime::operator gm_tm_nano_sec () const +{ + time_t_wrapper ansiTimeTicks = *this; + + gm_tm_nano_sec tm; + + int status = epicsTime_gmtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); + if ( status ) { + throw std::logic_error ( "epicsTime_gmtime failed" ); + } + + tm.nSec = this->nSec; + + return tm; +} + +// +// epicsTime (const local_tm_nano_sec &tm) +// +epicsTime::epicsTime (const local_tm_nano_sec &tm) +{ + struct tm tmp = tm.ansi_tm; + time_t_wrapper ansiTimeTicks = { mktime (&tmp) }; + + static const time_t mktimeError = static_cast (-1); + if (ansiTimeTicks.ts == mktimeError) { + throwWithLocation ( formatProblemWithStructTM () ); + } + + *this = epicsTime(ansiTimeTicks); + this->addNanoSec(tm.nSec); +} + +// +// epicsTime (const gm_tm_nano_sec &tm) +// + +// do conversion avoiding the timezone mechanism +static inline int is_leap(int year) +{ + if (year % 400 == 0) + return 1; + if (year % 100 == 0) + return 0; + if (year % 4 == 0) + return 1; + return 0; +} + +static inline int days_from_0(int year) +{ + year--; + return 365 * year + (year / 400) - (year / 100) + (year / 4); +} + +static inline int days_from_1970(int year) +{ + static const int days_from_0_to_1970 = days_from_0(1970); + return days_from_0(year) - days_from_0_to_1970; +} + +static inline int days_from_1jan(int year, int month, int day) +{ + static const int days[2][12] = + { + { 0,31,59,90,120,151,181,212,243,273,304,334}, + { 0,31,60,91,121,152,182,213,244,274,305,335} + }; + return days[is_leap(year)][month-1] + day - 1; +} + +epicsTime::epicsTime (const gm_tm_nano_sec &tm) +{ + int year = tm.ansi_tm.tm_year + 1900; + int month = tm.ansi_tm.tm_mon; + if (month > 11) { + year += month / 12; + month %= 12; + } else if (month < 0) { + int years_diff = (-month + 11) / 12; + year -= years_diff; + month += 12 * years_diff; + } + month++; + + int day = tm.ansi_tm.tm_mday; + int day_of_year = days_from_1jan(year, month, day); + int days_since_epoch = days_from_1970(year) + day_of_year; + + time_t_wrapper ansiTimeTicks; + ansiTimeTicks.ts = ((days_since_epoch + * 24 + tm.ansi_tm.tm_hour) + * 60 + tm.ansi_tm.tm_min) + * 60 + tm.ansi_tm.tm_sec; + + *this = epicsTime(ansiTimeTicks); + this->addNanoSec(tm.nSec); +} + +// +// operator struct timespec () +// +epicsTime::operator struct timespec () const +{ + struct timespec ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_nsec = static_cast (this->nSec); + return ts; +} + +// +// epicsTime (const struct timespec &ts) +// +epicsTime::epicsTime (const struct timespec &ts) +{ + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_nsec); +} + +// +// operator struct timeval () +// +epicsTime::operator struct timeval () const +{ + struct timeval ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + // On Posix systems timeval :: tv_sec is a time_t so this can be + // a direct assignment. On other systems I dont know that we can + // guarantee that time_t and timeval :: tv_sec will have the + // same epoch or have the same scaling factor to discrete seconds. + // For example, on windows time_t changed recently to a 64 bit + // quantity but timeval is still a long. That can cause problems + // on 32 bit systems. So technically, we should have an os + // dependent conversion between time_t and timeval :: tv_sec? + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_usec = static_cast < long > ( this->nSec / nSecPerUSec ); + return ts; +} + +// +// epicsTime (const struct timeval &ts) +// +epicsTime::epicsTime (const struct timeval &ts) +{ + time_t_wrapper ansiTimeTicks; + // On Posix systems timeval :: tv_sec is a time_t so this can be + // a direct assignment. On other systems I dont know that we can + // guarantee that time_t and timeval :: tv_sec will have the + // same epoch or have the same scaling factor to discrete seconds. + // For example, on windows time_t changed recently to a 64 bit + // quantity but timeval is still a long. That can cause problems + // on 32 bit systems. So technically, we should have an os + // dependent conversion between time_t and timeval :: tv_sec? + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_usec * nSecPerUSec); +} + + +static const double NTP_FRACTION_DENOMINATOR = 1.0 + 0xffffffff; + +struct l_fp { /* NTP time stamp */ + epicsUInt32 l_ui; /* sec past NTP epoch */ + epicsUInt32 l_uf; /* fractional seconds */ +}; + +// +// epicsTime::l_fp () +// +epicsTime::operator l_fp () const +{ + l_fp ts; + ts.l_ui = this->secPastEpoch + NTP_TIME_AT_EPICS_EPOCH; + ts.l_uf = static_cast < unsigned long > + ( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec ); + return ts; +} + +// +// epicsTime::epicsTime ( const l_fp & ts ) +// +epicsTime::epicsTime ( const l_fp & ts ) +{ + this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH; + this->nSec = static_cast < unsigned long > + ( ( ts.l_uf / NTP_FRACTION_DENOMINATOR ) * nSecPerSec ); +} + +epicsTime::operator epicsTimeStamp () const +{ + if ( this->nSec >= nSecPerSec ) { + throw std::logic_error ( + "epicsTimeStamp has overflow in nano-seconds field?" ); + } + epicsTimeStamp ts; + // + // truncation by design + // ------------------- + // epicsTime::secPastEpoch is based on ulong and has much greater range + // on 64 bit hosts than the original epicsTimeStamp::secPastEpoch. The + // epicsTimeStamp::secPastEpoch is based on epicsUInt32 so that it will + // match the original network protocol. Of course one can anticipate + // that eventually, a epicsUInt64 based network time stamp will be + // introduced when 64 bit architectures are more ubiquitous. + // + // Truncation usually works fine here because the routines in this code + // that compute time stamp differences and compare time stamps produce + // good results when the operands are on either side of a time stamp + // rollover as long as the difference between the operands does not exceed + // 1/2 of full range. + // + ts.secPastEpoch = static_cast < epicsUInt32 > ( this->secPastEpoch ); + ts.nsec = static_cast < epicsUInt32 > ( this->nSec ); + return ts; +} + +// Break up a format string into "%0f" +// (where in an unsigned integer) +// Result: +// A) Copies a prefix which is valid for ANSI strftime into the supplied +// buffer setting the buffer to an empty string if no prefix is present. +// B) Indicates whether a valid "%0f]" is present or not and if so +// specifying its nnnn +// C) returning a pointer to the postfix (which might be passed again +// to fracFormatFind. +static const char * fracFormatFind ( + const char * const pFormat, + char * const pPrefixBuf, + const size_t prefixBufLen, + bool & fracFmtFound, + unsigned long & fracFmtWidth ) +{ + assert ( prefixBufLen > 1 ); + unsigned long width = ULONG_MAX; + bool fracFound = false; + const char * pAfter = pFormat; + const char * pFmt = pFormat; + while ( *pFmt != '\0' ) { + if ( *pFmt == '%' ) { + if ( pFmt[1] == '%' ) { + pFmt++; + } + else if ( pFmt[1] == 'f' ) { + fracFound = true; + pAfter = & pFmt[2]; + break; + } + else { + errno = 0; + char * pAfterTmp; + unsigned long result = strtoul ( pFmt + 1, & pAfterTmp, 10 ); + if ( errno == 0 && *pAfterTmp == 'f' && result > 0 ) { + width = result; + fracFound = true; + pAfter = pAfterTmp + 1; + break; + } + } + } + pFmt++; + pAfter = pFmt; + } + size_t len = pFmt - pFormat; + if ( len < prefixBufLen ) { + strncpy ( pPrefixBuf, pFormat, len ); + pPrefixBuf [ len ] = '\0'; + if ( fracFound ) { + fracFmtFound = true; + fracFmtWidth = width; + } + else { + fracFmtFound = false; + } + } + else { + strncpy ( pPrefixBuf, "", prefixBufLen - 1 ); + pPrefixBuf [ prefixBufLen - 1 ] = '\0'; + fracFmtFound = false; + pAfter = ""; + } + + return pAfter; +} + +// +// size_t epicsTime::strftime () +// +size_t epicsTime::strftime ( + char * pBuff, size_t bufLength, const char * pFormat ) const +{ + if ( bufLength == 0u ) { + return 0u; + } + + // presume that EPOCH date is an uninitialized time stamp + if ( this->secPastEpoch == 0 && this->nSec == 0u ) { + strncpy ( pBuff, "", bufLength ); + pBuff[bufLength-1] = '\0'; + return strlen ( pBuff ); + } + + char * pBufCur = pBuff; + const char * pFmt = pFormat; + size_t bufLenLeft = bufLength; + while ( *pFmt != '\0' && bufLenLeft > 1 ) { + // look for "%0f" at the end (used for fractional second formatting) + char strftimePrefixBuf [256]; + bool fracFmtFound; + unsigned long fracWid = 0; + pFmt = fracFormatFind ( + pFmt, + strftimePrefixBuf, sizeof ( strftimePrefixBuf ), + fracFmtFound, fracWid ); + + // nothing more in the string, then quit + if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) { + break; + } + // all but fractional seconds use strftime formatting + if ( strftimePrefixBuf[0] != '\0' ) { + local_tm_nano_sec tmns = *this; + size_t strftimeNumChar = :: strftime ( + pBufCur, bufLenLeft, strftimePrefixBuf, & tmns.ansi_tm ); + pBufCur [ strftimeNumChar ] = '\0'; + pBufCur += strftimeNumChar; + bufLenLeft -= strftimeNumChar; + } + + // fractional seconds formating + if ( fracFmtFound && bufLenLeft > 1 ) { + if ( fracWid > nSecFracDigits ) { + fracWid = nSecFracDigits; + } + // verify that there are enough chars left for the fractional seconds + if ( fracWid < bufLenLeft ) + { + local_tm_nano_sec tmns = *this; + if ( tmns.nSec < nSecPerSec ) { + // divisors for fraction (see below) + static const unsigned long div[] = { + static_cast < unsigned long > ( 1e9 ), + static_cast < unsigned long > ( 1e8 ), + static_cast < unsigned long > ( 1e7 ), + static_cast < unsigned long > ( 1e6 ), + static_cast < unsigned long > ( 1e5 ), + static_cast < unsigned long > ( 1e4 ), + static_cast < unsigned long > ( 1e3 ), + static_cast < unsigned long > ( 1e2 ), + static_cast < unsigned long > ( 1e1 ), + static_cast < unsigned long > ( 1e0 ) + }; + // round without overflowing into whole seconds + unsigned long frac = tmns.nSec + div[fracWid] / 2; + if (frac >= nSecPerSec) + frac = nSecPerSec - 1; + // convert nanosecs to integer of correct range + frac /= div[fracWid]; + char fracFormat[32]; + sprintf ( fracFormat, "%%0%lulu", fracWid ); + int status = epicsSnprintf ( pBufCur, bufLenLeft, fracFormat, frac ); + if ( status > 0 ) { + unsigned long nChar = static_cast < unsigned long > ( status ); + if ( nChar >= bufLenLeft ) { + nChar = bufLenLeft - 1; + } + pBufCur[nChar] = '\0'; + pBufCur += nChar; + bufLenLeft -= nChar; + } + } + else { + static const char pOVF [] = "OVF"; + size_t tmpLen = sizeof ( pOVF ) - 1; + if ( tmpLen >= bufLenLeft ) { + tmpLen = bufLenLeft - 1; + } + strncpy ( pBufCur, pOVF, tmpLen ); + pBufCur[tmpLen] = '\0'; + pBufCur += tmpLen; + bufLenLeft -= tmpLen; + } + } + else { + static const char pDoesntFit [] = "************"; + size_t tmpLen = sizeof ( pDoesntFit ) - 1; + if ( tmpLen >= bufLenLeft ) { + tmpLen = bufLenLeft - 1; + } + strncpy ( pBufCur, pDoesntFit, tmpLen ); + pBufCur[tmpLen] = '\0'; + pBufCur += tmpLen; + bufLenLeft -= tmpLen; + break; + } + } + } + return pBufCur - pBuff; +} + +// +// epicsTime::show (unsigned) +// +void epicsTime::show ( unsigned level ) const +{ + char bigBuffer[256]; + + size_t numChar = this->strftime ( bigBuffer, sizeof ( bigBuffer ), + "%a %b %d %Y %H:%M:%S.%09f" ); + if ( numChar > 0 ) { + printf ( "epicsTime: %s\n", bigBuffer ); + } + + if ( level > 1 ) { + // this also suppresses the "defined, but not used" + // warning message + printf ( "epicsTime: revision \"%s\"\n", + pEpicsTimeVersion ); + } + +} + +// +// epicsTime::operator + (const double &rhs) +// +// rhs has units seconds +// +epicsTime epicsTime::operator + (const double &rhs) const +{ + unsigned long newSec, newNSec, secOffset, nSecOffset; + double fnsec; + + if (rhs >= 0) { + secOffset = static_cast (rhs); + fnsec = rhs - secOffset; + nSecOffset = static_cast ( (fnsec * nSecPerSec) + 0.5 ); + + newSec = this->secPastEpoch + secOffset; // overflow expected + newNSec = this->nSec + nSecOffset; + if (newNSec >= nSecPerSec) { + newSec++; // overflow expected + newNSec -= nSecPerSec; + } + } + else { + secOffset = static_cast (-rhs); + fnsec = rhs + secOffset; + nSecOffset = static_cast ( (-fnsec * nSecPerSec) + 0.5 ); + + newSec = this->secPastEpoch - secOffset; // underflow expected + if (this->nSec>=nSecOffset) { + newNSec = this->nSec - nSecOffset; + } + else { + // borrow + newSec--; // underflow expected + newNSec = this->nSec + (nSecPerSec - nSecOffset); + } + } + return epicsTime (newSec, newNSec); +} + +// +// operator - +// +// To make this code robust during timestamp rollover events +// time stamp differences greater than one half full scale are +// interpreted as rollover situations: +// +// when RHS is greater than THIS: +// RHS-THIS > one half full scale => return THIS + (ULONG_MAX-RHS) +// RHS-THIS <= one half full scale => return -(RHS-THIS) +// +// when THIS is greater than or equal to RHS +// THIS-RHS > one half full scale => return -(RHS + (ULONG_MAX-THIS)) +// THIS-RHS <= one half full scale => return THIS-RHS +// +double epicsTime::operator - (const epicsTime &rhs) const +{ + double nSecRes, secRes; + + // + // first compute the difference between the nano-seconds members + // + // nano sec member is not allowed to be greater that 1/2 full scale + // so the unsigned to signed conversion is ok + // + if (this->nSec>=rhs.nSec) { + nSecRes = this->nSec - rhs.nSec; + } + else { + nSecRes = rhs.nSec - this->nSec; + nSecRes = -nSecRes; + } + + // + // next compute the difference between the seconds members + // and invert the sign of the nano seconds result if there + // is a range violation + // + if (this->secPastEpochsecPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + nSecRes = -nSecRes; + } + else { + secRes = -secRes; + } + } + else { + secRes = this->secPastEpoch - rhs.secPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + secRes = -secRes; + nSecRes = -nSecRes; + } + } + + return secRes + nSecRes/nSecPerSec; +} + +// +// operator <= +// +bool epicsTime::operator <= (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec<=rhs.nSec) { + rc = true; + } + else { + rc = false; + } + } + return rc; +} + +// +// operator < +// +bool epicsTime::operator < (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec epicsTime (*pRight); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) + { + try { + return epicsTime (*pLeft) >= epicsTime (*pRight); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) + { + try { + return epicsTime(*pTS).strftime (pBuff, bufLength, pFormat); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc void epicsShareAPI epicsTimeShow (const epicsTimeStamp *pTS, unsigned interestLevel) + { + try { + epicsTime(*pTS).show (interestLevel); + } + catch ( ... ) { + printf ( "Invalid epicsTimeStamp\n" ); + } + } +} diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h new file mode 100644 index 000000000..862bc22d2 --- /dev/null +++ b/modules/libcom/src/osi/epicsTime.h @@ -0,0 +1,368 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTime.h */ +/* Author Jeffrey O. Hill */ + +#ifndef epicsTimehInclude +#define epicsTimehInclude + +#include + +#include "shareLib.h" +#include "epicsTypes.h" +#include "osdTime.h" +#include "errMdef.h" + +/* The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ +#define POSIX_TIME_AT_EPICS_EPOCH 631152000u + +/* epics time stamp for C interface*/ +typedef struct epicsTimeStamp { + epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ + epicsUInt32 nsec; /* nanoseconds within second */ +} epicsTimeStamp; + +/*TS_STAMP is deprecated */ +#define TS_STAMP epicsTimeStamp + + +struct timespec; /* POSIX real time */ +struct timeval; /* BSD */ +struct l_fp; /* NTP timestamp */ + +#ifdef __cplusplus + +/* + * extend ANSI C RTL "struct tm" to include nano seconds within a second + * and a struct tm that is adjusted for the local timezone + */ +struct local_tm_nano_sec { + struct tm ansi_tm; /* ANSI C time details */ + unsigned long nSec; /* nano seconds extension */ +}; + +/* + * extend ANSI C RTL "struct tm" to includes nano seconds within a second + * and a struct tm that is adjusted for GMT (UTC) + */ +struct gm_tm_nano_sec { + struct tm ansi_tm; /* ANSI C time details */ + unsigned long nSec; /* nano seconds extension */ +}; + +/* + * wrapping this in a struct allows conversion to and + * from ANSI time_t but does not allow unexpected + * conversions to occur + */ +struct time_t_wrapper { + time_t ts; +}; + +class epicsShareClass epicsTimeEvent +{ +public: + epicsTimeEvent (const int &number); + operator int () const; +private: + int eventNumber; +}; + +class epicsShareClass epicsTime +{ +public: + /* exceptions */ + class unableToFetchCurrentTime {}; + class formatProblemWithStructTM {}; + + epicsTime (); + epicsTime ( const epicsTime & t ); + + static epicsTime getEvent ( const epicsTimeEvent & ); + static epicsTime getCurrent (); + + /* convert to and from EPICS epicsTimeStamp format */ + operator epicsTimeStamp () const; + epicsTime ( const epicsTimeStamp & ts ); + epicsTime & operator = ( const epicsTimeStamp & ); + + /* convert to and from ANSI time_t */ + operator time_t_wrapper () const; + epicsTime ( const time_t_wrapper & ); + epicsTime & operator = ( const time_t_wrapper & ); + + /* + * convert to and from ANSI Cs "struct tm" (with nano seconds) + * adjusted for the local time zone + */ + operator local_tm_nano_sec () const; + epicsTime ( const local_tm_nano_sec & ); + epicsTime & operator = ( const local_tm_nano_sec & ); + + /* + * convert to and from ANSI Cs "struct tm" (with nano seconds) + * adjusted for GM time (UTC) + */ + operator gm_tm_nano_sec () const; + epicsTime ( const gm_tm_nano_sec & ); + epicsTime & operator = ( const gm_tm_nano_sec & ); + + /* convert to and from POSIX RTs "struct timespec" */ + operator struct timespec () const; + epicsTime ( const struct timespec & ); + epicsTime & operator = ( const struct timespec & ); + + /* convert to and from BSDs "struct timeval" */ + operator struct timeval () const; + epicsTime ( const struct timeval & ); + epicsTime & operator = ( const struct timeval & ); + + /* convert to and from NTP timestamp format */ + operator l_fp () const; + epicsTime ( const l_fp & ); + epicsTime & operator = ( const l_fp & ); + + /* convert to and from WIN32s FILETIME (implemented only on WIN32) */ + operator struct _FILETIME () const; + epicsTime ( const struct _FILETIME & ); + epicsTime & operator = ( const struct _FILETIME & ); + + /* arithmetic operators */ + double operator- ( const epicsTime & ) const; /* returns seconds */ + epicsTime operator+ ( const double & ) const; /* add rhs seconds */ + epicsTime operator- ( const double & ) const; /* subtract rhs seconds */ + epicsTime operator+= ( const double & ); /* add rhs seconds */ + epicsTime operator-= ( const double & ); /* subtract rhs seconds */ + + /* comparison operators */ + bool operator == ( const epicsTime & ) const; + bool operator != ( const epicsTime & ) const; + bool operator <= ( const epicsTime & ) const; + bool operator < ( const epicsTime & ) const; + bool operator >= ( const epicsTime & ) const; + bool operator > ( const epicsTime & ) const; + + /* convert current state to user-specified string */ + size_t strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const; + + /* dump current state to standard out */ + void show ( unsigned interestLevel ) const; + +private: + /* + * private because: + * a) application does not break when EPICS epoch is changed + * b) no assumptions about internal storage or internal precision + * in the application + * c) it would be easy to forget which argument is nanoseconds + * and which argument is seconds (no help from compiler) + */ + epicsTime ( const unsigned long secPastEpoch, const unsigned long nSec ); + void addNanoSec ( long nanoSecAdjust ); + + unsigned long secPastEpoch; /* seconds since O000 Jan 1, 1990 */ + unsigned long nSec; /* nanoseconds within second */ +}; + +extern "C" { +#endif /* __cplusplus */ + +/* epicsTime routines return S_time_ error status values */ +#define epicsTimeOK 0 +#define S_time_noProvider (M_time| 1) /*No time provider*/ +#define S_time_badEvent (M_time| 2) /*Bad event number*/ +#define S_time_badArgs (M_time| 3) /*Invalid arguments*/ +#define S_time_noMemory (M_time| 4) /*Out of memory*/ +#define S_time_unsynchronized (M_time| 5) /*Provider not synchronized*/ +#define S_time_timezone (M_time| 6) /*Invalid timeone*/ +#define S_time_conversion (M_time| 7) /*Time conversion error*/ + +/*Some special values for eventNumber*/ +#define epicsTimeEventCurrentTime 0 +#define epicsTimeEventBestTime -1 +#define epicsTimeEventDeviceTime -2 + +/* These are implemented in the "generalTime" framework */ +epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp * pDest ); +epicsShareFunc int epicsShareAPI epicsTimeGetEvent ( + epicsTimeStamp *pDest, int eventNumber); + +/* These are callable from an Interrupt Service Routine */ +epicsShareFunc int epicsTimeGetCurrentInt(epicsTimeStamp *pDest); +epicsShareFunc int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber); + +/* convert to and from ANSI C's "time_t" */ +epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( + time_t * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( + epicsTimeStamp * pDest, time_t src ); + +/* convert to and from ANSI C's "struct tm" with nano seconds */ +epicsShareFunc int epicsShareAPI epicsTimeToTM ( + struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeToGMTM ( + struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTM ( + epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromGMTM ( + epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); + +/* convert to and from POSIX RT's "struct timespec" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimespec ( + struct timespec * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTimespec ( + epicsTimeStamp * pDest, const struct timespec * pSrc ); + +/* convert to and from BSD's "struct timeval" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimeval ( + struct timeval * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTimeval ( + epicsTimeStamp * pDest, const struct timeval * pSrc ); + +/*arithmetic operations */ +epicsShareFunc double epicsShareAPI epicsTimeDiffInSeconds ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight );/* left - right */ +epicsShareFunc void epicsShareAPI epicsTimeAddSeconds ( + epicsTimeStamp * pDest, double secondsToAdd ); /* adds seconds to *pDest */ + +/*comparison operations: returns (0,1) if (false,true) */ +epicsShareFunc int epicsShareAPI epicsTimeEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); +epicsShareFunc int epicsShareAPI epicsTimeNotEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); +epicsShareFunc int epicsShareAPI epicsTimeLessThan ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left < right */ +epicsShareFunc int epicsShareAPI epicsTimeLessThanEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left <= right) */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThan ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left > right */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left >= right */ + +/*convert to ASCII string */ +epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime ( + char * pBuff, size_t bufLength, const char * pFormat, const epicsTimeStamp * pTS ); + +/* dump current state to standard out */ +epicsShareFunc void epicsShareAPI epicsTimeShow ( + const epicsTimeStamp *, unsigned interestLevel ); + +/* OS dependent reentrant versions of the ANSI C interface because */ +/* vxWorks gmtime_r interface does not match POSIX standards */ +epicsShareFunc int epicsShareAPI epicsTime_localtime ( const time_t * clock, struct tm * result ); +epicsShareFunc int epicsShareAPI epicsTime_gmtime ( const time_t * clock, struct tm * result ); + +/* Advertised monotonic counter resolution (may not be accurate). + * Minimum non-zero difference between two calls to epicsMonotonicGet() + */ +epicsShareFunc epicsUInt64 epicsMonotonicResolution(void); +/* Fetch monotonic counter, return is nano-seconds since an unspecified time */ +epicsShareFunc epicsUInt64 epicsMonotonicGet(void); + +#ifdef EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +epicsShareFunc void osdMonotonicInit(void); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* inline member functions ,*/ +#ifdef __cplusplus + +/* epicsTimeEvent */ + +inline epicsTimeEvent::epicsTimeEvent (const int &number) : + eventNumber(number) {} + +inline epicsTimeEvent::operator int () const +{ + return this->eventNumber; +} + + +/* epicsTime */ + +inline epicsTime epicsTime::operator - ( const double & rhs ) const +{ + return epicsTime::operator + ( -rhs ); +} + +inline epicsTime epicsTime::operator += ( const double & rhs ) +{ + *this = epicsTime::operator + ( rhs ); + return *this; +} + +inline epicsTime epicsTime::operator -= ( const double & rhs ) +{ + *this = epicsTime::operator + ( -rhs ); + return *this; +} + +inline bool epicsTime::operator == ( const epicsTime & rhs ) const +{ + if ( this->secPastEpoch == rhs.secPastEpoch && this->nSec == rhs.nSec ) { + return true; + } + return false; +} + +inline bool epicsTime::operator != ( const epicsTime & rhs ) const +{ + return !epicsTime::operator == ( rhs ); +} + +inline bool epicsTime::operator >= ( const epicsTime & rhs ) const +{ + return ! ( *this < rhs ); +} + +inline bool epicsTime::operator > ( const epicsTime & rhs ) const +{ + return ! ( *this <= rhs ); +} + +inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs ) +{ + return *this = epicsTime ( rhs ); +} + +inline epicsTime & epicsTime::operator = ( const gm_tm_nano_sec & rhs ) +{ + return *this = epicsTime ( rhs ); +} + +inline epicsTime & epicsTime::operator = ( const struct timespec & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const epicsTimeStamp & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const l_fp & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const time_t_wrapper & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} +#endif /* __cplusplus */ + +#endif /* epicsTimehInclude */ + diff --git a/modules/libcom/src/osi/generalTimeSup.h b/modules/libcom/src/osi/generalTimeSup.h new file mode 100644 index 000000000..bd6627ef6 --- /dev/null +++ b/modules/libcom/src/osi/generalTimeSup.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_generalTimeSup_H +#define INC_generalTimeSup_H + +#include "epicsTime.h" +#include "epicsTimer.h" +#include "shareLib.h" + +#define LAST_RESORT_PRIORITY 999 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*TIMECURRENTFUN)(epicsTimeStamp *pDest); +typedef int (*TIMEEVENTFUN)(epicsTimeStamp *pDest, int event); + +epicsShareFunc int generalTimeRegisterCurrentProvider(const char *name, + int priority, TIMECURRENTFUN getTime); +epicsShareFunc int generalTimeRegisterEventProvider(const char *name, + int priority, TIMEEVENTFUN getEvent); + +/* Original names, for compatibility */ +#define generalTimeCurrentTpRegister generalTimeRegisterCurrentProvider +#define generalTimeEventTpRegister generalTimeRegisterEventProvider + +epicsShareFunc int generalTimeAddIntCurrentProvider(const char *name, + int priority, TIMECURRENTFUN getTime); +epicsShareFunc int generalTimeAddIntEventProvider(const char *name, + int priority, TIMEEVENTFUN getEvent); + +epicsShareFunc int generalTimeGetExceptPriority(epicsTimeStamp *pDest, + int *pPrio, int ignorePrio); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_generalTimeSup_H */ diff --git a/modules/libcom/src/osi/os/Darwin/epicsMath.h b/modules/libcom/src/osi/os/Darwin/epicsMath.h new file mode 100644 index 000000000..d540ba5d3 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/epicsMath.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#define finite(x) isfinite(x) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/Darwin/osdBackTrace.cpp b/modules/libcom/src/osi/os/Darwin/osdBackTrace.cpp new file mode 100644 index 000000000..4595fee3a --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdBackTrace.cpp @@ -0,0 +1,10 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include "osdExecinfoBackTrace.cpp" diff --git a/modules/libcom/src/osi/os/Darwin/osdEnv.c b/modules/libcom/src/osi/os/Darwin/osdEnv.c new file mode 100644 index 000000000..ab3f93644 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdEnv.c @@ -0,0 +1,61 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +/* + * Starting in Mac OS X 10.5 (Leopard) shared libraries and + * bundles don't have direct access to environ (man environ). + */ +#include +#define environ (*_NSGetEnviron()) + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "envDefs.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + iocshEnvClear(name); + setenv(name, value, 1); +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/Darwin/osdFindAddr.c b/modules/libcom/src/osi/os/Darwin/osdFindAddr.c new file mode 100644 index 000000000..b92770a54 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdFindAddr.c @@ -0,0 +1,43 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +/* Make sure dladdr() is visible */ +#define _DARWIN_C_SOURCE + +#include + +#define epicsExportSharedSymbols +#include "epicsStackTrace.h" +#include "epicsStackTracePvt.h" + +/* Darwin's finds local symbols, too :-) */ + +int epicsFindAddr(void *addr, epicsSymbol *sym_p) +{ + Dl_info inf; + + if ( ! dladdr(addr, &inf) ) { + sym_p->f_nam = 0; + sym_p->s_nam = 0; + sym_p->s_val = 0; + } else { + sym_p->f_nam = inf.dli_fname; + sym_p->s_nam = inf.dli_sname; + sym_p->s_val = inf.dli_saddr; + } + + return 0; +} + +int epicsFindAddrGetFeatures(void) +{ + return EPICS_STACKTRACE_LCL_SYMBOLS + | EPICS_STACKTRACE_GBL_SYMBOLS + | EPICS_STACKTRACE_DYN_SYMBOLS; +} diff --git a/modules/libcom/src/osi/os/Darwin/osdMonotonic.c b/modules/libcom/src/osi/os/Darwin/osdMonotonic.c new file mode 100644 index 000000000..2f81d002b --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdMonotonic.c @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2017 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "dbDefs.h" +#include "errlog.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +/* see https://developer.apple.com/library/content/qa/qa1398/_index.html */ +static mach_timebase_info_data_t tbinfo; + +void osdMonotonicInit(void) +{ + (void)mach_timebase_info(&tbinfo); +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return 1e-9 * tbinfo.numer / tbinfo.denom; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + uint64_t val = mach_absolute_time(); + return val * tbinfo.numer / tbinfo.denom; +} diff --git a/modules/libcom/src/osi/os/Darwin/osdSock.h b/modules/libcom/src/osi/os/Darwin/osdSock.h new file mode 100644 index 000000000..e7c344062 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdSock.h @@ -0,0 +1,72 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Eric Norum + */ + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; +typedef int osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/Darwin/osdSockAddrReuse.cpp b/modules/libcom/src/osi/os/Darwin/osdSockAddrReuse.cpp new file mode 100644 index 000000000..869514160 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdSockAddrReuse.cpp @@ -0,0 +1,48 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEPORT?\n"); + } +} diff --git a/modules/libcom/src/osi/os/Darwin/osdTime.cpp b/modules/libcom/src/osi/os/Darwin/osdTime.cpp new file mode 100644 index 000000000..315caeb9b --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdTime.cpp @@ -0,0 +1,90 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "osiSock.h" + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +static clock_serv_t host_clock; + +extern "C" { +static int osdTimeGetCurrent (epicsTimeStamp *pDest) +{ + mach_timespec_t mts; + struct timespec ts; + + clock_get_time(host_clock, &mts); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + *pDest = epicsTime(ts); + return epicsTimeOK; +} +} // extern "C" + + +static int timeRegister(void) +{ + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); + + generalTimeCurrentTpRegister("MachTime", \ + LAST_RESORT_PRIORITY, osdTimeGetCurrent); + + osdMonotonicInit(); + return 1; +} +static int done = timeRegister(); + + +int epicsTime_gmtime(const time_t *pAnsiTime, struct tm *pTM) +{ + return gmtime_r(pAnsiTime, pTM) ? + epicsTimeOK : errno; +} + +int epicsTime_localtime(const time_t *clock, struct tm *result) +{ + return localtime_r(clock, result) ? + epicsTimeOK : errno; +} + +extern "C" epicsShareFunc void +convertDoubleToWakeTime(double timeout, struct timespec *wakeTime) +{ + mach_timespec_t now; + struct timespec wait; + + if (timeout < 0.0) + timeout = 0.0; + else if (timeout > 60 * 60 * 24 * 3652.5) + timeout = 60 * 60 * 24 * 3652.5; /* 10 years */ + + clock_get_time(host_clock, &now); + + wait.tv_sec = static_cast< time_t >(timeout); + wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9); + + wakeTime->tv_sec = now.tv_sec + wait.tv_sec; + wakeTime->tv_nsec = now.tv_nsec + wait.tv_nsec; + if (wakeTime->tv_nsec >= 1000000000L) { + wakeTime->tv_nsec -= 1000000000L; + ++wakeTime->tv_sec; + } +} diff --git a/modules/libcom/src/osi/os/Darwin/osdTime.h b/modules/libcom/src/osi/os/Darwin/osdTime.h new file mode 100644 index 000000000..d0c361404 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdTime.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void convertDoubleToWakeTime(double timeout, + struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/modules/libcom/src/osi/os/Darwin/osiFileName.h b/modules/libcom/src/osi/os/Darwin/osiFileName.h new file mode 100644 index 000000000..a78118679 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osiFileName.h @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Eric Norum + */ + +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/Linux/osdBackTrace.cpp b/modules/libcom/src/osi/os/Linux/osdBackTrace.cpp new file mode 100644 index 000000000..4595fee3a --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdBackTrace.cpp @@ -0,0 +1,10 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include "osdExecinfoBackTrace.cpp" diff --git a/modules/libcom/src/osi/os/Linux/osdFindAddr.c b/modules/libcom/src/osi/os/Linux/osdFindAddr.c new file mode 100644 index 000000000..84d17d96f --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdFindAddr.c @@ -0,0 +1,10 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include "osdElfFindAddr.c" diff --git a/modules/libcom/src/osi/os/Linux/osdSock.h b/modules/libcom/src/osi/os/Linux/osdSock.h new file mode 100644 index 000000000..7164a54ee --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdSock.h @@ -0,0 +1,76 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Linux specific socket include + */ + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; +typedef int osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h new file mode 100644 index 000000000..7d2a4868d --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdThread.h @@ -0,0 +1,48 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef INC_osdThread_H +#define INC_osdThread_H + +#include +#include + +#include "shareLib.h" +#include "ellLib.h" +#include "epicsEvent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct epicsThreadOSD { + ELLNODE node; + pthread_t tid; + pid_t lwpId; + pthread_attr_t attr; + struct sched_param schedParam; + int schedPolicy; + EPICSTHREADFUNC createFunc; + void *createArg; + epicsEventId suspendEvent; + int isSuspended; + int isEpicsThread; + int isRealTimeScheduled; + int isOnThreadList; + unsigned int osiPriority; + char name[1]; /* actually larger */ +} epicsThreadOSD; + +epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); +epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId id); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_osdThread_H */ diff --git a/modules/libcom/src/osi/os/Linux/osdThreadExtra.c b/modules/libcom/src/osi/os/Linux/osdThreadExtra.c new file mode 100644 index 000000000..6423004c5 --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdThreadExtra.c @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This differs from the posix implementation of epicsThread by: + * - printing the Linux LWP ID instead of the POSIX thread ID in the show routines + * - installing a default thread start hook, that sets the Linux thread name to the + * EPICS thread name to make it visible on OS level, and discovers the LWP ID */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" + +void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level) +{ + if (!pthreadInfo) { + fprintf(epicsGetStdout(), " NAME EPICS ID " + "LWP ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int priority = 0; + + if (pthreadInfo->tid) { + int policy; + int status = pthread_getschedparam(pthreadInfo->tid, &policy, + ¶m); + + if (!status) + priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->lwpId, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended ? "SUSPEND" : "OK"); + } +} + +static void thread_hook(epicsThreadId pthreadInfo) +{ + /* Set the name of the thread's process. Limited to 16 characters. */ + char comm[16]; + + if (strcmp(pthreadInfo->name, "_main_")) { + snprintf(comm, sizeof(comm), "%s", pthreadInfo->name); + prctl(PR_SET_NAME, comm, 0l, 0l, 0l); + } + pthreadInfo->lwpId = syscall(SYS_gettid); +} + +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault = thread_hook; +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain = thread_hook; diff --git a/modules/libcom/src/osi/os/Linux/osdTime.h b/modules/libcom/src/osi/os/Linux/osdTime.h new file mode 100644 index 000000000..988f8747f --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdTime.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +/* + * Linux needs this include file since the POSIX version + * causes `struct timespec' to be defined in more than one place. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/modules/libcom/src/osi/os/Linux/osiFileName.h b/modules/libcom/src/osi/os/Linux/osiFileName.h new file mode 100644 index 000000000..3a6448d54 --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osiFileName.h @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/Linux/osiUnistd.h b/modules/libcom/src/osi/os/Linux/osiUnistd.h new file mode 100644 index 000000000..336a9a802 --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osiUnistd.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ diff --git a/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c b/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c new file mode 100644 index 000000000..0a96bad1a --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/devLibVMEOSD.c @@ -0,0 +1,365 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* RTEMS port by Till Straumann, + * 3/2002 + * + */ + +#include +#include +#include +#include +#include "devLibVME.h" +#include + +#if defined(__PPC__) || defined(__mcf528x__) + +#if defined(__PPC__) +#include +#include +#endif + + +typedef void myISR (void *pParam); + +static myISR *isrFetch(unsigned vectorNumber, void **parg); + +/* + * this routine needs to be in the symbol table + * for this code to work correctly + */ +static void unsolicitedHandlerEPICS(int vectorNumber); + +static myISR *defaultHandlerAddr[]={ + (myISR*)unsolicitedHandlerEPICS, +}; + +/* + * Make sure that the CR/CSR addressing mode is defined. + * (it may not be in some BSPs). + */ +#ifndef VME_AM_CSR +# define VME_AM_CSR (0x2f) +#endif + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ +#define EPICSAddrTypeNoConvert -1 +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert, + VME_AM_CSR + }; + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +static long rtemsDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter); + +static long rtemsDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +); + +static long rtemsDevEnableInterruptLevelVME (unsigned level); + +static long rtemsDevDisableInterruptLevelVME (unsigned level); + +static int rtemsDevInterruptInUseVME (unsigned vectorNumber); + +/* RTEMS specific init */ + +/*devA24Malloc and devA24Free are not implemented*/ +static void *devA24Malloc(size_t size) { return 0;} +static void devA24Free(void *pBlock) {}; +static long rtemsDevInit(void); + +/* + * used by bind in devLib.c + */ +static devLibVME rtemsVirtualOS = { + rtemsDevMapAddr, rtemsDevReadProbe, rtemsDevWriteProbe, + rtemsDevConnectInterruptVME, rtemsDevDisconnectInterruptVME, + rtemsDevEnableInterruptLevelVME, rtemsDevDisableInterruptLevelVME, + devA24Malloc,devA24Free,rtemsDevInit,rtemsDevInterruptInUseVME +}; +devLibVME *pdevLibVME = &rtemsVirtualOS; + +/* RTEMS specific initialization */ +static long +rtemsDevInit(void) +{ + /* assume the vme bridge has been initialized by bsp */ + /* init BSP extensions [memProbe etc.] */ + return bspExtInit(); +} + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on OS + */ +static long rtemsDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = BSP_installVME_isr( + vectorNumber, + pFunction, + parameter); + if (status) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * devDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on OS + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +static long rtemsDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + void *arg; + int status; + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber, &arg); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = BSP_removeVME_isr( + vectorNumber, + psub, + arg) || + BSP_installVME_isr( + vectorNumber, + (BSP_VME_ISR_t)unsolicitedHandlerEPICS, + (void*)vectorNumber); + if(status){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +static long rtemsDevEnableInterruptLevelVME (unsigned level) +{ + return BSP_enableVME_int_lvl(level); +} + +/* + * disable VME interrupt level + */ +static long rtemsDevDisableInterruptLevelVME (unsigned level) +{ + return BSP_disableVME_int_lvl(level); +} + +/* + * rtemsDevMapAddr () + */ +static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = BSP_vme2local_adrs(EPICStovxWorksAddrType[addrType], + logicalAddress, (unsigned long *)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +rtems_status_code bspExtMemProbe(void *addr, int write, int size, void *pval); +static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 0/*read*/, wordSize, pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 1/*write*/, wordSize, (void*)pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +static myISR *isrFetch(unsigned vectorNumber, void **parg) +{ + /* + * fetch the handler or C stub attached at this vector + */ + return (myISR *) BSP_getVME_isr(vectorNumber,parg); +} + +/* + * determine if a VME interrupt vector is in use + */ +static int rtemsDevInterruptInUseVME (unsigned vectorNumber) +{ + int i; + myISR *psub; + void *arg; + + psub = isrFetch (vectorNumber,&arg); + + if (!psub) + return FALSE; + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i +#include "epicsMMIO.h" +#include "compilerSpecific.h" +#include "epicsInterrupt.h" + +#define EPICS_ATOMIC_OS_NAME "RTEMS" + +typedef struct EpicsAtomicLockKey { + int key; +} EpicsAtomicLockKey; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + epicsAtomicMemoryBarrierFallback(); +} +#endif + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + rwbarr(); +} +#endif + +EPICS_ATOMIC_INLINE void epicsAtomicLock ( struct EpicsAtomicLockKey * pkey ) +{ + pkey->key = epicsInterruptLock(); +} + +EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( struct EpicsAtomicLockKey * pkey ) +{ + epicsInterruptUnlock(pkey->key); +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/modules/libcom/src/osi/os/RTEMS/epicsMMIO.h b/modules/libcom/src/osi/os/RTEMS/epicsMMIO.h new file mode 100644 index 000000000..293886b78 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/epicsMMIO.h @@ -0,0 +1,78 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Michael Davidsaver + */ + +#ifndef EPICSMMIO_H +#define EPICSMMIO_H + +#include +#include +#include + +#if defined(_ARCH_PPC) || defined(__PPC__) || defined(__PPC) +# include + +/*NOTE: All READ/WRITE operations have an implicit read or write barrier */ + +# define ioread8(A) in_8((volatile epicsUInt8*)(A)) +# define iowrite8(A,D) out_8((volatile epicsUInt8*)(A), D) +# define le_ioread16(A) in_le16((volatile epicsUInt16*)(A)) +# define le_ioread32(A) in_le32((volatile epicsUInt32*)(A)) +# define le_iowrite16(A,D) out_le16((volatile epicsUInt16*)(A), D) +# define le_iowrite32(A,D) out_le32((volatile epicsUInt32*)(A), D) +# define be_ioread16(A) in_be16((volatile epicsUInt16*)(A)) +# define be_ioread32(A) in_be32((volatile epicsUInt32*)(A)) +# define be_iowrite16(A,D) out_be16((volatile epicsUInt16*)(A), D) +# define be_iowrite32(A,D) out_be32((volatile epicsUInt32*)(A), D) + +# define rbarr() iobarrier_r() +# define wbarr() iobarrier_w() +# define rwbarr() iobarrier_rw() + +/* Define native operations */ +# define nat_ioread16 be_ioread16 +# define nat_ioread32 be_ioread32 +# define nat_iowrite16 be_iowrite16 +# define nat_iowrite32 be_iowrite32 + +static EPICS_ALWAYS_INLINE +epicsUInt16 +bswap16(epicsUInt16 value) +{ + return (((epicsUInt16)(value) & 0x00ff) << 8) | + (((epicsUInt16)(value) & 0xff00) >> 8); +} + +static EPICS_ALWAYS_INLINE +epicsUInt32 +bswap32(epicsUInt32 value) +{ + return (((epicsUInt32)(value) & 0x000000ff) << 24) | + (((epicsUInt32)(value) & 0x0000ff00) << 8) | + (((epicsUInt32)(value) & 0x00ff0000) >> 8) | + (((epicsUInt32)(value) & 0xff000000) >> 24); +} + +#elif defined(i386) || defined(__i386__) || defined(__i386) || defined(__m68k__) + +/* X86 does not need special handling for read/write width. + * + * TODO: Memory barriers? + */ + +#include "epicsMMIODef.h" + +#else +# warning I/O operations not defined for this RTEMS architecture + +#include "epicsMMIODef.h" + +#endif /* if defined PPC */ + +#endif /* EPICSMMIO_H */ diff --git a/modules/libcom/src/osi/os/RTEMS/epicsMath.h b/modules/libcom/src/osi/os/RTEMS/epicsMath.h new file mode 100644 index 000000000..83115f06e --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/epicsMath.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/RTEMS/osdEvent.c b/modules/libcom/src/osi/os/RTEMS/osdEvent.c new file mode 100644 index 000000000..455413870 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdEvent.c @@ -0,0 +1,200 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdEvent.c + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include + +#include "epicsEvent.h" +#include "epicsThread.h" +#include "errlog.h" + +/* #define EPICS_RTEMS_SEMAPHORE_STATS */ +/* + * Some performance tuning instrumentation + */ +#ifdef EPICS_RTEMS_SEMAPHORE_STATS +unsigned long semEstat[4]; +#define SEMSTAT(i) semEstat[i]++; +#else +#define SEMSTAT(i) +#endif + +/* + * Create a simple binary semaphore + */ +epicsEventId +epicsEventCreate(epicsEventInitialState initialState) +{ + rtems_status_code sc; + rtems_id sid; + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1), + initialState, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &sid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create binary semaphore: %s\n", rtems_status_text (sc)); + return NULL; + } + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); + return (epicsEventId)sid; +} + +void +epicsEventDestroy(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_semaphore_delete (sid); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("Can't destroy semaphore: %s\n", rtems_status_text (sc)); +} + +epicsEventStatus +epicsEventTrigger(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_semaphore_release (sid); + if (sc == RTEMS_SUCCESSFUL) + return epicsEventOK; + errlogPrintf ("Can't release semaphore: %s\n", rtems_status_text (sc)); + return epicsEventError; +} + +epicsEventStatus +epicsEventWait(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + SEMSTAT(0) + sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return epicsEventError; + return epicsEventOK; +} + +epicsEventStatus +epicsEventWaitWithTimeout(epicsEventId id, double timeOut) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + rtems_interval delay; + extern double rtemsTicksPerSecond_double; + + if (timeOut <= 0.0) + return epicsEventTryWait(id); + SEMSTAT(1) + delay = timeOut * rtemsTicksPerSecond_double; + if (delay == 0) + delay++; + sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, delay); + if (sc == RTEMS_SUCCESSFUL) + return epicsEventOK; + else if (sc == RTEMS_TIMEOUT) + return epicsEventWaitTimeout; + else + return epicsEventError; +} + +epicsEventStatus +epicsEventTryWait(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + SEMSTAT(2) + sc = rtems_semaphore_obtain (sid, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT); + if (sc == RTEMS_SUCCESSFUL) + return epicsEventOK; + else if (sc == RTEMS_UNSATISFIED) + return epicsEventWaitTimeout; + else + return epicsEventError; +} + +void +epicsEventShow(epicsEventId id, unsigned int level) +{ +#if __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + rtems_id sid = (rtems_id)id; + Semaphore_Control *the_semaphore; + Semaphore_Control semaphore; + Objects_Locations location; + + the_semaphore = _Semaphore_Get (sid, &location); + if (location != OBJECTS_LOCAL) + return; + /* + * Yes, there's a race condition here since an interrupt might + * change things while the copy is in progress, but the information + * is only for display, so it's not that critical. + */ + semaphore = *the_semaphore; + _Thread_Enable_dispatch(); + printf (" %8.8x ", (int)sid); + if (_Attributes_Is_counting_semaphore (semaphore.attribute_set)) { + printf ("Count: %d", (int)semaphore.Core_control.semaphore.count); + } + else { + if (_CORE_mutex_Is_locked(&semaphore.Core_control.mutex)) { + char name[30]; + epicsThreadGetName ((epicsThreadId)semaphore.Core_control.mutex.holder_id, name, sizeof name); + printf ("Held by:%8.8x (%s) Nest count:%d", + (unsigned int)semaphore.Core_control.mutex.holder_id, + name, + (int)semaphore.Core_control.mutex.nest_count); + } + else { + printf ("Not Held"); + } + } + printf ("\n"); +#endif +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdEvent.h b/modules/libcom/src/osi/os/RTEMS/osdEvent.h new file mode 100644 index 000000000..6c53aada7 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdEvent.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdEvent.h + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* osdEvent.h not needed */ diff --git a/modules/libcom/src/osi/os/RTEMS/osdFindSymbol.c b/modules/libcom/src/osi/os/RTEMS/osdFindSymbol.c new file mode 100644 index 000000000..9e947f95c --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdFindSymbol.c @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/RTEMS/osdFindSymbol.c */ + +/* GESYS could support this, but must use compile-time detection + * as the code must build for non-GESYS systems as well. + */ + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return 0; +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return "epicsLoadLibrary not implemented"; +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return 0; +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdInterrupt.c b/modules/libcom/src/osi/os/RTEMS/osdInterrupt.c new file mode 100644 index 000000000..88ce664e5 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdInterrupt.c @@ -0,0 +1,94 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdInterrupt.c + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +#include +#include +#include +#include "errlog.h" +#include "epicsInterrupt.h" +#include "epicsThread.h" + +#define INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT 100 + +static rtems_id interruptContextMessageQueue; + +int +epicsInterruptLock (void) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + return level; +} + +void +epicsInterruptUnlock (int key) +{ + rtems_interrupt_level level = key; + + rtems_interrupt_enable (level); +} + +int +epicsInterruptIsInterruptContext (void) +{ + return rtems_interrupt_is_in_progress (); +} + +/* + * Pass a message from an interrupt context. + * Note that this passes a pointer to the message, not the message itself. + * This implies that the message must remain valid after the + * interrupt context is no longer active. + */ +void +epicsInterruptContextMessage (const char *message) +{ + rtems_message_queue_send (interruptContextMessageQueue, &message, sizeof message); +} + +/* + * Daemon to process interrupt context messages + */ +void +InterruptContextMessageDaemon (void *unused) +{ + const char *message; + size_t size; + rtems_status_code sc; + + sc = rtems_message_queue_create (rtems_build_name ('I', 'C', 'M', 'Q'), + INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT, + sizeof message, + RTEMS_FIFO | RTEMS_LOCAL, + &interruptContextMessageQueue); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create interrupt context message queue: %s\n", rtems_status_text (sc)); + epicsThreadSuspendSelf (); + } + for (;;) { + sc = rtems_message_queue_receive (interruptContextMessageQueue, + &message, + &size, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't receive message from interrupt context: %s\n", rtems_status_text (sc)); + epicsThreadSuspendSelf (); + } + if (size == sizeof message) + syslog (LOG_ERR, "%s", message); + else + errlogPrintf ("Received %u-byte message from interrupt context", (unsigned int)size); + } +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdInterrupt.h b/modules/libcom/src/osi/os/RTEMS/osdInterrupt.h new file mode 100644 index 000000000..2f3a2c596 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdInterrupt.h @@ -0,0 +1,13 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Daemon to soak up and report messages from interrupt contexts + */ +extern void InterruptContextMessageDaemon (void *); diff --git a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c new file mode 100644 index 000000000..a566de6f6 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c @@ -0,0 +1,251 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#define epicsExportSharedSymbols +#include +#include +#include +#include +#include +#include +#include "epicsMessageQueue.h" +#include "errlog.h" + +epicsShareFunc epicsMessageQueueId epicsShareAPI +epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) +{ + rtems_status_code sc; + epicsMessageQueueId id = calloc(1, sizeof(*id)); + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + if(!id) + return NULL; + + sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1), + capacity, + maximumMessageSize, + RTEMS_FIFO|RTEMS_LOCAL, + &id->id); + if (sc != RTEMS_SUCCESSFUL) { + free(id); + errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc)); + return NULL; + } + id->maxSize = maximumMessageSize; + id->localBuf = NULL; + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); + return id; +} + +static rtems_status_code rtems_message_queue_send_timeout( + rtems_id id, + void *buffer, + uint32_t size, + rtems_interval timeout) +{ + Message_queue_Control *the_message_queue; + Objects_Locations location; + CORE_message_queue_Status msg_status; + + the_message_queue = _Message_queue_Get( id, &location ); + switch ( location ) + { + case OBJECTS_ERROR: + return RTEMS_INVALID_ID; + + case OBJECTS_LOCAL: + msg_status = _CORE_message_queue_Send( + &the_message_queue->message_queue, + buffer, + size, + id, + NULL, + 1, + timeout + ); + + _Thread_Enable_dispatch(); + + /* + * If we had to block, then this is where the task returns + * after it wakes up. The returned status is correct for + * non-blocking operations but if we blocked, then we need + * to look at the status in our TCB. + */ + + if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT ) + msg_status = _Thread_Executing->Wait.return_code; + return _Message_queue_Translate_core_message_queue_return_code( msg_status ); + } + return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */ +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL) + return 0; + else + return -1; +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout) +{ + rtems_interval delay; + extern double rtemsTicksPerSecond_double; + + /* + * Convert time to ticks + */ + if (timeout <= 0.0) + return epicsMessageQueueTrySend(id, message, messageSize); + delay = (int)(timeout * rtemsTicksPerSecond_double); + if (delay == 0) + delay++; + if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL) + return 0; + else + return -1; +} + +static int receiveMessage( + epicsMessageQueueId id, + void *buffer, + uint32_t size, + uint32_t wait, + rtems_interval delay) +{ + size_t rsize; + rtems_status_code sc; + + if (size < id->maxSize) { + if (id->localBuf == NULL) { + id->localBuf = malloc(id->maxSize); + if (id->localBuf == NULL) + return -1; + } + rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay); + if (rsize > size) + return -1; + memcpy(buffer, id->localBuf, rsize); + } + else { + sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay); + if (sc != RTEMS_SUCCESSFUL) + return -1; + } + return rsize; +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( + epicsMessageQueueId id, + void *message, + unsigned int size) +{ + return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int size) +{ + return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout) +{ + rtems_interval delay; + uint32_t wait; + extern double rtemsTicksPerSecond_double; + + /* + * Convert time to ticks + */ + if (timeout <= 0.0) { + wait = RTEMS_NO_WAIT; + delay = 0; + } + else { + wait = RTEMS_WAIT; + delay = (int)(timeout * rtemsTicksPerSecond_double); + if (delay == 0) + delay++; + } + return receiveMessage(id, message, size, wait, delay); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueuePending( + epicsMessageQueueId id) +{ + uint32_t count; + rtems_status_code sc; + + sc = rtems_message_queue_get_number_pending(id->id, &count); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf("Message queue %x get number pending failed: %s\n", + (unsigned int)id, + rtems_status_text(sc)); + return -1; + } + return count; +} + +epicsShareFunc void epicsShareAPI epicsMessageQueueShow( + epicsMessageQueueId id, + int level) +{ + int pending = epicsMessageQueuePending(id); + if (pending >= 0) + printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending); +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h new file mode 100644 index 000000000..0244a1f0b --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Very thin shims around RTEMS routines + */ +#include + +struct epicsMessageQueueOSD { + rtems_id id; + unsigned int maxSize; + void *localBuf; + +}; +#define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id)) + +#define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1) diff --git a/modules/libcom/src/osi/os/RTEMS/osdMutex.c b/modules/libcom/src/osi/os/RTEMS/osdMutex.c new file mode 100644 index 000000000..96fde6ea0 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdMutex.c @@ -0,0 +1,195 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdMutex.c + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include +#include + +#include "epicsStdio.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "errlog.h" + +#define RTEMS_FAST_MUTEX +/* #define EPICS_RTEMS_SEMAPHORE_STATS */ +/* + * Some performance tuning instrumentation + */ +#ifdef EPICS_RTEMS_SEMAPHORE_STATS +unsigned long semMstat[4]; +#define SEMSTAT(i) semMstat[i]++; +#else +#define SEMSTAT(i) +#endif + +struct epicsMutexOSD * +epicsMutexOsdCreate(void) +{ + rtems_status_code sc; + rtems_id sid; + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1), + 1, + RTEMS_PRIORITY|RTEMS_BINARY_SEMAPHORE|RTEMS_INHERIT_PRIORITY|RTEMS_NO_PRIORITY_CEILING|RTEMS_LOCAL, + 0, + &sid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create mutex semaphore: %s\n", rtems_status_text (sc)); + return NULL; + } + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); +#ifdef RTEMS_FAST_MUTEX + { + Semaphore_Control *the_semaphore; + Objects_Locations location; + + the_semaphore = _Semaphore_Get( sid, &location ); + _Thread_Enable_dispatch(); + + return (struct epicsMutexOSD *)the_semaphore; + } +#endif + return (struct epicsMutexOSD *)sid; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +{ + rtems_status_code sc; + rtems_id sid; +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + sid = the_semaphore->Object.id; +#else + sid = (rtems_id)id; +#endif + sc = rtems_semaphore_delete (sid); + if (sc == RTEMS_RESOURCE_IN_USE) { + rtems_semaphore_release (sid); + sc = rtems_semaphore_delete (sid); + } + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", id, (unsigned long)sid, rtems_status_text (sc)); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + _Thread_Disable_dispatch(); + _CORE_mutex_Surrender ( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + NULL + ); + _Thread_Enable_dispatch(); +#else + epicsEventSignal (id); +#endif + +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + ISR_Level level; + SEMSTAT(0) + _ISR_Disable( level ); + _CORE_mutex_Seize( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + 1, /* TRUE or FALSE */ + 0, /* same as passed to obtain -- ticks */ + level + ); + if (_Thread_Executing->Wait.return_code == 0) + return epicsMutexLockOK; + else + return epicsMutexLockError; +#else + SEMSTAT(0) + return((epicsEventWait (id) == epicsEventWaitOK) + ?epicsMutexLockOK : epicsMutexLockError); +#endif +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + ISR_Level level; + SEMSTAT(2) + _ISR_Disable( level ); + _CORE_mutex_Seize( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + 0, /* TRUE or FALSE */ + 0, /* same as passed to obtain -- ticks */ + level + ); + if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_SUCCESSFUL) + return epicsMutexLockOK; + else if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_UNSATISFIED_NOWAIT) + return epicsMutexLockTimeout; + else + return epicsMutexLockError; +#else + epicsEventWaitStatus status; + SEMSTAT(2) + status = epicsEventTryWait(id); + return((status==epicsEventWaitOK + ? epicsMutexLockOK + : (status==epicsEventWaitTimeout) + ? epicsMutexLockTimeout + : epicsMutexLockError)); +#endif +} + +epicsShareFunc void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + id = (struct epicsMutexOSD *)the_semaphore->Object.id; +#endif + epicsEventShow ((epicsEventId)id,level); +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdMutex.h b/modules/libcom/src/osi/os/RTEMS/osdMutex.h new file mode 100644 index 000000000..7e2ecc77d --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdMutex.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdMutex.h + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* osdSem.h not needed */ diff --git a/modules/libcom/src/osi/os/RTEMS/osdPoolStatus.c b/modules/libcom/src/osi/os/RTEMS/osdPoolStatus.c new file mode 100644 index 000000000..b2f401190 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdPoolStatus.c @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + rtems_malloc_statistics_t s; + unsigned long n; + + malloc_get_statistics(&s); + n = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); + return (n > (50000 + contiguousBlockSize)); +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdProcess.c b/modules/libcom/src/osi/os/RTEMS/osdProcess.c new file mode 100644 index 000000000..2768dbb0d --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdProcess.c @@ -0,0 +1,50 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + const char *pName = "rtems"; + unsigned uiLength; + size_t len; + + len = strlen (pName); + + if ( len>UINT_MAX || len<=0 ) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, pName, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + return osiSpawnDetachedProcessNoSupport; +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdReadline.c b/modules/libcom/src/osi/os/RTEMS/osdReadline.c new file mode 100644 index 000000000..877db53f6 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdReadline.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +struct osdContext {}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin (struct readlineContext *context) +{ + GetLine *gl; + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + + gl = new_GetLine(200, i * 40); + if (gl) { + context->osd = (struct osdContext *) gl; + if (context->in) + gl_change_terminal(gl, context->in, stdout, NULL); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + char *line; + + line = gl_get_line(gl, prompt ? prompt : "", NULL, -1); + if (line) { + char *nl = strchr(line, '\n'); + + if (nl) + *nl = '\0'; + } + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd(struct readlineContext *context) +{ + GetLine *gl = (GetLine *) context->osd; + + del_GetLine(gl); +} + diff --git a/modules/libcom/src/osi/os/RTEMS/osdSignal.cpp b/modules/libcom/src/osi/os/RTEMS/osdSignal.cpp new file mode 100644 index 000000000..08dfa023c --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * Copyright (c) 2002 The University of Chicago, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * All NOOPs if the os isnt POSIX + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} + diff --git a/modules/libcom/src/osi/os/RTEMS/osdSock.h b/modules/libcom/src/osi/os/RTEMS/osdSock.h new file mode 100644 index 000000000..6177c3033 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdSock.h @@ -0,0 +1,102 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdSock.h + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; +typedef char osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD) +#include +#include + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (u_long)0x7F000001 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +/* + * For shutdown() + */ +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +/* + * Ensure that we get the right network code in default/osdNetIntf.c. + */ +#define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/RTEMS/osdSpin.c b/modules/libcom/src/osi/os/RTEMS/osdSpin.c new file mode 100644 index 000000000..7a4546812 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdSpin.c @@ -0,0 +1,106 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: Ralph Lange + * Andrew Johnson + * Michael Davidsaver + * + * Inspired by Linux UP spinlocks implemention + * include/linux/spinlock_api_up.h + */ + +/* + * RTEMS (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION + * + * CAVEAT: + * This implementation is intended for UP architectures only. + */ + +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include + +#include "cantProceed.h" +#include "epicsSpin.h" + +typedef struct epicsSpin { + rtems_interrupt_level level; + unsigned int locked; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + return calloc(1, sizeof(epicsSpin)); +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if (!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + _Thread_Disable_dispatch(); + if (spin->locked) { + rtems_interrupt_enable(level); + _Thread_Enable_dispatch(); + if (!rtems_interrupt_is_in_progress()) { + printk("epicsSpinLock(%p): Deadlock.\n", spin); + cantProceed("Recursive lock, missed unlock or block when locked."); + } + else { + printk("epicsSpinLock(%p): Deadlock in ISR.\n" + "Recursive lock, missed unlock or block when locked.\n", + spin); + } + return; + } + spin->level = level; + spin->locked = 1; +} + +int epicsSpinTryLock(epicsSpinId spin) { + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + _Thread_Disable_dispatch(); + if (spin->locked) { + rtems_interrupt_enable(level); + _Thread_Enable_dispatch(); + return 1; + } + spin->level = level; + spin->locked = 1; + return 0; +} + +void epicsSpinUnlock(epicsSpinId spin) { + rtems_interrupt_level level = spin->level; + + if (!spin->locked) { + printk("epicsSpinUnlock(%p): not locked\n", spin); + return; + } + spin->level = spin->locked = 0; + rtems_interrupt_enable (level); + _Thread_Enable_dispatch(); +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdStrtod.h b/modules/libcom/src/osi/os/RTEMS/osdStrtod.h new file mode 100644 index 000000000..39fda698d --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdStrtod.h @@ -0,0 +1,11 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * epicsStrtod() for systems with working strtod() routine + */ +#define epicsStrtod strtod diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c new file mode 100644 index 000000000..769e95820 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c @@ -0,0 +1,748 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osdThread.c + * Author: W. Eric Norum + */ + +/* + * We want to print out some task information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "epicsStdio.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsString.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "osiUnistd.h" +#include "osdInterrupt.h" +#include "epicsExit.h" + +epicsShareFunc void osdThreadHooksRun(epicsThreadId id); +epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id); + +/* + * Per-task variables + */ +struct taskVar { + struct taskVar *forw; + struct taskVar *back; + char *name; + rtems_id id; + EPICSTHREADFUNC funptr; + void *parm; + unsigned int threadVariableCapacity; + void **threadVariables; +}; +static struct epicsMutexOSD *taskVarMutex; +static struct taskVar *taskVarHead; +#define RTEMS_NOTEPAD_TASKVAR 11 + +/* + * Support for `once-only' execution + */ +static volatile int initialized = 0; /* strictly speaking 'volatile' is not enough here, but it shouldn't hurt */ +static struct epicsMutexOSD *onceMutex; + +static +void epicsMutexOsdMustLock(struct epicsMutexOSD * L) +{ + while(epicsMutexOsdLock(L)!=epicsMutexLockOK) { + cantProceed("epicsThreadOnce() mutex error"); + } +} + +/* + * Just map osi 0 to 99 into RTEMS 199 to 100 + * For RTEMS lower number means higher priority + * RTEMS = 100 + (99 - osi) + * = 199 - osi + * osi = 199 - RTEMS + */ +int epicsThreadGetOsiPriorityValue(int ossPriority) +{ + if (ossPriority < 100) { + return epicsThreadPriorityMax; + } + else if (ossPriority > 199) { + return epicsThreadPriorityMin; + } + else { + return (199u - (unsigned int)ossPriority); + } +} + +int epicsThreadGetOssPriorityValue(unsigned int osiPriority) +{ + if (osiPriority > 99) { + return 100; + } + else { + return (199 - (signed int)osiPriority); + } +} + +/* + * epicsThreadLowestPriorityLevelAbove () + */ +epicsShareFunc epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove + (unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + + newPriority = priority + 1; + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +/* + * epicsThreadHighestPriorityLevelBelow () + */ +epicsShareFunc epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow + (unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; + + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +unsigned int +epicsThreadGetStackSize (epicsThreadStackSizeClass size) +{ + unsigned int stackSize = 11000; + switch(size) { + case epicsThreadStackSmall: stackSize = 5000; break; + case epicsThreadStackMedium: stackSize = 8000; break; + case epicsThreadStackBig: break; + default: + errlogPrintf("epicsThreadGetStackSize illegal argument"); + break; + } + if (stackSize < RTEMS_MINIMUM_STACK_SIZE) + stackSize = RTEMS_MINIMUM_STACK_SIZE; + return stackSize; +} + +/* + * Ensure integrity of task variable list + */ +static void +taskVarLock (void) +{ + epicsMutexOsdMustLock (taskVarMutex); +} + +static void +taskVarUnlock (void) +{ + epicsMutexOsdUnlock (taskVarMutex); +} + +/* + * EPICS threads destroy themselves by returning from the thread entry function. + * This simple wrapper provides the same semantics on RTEMS. + */ +static rtems_task +threadWrapper (rtems_task_argument arg) +{ + struct taskVar *v = (struct taskVar *)arg; + + osdThreadHooksRun((epicsThreadId)v->id); + (*v->funptr)(v->parm); + epicsExitCallAtThreadExits (); + taskVarLock (); + if (v->back) + v->back->forw = v->forw; + else + taskVarHead = v->forw; + if (v->forw) + v->forw->back = v->back; + taskVarUnlock (); + free (v->threadVariables); + free (v->name); + free (v); + rtems_task_delete (RTEMS_SELF); +} + +/* + * The task wrapper takes care of cleanup + */ +void epicsThreadExitMain (void) +{ +} + +static void +setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr, + void *parm) +{ + struct taskVar *v; + uint32_t note; + rtems_status_code sc; + + v = mallocMustSucceed (sizeof *v, "epicsThreadCreate_vars"); + v->name = epicsStrDup(name); + v->id = tid; + v->funptr = funptr; + v->parm = parm; + v->threadVariableCapacity = 0; + v->threadVariables = NULL; + note = (uint32_t)v; + rtems_task_set_note (tid, RTEMS_NOTEPAD_TASKVAR, note); + taskVarLock (); + v->forw = taskVarHead; + v->back = NULL; + if (v->forw) + v->forw->back = v; + taskVarHead = v; + taskVarUnlock (); + if (funptr) { + sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("setThreadInfo: Can't start %s: %s\n", + name, rtems_status_text(sc)); + } +} + +/* + * OS-dependent initialization + * No need to worry about making this thread-safe since + * it must be called before epicsThreadCreate creates + * any new threads. + */ +static void +epicsThreadInit (void) +{ + if (!initialized) { + rtems_id tid; + rtems_task_priority old; + + rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old); + onceMutex = epicsMutexOsdCreate(); + taskVarMutex = epicsMutexOsdCreate(); + if (!onceMutex || !taskVarMutex) + cantProceed("epicsThreadInit() can't create global mutexes\n"); + rtems_task_ident (RTEMS_SELF, 0, &tid); + setThreadInfo (tid, "_main_", NULL, NULL); + osdThreadHooksRunMain((epicsThreadId)tid); + initialized = 1; + epicsThreadCreate ("ImsgDaemon", 99, + epicsThreadGetStackSize (epicsThreadStackSmall), + InterruptContextMessageDaemon, NULL); + } +} + +void epicsThreadRealtimeLock(void) +{} + +/* + * Create and start a new thread + */ +epicsThreadId +epicsThreadCreate (const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + rtems_id tid; + rtems_status_code sc; + char c[4]; + + if (!initialized) epicsThreadInit(); + if (stackSize < RTEMS_MINIMUM_STACK_SIZE) { + errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n", + name, stackSize); + stackSize = RTEMS_MINIMUM_STACK_SIZE; + } + strncpy (c, name, sizeof c); + sc = rtems_task_create (rtems_build_name (c[0], c[1], c[2], c[3]), + epicsThreadGetOssPriorityValue (priority), + stackSize, + RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0), + RTEMS_FLOATING_POINT|RTEMS_LOCAL, + &tid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("epicsThreadCreate create failure for %s: %s\n", + name, rtems_status_text(sc)); + return 0; + } + setThreadInfo (tid, name, funptr,parm); + return (epicsThreadId)tid; +} + +epicsThreadId +threadMustCreate (const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadId tid = epicsThreadCreate(name, priority, stackSize, funptr, parm); + + if (tid == NULL) + cantProceed(0); + return tid; +} + +void +epicsThreadSuspendSelf (void) +{ + rtems_status_code sc; + + sc = rtems_task_suspend (RTEMS_SELF); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSuspendSelf failed: %s\n", + rtems_status_text(sc)); +} + +void epicsThreadResume(epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_task_resume (tid); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadResume failed: %s\n", + rtems_status_text (sc)); +} + +unsigned int epicsThreadGetPriority(epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + rtems_task_priority pri; + + sc = rtems_task_set_priority (tid, RTEMS_CURRENT_PRIORITY, &pri); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadGetPriority failed: %s\n", + rtems_status_text(sc)); + return epicsThreadGetOsiPriorityValue(pri); +} + +unsigned int epicsThreadGetPrioritySelf(void) +{ + return epicsThreadGetPriority((epicsThreadId)RTEMS_SELF); +} + +void +epicsThreadSetPriority (epicsThreadId id,unsigned int osip) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + rtems_task_priority pri = epicsThreadGetOssPriorityValue(osip); + + sc = rtems_task_set_priority (tid, pri, &pri); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSetPriority failed: %s\n", + rtems_status_text(sc)); +} + +int +epicsThreadIsEqual (epicsThreadId id1, epicsThreadId id2) +{ + return (id1 == id2); +} + +int +epicsThreadIsSuspended (epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + + switch (sc = rtems_task_is_suspended (tid)) { + case RTEMS_SUCCESSFUL: + return 0; + + case RTEMS_ALREADY_SUSPENDED: + return 1; + + default: + return 1; + } +} + +void +epicsThreadSleep (double seconds) +{ + rtems_status_code sc; + rtems_interval delay; + extern double rtemsTicksPerSecond_double; + + if (seconds > 0.0) { + seconds *= rtemsTicksPerSecond_double; + seconds += 0.99999999; /* 8 9s here is optimal */ + delay = (seconds >= INT_MAX) ? INT_MAX : (int) seconds; + } + else { /* seconds <= 0 or NAN */ + delay = 0; + } + sc = rtems_task_wake_after (delay); + if(sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text(sc)); +} + +epicsThreadId +epicsThreadGetIdSelf (void) +{ + rtems_id tid; + + rtems_task_ident (RTEMS_SELF, 0, &tid); + return (epicsThreadId)tid; +} + +const char *epicsThreadGetNameSelf(void) +{ + uint32_t note; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (void *)note; + return v->name; +} + +void epicsThreadGetName (epicsThreadId id, char *name, size_t size) +{ + rtems_id tid = (rtems_id)id; + struct taskVar *v; + int haveName = 0; + + taskVarLock (); + for (v=taskVarHead ; v != NULL ; v=v->forw) { + if (v->id == tid) { + strncpy(name, v->name, size); + haveName = 1; + break; + } + } + taskVarUnlock (); + if (!haveName) { +#if (__RTEMS_MAJOR__>4 || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99)) + if (_Objects_Get_name_as_string((rtems_id)id, size, name) != NULL) + haveName = 1; +#else + /* + * Try to get the RTEMS task name + */ + Thread_Control *thr; + Objects_Locations l; + if ((thr=_Thread_Get(tid, &l)) != NULL) { + if (OBJECTS_LOCAL == l) { + int length; + Objects_Information *oi = _Objects_Get_information(tid); + if (oi->name_length >= size) + length = size - 1; + else + length = oi->name_length; + if (oi->is_string) + strncpy(name, thr->Object.name, length); + else + _Objects_Copy_name_raw( &thr->Object.name, name, length); + name[length] = '\0'; + haveName = 1; + } + _Thread_Enable_dispatch(); + } +#endif + } + if (!haveName) + snprintf(name, size, "0x%lx", (long)tid); + name[size-1] = '\0'; +} + +epicsThreadId epicsThreadGetId (const char *name) +{ + struct taskVar *v; + rtems_id tid = 0; + + /* + * Linear search is good enough since this routine + * is invoked only by command-line calls. + */ + taskVarLock (); + for (v = taskVarHead ; v != NULL ; v = v->forw) { + if (strcmp (name, v->name) == 0) { + tid = v->id; + break; + } + } + taskVarUnlock (); + return (epicsThreadId)tid; +} + +/* + * Ensure func() is run only once. + */ +void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) +{ + #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 + + if (!initialized) epicsThreadInit(); + epicsMutexOsdMustLock(onceMutex); + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + epicsMutexOsdUnlock(onceMutex); + func(arg); + epicsMutexOsdMustLock(onceMutex); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + epicsMutexOsdUnlock(onceMutex); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + epicsMutexOsdUnlock(onceMutex); + epicsThreadSleep(epicsThreadSleepQuantum()); + epicsMutexOsdMustLock(onceMutex); + } + } + epicsMutexOsdUnlock(onceMutex); +} + +/* + * Thread private storage implementation based on the vxWorks + * implementation by Andrew Johnson APS/ASD. + */ +epicsThreadPrivateId epicsThreadPrivateCreate () +{ + unsigned int taskVarIndex; + static volatile unsigned int threadVariableCount = 0; + + if (!initialized) epicsThreadInit (); + taskVarLock (); + taskVarIndex = ++threadVariableCount; + taskVarUnlock (); + return (epicsThreadPrivateId)taskVarIndex; +} + +void epicsThreadPrivateDelete (epicsThreadPrivateId id) +{ + /* empty */ +} + +void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) +{ + unsigned int varIndex = (unsigned int)id; + uint32_t note; + struct taskVar *v; + int i; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (struct taskVar *)note; + if (varIndex >= v->threadVariableCapacity) { + v->threadVariables = realloc (v->threadVariables, + (varIndex + 1) * sizeof(void *)); + if (v->threadVariables == NULL) + cantProceed("epicsThreadPrivateSet realloc failed\n"); + for (i = v->threadVariableCapacity ; i < varIndex ; i++) + v->threadVariables[i] = NULL; + v->threadVariableCapacity = varIndex + 1; + } + v->threadVariables[varIndex] = pvt; +} + +void * epicsThreadPrivateGet (epicsThreadPrivateId id) +{ + unsigned int varIndex = (unsigned int)id; + uint32_t note; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (struct taskVar *)note; + if (varIndex >= v->threadVariableCapacity) + return NULL; + return v->threadVariables[varIndex]; +} + +/* + * Show task info + */ +struct bitmap { + char *msg; + unsigned long mask; + unsigned long state; +}; + +static void +showBitmap (char *cbuf, unsigned long bits, const struct bitmap *bp) +{ + for ( ; bp->msg != NULL ; bp++) { + if ((bp->mask & bits) == bp->state) { + strcpy (cbuf, bp->msg); + cbuf += strlen (bp->msg); + } + } +} + +static void +showInternalTaskInfo (rtems_id tid) +{ +#ifdef __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + int epicsPri; + Thread_Control *the_thread; + Objects_Locations location; + static Thread_Control thread; + static char bitbuf[120]; + static const struct bitmap taskState[] = { + { "RUN", STATES_ALL_SET, STATES_READY }, + { "DORM", STATES_DORMANT, STATES_DORMANT }, + { "SUSP", STATES_SUSPENDED, STATES_SUSPENDED }, + { "TRANS", STATES_TRANSIENT, STATES_TRANSIENT }, + { "Delay", STATES_DELAYING, STATES_DELAYING }, + { "Wtime", STATES_WAITING_FOR_TIME, STATES_WAITING_FOR_TIME }, + { "Wbuf", STATES_WAITING_FOR_BUFFER, STATES_WAITING_FOR_BUFFER }, + { "Wseg", STATES_WAITING_FOR_SEGMENT, STATES_WAITING_FOR_SEGMENT }, + { "Wmsg" , STATES_WAITING_FOR_MESSAGE, STATES_WAITING_FOR_MESSAGE }, + { "Wevnt", STATES_WAITING_FOR_EVENT, STATES_WAITING_FOR_EVENT }, + { "Wsem", STATES_WAITING_FOR_SEMAPHORE,STATES_WAITING_FOR_SEMAPHORE }, + { "Wmtx", STATES_WAITING_FOR_MUTEX, STATES_WAITING_FOR_MUTEX }, + { "Wjoin", STATES_WAITING_FOR_JOIN_AT_EXIT,STATES_WAITING_FOR_JOIN_AT_EXIT }, + { "Wrpc", STATES_WAITING_FOR_RPC_REPLY,STATES_WAITING_FOR_RPC_REPLY }, + { "Wrate", STATES_WAITING_FOR_PERIOD, STATES_WAITING_FOR_PERIOD }, + { "Wsig", STATES_WAITING_FOR_SIGNAL, STATES_WAITING_FOR_SIGNAL }, + { NULL, 0, 0 }, + }; + + the_thread = _Thread_Get (tid, &location); + if (location != OBJECTS_LOCAL) { + fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***"); + return; + } + thread = *the_thread; + _Thread_Enable_dispatch(); + /* + * Show both real and current priorities if they differ. + * Note that the epicsThreadGetOsiPriorityValue routine is not used here. + * If a thread has a priority outside the normal EPICS range then + * that priority should be displayed, not the value truncated to + * the EPICS range. + */ + epicsPri = 199-thread.real_priority; + if (epicsPri < 0) + fprintf(epicsGetStdout()," <0"); + else if (epicsPri > 99) + fprintf(epicsGetStdout()," >99"); + else + fprintf(epicsGetStdout()," %4d", epicsPri); + if (thread.current_priority == thread.real_priority) + fprintf(epicsGetStdout(),"%4d ", (int)thread.current_priority); + else + fprintf(epicsGetStdout(),"%4d/%-3d", (int)thread.real_priority, (int)thread.current_priority); + showBitmap (bitbuf, thread.current_state, taskState); + fprintf(epicsGetStdout(),"%8.8s", bitbuf); + if (thread.current_state & (STATES_WAITING_FOR_SEMAPHORE | + STATES_WAITING_FOR_MUTEX | + STATES_WAITING_FOR_MESSAGE)) + fprintf(epicsGetStdout()," %8.8x", (int)thread.Wait.id); + else + fprintf(epicsGetStdout()," %8.8s", ""); +#endif +} + +static void +epicsThreadShowInfo (struct taskVar *v, unsigned int level) +{ + if (!v) { + fprintf(epicsGetStdout()," PRIORITY\n"); + fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); + fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); + } else { + fprintf(epicsGetStdout(),"%9.8x", (int)v->id); + showInternalTaskInfo (v->id); + fprintf(epicsGetStdout()," %s\n", v->name); + } +} + +void epicsThreadShow (epicsThreadId id, unsigned int level) +{ + struct taskVar *v; + + if (!id) { + epicsThreadShowInfo (NULL, level); + return; + } + taskVarLock (); + for (v = taskVarHead ; v != NULL ; v = v->forw) { + if ((rtems_id)id == v->id) { + epicsThreadShowInfo (v, level); + taskVarUnlock (); + return; + } + } + taskVarUnlock (); + fprintf(epicsGetStdout(),"*** Thread %x does not exist.\n", (unsigned int)id); +} + +void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) +{ + struct taskVar *v; + + taskVarLock (); + /* + * Map tasks in the order of creation (backwards through list) + */ + for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) + continue; + while (v) { + func ((epicsThreadId)v->id); + v = v->back; + } + taskVarUnlock (); +} + +void epicsThreadShowAll (unsigned int level) +{ + struct taskVar *v; + + epicsThreadShowInfo (NULL, level); + taskVarLock (); + /* + * Show tasks in the order of creation (backwards through list) + */ + for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) + continue; + while (v) { + epicsThreadShowInfo (v, level); + v = v->back; + } + taskVarUnlock (); +} + +double epicsThreadSleepQuantum ( void ) +{ + extern double rtemsTicksPerSecond_double; + + return 1.0 / rtemsTicksPerSecond_double; +} + +epicsShareFunc int epicsThreadGetCPUs(void) +{ +#if defined(RTEMS_SMP) + return rtems_smp_get_number_of_processors(); +#else + return 1; +#endif +} diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.h b/modules/libcom/src/osi/os/RTEMS/osdThread.h new file mode 100644 index 000000000..4451f845a --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdThread.h @@ -0,0 +1,12 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +int epicsThreadGetOssPriorityValue(unsigned int osiPriority); + diff --git a/modules/libcom/src/osi/os/RTEMS/osdThreadExtra.c b/modules/libcom/src/osi/os/RTEMS/osdThreadExtra.c new file mode 100644 index 000000000..0a7c7ae95 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdThreadExtra.c @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Ralph Lange Date: 26 Jun 2012 */ + +/* Null default thread hooks for all platforms that do not do anything special */ + +#define epicsExportSharedSymbols +#include "epicsThread.h" + +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; diff --git a/modules/libcom/src/osi/os/RTEMS/osdTime.cpp b/modules/libcom/src/osi/os/RTEMS/osdTime.cpp new file mode 100644 index 000000000..1350c1cf2 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdTime.cpp @@ -0,0 +1,171 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: W. Eric Norum + */ +#define __BSD_VISIBLE 1 +#include +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include +#include +#include +#include +#include +#include +#include +#include +#include "epicsTime.h" +#include "osdTime.h" +#include "osiNTPTime.h" +#include "osiClockTime.h" +#include "generalTimeSup.h" + +extern "C" { + +extern rtems_interval rtemsTicksPerSecond; +int rtems_bsdnet_get_ntp(int, int(*)(), struct timespec *); +static int ntpSocket = -1; + +void osdTimeRegister(void) +{ + /* Init NTP first so it can be used to sync ClockTime */ + NTPTime_Init(100); + ClockTime_Init(CLOCKTIME_SYNC); + + osdMonotonicInit(); +} + +int osdNTPGet(struct timespec *ts) +{ + static unsigned bequiet; + ssize_t ret; + + if (ntpSocket < 0) + return -1; + + /* rtems_bsdnet_get_ntp() will send an NTP request, then + * call recvfrom() exactly once to process the expected reply. + * Any leftovers in the socket buffer (ie. duplicates of + * previous replies) will cause problems. + * So flush out the socket buffer first. + */ + do { + char junk[16]; + + ret = recvfrom(ntpSocket, junk, sizeof(junk), MSG_DONTWAIT, NULL, NULL); + if (ret == -1 && errno == EAGAIN) { + break; + } + else if (ret == -1) { + if (!bequiet) { + printf("osdNTPGet cleaner error: %s\n", strerror(errno)); + bequiet = 1; + } + break; + } + else { + bequiet = 0; + } + } while (ret > 0); + + return rtems_bsdnet_get_ntp(ntpSocket, NULL, ts); +} + +void osdNTPInit(void) +{ + struct sockaddr_in myAddr; + + ntpSocket = socket (AF_INET, SOCK_DGRAM, 0); + if (ntpSocket < 0) { + printf("osdNTPInit() Can't create socket: %s\n", strerror (errno)); + return; + } + memset (&myAddr, 0, sizeof myAddr); + myAddr.sin_family = AF_INET; + myAddr.sin_port = htons (0); + myAddr.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (ntpSocket, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { + printf("osdNTPInit() Can't bind socket: %s\n", strerror (errno)); + close (ntpSocket); + ntpSocket = -1; + } +} + +void osdNTPReport(void) +{ +} + +int osdTickGet(void) +{ + rtems_interval t; + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &t); + return t; +} + +int osdTickRateGet(void) +{ + return rtemsTicksPerSecond; +} + +/* + * Use reentrant versions of time access + */ +int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return errno; + } +} + +int epicsTime_localtime ( const time_t *clock, struct tm *result ) +{ + struct tm * pRet = localtime_r ( clock, result ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return errno; + } +} + +rtems_interval rtemsTicksPerSecond; +double rtemsTicksPerSecond_double, rtemsTicksPerTwoSeconds_double; + +} // extern "C" + +/* + * Static constructors are run too early in a standalone binary + * to be able to initialize the NTP time provider (the network + * is not available yet), so the RTEMS standalone startup code + * explicitly calls osdTimeRegister() at the appropriate time. + * However if we are loaded dynamically we *do* register our + * standard time providers at static constructor time; in this + * case the network is available already. + */ +static int staticTimeRegister(void) +{ + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &rtemsTicksPerSecond); + rtemsTicksPerSecond_double = rtemsTicksPerSecond; + rtemsTicksPerTwoSeconds_double = rtemsTicksPerSecond_double * 2.0; + + /* If networking is already up at the time static constructors + * are executed then we are probably run-time loaded and it's + * OK to osdTimeRegister() at this point. + */ + if (rtems_bsdnet_ticks_per_second != 0) + osdTimeRegister(); + + return 1; +} +static int done = staticTimeRegister(); diff --git a/modules/libcom/src/osi/os/RTEMS/osdTime.h b/modules/libcom/src/osi/os/RTEMS/osdTime.h new file mode 100644 index 000000000..55e3bc2b0 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdTime.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osdTime_H +#define INC_osdTime_H + +#ifdef __cplusplus +extern "C" { +#endif + +void osdTimeRegister(void); + +void osdNTPInit(void); +int osdNTPGet(struct timespec *); +void osdNTPReport(void); + +int osdTickRateGet(void); +int osdTickGet(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_osdTime_H */ diff --git a/modules/libcom/src/osi/os/RTEMS/osdVME.h b/modules/libcom/src/osi/os/RTEMS/osdVME.h new file mode 100644 index 000000000..65548abcb --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osdVME.h @@ -0,0 +1,22 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * OS-dependent VME support + */ +#ifndef __i386__ +#ifndef __mc68000 +#ifndef __arm__ +#include +#endif +#endif +#endif + +void bcopyLongs(char *source, char *destination, int nlongs); diff --git a/modules/libcom/src/osi/os/RTEMS/osiFileName.h b/modules/libcom/src/osi/os/RTEMS/osiFileName.h new file mode 100644 index 000000000..8c9226898 --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osiFileName.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * RTEMS osiFileName.h + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/RTEMS/osiUnistd.h b/modules/libcom/src/osi/os/RTEMS/osiUnistd.h new file mode 100644 index 000000000..c0a6e222d --- /dev/null +++ b/modules/libcom/src/osi/os/RTEMS/osiUnistd.h @@ -0,0 +1,41 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int putenv (char *); +char *strdup (const char *); +char *strtok_r(char*, const char*, char**); + +int snprintf(char *str, size_t size, const char *format, ...); +#include +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + + +#ifdef __cplusplus +} +#endif diff --git a/modules/libcom/src/osi/os/WIN32/epicsAtomicMS.h b/modules/libcom/src/osi/os/WIN32/epicsAtomicMS.h new file mode 100644 index 000000000..14d2c9bac --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsAtomicMS.h @@ -0,0 +1,222 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicMS_h +#define epicsAtomicMS_h + +#include "epicsAssert.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_INCR_INTT +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); + MS_LONG * const pTarg = ( MS_LONG * ) pTarget; + return MS_InterlockedIncrement ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_INTT +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + return MS_InterlockedDecrement ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_INTT +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + /* we dont use InterlockedAdd because only latest windows is supported */ + return delta + ( int ) MS_InterlockedExchangeAdd ( pTarg, + ( MS_LONG ) delta ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_INTT +#define EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ) +{ + STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( int ) ); + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + return (int) MS_InterlockedCompareExchange ( pTarg, + (MS_LONG) newVal, (MS_LONG) oldVal ); +} +#endif + +#if ! defined ( MS_ATOMIC_64 ) + +/* + * necessary for next three functions + * + * looking at the MS documentation it appears that they will + * keep type long the same size as an int on 64 bit builds + */ +STATIC_ASSERT ( sizeof ( MS_LONG ) == sizeof ( size_t ) ); + +#ifndef EPICS_ATOMIC_INCR_SIZET +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) pTarget; + return MS_InterlockedIncrement ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + return MS_InterlockedDecrement ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, + size_t delta ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + /* we dont use InterlockedAdd because only latest windows is supported */ + return delta + ( size_t ) MS_InterlockedExchangeAdd ( pTarg, + ( MS_LONG ) delta ); +} +#endif + +#ifndef EPICS_ATOMIC_SUB_SIZET +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + MS_LONG ldelta = (MS_LONG) delta; + /* we dont use InterlockedAdd because only latest windows is supported */ + return ( ( size_t ) MS_InterlockedExchangeAdd ( pTarg, -ldelta ) ) - delta; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_SIZET +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( + size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + return (size_t) MS_InterlockedCompareExchange ( pTarg, + (MS_LONG) newVal, (MS_LONG) oldVal ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_PTRT +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + MS_LONG * const pTarg = ( MS_LONG * ) ( pTarget ); + return (EpicsAtomicPtrT) MS_InterlockedCompareExchange ( pTarg, + (MS_LONG) newVal, (MS_LONG) oldVal ); +} +#endif + +#else /* ! MS_ATOMIC_64 */ + +/* + * necessary for next three functions + */ +STATIC_ASSERT ( sizeof ( MS_LONGLONG ) == sizeof ( size_t ) ); + +#ifndef EPICS_ATOMIC_INCR_SIZET +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) pTarget; + return ( size_t ) MS_InterlockedIncrement64 ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); + return ( size_t ) MS_InterlockedDecrement64 ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); + /* we dont use InterlockedAdd64 because only latest windows is supported */ + return delta + ( size_t ) MS_InterlockedExchangeAdd64 ( pTarg, + ( MS_LONGLONG ) delta ); +} +#endif + +#ifndef EPICS_ATOMIC_SUB_SIZET +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); + MS_LONGLONG ldelta = (MS_LONGLONG) delta; + /* we dont use InterlockedAdd64 because only latest windows is supported */ + return (( size_t ) MS_InterlockedExchangeAdd64 ( pTarg, -ldelta )) - delta; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_SIZET +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); + return (size_t) MS_InterlockedCompareExchange64 ( pTarg, + (MS_LONGLONG) newVal, + (MS_LONGLONG) oldVal ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_PTRT +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + MS_LONGLONG * const pTarg = ( MS_LONGLONG * ) ( pTarget ); + return (EpicsAtomicPtrT) MS_InterlockedCompareExchange64 ( pTarg, + (MS_LONGLONG) newVal, (MS_LONGLONG) oldVal ); +} +#endif + +#endif /* ! MS_ATOMIC_64 */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* ifdef epicsAtomicMS_h */ + diff --git a/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h new file mode 100644 index 000000000..2170fceff --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsAtomicOSD.h @@ -0,0 +1,67 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicOSD_h +#define epicsAtomicOSD_h + +#define EPICS_ATOMIC_OS_NAME "WIN32" + +#ifdef VC_EXTRALEAN +# define VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h +#else +# define VC_EXTRALEAN +#endif + +#ifdef STRICT +# define STRICT_DETECTED_epicsAtomicOSD_h +#else +# define STRICT +#endif + +#include "windows.h" + +#ifndef VC_EXTRALEAN_DETECTED_epicsAtomicOSD_h +# undef VC_EXTRALEAN +#endif + +#ifndef STRICT_DETECTED_epicsAtomicOSD_h +# undef STRICT +#endif + +#if defined ( _WIN64 ) +# define MS_ATOMIC_64 +#endif + +#define MS_LONG LONG +#define MS_InterlockedExchange InterlockedExchange +#define MS_InterlockedCompareExchange InterlockedCompareExchange +#define MS_InterlockedIncrement InterlockedIncrement +#define MS_InterlockedDecrement InterlockedDecrement +#define MS_InterlockedExchange InterlockedExchange +#define MS_InterlockedExchangeAdd InterlockedExchangeAdd +#if defined ( MS_ATOMIC_64 ) +# define MS_LONGLONG LONGLONG +# define MS_InterlockedIncrement64 InterlockedIncrement64 +# define MS_InterlockedDecrement64 InterlockedDecrement64 +# define MS_InterlockedExchange64 InterlockedExchange64 +# define MS_InterlockedExchangeAdd64 InterlockedExchangeAdd64 +# define MS_InterlockedCompareExchange64 InterlockedCompareExchange64 +#endif + +#include "epicsAtomicMS.h" +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/modules/libcom/src/osi/os/WIN32/epicsGetopt.c b/modules/libcom/src/osi/os/WIN32/epicsGetopt.c new file mode 100644 index 000000000..1ea064ea2 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsGetopt.c @@ -0,0 +1,127 @@ +#ifndef _MINGW + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +#define _getprogname() nargv[0] + +epicsShareDef +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +int optreset; /* reset getopt */ + +epicsShareDef +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", + _getprogname(), optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + _getprogname(), optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} + +#endif /* !_MINGW */ diff --git a/modules/libcom/src/osi/os/WIN32/epicsGetopt.h b/modules/libcom/src/osi/os/WIN32/epicsGetopt.h new file mode 100644 index 000000000..f92041085 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsGetopt.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef _EPICS_GETOPT_H +#define _EPICS_GETOPT_H + +#ifdef _MINGW + +#include + +#else /* _MINGW */ + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int getopt(int argc, char * const argv[], const char *optstring); + +epicsShareExtern char *optarg; + +epicsShareExtern int optind, opterr, optopt; + +#ifdef __cplusplus +} +#endif + +#endif /* _MINGW */ + +#endif /* _EPICS_GETOPT_H */ diff --git a/modules/libcom/src/osi/os/WIN32/epicsMath.h b/modules/libcom/src/osi/os/WIN32/epicsMath.h new file mode 100644 index 000000000..33eb9dddd --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsMath.h @@ -0,0 +1,40 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include +#include + +#ifndef finite +#define finite(D) _finite(D) +#endif + +#ifndef isnan +#define isnan(D) _isnan(D) +#endif + +#ifndef isinf +#define isinf(D) ( !_finite(D) && !_isnan(D) ) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp b/modules/libcom/src/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp new file mode 100644 index 000000000..3db87ac84 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "epicsStdio.h" + +/* + * epicsSocketConvertErrorToString () + */ +void epicsSocketConvertErrorToString ( + char * pBuf, unsigned bufSize, int theSockError ) +{ + if ( bufSize ) { + /* + * this does not work on systems prior to W2K + */ + DWORD success = FormatMessage ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, theSockError, + MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ), /* Default language */ + pBuf, bufSize, NULL ); + if ( ! success ) { + int status = epicsSnprintf ( + pBuf, bufSize, "WINSOCK Error %d", theSockError ); + if ( status <= 0 ) { + strncpy ( pBuf, "WINSOCK Error", bufSize ); + pBuf [bufSize - 0] = '\0'; + } + } + } +} + +/* + * epicsSocketConvertErrnoToString () + */ +void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ) +{ + epicsSocketConvertErrorToString ( pBuf, bufSize, SOCKERRNO ); +} diff --git a/modules/libcom/src/osi/os/WIN32/epicsTempFile.cpp b/modules/libcom/src/osi/os/WIN32/epicsTempFile.cpp new file mode 100644 index 000000000..11b835134 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/epicsTempFile.cpp @@ -0,0 +1,86 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsTempFile.h" + +// +// epicsTempName +// +// allow the teporary file directory to be set with the +// TMP environment varianble +// +extern "C" +epicsShareFunc void epicsShareAPI epicsTempName ( + char * pNameBuf, size_t nameBufLength ) +{ + if ( nameBufLength ) { + pNameBuf[0] = '\0'; + char * pName = _tempnam ( "c:\\tmp", "epics" ); + if ( pName ) { + if ( nameBufLength > strlen ( pName ) ) { + strncpy ( pNameBuf, pName, nameBufLength ); + } + free ( pName ); + } + } +} + +// +// epicsTmpFile +// +// allow the teporary file directory to be set with the +// TMP environment varianble +// +extern "C" +epicsShareFunc FILE * epicsShareAPI epicsTempFile () +{ + char * pName = _tempnam ( "c:\\tmp", "epics" ); + if( ! pName ) { + return 0; + } + // We use open followed by fdopen so that the _O_EXCL + // flag can be used. This causes detection of a race + // condition where two programs end up receiving the + // same temporary file name. + // + // _O_CREAT create if non-existant + // _O_EXCL file must not exist + // _O_RDWR read and write the file + // _O_TEMPORARY delete file on close + // _O_BINARY no translation + // _O_SHORT_LIVED avoid flush to disk + // + const int openFlag = _O_CREAT | _O_EXCL | _O_RDWR | + _O_SHORT_LIVED | _O_BINARY | _O_TEMPORARY; + int fd = open ( pName, openFlag, _S_IWRITE ); + FILE * pNewFile = 0; + if ( fd >=0 ) { + pNewFile = _fdopen ( fd, "w+b" ); + } + else { + printf ( + "Temporary file \"%s\" open failed because " + "\"%s\"\n", pName, strerror ( errno ) ); + } + free ( pName ); + return pNewFile; +} + diff --git a/modules/libcom/src/osi/os/WIN32/forceBadAllocException.cpp b/modules/libcom/src/osi/os/WIN32/forceBadAllocException.cpp new file mode 100644 index 000000000..985ec988e --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/forceBadAllocException.cpp @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// On older version of ms visual c++ operator new +// does not throw a bad_alloc exception +// when it fails. It simply returns a null pointer. +// This behavior is not in conformance with the +// ANSI / ISO C++. +// +#if _MSC_VER > 1000 && _MSC_VER < 1400 + +#include +#include + +// instruct loader to call this gllobal object +// constructor before user global object constructors +#pragma warning (disable: 4073) +#pragma init_seg(lib) +#pragma warning (default: 4073) + +static int my_new_handler (size_t size) +{ + throw std::bad_alloc(); + return 0; +} + +class my_new_handler_obj +{ +public: + my_new_handler_obj() + { + //_old_new_mode = _set_new_mode(1); // cause malloc to throw like new + _old_new_handler = _set_new_handler(my_new_handler); + } + + ~my_new_handler_obj() + { + _set_new_handler(_old_new_handler); + //_set_new_mode(_old_new_mode); + } + +private: + _PNH _old_new_handler; + //int _old_new_mode; +}; + +static my_new_handler_obj _g_new_handler_obj; + +#endif diff --git a/modules/libcom/src/osi/os/WIN32/osdBackTrace.cpp b/modules/libcom/src/osi/os/WIN32/osdBackTrace.cpp new file mode 100644 index 000000000..8f5896a37 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdBackTrace.cpp @@ -0,0 +1,28 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2014 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsStackTracePvt.h" + +int epicsBackTrace(void **buf, int buf_sz) +{ +#ifdef CaptureStackBackTrace + /* Docs say that (for some windows versions) the sum of + * skipped + captured frames must be less than 63 + */ + if ( buf_sz >= 63 ) + buf_sz = 62; + return CaptureStackBackTrace(0, buf_sz, buf, 0); +#else + /* Older versions of MinGW */ + return -1; +#endif +} diff --git a/modules/libcom/src/osi/os/WIN32/osdEvent.c b/modules/libcom/src/osi/os/WIN32/osdEvent.c new file mode 100644 index 000000000..314138b27 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdEvent.c @@ -0,0 +1,145 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEvent.c */ +/* + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#include + +#define VC_EXTRALEAN +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "shareLib.h" +#include "epicsEvent.h" + +typedef struct epicsEventOSD { + HANDLE handle; +} epicsEventOSD; + +/* + * epicsEventCreate () + */ +epicsShareFunc epicsEventId epicsEventCreate ( + epicsEventInitialState initialState ) +{ + epicsEventOSD *pSem; + + pSem = malloc ( sizeof ( *pSem ) ); + if ( pSem ) { + pSem->handle = CreateEvent ( NULL, FALSE, initialState?TRUE:FALSE, NULL ); + if ( pSem->handle == 0 ) { + free ( pSem ); + pSem = 0; + } + } + + return pSem; +} + +/* + * epicsEventDestroy () + */ +epicsShareFunc void epicsEventDestroy ( epicsEventId pSem ) +{ + CloseHandle ( pSem->handle ); + free ( pSem ); +} + +/* + * epicsEventTrigger () + */ +epicsShareFunc epicsEventStatus epicsEventTrigger ( epicsEventId pSem ) +{ + BOOL status; + status = SetEvent ( pSem->handle ); + return status ? epicsEventOK : epicsEventError; +} + +/* + * epicsEventWait () + */ +epicsShareFunc epicsEventStatus epicsEventWait ( epicsEventId pSem ) +{ + DWORD status; + status = WaitForSingleObject (pSem->handle, INFINITE); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventOK; + } + else { + return epicsEventError; + } +} + +/* + * epicsEventWaitWithTimeout () + */ +epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout ( + epicsEventId pSem, double timeOut ) +{ + static const unsigned mSecPerSec = 1000; + DWORD status; + DWORD tmo; + + if ( timeOut <= 0.0 ) { + tmo = 0u; + } + else if ( timeOut >= INFINITE / mSecPerSec ) { + tmo = INFINITE - 1; + } + else { + tmo = ( DWORD ) ( ( timeOut * mSecPerSec ) + 0.5 ); + if ( tmo == 0 ) { + tmo = 1; + } + } + status = WaitForSingleObject ( pSem->handle, tmo ); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventOK; + } + else if ( status == WAIT_TIMEOUT ) { + return epicsEventWaitTimeout; + } + else { + return epicsEventError; + } +} + +/* + * epicsEventTryWait () + */ +epicsShareFunc epicsEventStatus epicsEventTryWait ( epicsEventId pSem ) +{ + DWORD status; + + status = WaitForSingleObject ( pSem->handle, 0 ); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventOK; + } + else if ( status == WAIT_TIMEOUT ) { + return epicsEventWaitTimeout; + } + else { + return epicsEventError; + } +} + +/* + * epicsEventShow () + */ +epicsShareFunc void epicsEventShow ( epicsEventId id, unsigned level ) +{ +} diff --git a/modules/libcom/src/osi/os/WIN32/osdEvent.h b/modules/libcom/src/osi/os/WIN32/osdEvent.h new file mode 100644 index 000000000..f57f2cc49 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdEvent.h @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEvent.c */ +/* + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +/* osdEvent.h not needed */ diff --git a/modules/libcom/src/osi/os/WIN32/osdFindSymbol.c b/modules/libcom/src/osi/os/WIN32/osdFindSymbol.c new file mode 100644 index 000000000..6de3c428e --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdFindSymbol.c @@ -0,0 +1,46 @@ +/*************************************************************************\ +* Copyright (c) 2013 Dirk Zimoch, PSI +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/WIN32/osdFindSymbol.c */ + + +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +static int epicsLoadErrorCode = 0; + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + HMODULE lib; + + epicsLoadErrorCode = 0; + lib = LoadLibrary(name); + if (lib == NULL) + { + epicsLoadErrorCode = GetLastError(); + } + return lib; +} + +epicsShareFunc const char *epicsLoadError(void) +{ + static char buffer[100]; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + epicsLoadErrorCode, + 0, + buffer, + sizeof(buffer)-1, NULL ); + return buffer; +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return GetProcAddress(0, name); +} diff --git a/modules/libcom/src/osi/os/WIN32/osdMonotonic.c b/modules/libcom/src/osi/os/WIN32/osdMonotonic.c new file mode 100644 index 000000000..e7da25da4 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdMonotonic.c @@ -0,0 +1,53 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "dbDefs.h" +#include "errlog.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +static unsigned char osdUsePrefCounter; +static epicsUInt64 osdMonotonicResolution; + +void osdMonotonicInit(void) +{ + LARGE_INTEGER freq, val; + + if(!QueryPerformanceFrequency(&freq) || + !QueryPerformanceCounter(&val)) + { + double period = 1.0/freq.QuadPart; + osdMonotonicResolution = period*1e9; + osdUsePrefCounter = 1; + } else { + osdMonotonicResolution = 1e6; /* 1 ms TODO place holder */ + } +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return osdMonotonicResolution; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + LARGE_INTEGER val; + if(osdUsePrefCounter) { + if(!QueryPerformanceCounter(&val)) { + errMessage(errlogMinor, "Warning: failed to fetch performance counter\n"); + return 0; + } else + return val.QuadPart; + } else { + epicsUInt64 ret = GetTickCount(); + ret *= 1000000; + return ret; + } +} diff --git a/modules/libcom/src/osi/os/WIN32/osdMutex.c b/modules/libcom/src/osi/os/WIN32/osdMutex.c new file mode 100644 index 000000000..63a51efd5 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdMutex.c @@ -0,0 +1,179 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdMutex.c */ +/* + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#include +#include + +#define VC_EXTRALEAN +#define STRICT +/* + * Defining this allows the *much* faster critical + * section mutex primitive to be used. Unfortunately, + * using certain of these functions drops support for W95\W98\WME + * unless we specify "delay loading" when we link with the + * DLL so that DLL entry points are not resolved until they + * are used. The code does have run time switches so + * that the more advanced calls are not called unless + * they are available in the windows OS, but this feature + * isnt going to be very useful unless we specify "delay + * loading" when we link with the DLL. + * + * It appears that the only entry point used here that causes + * portability problems with W95\W98\WME is TryEnterCriticalSection. + */ +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0400 +#endif +#include + +#define epicsExportSharedSymbols +#include "shareLib.h" +#include "epicsMutex.h" +#include "epicsAssert.h" +#include "epicsStdio.h" + +typedef struct epicsMutexOSD { + union { + HANDLE mutex; + CRITICAL_SECTION criticalSection; + } os; +} epicsMutexOSD; + +static BOOL thisIsNT = FALSE; +static LONG weHaveInitialized = 0; + +/* + * epicsMutexCreate () + */ +epicsMutexOSD * epicsMutexOsdCreate ( void ) +{ + epicsMutexOSD * pSem; + + if ( ! weHaveInitialized ) { + BOOL status; + OSVERSIONINFO osInfo; + osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ); + status = GetVersionEx ( & osInfo ); + thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ); + weHaveInitialized = 1; + } + + pSem = malloc ( sizeof (*pSem) ); + if ( pSem ) { + if ( thisIsNT ) { + InitializeCriticalSection ( &pSem->os.criticalSection ); + } + else { + pSem->os.mutex = CreateMutex ( NULL, FALSE, NULL ); + if ( pSem->os.mutex == 0 ) { + free ( pSem ); + pSem = 0; + } + } + } + return pSem; +} + +/* + * epicsMutexOsdDestroy () + */ +void epicsMutexOsdDestroy ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + DeleteCriticalSection ( &pSem->os.criticalSection ); + } + else { + CloseHandle ( pSem->os.mutex ); + } + free ( pSem ); +} + +/* + * epicsMutexOsdUnlock () + */ +void epicsMutexOsdUnlock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + LeaveCriticalSection ( &pSem->os.criticalSection ); + } + else { + BOOL success = ReleaseMutex ( pSem->os.mutex ); + assert ( success ); + } +} + +/* + * epicsMutexOsdLock () + */ +epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + EnterCriticalSection ( &pSem->os.criticalSection ); + } + else { + DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE ); + if ( status != WAIT_OBJECT_0 ) { + return epicsMutexLockError; + } + } + return epicsMutexLockOK; +} + +/* + * epicsMutexOsdTryLock () + */ +epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + if ( TryEnterCriticalSection ( &pSem->os.criticalSection ) ) { + return epicsMutexLockOK; + } + else { + return epicsMutexLockTimeout; + } + } + else { + DWORD status = WaitForSingleObject ( pSem->os.mutex, 0 ); + if ( status != WAIT_OBJECT_0 ) { + if (status == WAIT_TIMEOUT) { + return epicsMutexLockTimeout; + } + else { + return epicsMutexLockError; + } + } + } + return epicsMutexLockOK; +} + +/* + * epicsMutexOsdShow () + */ +void epicsMutexOsdShow ( epicsMutexOSD * pSem, unsigned level ) +{ + if ( thisIsNT ) { + printf ("epicsMutex: win32 critical section at %p\n", + (void * ) & pSem->os.criticalSection ); + } + else { + printf ( "epicsMutex: win32 mutex at %p\n", + ( void * ) pSem->os.mutex ); + } +} + diff --git a/modules/libcom/src/osi/os/WIN32/osdMutex.h b/modules/libcom/src/osi/os/WIN32/osdMutex.h new file mode 100644 index 000000000..d8f3f78bd --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdMutex.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* osdMutex.h */ +/* + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef osdMutexh +#define osdMutexh + +#endif /* osdMutexh */ diff --git a/modules/libcom/src/osi/os/WIN32/osdNetIntf.c b/modules/libcom/src/osi/os/WIN32/osdNetIntf.c new file mode 100644 index 000000000..db445d494 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdNetIntf.c @@ -0,0 +1,253 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * WIN32 specific initialisation for bsd sockets, + * based on Chris Timossi's base/src/ca/windows_depend.c, + * and also further additions by Kay Kasemir when this was in + * dllmain.cc + * + * 7-1-97 -joh- + * + */ + +/* + * ANSI C + */ +#include +#include + +/* + * WIN32 specific + */ +#define VC_EXTRALEAN +#define STRICT +#include +#include + +/* + * EPICS + */ +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" +#include "epicsThread.h" +#include "epicsVersion.h" + +static osiSockAddr osiLocalAddrResult; +static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; + +/* + * osiLocalAddr () + */ +static void osiLocalAddrOnce ( void *raw ) +{ + SOCKET *psocket = raw; + osiSockAddr addr; + int status; + INTERFACE_INFO *pIfinfo; + INTERFACE_INFO *pIfinfoList = NULL; + unsigned nelem; + DWORD numifs; + DWORD cbBytesReturned; + + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.sa.sa_family = AF_UNSPEC; + + /* only valid for winsock 2 and above */ + if ( wsaMajorVersion() < 2 ) { + goto fail; + } + + nelem = 100; + pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); + if (!pIfinfoList) { + errlogPrintf ("calloc failed\n"); + goto fail; + } + + status = WSAIoctl (*psocket, SIO_GET_INTERFACE_LIST, NULL, 0, + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); + + if (status != 0 || cbBytesReturned == 0) { + errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + goto fail; + } + + numifs = cbBytesReturned / sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + + /* + * dont use interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { + continue; + } + + /* + * dont use the loop back interface + */ + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } + + addr.sa = pIfinfo->iiAddress.Address; + + /* Work around MS Winsock2 bugs */ + if (addr.sa.sa_family == 0) { + addr.sa.sa_family = AF_INET; + } + + osiLocalAddrResult = addr; + free ( pIfinfoList ); + return; + } + + errlogPrintf ( + "osiLocalAddr(): only loopback found\n"); +fail: + /* fallback to loopback */ + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + osiLocalAddrResult = addr; + + free ( pIfinfoList ); +} + +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +{ + epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, (void*)&socket); + return osiLocalAddrResult; +} + +/* + * osiSockDiscoverBroadcastAddresses () + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) +{ + int status; + INTERFACE_INFO *pIfinfo; + INTERFACE_INFO *pIfinfoList; + unsigned nelem; + int numifs; + DWORD cbBytesReturned; + osiSockAddrNode *pNewNode; + + if ( pMatchAddr->sa.sa_family == AF_INET ) { + if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + return; + } + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_port = htons ( 0 ); + pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + ellAdd ( pList, &pNewNode->node ); + return; + } + } + + /* only valid for winsock 2 and above */ + if (wsaMajorVersion() < 2 ) { + fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); + return; + } + + nelem = 100; + pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); + if(!pIfinfoList){ + return; + } + + status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, + NULL, 0, + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); + + if (status != 0 || cbBytesReturned == 0) { + fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + free(pIfinfoList); + return; + } + + numifs = cbBytesReturned/sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + + /* + * dont bother with interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { + continue; + } + + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } + + /* + * work around WS2 bug + */ + if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { + if (pIfinfo->iiAddress.Address.sa_family == 0) { + pIfinfo->iiAddress.Address.sa_family = AF_INET; + } + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + if (pMatchAddr->sa.sa_family != AF_UNSPEC) { + if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) { + continue; + } + if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { + continue; + } + if (pMatchAddr->sa.sa_family != AF_INET) { + continue; + } + if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { + continue; + } + } + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); + if (pNewNode==NULL) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n"); + return; + } + + if (pIfinfo->iiAddress.Address.sa_family == AF_INET && + pIfinfo->iiFlags & IFF_BROADCAST) { + const unsigned mask = pIfinfo->iiNetmask.AddressIn.sin_addr.s_addr; + const unsigned bcast = pIfinfo->iiBroadcastAddress.AddressIn.sin_addr.s_addr; + const unsigned addr = pIfinfo->iiAddress.AddressIn.sin_addr.s_addr; + unsigned result = (addr & mask) | (bcast &~mask); + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_addr.s_addr = result; + pNewNode->addr.ia.sin_port = htons ( 0 ); + } + else { + pNewNode->addr.sa = pIfinfo->iiBroadcastAddress.Address; + } + + /* + * LOCK applied externally + */ + ellAdd (pList, &pNewNode->node); + } + + free (pIfinfoList); +} diff --git a/modules/libcom/src/osi/os/WIN32/osdPoolStatus.c b/modules/libcom/src/osi/os/WIN32/osdPoolStatus.c new file mode 100644 index 000000000..bb764b2ad --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdPoolStatus.c @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + * + * @@@@@ not implemented @@@@@ + * + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + return 1; +} diff --git a/modules/libcom/src/osi/os/WIN32/osdPoolStatus.h b/modules/libcom/src/osi/os/WIN32/osdPoolStatus.h new file mode 100644 index 000000000..178434b11 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdPoolStatus.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifdef osdPoolStatush +#define osdPoolStatush + +#endif /* osdPoolStatush */ diff --git a/modules/libcom/src/osi/os/WIN32/osdProcess.c b/modules/libcom/src/osi/os/WIN32/osdProcess.c new file mode 100644 index 000000000..6e69c0485 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdProcess.c @@ -0,0 +1,137 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#ifndef _WIN32 +#error This source is specific to WIN32 +#endif + +#include + +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + DWORD bufsize; + + if ( bufSizeIn > 0xffffffff ) { + return osiGetUserNameFail; + } + + bufsize = (DWORD) bufSizeIn; + + if ( ! GetUserName (pBuf, &bufsize) ) { + return osiGetUserNameFail; + } + + if ( *pBuf == '\0' ) { + return osiGetUserNameFail; + } + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + ( const char *pProcessName, const char *pBaseExecutableName ) +{ + BOOL status; + STARTUPINFO startupInfo; + PROCESS_INFORMATION processInfo; + + GetStartupInfo ( &startupInfo ); + startupInfo.lpReserved = NULL; + startupInfo.lpTitle = (char *) pProcessName; + startupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.wShowWindow = SW_SHOWMINNOACTIVE; + + status = CreateProcess ( + NULL, /* pointer to name of executable module (not required if command line is specified) */ + (char *) pBaseExecutableName, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + FALSE, /* handle inheritance flag */ + CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS, /* creation flags */ + NULL, /* pointer to new environment block (defaults to caller's environement) */ + NULL, /* pointer to current directory name (defaults to caller's current directory) */ + &startupInfo, /* pointer to STARTUPINFO */ + &processInfo /* pointer to PROCESS_INFORMATION */ + ); + if ( status == 0 ) { + DWORD W32status; + LPVOID errStrMsgBuf; + LPVOID complteMsgBuf; + + W32status = FormatMessage ( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &errStrMsgBuf, + 0, + NULL + ); + + if ( W32status ) { + char *pFmtArgs[6]; + pFmtArgs[0] = "Failed to start executable -"; + pFmtArgs[1] = (char *) pBaseExecutableName; + pFmtArgs[2] = errStrMsgBuf; + pFmtArgs[3] = "Changes may be required in your \"path\" environment variable."; + pFmtArgs[4] = "PATH = "; + pFmtArgs[5] = getenv ("path"); + if ( pFmtArgs[5] == NULL ) { + pFmtArgs[5] = ""; + } + + W32status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY | 80, + "%1 \"%2\". %3 %4 %5 \"%6\"", + 0, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &complteMsgBuf, + 0, + pFmtArgs + ); + if (W32status) { + /* Display the string. */ + MessageBox (NULL, complteMsgBuf, "Configuration Problem", + MB_OK | MB_ICONINFORMATION); + LocalFree (complteMsgBuf); + } + else { + /* Display the string. */ + MessageBox (NULL, errStrMsgBuf, "Failed to start executable", + MB_OK | MB_ICONINFORMATION); + } + + /* Free the buffer. */ + LocalFree (errStrMsgBuf); + } + else { + errlogPrintf ("!!WARNING!!\n"); + errlogPrintf ("Unable to locate executable \"%s\".\n", pBaseExecutableName); + errlogPrintf ("You may need to modify your \"path\" environment variable.\n"); + } + return osiSpawnDetachedProcessFail; + } + + return osiSpawnDetachedProcessSuccess; +} diff --git a/modules/libcom/src/osi/os/WIN32/osdSignal.cpp b/modules/libcom/src/osi/os/WIN32/osdSignal.cpp new file mode 100644 index 000000000..5f799273a --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * Copyright (c) 2002 The University of Chicago, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * NOOP + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadid */ ) {} + diff --git a/modules/libcom/src/osi/os/WIN32/osdSock.c b/modules/libcom/src/osi/os/WIN32/osdSock.c new file mode 100644 index 000000000..b8c8363fb --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdSock.c @@ -0,0 +1,190 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * WIN32 specific initialisation for bsd sockets, + * based on Chris Timossi's base/src/ca/windows_depend.c, + * and also further additions by Kay Kasemir when this was in + * dllmain.cc + * + * 7-1-97 -joh- + * + */ + +/* + * ANSI C + */ +#include +#include + +/* + * WIN32 specific + */ +#define VC_EXTRALEAN +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" +#include "epicsVersion.h" + +static unsigned nAttached = 0; +static WSADATA WsaData; /* version of winsock */ + +epicsShareFunc unsigned epicsShareAPI wsaMajorVersion () +{ + return (unsigned) LOBYTE( WsaData.wVersion ); +} + +/* + * osiSockAttach() + */ +epicsShareFunc int epicsShareAPI osiSockAttach() +{ + int status; + + if (nAttached) { + nAttached++; + return TRUE; + } + +#if _DEBUG + /* for gui applications, setup console for error messages */ + if (AllocConsole()) + { + char title[256]; + DWORD titleLength = GetConsoleTitle(title, sizeof(title)); + if (titleLength) { + titleLength = strlen (title); + strncat (title, " " EPICS_VERSION_STRING, sizeof(title)); + } + else { + strncpy(title, EPICS_VERSION_STRING, sizeof(title)); + } + title[sizeof(title)-1]= '\0'; + SetConsoleTitle(title); + freopen( "CONOUT$", "a", stderr ); + } +#endif + + /* + * attach to win sock + */ + status = WSAStartup(MAKEWORD(/*major*/2,/*minor*/2), &WsaData); + if (status != 0) { + fprintf(stderr, + "Unable to attach to windows sockets version 2. error=%d\n", status); + fprintf(stderr, + "A Windows Sockets II update for windows 95 is available at\n"); + fprintf(stderr, + "http://www.microsoft.com/windows95/info/ws2.htm"); + return FALSE; + } + +# if defined ( _DEBUG ) && 0 + fprintf(stderr, "EPICS attached to winsock version %s\n", WsaData.szDescription); +# endif + + nAttached = 1u; + + return TRUE; +} + +/* + * osiSockRelease() + */ +epicsShareFunc void epicsShareAPI osiSockRelease() +{ + if (nAttached) { + if (--nAttached==0u) { + WSACleanup(); +# if defined ( _DEBUG ) && 0 + fprintf(stderr, "EPICS released winsock version %s\n", WsaData.szDescription); +# endif + memset (&WsaData, '\0', sizeof(WsaData)); + } + } +} + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + return socket ( domain, type, protocol ); +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + return accept ( sock, pAddr, addrlen ); +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = closesocket ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", buf ); + } +} + +/* + * ipAddrToHostName + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + struct hostent *ent; + + if (bufSize<1) { + return 0; + } + + ent = gethostbyaddr((char *) pAddr, sizeof (*pAddr), AF_INET); + if (ent) { + strncpy (pBuf, ent->h_name, bufSize); + pBuf[bufSize-1] = '\0'; + return strlen (pBuf); + } + return 0; +} + +/* + * hostToIPAddr () + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA) +{ + struct hostent *phe; + + phe = gethostbyname (pHostName); + if (phe && phe->h_addr_list[0]) { + if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { + struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; + + *pIPA = *pInAddrIn; + + /* + * success + */ + return 0; + } + } + + /* + * return indicating an error + */ + return -1; +} + + diff --git a/modules/libcom/src/osi/os/WIN32/osdSock.h b/modules/libcom/src/osi/os/WIN32/osdSock.h new file mode 100644 index 000000000..2ff23c4a6 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdSock.h @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdSockH +#define osdSockH + +#include +#include + +/* + * winsock2.h changes the structure alignment to 4 if + * WIN32 isnt set which can be a source of confusion + */ +#ifndef WIN32 +# define WIN32 +#endif +#include +#include + +#define SOCKERRNO WSAGetLastError() + +#define socket_ioctl(A,B,C) ioctlsocket(A,B,C) +typedef u_long FAR osiSockIoctl_t; +typedef int osiSocklen_t; +typedef BOOL osiSockOptMcastLoop_t; + +#ifndef SHUT_RD +# define SHUT_RD SD_RECEIVE +#endif + +#ifndef SHUT_WR +# define SHUT_WR SD_SEND +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR SD_BOTH +#endif + +#define MAXHOSTNAMELEN 75 +#define IPPORT_USERRESERVED 5000U + +#define SOCK_EWOULDBLOCK WSAEWOULDBLOCK +#define SOCK_ENOBUFS WSAENOBUFS +#define SOCK_ECONNRESET WSAECONNRESET +#define SOCK_ETIMEDOUT WSAETIMEDOUT +#define SOCK_EACCES WSAEACCES +#define SOCK_EADDRINUSE WSAEADDRINUSE +#define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL +#define SOCK_ECONNREFUSED WSAECONNREFUSED +#define SOCK_ECONNABORTED WSAECONNABORTED +#define SOCK_EINPROGRESS WSAEINPROGRESS +#define SOCK_EISCONN WSAEISCONN +#define SOCK_EALREADY WSAEALREADY +#define SOCK_EINVAL WSAEINVAL +#define SOCK_EINTR WSAEINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE WSAEMFILE +#define SOCK_SHUTDOWN WSAESHUTDOWN +#define SOCK_ENOTSOCK WSAENOTSOCK +#define SOCK_EBADF WSAENOTSOCK + +/* + * Under WIN32, FD_SETSIZE is the max. number of sockets, + * not the max. fd value that you use in select(). + * + * Therefore, it is difficult to detemine if any given + * fd can be used with FD_SET(), FD_CLR(), and FD_ISSET(). + */ +#define FD_IN_FDSET(FD) (1) + +epicsShareFunc unsigned epicsShareAPI wsaMajorVersion (); + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/WIN32/osdSockAddrReuse.cpp b/modules/libcom/src/osi/os/WIN32/osdSockAddrReuse.cpp new file mode 100644 index 000000000..d441d7511 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdSockAddrReuse.cpp @@ -0,0 +1,44 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +/* + * Note: WINSOCK appears to assign a different functionality for + * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates + * that simultaneously servers can bind to the same TCP port on the same host! + * Also, servers are always enabled to reuse a port immediately after + * they exit ( even if SO_REUSEADDR isnt set ). + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ +} + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/modules/libcom/src/osi/os/WIN32/osdStdio.c b/modules/libcom/src/osi/os/WIN32/osdStdio.c new file mode 100644 index 000000000..8a2d58ea9 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdStdio.c @@ -0,0 +1,45 @@ +/* osdStdio.c */ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#ifndef _MSC_VER +/* Older versions of MinGW omitted this prototype from stdio.h */ +_CRTIMP int __cdecl __MINGW_NOTHROW _vscprintf (const char*, va_list); +#endif + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +int epicsShareAPI epicsVsnprintf(char *str, size_t len, + const char *fmt, va_list ap) +{ + int retval = _vsnprintf(str, len, fmt, ap); + int needed = _vscprintf(fmt, ap); + + if ((int) len < needed + 1) { + str[len - 1] = 0; + return needed; + } + + return retval; +} + +int epicsShareAPI epicsSnprintf (char *str, size_t len, const char *fmt, ...) +{ + int rtn; + va_list pvar; + + va_start (pvar, fmt); + rtn = epicsVsnprintf (str, len, fmt, pvar); + va_end (pvar); + return (rtn); +} diff --git a/modules/libcom/src/osi/os/WIN32/osdStrtod.h b/modules/libcom/src/osi/os/WIN32/osdStrtod.h new file mode 100644 index 000000000..4edb444ee --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdStrtod.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); + +/* + * Microsoft apparently added strto[u]ll() in VS2013 + * Older compilers have these equivalents though + */ + +#if !defined(_MINGW) && (_MSC_VER < 1800) +# define strtoll _strtoi64 +# define strtoull _strtoui64 +#endif + +#ifdef __cplusplus +} +#endif diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c new file mode 100644 index 000000000..8cdb4a3f4 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdThread.c @@ -0,0 +1,1125 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#include +#include +#include +#include + +#define VC_EXTRALEAN +#define STRICT +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x400 /* No support for W95 */ +#endif +#include +#include /* for _endthread() etc */ + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "shareLib.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsAssert.h" +#include "ellLib.h" +#include "epicsExit.h" + +epicsShareFunc void osdThreadHooksRun(epicsThreadId id); + +void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); +static void threadCleanupWIN32 ( void ); + +typedef struct win32ThreadGlobal { + CRITICAL_SECTION mutex; + ELLLIST threadList; + DWORD tlsIndexThreadLibraryEPICS; +} win32ThreadGlobal; + +typedef struct epicsThreadOSD { + ELLNODE node; + HANDLE handle; + EPICSTHREADFUNC funptr; + void * parm; + char * pName; + DWORD id; + unsigned epicsPriority; + char isSuspended; +} win32ThreadParam; + +typedef struct epicsThreadPrivateOSD { + DWORD key; +} epicsThreadPrivateOSD; + +#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION +# define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 +#endif + +#define osdOrdinaryPriorityStateCount 5u +static const int osdOrdinaryPriorityList [osdOrdinaryPriorityStateCount] = +{ + THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ + THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ + THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_HIGHEST /* 2 on >= W2K ??? on W95 */ +}; + +# define osdRealtimePriorityStateCount 14u +static const int osdRealtimePriorityList [osdRealtimePriorityStateCount] = +{ + -7, /* allowed on >= W2k, but no #define supplied */ + -6, /* allowed on >= W2k, but no #define supplied */ + -5, /* allowed on >= W2k, but no #define supplied */ + -4, /* allowed on >= W2k, but no #define supplied */ + -3, /* allowed on >= W2k, but no #define supplied */ + THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ + THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ + THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_HIGHEST, /* 2 on >= W2K ??? on W95 */ + 3, /* allowed on >= W2k, but no #define supplied */ + 4, /* allowed on >= W2k, but no #define supplied */ + 5, /* allowed on >= W2k, but no #define supplied */ + 6 /* allowed on >= W2k, but no #define supplied */ +}; + +#if defined(EPICS_BUILD_DLL) +BOOL WINAPI DllMain ( + HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved ) +{ + static DWORD dllHandleIndex; + HMODULE dllHandle = 0; + BOOL success = TRUE; + + switch ( dwReason ) + { + case DLL_PROCESS_ATTACH: + dllHandleIndex = TlsAlloc (); + if ( dllHandleIndex == TLS_OUT_OF_INDEXES ) { + success = FALSE; + } + break; + + case DLL_PROCESS_DETACH: + success = TlsFree ( dllHandleIndex ); + break; + + case DLL_THREAD_ATTACH: + /* + * Dont allow user's explicitly calling FreeLibrary for Com.dll to yank + * the carpet out from under EPICS threads that are still using Com.dll + */ +#if _WIN32_WINNT >= 0x0501 + /* + * Only in WXP + * Thats a shame because this is probably much faster + */ + success = GetModuleHandleEx ( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + ( LPCTSTR ) DllMain, & dllHandle ); +#else + { + char name[256]; + DWORD nChar = GetModuleFileName ( + hModule, name, sizeof ( name ) ); + if ( nChar && nChar < sizeof ( name ) ) { + dllHandle = LoadLibrary ( name ); + if ( ! dllHandle ) { + success = FALSE; + } + } + else { + success = FALSE; + } + } +#endif + if ( success ) { + success = TlsSetValue ( dllHandleIndex, dllHandle ); + } + break; + case DLL_THREAD_DETACH: + /* + * Thread is exiting, release Com.dll. I am assuming that windows is + * smart enough to postpone the unload until this function returns. + */ + dllHandle = TlsGetValue ( dllHandleIndex ); + if ( dllHandle ) { + success = FreeLibrary ( dllHandle ); + } + break; + } + + return success; +} +#endif + +/* + * fetchWin32ThreadGlobal () + * Search for "Synchronization and Multiprocessor Issues" in ms doc + * to understand why this is necessary and why this works on smp systems. + */ +static win32ThreadGlobal * fetchWin32ThreadGlobal ( void ) +{ + static win32ThreadGlobal * pWin32ThreadGlobal = 0; + static LONG initStarted = 0; + static LONG initCompleted = 0; + int crtlStatus; + LONG started; + LONG done; + + done = InterlockedCompareExchange ( & initCompleted, 0, 0 ); + if ( done ) { + return pWin32ThreadGlobal; + } + + started = InterlockedCompareExchange ( & initStarted, 0, 1 ); + if ( started ) { + unsigned tries = 0u; + while ( ! InterlockedCompareExchange ( & initCompleted, 0, 0 ) ) { + /* + * I am not fond of busy loops, but since this will + * collide very infrequently and this is the lowest + * level init then perhaps this is ok + */ + Sleep ( 1 ); + if ( tries++ > 1000 ) { + return 0; + } + } + return pWin32ThreadGlobal; + } + + pWin32ThreadGlobal = ( win32ThreadGlobal * ) + calloc ( 1, sizeof ( * pWin32ThreadGlobal ) ); + if ( ! pWin32ThreadGlobal ) { + InterlockedExchange ( & initStarted, 0 ); + return 0; + } + + InitializeCriticalSection ( & pWin32ThreadGlobal->mutex ); + ellInit ( & pWin32ThreadGlobal->threadList ); + pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS = TlsAlloc(); + if ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS == 0xFFFFFFFF ) { + DeleteCriticalSection ( & pWin32ThreadGlobal->mutex ); + free ( pWin32ThreadGlobal ); + pWin32ThreadGlobal = 0; + return 0; + } + + crtlStatus = atexit ( threadCleanupWIN32 ); + if ( crtlStatus ) { + TlsFree ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS ); + DeleteCriticalSection ( & pWin32ThreadGlobal->mutex ); + free ( pWin32ThreadGlobal ); + pWin32ThreadGlobal = 0; + return 0; + } + + InterlockedExchange ( & initCompleted, 1 ); + + return pWin32ThreadGlobal; +} + +static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + if ( ! pGbl ) { + fprintf ( stderr, "epicsParmCleanupWIN32: unable to find ctx\n" ); + return; + } + + if ( pParm ) { + /* fprintf ( stderr, "thread %s is exiting\n", pParm->pName ); */ + EnterCriticalSection ( & pGbl->mutex ); + ellDelete ( & pGbl->threadList, & pParm->node ); + LeaveCriticalSection ( & pGbl->mutex ); + + CloseHandle ( pParm->handle ); + free ( pParm ); + TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, 0 ); + } +} + +/* + * threadCleanupWIN32 () + */ +static void threadCleanupWIN32 ( void ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + fprintf ( stderr, "threadCleanupWIN32: unable to find ctx\n" ); + return; + } + + while ( ( pParm = ( win32ThreadParam * ) + ellFirst ( & pGbl->threadList ) ) ) { + epicsParmCleanupWIN32 ( pParm ); + } +} + +/* + * epicsThreadExitMain () + */ +epicsShareFunc void epicsShareAPI epicsThreadExitMain ( void ) +{ + _endthread (); +} + +/* + * osdPriorityMagFromPriorityOSI () + */ +static unsigned osdPriorityMagFromPriorityOSI ( unsigned osiPriority, unsigned priorityStateCount ) +{ + unsigned magnitude; + + /* optimizer will remove this one if epicsThreadPriorityMin is zero */ + /* and osiPriority is unsigned */ + if ( osiPriority < epicsThreadPriorityMin ) { + osiPriority = epicsThreadPriorityMin; + } + + if ( osiPriority > epicsThreadPriorityMax ) { + osiPriority = epicsThreadPriorityMax; + } + + magnitude = osiPriority * priorityStateCount; + magnitude /= ( epicsThreadPriorityMax - epicsThreadPriorityMin ) + 1; + + return magnitude; +} + +epicsShareFunc +void epicsThreadRealtimeLock(void) +{} + +/* + * epicsThreadGetOsdPriorityValue () + */ +static int epicsThreadGetOsdPriorityValue ( unsigned osiPriority ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + const int * pStateList; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + pStateList = osdRealtimePriorityList; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + pStateList = osdOrdinaryPriorityList; + } + + magnitude = osdPriorityMagFromPriorityOSI ( osiPriority, stateCount ); + return pStateList[magnitude]; +} + +/* + * osiPriorityMagFromMagnitueOSD () + */ +static unsigned osiPriorityMagFromMagnitueOSD ( unsigned magnitude, unsigned osdPriorityStateCount ) +{ + unsigned osiPriority; + + osiPriority = magnitude * ( epicsThreadPriorityMax - epicsThreadPriorityMin ); + osiPriority /= osdPriorityStateCount - 1u; + osiPriority += epicsThreadPriorityMin; + + return osiPriority; +} + + +/* + * epicsThreadGetOsiPriorityValue () + */ +static unsigned epicsThreadGetOsiPriorityValue ( int osdPriority ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + const int * pStateList; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + pStateList = osdRealtimePriorityList; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + pStateList = osdOrdinaryPriorityList; + } + + for ( magnitude = 0u; magnitude < stateCount; magnitude++ ) { + if ( osdPriority == pStateList[magnitude] ) { + break; + } + } + + if ( magnitude >= stateCount ) { + fprintf ( stderr, + "Unrecognized WIN32 thread priority level %d.\n", + osdPriority ); + fprintf ( stderr, + "Mapping to EPICS thread priority level epicsThreadPriorityMin.\n" ); + return epicsThreadPriorityMin; + } + + return osiPriorityMagFromMagnitueOSD ( magnitude, stateCount ); +} + +/* + * epicsThreadLowestPriorityLevelAbove () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove + ( unsigned int priority, unsigned * pPriorityJustAbove ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + epicsThreadBooleanStatus status; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + } + + magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); + + if ( magnitude < ( stateCount - 1 ) ) { + *pPriorityJustAbove = osiPriorityMagFromMagnitueOSD ( magnitude + 1u, stateCount ); + status = epicsThreadBooleanStatusSuccess; + } + else { + status = epicsThreadBooleanStatusFail; + } + return status; +} + +/* + * epicsThreadHighestPriorityLevelBelow () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow + ( unsigned int priority, unsigned * pPriorityJustBelow ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + epicsThreadBooleanStatus status; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + } + + magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); + + if ( magnitude > 0u ) { + *pPriorityJustBelow = osiPriorityMagFromMagnitueOSD ( magnitude - 1u, stateCount ); + status = epicsThreadBooleanStatusSuccess; + } + else { + status = epicsThreadBooleanStatusFail; + } + return status; +} + +/* + * epicsThreadGetStackSize () + */ +epicsShareFunc unsigned int epicsShareAPI + epicsThreadGetStackSize ( epicsThreadStackSizeClass stackSizeClass ) +{ + #define STACK_SIZE(f) (f * 0x10000 * sizeof(void *)) + static const unsigned stackSizeTable[epicsThreadStackBig+1] = { + STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4) + }; + + if (stackSizeClassepicsThreadStackBig) { + fprintf ( stderr, + "epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +} + +void epicsThreadCleanupWIN32 () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + fprintf ( stderr, "epicsThreadCleanupWIN32: unable to find ctx\n" ); + return; + } + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + epicsParmCleanupWIN32 ( pParm ); +} + +/* + * epicsWin32ThreadEntry() + */ +static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm = ( win32ThreadParam * ) lpParameter; + unsigned retStat = 0u; + BOOL success; + + if ( pGbl ) { + setThreadName ( pParm->id, pParm->pName ); + + success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); + if ( success ) { + osdThreadHooksRun ( ( epicsThreadId ) pParm ); + /* printf ( "starting thread %d\n", pParm->id ); */ + ( *pParm->funptr ) ( pParm->parm ); + /* printf ( "terminating thread %d\n", pParm->id ); */ + retStat = 1; + } + else { + fprintf ( stderr, "epicsWin32ThreadEntry: unable to set private\n" ); + } + } + else { + fprintf ( stderr, "epicsWin32ThreadEntry: unable to find ctx\n" ); + } + + epicsExitCallAtThreadExits (); + /* + * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! + */ + epicsParmCleanupWIN32 ( pParm ); + + return retStat; /* this indirectly closes the thread handle */ +} + +static win32ThreadParam * epicsThreadParmCreate ( const char *pName ) +{ + win32ThreadParam *pParmWIN32; + + pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 ); + if ( pParmWIN32 ) { + pParmWIN32->pName = (char *) ( pParmWIN32 + 1 ); + strcpy ( pParmWIN32->pName, pName ); + pParmWIN32->isSuspended = 0; + } + return pParmWIN32; +} + +static win32ThreadParam * epicsThreadImplicitCreate ( void ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + DWORD id = GetCurrentThreadId (); + win32ThreadParam * pParm; + char name[64]; + HANDLE handle; + BOOL success; + + if ( ! pGbl ) { + fprintf ( stderr, "epicsThreadImplicitCreate: unable to find ctx\n" ); + return 0; + } + + success = DuplicateHandle ( GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), & handle, 0, FALSE, DUPLICATE_SAME_ACCESS ); + if ( ! success ) { + return 0; + } + { + unsigned long idForFormat = id; + sprintf ( name, "win%lx", idForFormat ); + } + pParm = epicsThreadParmCreate ( name ); + if ( pParm ) { + int win32ThreadPriority; + + pParm->handle = handle; + pParm->id = id; + win32ThreadPriority = GetThreadPriority ( pParm->handle ); + assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); + pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); + success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); + if ( ! success ) { + epicsParmCleanupWIN32 ( pParm ); + pParm = 0; + } + else { + EnterCriticalSection ( & pGbl->mutex ); + ellAdd ( & pGbl->threadList, & pParm->node ); + LeaveCriticalSection ( & pGbl->mutex ); + } + } + return pParm; +} + +/* + * epicsThreadCreate () + */ +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName, + unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC pFunc,void *pParm) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParmWIN32; + int osdPriority; + DWORD wstat; + BOOL bstat; + + if ( ! pGbl ) { + return NULL; + } + + pParmWIN32 = epicsThreadParmCreate ( pName ); + if ( pParmWIN32 == 0 ) { + return ( epicsThreadId ) pParmWIN32; + } + pParmWIN32->funptr = pFunc; + pParmWIN32->parm = pParm; + pParmWIN32->epicsPriority = priority; + + { + unsigned threadId; + pParmWIN32->handle = (HANDLE) _beginthreadex ( + 0, stackSize, epicsWin32ThreadEntry, + pParmWIN32, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + & threadId ); + if ( pParmWIN32->handle == 0 ) { + free ( pParmWIN32 ); + return NULL; + } + /* weird win32 interface threadId parameter inconsistency */ + pParmWIN32->id = ( DWORD ) threadId ; + } + + osdPriority = epicsThreadGetOsdPriorityValue (priority); + bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority ); + if (!bstat) { + CloseHandle ( pParmWIN32->handle ); + free ( pParmWIN32 ); + return NULL; + } + + EnterCriticalSection ( & pGbl->mutex ); + ellAdd ( & pGbl->threadList, & pParmWIN32->node ); + LeaveCriticalSection ( & pGbl->mutex ); + + wstat = ResumeThread ( pParmWIN32->handle ); + if (wstat==0xFFFFFFFF) { + EnterCriticalSection ( & pGbl->mutex ); + ellDelete ( & pGbl->threadList, & pParmWIN32->node ); + LeaveCriticalSection ( & pGbl->mutex ); + CloseHandle ( pParmWIN32->handle ); + free ( pParmWIN32 ); + return NULL; + } + + return ( epicsThreadId ) pParmWIN32; +} + +/* + * epicsThreadSuspendSelf () + */ +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + DWORD stat; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + if ( pParm ) { + EnterCriticalSection ( & pGbl->mutex ); + pParm->isSuspended = 1; + LeaveCriticalSection ( & pGbl->mutex ); + } + stat = SuspendThread ( GetCurrentThread () ); + assert ( stat != 0xFFFFFFFF ); +} + +/* + * epicsThreadResume () + */ +epicsShareFunc void epicsShareAPI epicsThreadResume ( epicsThreadId id ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + DWORD stat; + + assert ( pGbl ); + + EnterCriticalSection ( & pGbl->mutex ); + + stat = ResumeThread ( pParm->handle ); + pParm->isSuspended = 0; + + LeaveCriticalSection ( & pGbl->mutex ); + + assert ( stat != 0xFFFFFFFF ); +} + +/* + * epicsThreadGetPriority () + */ +epicsShareFunc unsigned epicsShareAPI epicsThreadGetPriority (epicsThreadId id) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + return pParm->epicsPriority; +} + +/* + * epicsThreadGetPrioritySelf () + */ +epicsShareFunc unsigned epicsShareAPI epicsThreadGetPrioritySelf () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + if ( pParm ) { + return pParm->epicsPriority; + } + else { + int win32ThreadPriority = + GetThreadPriority ( GetCurrentThread () ); + assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); + return epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); + } +} + +/* + * epicsThreadSetPriority () + */ +epicsShareFunc void epicsShareAPI epicsThreadSetPriority ( epicsThreadId id, unsigned priority ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + BOOL stat = SetThreadPriority ( pParm->handle, epicsThreadGetOsdPriorityValue (priority) ); + assert (stat); +} + +/* + * epicsThreadIsEqual () + */ +epicsShareFunc int epicsShareAPI epicsThreadIsEqual ( epicsThreadId id1, epicsThreadId id2 ) +{ + win32ThreadParam * pParm1 = ( win32ThreadParam * ) id1; + win32ThreadParam * pParm2 = ( win32ThreadParam * ) id2; + return ( id1 == id2 && pParm1->id == pParm2->id ); +} + +/* + * epicsThreadIsSuspended () + */ +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended ( epicsThreadId id ) +{ + win32ThreadParam *pParm = ( win32ThreadParam * ) id; + DWORD exitCode; + BOOL stat; + + stat = GetExitCodeThread ( pParm->handle, & exitCode ); + if ( stat ) { + if ( exitCode != STILL_ACTIVE ) { + return 1; + } + else { + return pParm->isSuspended; + } + } + else { + return 1; + } +} + +/* + * epicsThreadSleep () + */ +epicsShareFunc void epicsShareAPI epicsThreadSleep ( double seconds ) +{ + static const unsigned mSecPerSec = 1000; + DWORD milliSecDelay; + + if ( seconds > 0.0 ) { + seconds *= mSecPerSec; + seconds += 0.99999999; /* 8 9s here is optimal */ + milliSecDelay = ( seconds >= INFINITE ) ? + INFINITE - 1 : ( DWORD ) seconds; + } + else { /* seconds <= 0 or NAN */ + milliSecDelay = 0u; + } + Sleep ( milliSecDelay ); +} + +/* + * epicsThreadSleepQuantum () + */ +double epicsShareAPI epicsThreadSleepQuantum () +{ + /* + * Its worth noting here that the sleep quantum on windows can + * mysteriously get better. I eventually tracked this down to + * codes that call timeBeginPeriod(1). Calling timeBeginPeriod() + * specifying a better timer resolution also increases the interrupt + * load. This appears to be related to java applet activity. + * The function timeGetDevCaps can tell us the range of periods + * that can be specified to timeBeginPeriod, but alas there + * appears to be no way to find out what the value of the global + * minimum of all timeBeginPeriod calls for all processes is. + */ + static const double secPerTick = 100e-9; + DWORD adjustment; + DWORD delay; + BOOL disabled; + BOOL success; + + success = GetSystemTimeAdjustment ( + & adjustment, & delay, & disabled ); + if ( success ) { + return delay * secPerTick; + } + else { + return 0.0; + } +} + +/* + * epicsThreadGetIdSelf () + */ +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf (void) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) TlsGetValue ( + pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + assert ( pParm ); /* very dangerous to allow non-unique thread id into use */ + } + return ( epicsThreadId ) pParm; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId ( const char * pName ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return 0; + } + + EnterCriticalSection ( & pGbl->mutex ); + + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { + if ( pParm->pName ) { + if ( strcmp ( pParm->pName, pName ) == 0 ) { + break; + } + } + } + + LeaveCriticalSection ( & pGbl->mutex ); + + /* !!!! warning - the thread parm could vanish at any time !!!! */ + + return ( epicsThreadId ) pParm; +} + + +/* + * epicsThreadGetNameSelf () + */ +epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf (void) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return "thread library not initialized"; + } + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + + if ( pParm ) { + if ( pParm->pName ) { + return pParm->pName; + } + } + return "anonymous"; +} + +/* + * epicsThreadGetName () + */ +epicsShareFunc void epicsShareAPI epicsThreadGetName ( + epicsThreadId id, char * pName, size_t size ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + + if ( size ) { + size_t sizeMinusOne = size-1; + strncpy ( pName, pParm->pName, sizeMinusOne ); + pName [sizeMinusOne] = '\0'; + } +} + +/* + * epics_GetThreadPriorityAsString () + */ +static const char * epics_GetThreadPriorityAsString ( HANDLE thr ) +{ + const char * pPriName = "?????"; + switch ( GetThreadPriority ( thr ) ) { + case THREAD_PRIORITY_TIME_CRITICAL: + pPriName = "tm-crit"; + break; + case THREAD_PRIORITY_HIGHEST: + pPriName = "high"; + break; + case THREAD_PRIORITY_ABOVE_NORMAL: + pPriName = "normal+"; + break; + case THREAD_PRIORITY_NORMAL: + pPriName = "normal"; + break; + case THREAD_PRIORITY_BELOW_NORMAL: + pPriName = "normal-"; + break; + case THREAD_PRIORITY_LOWEST: + pPriName = "low"; + break; + case THREAD_PRIORITY_IDLE: + pPriName = "idle"; + break; + } + return pPriName; +} + +/* + * epicsThreadShowInfo () + */ +static void epicsThreadShowInfo ( epicsThreadId id, unsigned level ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + + if ( pParm ) { + unsigned long idForFormat = pParm->id; + fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName, + (void *) pParm, idForFormat, pParm->epicsPriority, + epics_GetThreadPriorityAsString ( pParm->handle ), + epicsThreadIsSuspended ( id ) ? "suspend" : "ok" ); + if ( level ) { + fprintf (epicsGetStdout(), " %-8p %-8p ", + (void *) pParm->handle, (void *) pParm->parm ); + } + } + else { + fprintf (epicsGetStdout(), + "NAME EPICS-ID WIN32-ID EPICS-PRI WIN32-PRI STATE " ); + if ( level ) { + fprintf (epicsGetStdout(), " HANDLE FUNCTION PARAMETER" ); + } + } + fprintf (epicsGetStdout(),"\n" ); +} + +/* + * epicsThreadMap () + */ +epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return; + } + + EnterCriticalSection ( & pGbl->mutex ); + + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { + func ( ( epicsThreadId ) pParm ); + } + + LeaveCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadShowAll () + */ +epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return; + } + + EnterCriticalSection ( & pGbl->mutex ); + + epicsThreadShowInfo ( 0, level ); + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { + epicsThreadShowInfo ( ( epicsThreadId ) pParm, level ); + } + + LeaveCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadShow () + */ +epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned level ) +{ + epicsThreadShowInfo ( 0, level ); + epicsThreadShowInfo ( id, level ); +} + +/* + * epicsThreadOnce () + */ +epicsShareFunc void epicsShareAPI epicsThreadOnce ( + epicsThreadOnceId *id, void (*func)(void *), void *arg ) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE & threadOnceComplete + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + + assert ( pGbl ); + + EnterCriticalSection ( & pGbl->mutex ); + + if ( *id != EPICS_THREAD_ONCE_DONE ) { + if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + LeaveCriticalSection ( & pGbl->mutex ); + func ( arg ); + EnterCriticalSection ( & pGbl->mutex ); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if ( *id == epicsThreadGetIdSelf() ) { + LeaveCriticalSection ( & pGbl->mutex ); + cantProceed( "Recursive epicsThreadOnce() initialization\n" ); + } else + while ( *id != EPICS_THREAD_ONCE_DONE ) { + /* Another thread is in the above func(arg) call. */ + LeaveCriticalSection ( & pGbl->mutex ); + epicsThreadSleep ( epicsThreadSleepQuantum() ); + EnterCriticalSection ( & pGbl->mutex ); + } + } + LeaveCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadPrivateCreate () + */ +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate () +{ + epicsThreadPrivateOSD *p = ( epicsThreadPrivateOSD * ) malloc ( sizeof ( *p ) ); + if ( p ) { + p->key = TlsAlloc (); + if ( p->key == 0xFFFFFFFF ) { + free ( p ); + p = 0; + } + } + return p; +} + +/* + * epicsThreadPrivateDelete () + */ +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete ( epicsThreadPrivateId p ) +{ + BOOL stat = TlsFree ( p->key ); + assert ( stat ); + free ( p ); +} + +/* + * epicsThreadPrivateSet () + */ +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal ) +{ + BOOL stat = TlsSetValue ( pPvt->key, (void *) pVal ); + assert (stat); +} + +/* + * epicsThreadPrivateGet () + */ +epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet ( epicsThreadPrivateId pPvt ) +{ + return ( void * ) TlsGetValue ( pPvt->key ); +} + +/* + * epicsThreadGetCPUs () + */ +epicsShareFunc int epicsThreadGetCPUs ( void ) +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + if (sysinfo.dwNumberOfProcessors > 0) + return sysinfo.dwNumberOfProcessors; + return 1; +} + +#ifdef TEST_CODES +void testPriorityMapping () +{ + unsigned i; + + for (i=epicsThreadPriorityMin; i +#include +#include +#include + +// +// WIN32 +// +#define VC_EXTRALEAN +#define STRICT +#include +#include + +// +// EPICS +// +#define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "generalTimeSup.h" +#include "epicsTimer.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsThread.h" + +#if defined ( DEBUG ) +# define debugPrintf(argsInParen) ::printf argsInParen +#else +# define debugPrintf(argsInParen) +#endif + +extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); + +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ); + +// for mingw +#if !defined ( MAXLONGLONG ) +# define MAXLONGLONG 0x7fffffffffffffffLL +#endif +#if !defined ( MINLONGLONG ) +# define MINLONGLONG ~0x7fffffffffffffffLL +#endif +#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION +# define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 +#endif + +static const LONGLONG epicsEpochInFileTime = 0x01b41e2a18d64000LL; + +static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ); + +class currentTime { +public: + currentTime (); + ~currentTime (); + void getCurrentTime ( epicsTimeStamp & dest ); + void startPLL (); +private: + CRITICAL_SECTION mutex; + LONGLONG lastPerfCounter; + LONGLONG perfCounterFreq; + LONGLONG epicsTimeLast; // nano-sec since the EPICS epoch + LONGLONG perfCounterFreqPLL; + LONGLONG lastPerfCounterPLL; + LONGLONG lastFileTimePLL; + HANDLE threadHandle; + unsigned threadId; + bool perfCtrPresent; + bool threadShutdownCmd; + bool threadHasExited; + void updatePLL (); + static const int pllDelay; /* integer seconds */ + // cant be static because of diff btw __stdcall and __cdecl + friend unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ); +}; + +const int currentTime :: pllDelay = 5; + +static currentTime * pCurrentTime = 0; +static const LONGLONG FILE_TIME_TICKS_PER_SEC = 10000000; +static const LONGLONG EPICS_TIME_TICKS_PER_SEC = 1000000000; +static const LONGLONG ET_TICKS_PER_FT_TICK = + EPICS_TIME_TICKS_PER_SEC / FILE_TIME_TICKS_PER_SEC; + +// +// Start and register time provider +// +static int timeRegister(void) +{ + pCurrentTime = new currentTime (); + + generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent); + + pCurrentTime->startPLL (); + + osdMonotonicInit(); + return 1; +} +static int done = timeRegister(); + +// +// osdTimeGetCurrent () +// +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ) +{ + assert ( pCurrentTime ); + + pCurrentTime->getCurrentTime ( *pDest ); + return epicsTimeOK; +} + +// synthesize a reentrant gmtime on WIN32 +int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + struct tm * pRet = gmtime ( pAnsiTime ); + if ( pRet ) { + *pTM = *pRet; + return epicsTimeOK; + } + else { + return errno; + } +} + +// synthesize a reentrant localtime on WIN32 +int epicsShareAPI epicsTime_localtime ( + const time_t * pAnsiTime, struct tm * pTM ) +{ + struct tm * pRet = localtime ( pAnsiTime ); + if ( pRet ) { + *pTM = *pRet; + return epicsTimeOK; + } + else { + return errno; + } +} + +currentTime::currentTime () : + lastPerfCounter ( 0 ), + perfCounterFreq ( 0 ), + epicsTimeLast ( 0 ), + perfCounterFreqPLL ( 0 ), + lastPerfCounterPLL ( 0 ), + lastFileTimePLL ( 0 ), + threadHandle ( 0 ), + threadId ( 0 ), + perfCtrPresent ( false ), + threadShutdownCmd ( false ), + threadHasExited ( false ) +{ + InitializeCriticalSection ( & this->mutex ); + + // avoid interruptions by briefly becoming a time critical thread + int originalPriority = GetThreadPriority ( GetCurrentThread () ); + SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); + + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + LARGE_INTEGER tmp; + QueryPerformanceCounter ( & tmp ); + this->lastPerfCounter = tmp.QuadPart; + // if no high resolution counters then default to low res file time + if ( QueryPerformanceFrequency ( & tmp ) ) { + this->perfCounterFreq = tmp.QuadPart; + this->perfCtrPresent = true; + } + SetThreadPriority ( GetCurrentThread (), originalPriority ); + + LARGE_INTEGER liFileTime; + liFileTime.LowPart = ft.dwLowDateTime; + liFileTime.HighPart = ft.dwHighDateTime; + + if ( liFileTime.QuadPart >= epicsEpochInFileTime ) { + // the windows file time has a maximum resolution of 100 nS + // and a nominal resolution of 10 mS - 16 mS + this->epicsTimeLast = + ( liFileTime.QuadPart - epicsEpochInFileTime ) * + ET_TICKS_PER_FT_TICK; + } + else { + errlogPrintf ( + "win32 osdTime.cpp init detected questionable " + "system date prior to EPICS epoch, epics time will not advance\n" ); + this->epicsTimeLast = 0; + } + + this->perfCounterFreqPLL = this->perfCounterFreq; + this->lastPerfCounterPLL = this->lastPerfCounter; + this->lastFileTimePLL = liFileTime.QuadPart; +} + +void currentTime :: startPLL () +{ + // create frequency estimation thread when needed + if ( this->perfCtrPresent && ! this->threadHandle ) { + this->threadHandle = (HANDLE) + _beginthreadex ( 0, 4096, _pllThreadEntry, this, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + & this->threadId ); + assert ( this->threadHandle ); + BOOL bstat = SetThreadPriority ( + this->threadHandle, THREAD_PRIORITY_HIGHEST ); + assert ( bstat ); + DWORD wstat = ResumeThread ( this->threadHandle ); + assert ( wstat != 0xFFFFFFFF ); + } +} + +currentTime::~currentTime () +{ + EnterCriticalSection ( & this->mutex ); + this->threadShutdownCmd = true; + while ( ! this->threadHasExited ) { + LeaveCriticalSection ( & this->mutex ); + Sleep ( 250 /* mS */ ); + EnterCriticalSection ( & this->mutex ); + } + LeaveCriticalSection ( & this->mutex ); + DeleteCriticalSection ( & this->mutex ); +} + +void currentTime::getCurrentTime ( epicsTimeStamp & dest ) +{ + if ( this->perfCtrPresent ) { + EnterCriticalSection ( & this->mutex ); + + LARGE_INTEGER curPerfCounter; + QueryPerformanceCounter ( & curPerfCounter ); + + LONGLONG offset; + if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { + offset = curPerfCounter.QuadPart - this->lastPerfCounter; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + offset = ( MAXLONGLONG - this->lastPerfCounter ) + + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; + } + if ( offset < MAXLONGLONG / EPICS_TIME_TICKS_PER_SEC ) { + offset *= EPICS_TIME_TICKS_PER_SEC; + offset /= this->perfCounterFreq; + } + else { + double fpOffset = static_cast < double > ( offset ); + fpOffset *= EPICS_TIME_TICKS_PER_SEC; + fpOffset /= static_cast < double > ( this->perfCounterFreq ); + offset = static_cast < LONGLONG > ( fpOffset ); + } + LONGLONG epicsTimeCurrent = this->epicsTimeLast + offset; + if ( this->epicsTimeLast > epicsTimeCurrent ) { + double diff = static_cast < double > + ( this->epicsTimeLast - epicsTimeCurrent ) / EPICS_TIME_TICKS_PER_SEC; + errlogPrintf ( + "currentTime::getCurrentTime(): %f sec " + "time discontinuity detected\n", + diff ); + } + this->epicsTimeLast = epicsTimeCurrent; + this->lastPerfCounter = curPerfCounter.QuadPart; + + LeaveCriticalSection ( & this->mutex ); + + dest.secPastEpoch = static_cast < epicsUInt32 > + ( epicsTimeCurrent / EPICS_TIME_TICKS_PER_SEC ); + dest.nsec = static_cast < epicsUInt32 > + ( epicsTimeCurrent % EPICS_TIME_TICKS_PER_SEC ); + } + else { + // if high resolution performance counters are not supported then + // fall back to low res file time + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + dest = epicsTime ( ft ); + } +} + +// +// Maintain corrected version of the performance counter's frequency using +// a phase locked loop. This approach is similar to NTP's. +// +void currentTime :: updatePLL () +{ + EnterCriticalSection ( & this->mutex ); + + // avoid interruptions by briefly becoming a time critical thread + LARGE_INTEGER curFileTime; + LARGE_INTEGER curPerfCounter; + { + int originalPriority = GetThreadPriority ( GetCurrentThread () ); + SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + QueryPerformanceCounter ( & curPerfCounter ); + SetThreadPriority ( GetCurrentThread (), originalPriority ); + + curFileTime.LowPart = ft.dwLowDateTime; + curFileTime.HighPart = ft.dwHighDateTime; + } + + LONGLONG perfCounterDiff; + if ( curPerfCounter.QuadPart >= this->lastPerfCounterPLL ) { + perfCounterDiff = curPerfCounter.QuadPart - this->lastPerfCounterPLL; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + perfCounterDiff = ( MAXLONGLONG - this->lastPerfCounterPLL ) + + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; + } + this->lastPerfCounterPLL = curPerfCounter.QuadPart; + + LONGLONG fileTimeDiff = curFileTime.QuadPart - this->lastFileTimePLL; + this->lastFileTimePLL = curFileTime.QuadPart; + + // discard glitches + if ( fileTimeDiff <= 0 ) { + LeaveCriticalSection( & this->mutex ); + debugPrintf ( ( "currentTime: file time difference in PLL was less than zero\n" ) ); + return; + } + + LONGLONG freq = ( FILE_TIME_TICKS_PER_SEC * perfCounterDiff ) / fileTimeDiff; + LONGLONG delta = freq - this->perfCounterFreqPLL; + + // discard glitches + LONGLONG bound = this->perfCounterFreqPLL >> 10; + if ( delta < -bound || delta > bound ) { + LeaveCriticalSection( & this->mutex ); + debugPrintf ( ( "freq est out of bounds l=%d e=%d h=%d\n", + static_cast < int > ( -bound ), + static_cast < int > ( delta ), + static_cast < int > ( bound ) ) ); + return; + } + + // update feedback loop estimating the performance counter's frequency + LONGLONG feedback = delta >> 8; + this->perfCounterFreqPLL += feedback; + + LONGLONG perfCounterDiffSinceLastFetch; + if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { + perfCounterDiffSinceLastFetch = + curPerfCounter.QuadPart - this->lastPerfCounter; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + perfCounterDiffSinceLastFetch = + ( MAXLONGLONG - this->lastPerfCounter ) + + ( curPerfCounter.QuadPart - MINLONGLONG ) + 1; + } + + // discard performance counter delay measurement glitches + { + const LONGLONG expectedDly = this->perfCounterFreq * pllDelay; + const LONGLONG bnd = expectedDly / 4; + if ( perfCounterDiffSinceLastFetch <= 0 || + perfCounterDiffSinceLastFetch >= expectedDly + bnd ) { + LeaveCriticalSection( & this->mutex ); + debugPrintf ( ( "perf ctr measured delay out of bounds m=%d max=%d\n", + static_cast < int > ( perfCounterDiffSinceLastFetch ), + static_cast < int > ( expectedDly + bnd ) ) ); + return; + } + } + + // Update the current estimated time. + this->epicsTimeLast += + ( perfCounterDiffSinceLastFetch * EPICS_TIME_TICKS_PER_SEC ) + / this->perfCounterFreq; + this->lastPerfCounter = curPerfCounter.QuadPart; + + LONGLONG epicsTimeFromCurrentFileTime; + + { + static bool firstMessageWasSent = false; + if ( curFileTime.QuadPart >= epicsEpochInFileTime ) { + epicsTimeFromCurrentFileTime = + ( curFileTime.QuadPart - epicsEpochInFileTime ) * + ET_TICKS_PER_FT_TICK; + firstMessageWasSent = false; + } + else { + /* + * if the system time jumps to before the EPICS epoch + * then latch to the EPICS epoch printing only one + * warning message the first time that the issue is + * detected + */ + if ( ! firstMessageWasSent ) { + errlogPrintf ( + "win32 osdTime.cpp time PLL update detected questionable " + "system date prior to EPICS epoch, epics time will not advance\n" ); + firstMessageWasSent = true; + } + epicsTimeFromCurrentFileTime = 0; + } + } + + delta = epicsTimeFromCurrentFileTime - this->epicsTimeLast; + if ( delta > EPICS_TIME_TICKS_PER_SEC || delta < -EPICS_TIME_TICKS_PER_SEC ) { + // When there is an abrupt shift in the current computed time vs + // the time derived from the current file time then someone has + // probably adjusted the real time clock and the best reaction + // is to just assume the new time base + this->epicsTimeLast = epicsTimeFromCurrentFileTime; + this->perfCounterFreq = this->perfCounterFreqPLL; + debugPrintf ( ( "currentTime: did someone adjust the date?\n" ) ); + } + else { + // update the effective performance counter frequency that will bring + // our calculated time base in syncy with file time one second from now. + this->perfCounterFreq = + ( EPICS_TIME_TICKS_PER_SEC * this->perfCounterFreqPLL ) + / ( delta + EPICS_TIME_TICKS_PER_SEC ); + + // limit effective performance counter frequency rate of change + LONGLONG lowLimit = this->perfCounterFreqPLL - bound; + if ( this->perfCounterFreq < lowLimit ) { + debugPrintf ( ( "currentTime: out of bounds low freq excursion %d\n", + static_cast ( lowLimit - this->perfCounterFreq ) ) ); + this->perfCounterFreq = lowLimit; + } + else { + LONGLONG highLimit = this->perfCounterFreqPLL + bound; + if ( this->perfCounterFreq > highLimit ) { + debugPrintf ( ( "currentTime: out of bounds high freq excursion %d\n", + static_cast ( this->perfCounterFreq - highLimit ) ) ); + this->perfCounterFreq = highLimit; + } + } + +# if defined ( DEBUG ) + LARGE_INTEGER sysFreq; + QueryPerformanceFrequency ( & sysFreq ); + double freqDiff = static_cast + ( this->perfCounterFreq - sysFreq.QuadPart ); + freqDiff /= sysFreq.QuadPart; + freqDiff *= 100.0; + double freqEstDiff = static_cast + ( this->perfCounterFreqPLL - sysFreq.QuadPart ); + freqEstDiff /= sysFreq.QuadPart; + freqEstDiff *= 100.0; + debugPrintf ( ( "currentTime: freq delta %f %% freq est " + "delta %f %% time delta %f sec\n", + freqDiff, + freqEstDiff, + static_cast < double > ( delta ) / + EPICS_TIME_TICKS_PER_SEC ) ); +# endif + } + + LeaveCriticalSection ( & this->mutex ); +} + +static unsigned __stdcall _pllThreadEntry ( void * pCurrentTimeIn ) +{ + currentTime * pCT = + reinterpret_cast < currentTime * > ( pCurrentTimeIn ); + setThreadName ( pCT->threadId, "EPICS Time PLL" ); + while ( ! pCT->threadShutdownCmd ) { + Sleep ( currentTime :: pllDelay * 1000 /* mS */ ); + pCT->updatePLL (); + } + EnterCriticalSection ( & pCT->mutex ); + pCT->threadHasExited = true; + LeaveCriticalSection ( & pCT->mutex ); + return 1; +} + +epicsTime::operator FILETIME () const +{ + LARGE_INTEGER ftTicks; + ftTicks.QuadPart = ( this->secPastEpoch * FILE_TIME_TICKS_PER_SEC ) + + ( this->nSec / ET_TICKS_PER_FT_TICK ); + ftTicks.QuadPart += epicsEpochInFileTime; + FILETIME ts; + ts.dwLowDateTime = ftTicks.LowPart; + ts.dwHighDateTime = ftTicks.HighPart; + return ts; +} + +epicsTime::epicsTime ( const FILETIME & ts ) +{ + LARGE_INTEGER lift; + lift.LowPart = ts.dwLowDateTime; + lift.HighPart = ts.dwHighDateTime; + if ( lift.QuadPart > epicsEpochInFileTime ) { + LONGLONG fileTimeTicksSinceEpochEPICS = + lift.QuadPart - epicsEpochInFileTime; + this->secPastEpoch = static_cast < epicsUInt32 > + ( fileTimeTicksSinceEpochEPICS / FILE_TIME_TICKS_PER_SEC ); + this->nSec = static_cast < epicsUInt32 > + ( ( fileTimeTicksSinceEpochEPICS % FILE_TIME_TICKS_PER_SEC ) * + ET_TICKS_PER_FT_TICK ); + } + else { + this->secPastEpoch = 0; + this->nSec = 0; + } +} + +epicsTime & epicsTime::operator = ( const FILETIME & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} diff --git a/modules/libcom/src/osi/os/WIN32/osdTime.h b/modules/libcom/src/osi/os/WIN32/osdTime.h new file mode 100644 index 000000000..35848f6dc --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdTime.h @@ -0,0 +1,38 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#ifndef INC_osdTime_H +#define INC_osdTime_H + +/* MinGW only has a snippet time.h not protected against multiple inclusion */ +#if defined(__struct_timespec_defined) +#define _TIMESPEC_DEFINED 1 +#endif + +#if ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) +# if _MSC_VER >= 1900 +# include +# else + +#define __struct_timespec_defined 1 +#define _TIMESPEC_DEFINED 1 +struct timespec { + time_t tv_sec; /* seconds since some epoch */ + long tv_nsec; /* nanoseconds within the second */ +}; + +# endif /* _MSC_VER */ +#endif /* ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) */ + +#endif /* ifndef INC_osdTime_H */ + diff --git a/modules/libcom/src/osi/os/WIN32/osdWireConfig.h b/modules/libcom/src/osi/os/WIN32/osdWireConfig.h new file mode 100644 index 000000000..1715786e0 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdWireConfig.h @@ -0,0 +1,16 @@ +/* + * WIN32 version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +/* for now, assume that win32 runs only on generic little endian */ +#define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ diff --git a/modules/libcom/src/osi/os/WIN32/osiFileName.h b/modules/libcom/src/osi/os/WIN32/osiFileName.h new file mode 100644 index 000000000..6ff0308b2 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osiFileName.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#define OSI_PATH_LIST_SEPARATOR ";" +#define OSI_PATH_SEPARATOR "\\" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/WIN32/osiUnistd.h b/modules/libcom/src/osi/os/WIN32/osiUnistd.h new file mode 100644 index 000000000..3f34e74cc --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osiUnistd.h @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include diff --git a/modules/libcom/src/osi/os/WIN32/setThreadName.cpp b/modules/libcom/src/osi/os/WIN32/setThreadName.cpp new file mode 100644 index 000000000..49663c8af --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/setThreadName.cpp @@ -0,0 +1,51 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define VC_EXTRALEAN +#define STRICT +#if _WIN64 +# define _WIN32_WINNT 0x400 /* defining this drops support for W95 */ +#endif +#include + +/* + * this was copied directly from an example in visual c++ 7 documentation, + * It uses visual C++ specific keywords for exception handling, but is + * probably only useful when using the visual c++ or later debugger. + * + * Usage: setThreadName (-1, "MainThread"); + */ +extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ) +{ +#if _MSC_VER >= 1300 && defined ( _DEBUG ) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } THREADNAME_INFO; + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( 0x406D1388, 0, + sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +#endif +} diff --git a/modules/libcom/src/osi/os/WIN32/systemCallIntMech.cpp b/modules/libcom/src/osi/os/WIN32/systemCallIntMech.cpp new file mode 100644 index 000000000..5e13299c0 --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/systemCallIntMech.cpp @@ -0,0 +1,22 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ + return esscimqi_socketCloseRequired; +} diff --git a/modules/libcom/src/osi/os/cygwin32/devLibVMEOSD.c b/modules/libcom/src/osi/os/cygwin32/devLibVMEOSD.c new file mode 100644 index 000000000..53cbd58cd --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/devLibVMEOSD.c @@ -0,0 +1,13 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "devLibVME.h" + +epicsShareDef devLibVME *pdevLibVME = NULL; diff --git a/modules/libcom/src/osi/os/cygwin32/osdSock.h b/modules/libcom/src/osi/os/cygwin32/osdSock.h new file mode 100644 index 000000000..75288c865 --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/osdSock.h @@ -0,0 +1,71 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * cygwin32 specific include + */ + + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef int osiSocklen_t; +typedef int osiSockOptMcastLoop_t; +#define FD_IN_FDSET(FD) ((FD)=0) +#ifndef SHUT_RD +#define SHUT_RD 0 +#endif +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EACCES EACCES +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/cygwin32/osdSockAddrReuse.cpp b/modules/libcom/src/osi/os/cygwin32/osdSockAddrReuse.cpp new file mode 100644 index 000000000..d441d7511 --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/osdSockAddrReuse.cpp @@ -0,0 +1,44 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +/* + * Note: WINSOCK appears to assign a different functionality for + * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates + * that simultaneously servers can bind to the same TCP port on the same host! + * Also, servers are always enabled to reuse a port immediately after + * they exit ( even if SO_REUSEADDR isnt set ). + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ +} + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/modules/libcom/src/osi/os/cygwin32/osdStrtod.h b/modules/libcom/src/osi/os/cygwin32/osdStrtod.h new file mode 100644 index 000000000..b5fda31c3 --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/osdStrtod.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); + +#ifdef __cplusplus +} +#endif diff --git a/modules/libcom/src/osi/os/cygwin32/osiFileName.h b/modules/libcom/src/osi/os/cygwin32/osiFileName.h new file mode 100644 index 000000000..6d7fd6eb9 --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/osiFileName.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + */ +#ifndef osiFileNameH +#define osiFileNameH + +#define OSI_PATH_LIST_SEPARATOR ";" +#define OSI_PATH_SEPARATOR "\\" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/cygwin32/systemCallIntMech.cpp b/modules/libcom/src/osi/os/cygwin32/systemCallIntMech.cpp new file mode 100644 index 000000000..9c0df3293 --- /dev/null +++ b/modules/libcom/src/osi/os/cygwin32/systemCallIntMech.cpp @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeff Hill + */ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ +#if (CYGWIN_VERSION_DLL_MAJOR == 1007) && (CYGWIN_VERSION_DLL_MINOR < 15) + // Behaviour changed in early Cygwin 1.7 releases, reverted later. + return esscimqi_socketCloseRequired; +#else + return esscimqi_socketBothShutdownRequired; +#endif +} diff --git a/modules/libcom/src/osi/os/default/devLibVMEOSD.c b/modules/libcom/src/osi/os/default/devLibVMEOSD.c new file mode 100644 index 000000000..b8b1d5047 --- /dev/null +++ b/modules/libcom/src/osi/os/default/devLibVMEOSD.c @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "devLibVME.h" + +/* This file must contain no definitions other than the following: */ + +epicsShareDef devLibVME *pdevLibVME; diff --git a/modules/libcom/src/osi/os/default/epicsAtomicOSD.cpp b/modules/libcom/src/osi/os/default/epicsAtomicOSD.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/modules/libcom/src/osi/os/default/epicsGetopt.h b/modules/libcom/src/osi/os/default/epicsGetopt.h new file mode 100644 index 000000000..4bcc962cb --- /dev/null +++ b/modules/libcom/src/osi/os/default/epicsGetopt.h @@ -0,0 +1,17 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef _EPICS_GETOPT_H +#define _EPICS_GETOPT_H + +#include + +#endif /* _EPICS_GETOPT_H */ diff --git a/modules/libcom/src/osi/os/default/epicsMMIO.h b/modules/libcom/src/osi/os/default/epicsMMIO.h new file mode 100644 index 000000000..79040932a --- /dev/null +++ b/modules/libcom/src/osi/os/default/epicsMMIO.h @@ -0,0 +1,2 @@ + +#include "epicsMMIODef.h" diff --git a/modules/libcom/src/osi/os/default/epicsMMIODef.h b/modules/libcom/src/osi/os/default/epicsMMIODef.h new file mode 100644 index 000000000..dc4bb4cb7 --- /dev/null +++ b/modules/libcom/src/osi/os/default/epicsMMIODef.h @@ -0,0 +1,274 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Michael Davidsaver + */ + +#ifndef EPICSMMIODEF_H +#define EPICSMMIODEF_H + +#include +#include +#include +#include + +#ifdef __cplusplus +# ifndef INLINE +# define INLINE inline +# endif +#endif + +/** @ingroup mmio + *@{ + */ + +/** @brief Read a single byte. + */ +static EPICS_ALWAYS_INLINE +epicsUInt8 +ioread8(volatile void* addr) +{ + return *(volatile epicsUInt8*)(addr); +} + +/** @brief Write a single byte. + */ +static EPICS_ALWAYS_INLINE +void +iowrite8(volatile void* addr, epicsUInt8 val) +{ + *(volatile epicsUInt8*)(addr) = val; +} + +/** @brief Read two bytes in host order. + * Not byte swapping + */ +static EPICS_ALWAYS_INLINE +epicsUInt16 +nat_ioread16(volatile void* addr) +{ + return *(volatile epicsUInt16*)(addr); +} + +/** @brief Write two byte in host order. + * Not byte swapping + */ +static EPICS_ALWAYS_INLINE +void +nat_iowrite16(volatile void* addr, epicsUInt16 val) +{ + *(volatile epicsUInt16*)(addr) = val; +} + +/** @brief Read four bytes in host order. + * Not byte swapping + */ +static EPICS_ALWAYS_INLINE +epicsUInt32 +nat_ioread32(volatile void* addr) +{ + return *(volatile epicsUInt32*)(addr); +} + +/** @brief Write four byte in host order. + * Not byte swapping + */ +static EPICS_ALWAYS_INLINE +void +nat_iowrite32(volatile void* addr, epicsUInt32 val) +{ + *(volatile epicsUInt32*)(addr) = val; +} + +#if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + +/** @ingroup mmio + *@{ + */ + +static EPICS_ALWAYS_INLINE +epicsUInt16 +bswap16(epicsUInt16 value) +{ + return (((epicsUInt16)(value) & 0x00ff) << 8) | + (((epicsUInt16)(value) & 0xff00) >> 8); +} + +static EPICS_ALWAYS_INLINE +epicsUInt32 +bswap32(epicsUInt32 value) +{ + return (((epicsUInt32)(value) & 0x000000ff) << 24) | + (((epicsUInt32)(value) & 0x0000ff00) << 8) | + (((epicsUInt32)(value) & 0x00ff0000) >> 8) | + (((epicsUInt32)(value) & 0xff000000) >> 24); +} + +# define be_ioread16(A) nat_ioread16(A) +# define be_ioread32(A) nat_ioread32(A) +# define be_iowrite16(A,D) nat_iowrite16(A,D) +# define be_iowrite32(A,D) nat_iowrite32(A,D) + +# define le_ioread16(A) bswap16(nat_ioread16(A)) +# define le_ioread32(A) bswap32(nat_ioread32(A)) +# define le_iowrite16(A,D) nat_iowrite16(A,bswap16(D)) +# define le_iowrite32(A,D) nat_iowrite32(A,bswap32(D)) + +/** @} */ + +#elif EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + +/* Get hton[sl] declarations: */ +#include + +/** @ingroup mmio + *@{ + */ + +/* hton* is optimized or a builtin for most compilers + * so use it if possible + */ +#define bswap16(v) htons(v) +#define bswap32(v) htonl(v) + +# define be_ioread16(A) bswap16(nat_ioread16(A)) +# define be_ioread32(A) bswap32(nat_ioread32(A)) +# define be_iowrite16(A,D) nat_iowrite16(A,bswap16(D)) +# define be_iowrite32(A,D) nat_iowrite32(A,bswap32(D)) + +# define le_ioread16(A) nat_ioread16(A) +# define le_ioread32(A) nat_ioread32(A) +# define le_iowrite16(A,D) nat_iowrite16(A,D) +# define le_iowrite32(A,D) nat_iowrite32(A,D) + +/** @} */ + +#else +# error Unable to determine native byte order +#endif + +/** @def bswap16 + * @brief Unconditional two byte swap + */ +/** @def bswap32 + * @brief Unconditional four byte swap + */ +/** @def be_ioread16 + * @brief Read two byte in big endian order. + */ +/** @def be_iowrite16 + * @brief Write two byte in big endian order. + */ +/** @def be_ioread32 + * @brief Read four byte in big endian order. + */ +/** @def be_iowrite32 + * @brief Write four byte in big endian order. + */ +/** @def le_ioread16 + * @brief Read two byte in little endian order. + */ +/** @def le_iowrite16 + * @brief Write two byte in little endian order. + */ +/** @def le_ioread32 + * @brief Read four byte in little endian order. + */ +/** @def le_iowrite32 + * @brief Write four byte in little endian order. + */ + +/** @ingroup mmio + *@{ + */ + +/** @brief Explicit read memory barrier + * Prevents reordering of reads around it. + */ +#define rbarr() do{}while(0) +/** @brief Explicit write memory barrier + * Prevents reordering of writes around it. + */ +#define wbarr() do{}while(0) +/** @brief Explicit read/write memory barrier + * Prevents reordering of reads or writes around it. + */ +#define rwbarr() do{}while(0) + +/** @} */ + +/** @defgroup mmio Memory Mapped I/O + * + * Safe operations on I/O memory. + * + *This files defines a set of macros for access to Memory Mapped I/O + * + *They are named T_ioread# and T_iowrite# where # can be 8, 16, or 32. + *'T' can either be 'le', 'be', or 'nat' (except ioread8 and + *iowrite8). + * + *The macros defined use OS specific extensions (when available) + *to ensure the following. + * + *@li Width. A 16 bit operation will not be broken into two 8 bit operations, + * or one half of a 32 bit operation. + * + *@li Order. Writes to two different registers will not be reordered. + * This only applies to MMIO operations, not between MMIO and + * normal memory operations. + * + *PCI access should use either 'le_' or 'be_' as determined by the + *device byte order. + * + *VME access should always use 'nat_'. If the device byte order is + *little endian then an explicit swap is required. + * + *@section mmioex Examples: + * + *@subsection mmioexbe Big endian device: + * + *@b PCI + * + @code + be_iowrite16(base+off, 14); + var = be_ioread16(base+off); + @endcode + * + *@b VME + * + @code + nat_iowrite16(base+off, 14); + var = nat_ioread16(base+off); + @endcode + * + *@subsection mmioexle Little endian device + * + *@b PCI + @code + le_iowrite16(base+off, 14); + var = le_ioread16(base+off); + @endcode + *@b VME + @code + nat_iowrite16(base+off, bswap16(14)); + var = bswap16(nat_iowrite16(base+off)); + @endcode + *This difference arises because VME bridges implement hardware byte + *swapping on little endian systems, while PCI bridges do not. + *Software accessing PCI devices must know if byte swapping is required. + *This conditional swap is implemented by the 'be_' and 'le_' macros. + * + *This is a fundamental difference between PCI and VME. + * + *Software accessing PCI @b must do conditional swapping. + * + *Software accessing VME must @b not do conditional swapping. + * + *@note All read and write operations have an implicit read or write barrier. + */ + +#endif /* EPICSMMIODEF_H */ diff --git a/modules/libcom/src/osi/os/default/epicsSocketConvertErrnoToString.cpp b/modules/libcom/src/osi/os/default/epicsSocketConvertErrnoToString.cpp new file mode 100644 index 000000000..3cbfce60d --- /dev/null +++ b/modules/libcom/src/osi/os/default/epicsSocketConvertErrnoToString.cpp @@ -0,0 +1,40 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdSock.c */ +/* + * Author: Jeff Hill + * Date: 04-05-94 + * + */ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" + +/* + * epicsSocketConvertErrorToString() + */ +void epicsSocketConvertErrorToString ( + char * pBuf, unsigned bufSize, int theSockError ) +{ + if ( bufSize ) { + strncpy ( pBuf, strerror ( theSockError ), bufSize ); + pBuf[bufSize-1] = '\0'; + } +} + +/* + * epicsSocketConvertErrnoToString() + */ +void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ) +{ + epicsSocketConvertErrorToString ( pBuf, bufSize, SOCKERRNO ); +} diff --git a/modules/libcom/src/osi/os/default/gnuReadline.c b/modules/libcom/src/osi/os/default/gnuReadline.c new file mode 100644 index 000000000..ae646a53e --- /dev/null +++ b/modules/libcom/src/osi/os/default/gnuReadline.c @@ -0,0 +1,124 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +#include "epicsExit.h" + +static struct osdContext { + char dummy; /* Required for older compilers */ +} present; + +static enum {rlNone, rlIdle, rlBusy} rlState = rlNone; + +static void rlExit(void *dummy) { + if (rlState == rlBusy) + rl_cleanup_after_signal(); +} + + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + if (rlState == rlNone) { + epicsAtExit(rlExit, NULL); + rlState = rlIdle; + } + + context->osd = &present; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) + i = 0; + stifle_history(i); + rl_bind_key('\t', rl_insert); + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + char *line; + + free(context->line); + context->line = NULL; + if (context->in == NULL) { + rlState = rlBusy; + line = readline (prompt); + rlState = rlIdle; + } + else { + int c; /* char is unsigned on some archs; EOF is -ve */ + int linelen = 0; + int linesize = 50; + + line = malloc(linesize); + if (line == NULL) { + printf("Out of memory!\n"); + return NULL; + } + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + while ((c = getc(context->in)) != '\n') { + if (c == EOF) { + free(line); + line = NULL; + break; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc(line, linesize); + if (cp == NULL) { + printf ("Out of memory!\n"); + free(line); + line = NULL; + break; + } + line = cp; + } + line[linelen++] = c; + } + if (line) + line[linelen] = '\0'; + } + context->line = line; + if (line && *line) + add_history(line); + return line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + if (context->osd) { + free(context->line); + } +} + diff --git a/modules/libcom/src/osi/os/default/osdAssert.c b/modules/libcom/src/osi/os/default/osdAssert.c new file mode 100644 index 000000000..14d548c3d --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdAssert.c @@ -0,0 +1,55 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeffrey Hill + * Date: 02-27-95 + */ + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsVersion.h" +#include "epicsAssert.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "cantProceed.h" +#include "epicsStackTrace.h" + + +void epicsAssert (const char *pFile, const unsigned line, + const char *pExp, const char *pAuthorName) +{ + epicsTimeStamp current; + + errlogPrintf("\n\n\n" + "A call to 'assert(%s)'\n" + " by thread '%s' failed in %s line %u.\n", + pExp, epicsThreadGetNameSelf(), pFile, line); + + epicsStackTrace(); + + errlogPrintf("EPICS Release %s.\n", epicsReleaseVersion); + + if (epicsTimeGetCurrent(¤t) == 0) { + char date[64]; + + epicsTimeToStrftime(date, sizeof(date), + "%Y-%m-%d %H:%M:%S.%f %Z", ¤t); + errlogPrintf("Local time is %s\n", date); + } + + if (!pAuthorName) { + pAuthorName = "the author"; + } + errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n", + pAuthorName); + + errlogPrintf("Calling epicsThreadSuspendSelf()\n"); + epicsThreadSuspendSelf (); +} diff --git a/modules/libcom/src/osi/os/default/osdBackTrace.cpp b/modules/libcom/src/osi/os/default/osdBackTrace.cpp new file mode 100644 index 000000000..e1f96c033 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdBackTrace.cpp @@ -0,0 +1,16 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#define epicsExportSharedSymbols +#include "epicsStackTracePvt.h" + +int epicsBackTrace(void **buf, int buf_sz) +{ + return -1; +} diff --git a/modules/libcom/src/osi/os/default/osdEnv.c b/modules/libcom/src/osi/os/default/osdEnv.c new file mode 100644 index 000000000..682bcc934 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdEnv.c @@ -0,0 +1,77 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "errlog.h" +#include "cantProceed.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "epicsFindSymbol.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + iocshEnvClear(name); + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/default/osdFindAddr.c b/modules/libcom/src/osi/os/default/osdFindAddr.c new file mode 100644 index 000000000..582dd5442 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdFindAddr.c @@ -0,0 +1,25 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#define epicsExportSharedSymbols +#include "epicsStackTracePvt.h" +#include "epicsStackTrace.h" + +int epicsFindAddr(void *addr, epicsSymbol *sym_p) +{ + sym_p->f_nam = 0; + sym_p->s_nam = 0; + sym_p->s_val = 0; + return -1; +} + +int epicsFindAddrGetFeatures(void) +{ + return 0; +} diff --git a/modules/libcom/src/osi/os/default/osdFindSymbol.c b/modules/libcom/src/osi/os/default/osdFindSymbol.c new file mode 100644 index 000000000..9d9a8878c --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdFindSymbol.c @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/default/osdFindSymbol.c */ + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return 0; +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return "epicsLoadLibrary not implemented"; +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return 0; +} diff --git a/modules/libcom/src/osi/os/default/osdInterrupt.c b/modules/libcom/src/osi/os/default/osdInterrupt.c new file mode 100644 index 000000000..91c5c5681 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdInterrupt.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/default/osdInterrupt.c */ + +/* Author: Marty Kraimer Date: 15JUL99 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsInterrupt.h" + + +static epicsMutexId globalLock = NULL; +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + +static void initOnce(void *junk) +{ + globalLock = epicsMutexMustCreate(); +} + +epicsShareFunc int epicsInterruptLock() +{ + epicsThreadOnce(&onceId, initOnce, NULL); + epicsMutexMustLock(globalLock); + return 0; +} + +epicsShareFunc void epicsInterruptUnlock(int key) +{ + if (!globalLock) + cantProceed("epicsInterruptUnlock called before epicsInterruptLock\n"); + epicsMutexUnlock(globalLock); +} + +epicsShareFunc int epicsInterruptIsInterruptContext() +{ + return 0; +} + +epicsShareFunc void epicsInterruptContextMessage(const char *message) +{ + errlogPrintf("%s", message); +} + diff --git a/modules/libcom/src/osi/os/default/osdInterrupt.h b/modules/libcom/src/osi/os/default/osdInterrupt.h new file mode 100644 index 000000000..5559d6280 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdInterrupt.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdInterrupth +#define osdInterrupth + +#endif /* osdInterrupth */ + diff --git a/modules/libcom/src/osi/os/default/osdMessageQueue.cpp b/modules/libcom/src/osi/os/default/osdMessageQueue.cpp new file mode 100644 index 000000000..c86d8cc2b --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdMessageQueue.cpp @@ -0,0 +1,408 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#include +#include + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMessageQueue.h" +#include +#include +#include +#include + +/* + * Event cache + */ +struct eventNode { + ELLNODE link; + epicsEventId event; +}; + +/* + * List of threads waiting to send or receive a message + */ +struct threadNode { + ELLNODE link; + struct eventNode *evp; + void *buf; + unsigned int size; + volatile bool eventSent; +}; + +/* + * Message info + */ +struct epicsMessageQueueOSD { + ELLLIST sendQueue; + ELLLIST receiveQueue; + ELLLIST eventFreeList; + int numberOfSendersWaiting; + + epicsMutexId mutex; + unsigned long capacity; + unsigned long maxMessageSize; + + unsigned long *buf; + char *firstMessageSlot; + char *lastMessageSlot; + volatile char *inPtr; + volatile char *outPtr; + unsigned long slotSize; + + bool full; +}; + +epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( + unsigned int capacity, + unsigned int maxMessageSize) +{ + epicsMessageQueueId pmsg; + unsigned int slotBytes, slotLongs; + + if(capacity == 0) + return NULL; + + pmsg = (epicsMessageQueueId)calloc(1, sizeof(*pmsg)); + if(!pmsg) + return NULL; + + pmsg->capacity = capacity; + pmsg->maxMessageSize = maxMessageSize; + slotLongs = 1 + ((maxMessageSize + sizeof(unsigned long) - 1) / sizeof(unsigned long)); + slotBytes = slotLongs * sizeof(unsigned long); + + pmsg->mutex = epicsMutexCreate(); + pmsg->buf = (unsigned long*)calloc(pmsg->capacity, slotBytes); + if(!pmsg->buf || !pmsg->mutex) { + if(pmsg->mutex) + epicsMutexDestroy(pmsg->mutex); + free(pmsg->buf); + free(pmsg); + return NULL; + } + + pmsg->inPtr = pmsg->outPtr = pmsg->firstMessageSlot = (char *)&pmsg->buf[0]; + pmsg->lastMessageSlot = (char *)&pmsg->buf[(capacity - 1) * slotLongs]; + pmsg->full = false; + pmsg->slotSize = slotBytes; + + ellInit(&pmsg->sendQueue); + ellInit(&pmsg->receiveQueue); + ellInit(&pmsg->eventFreeList); + return pmsg; +} + +static void +freeEventNode(struct eventNode *enode) +{ + epicsEventDestroy(enode->event); + free(enode); +} + +epicsShareFunc void epicsShareAPI +epicsMessageQueueDestroy(epicsMessageQueueId pmsg) +{ + struct eventNode *evp; + + while ((evp = reinterpret_cast < struct eventNode * > + ( ellGet(&pmsg->eventFreeList) ) ) != NULL) { + freeEventNode(evp); + } + epicsMutexDestroy(pmsg->mutex); + free(pmsg->buf); + free(pmsg); +} + +static struct eventNode * +getEventNode(epicsMessageQueueId pmsg) +{ + struct eventNode *evp; + + evp = reinterpret_cast < struct eventNode * > ( ellGet(&pmsg->eventFreeList) ); + if (evp == NULL) { + evp = (struct eventNode *) calloc(1, sizeof(*evp)); + if (evp) { + evp->event = epicsEventCreate(epicsEventEmpty); + if (evp->event == NULL) { + free(evp); + return NULL; + } + } + } + return evp; +} + +static int +mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, + double timeout) +{ + char *myInPtr, *nextPtr; + struct threadNode *pthr; + + if(size > pmsg->maxMessageSize) + return -1; + + /* + * See if message can be sent + */ + epicsMutexMustLock(pmsg->mutex); + + if ((pmsg->numberOfSendersWaiting > 0) + || (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL))) { + /* + * Return if not allowed to wait + */ + if (timeout == 0) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + /* + * Wait + */ + struct threadNode threadNode; + threadNode.evp = getEventNode(pmsg); + threadNode.eventSent = false; + if (!threadNode.evp) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + ellAdd(&pmsg->sendQueue, &threadNode.link); + pmsg->numberOfSendersWaiting++; + + epicsMutexUnlock(pmsg->mutex); + + epicsEventStatus status; + if (timeout > 0) + status = epicsEventWaitWithTimeout(threadNode.evp->event, timeout); + else + status = epicsEventWait(threadNode.evp->event); + + epicsMutexMustLock(pmsg->mutex); + + if(!threadNode.eventSent) + ellDelete(&pmsg->sendQueue, &threadNode.link); + pmsg->numberOfSendersWaiting--; + + ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + + if ((pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL)) || + status != epicsEventOK) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + } + + /* + * Copy message to waiting receiver + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->receiveQueue) ) ) != NULL) { + if(size <= pthr->size) + memcpy(pthr->buf, message, size); + pthr->size = size; + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + epicsMutexUnlock(pmsg->mutex); + return 0; + } + + /* + * Copy to queue + */ + myInPtr = (char *)pmsg->inPtr; + if (myInPtr == pmsg->lastMessageSlot) + nextPtr = pmsg->firstMessageSlot; + else + nextPtr = myInPtr + pmsg->slotSize; + if (nextPtr == (char *)pmsg->outPtr) + pmsg->full = true; + *(volatile unsigned long *)myInPtr = size; + memcpy((unsigned long *)myInPtr + 1, message, size); + pmsg->inPtr = nextPtr; + epicsMutexUnlock(pmsg->mutex); + return 0; +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueTrySend(epicsMessageQueueId pmsg, void *message, + unsigned int size) +{ + return mySend(pmsg, message, size, 0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueSend(epicsMessageQueueId pmsg, void *message, + unsigned int size) +{ + return mySend(pmsg, message, size, -1); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueSendWithTimeout(epicsMessageQueueId pmsg, void *message, + unsigned int size, double timeout) +{ + return mySend(pmsg, message, size, timeout); +} + +static int +myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, + double timeout) +{ + char *myOutPtr; + unsigned long l; + struct threadNode *pthr; + + /* + * If there's a message on the queue, copy it + */ + epicsMutexMustLock(pmsg->mutex); + + myOutPtr = (char *)pmsg->outPtr; + if ((myOutPtr != pmsg->inPtr) || pmsg->full) { + int ret; + l = *(unsigned long *)myOutPtr; + if (l <= size) { + memcpy(message, (unsigned long *)myOutPtr + 1, l); + ret = l; + } + else { + ret = -1; + } + if (myOutPtr == pmsg->lastMessageSlot) + pmsg->outPtr = pmsg->firstMessageSlot; + else + pmsg->outPtr += pmsg->slotSize; + pmsg->full = false; + + /* + * Wake up the oldest task waiting to send + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + } + epicsMutexUnlock(pmsg->mutex); + return ret; + } + + /* + * Return if not allowed to wait + */ + if (timeout == 0) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + /* + * Wake up the oldest task waiting to send + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + } + + /* + * Wait for message to arrive + */ + struct threadNode threadNode; + threadNode.evp = getEventNode(pmsg); + threadNode.buf = message; + threadNode.size = size; + threadNode.eventSent = false; + + if (!threadNode.evp) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + ellAdd(&pmsg->receiveQueue, &threadNode.link); + epicsMutexUnlock(pmsg->mutex); + + epicsEventStatus status; + if (timeout > 0) + status = epicsEventWaitWithTimeout(threadNode.evp->event, timeout); + else + status = epicsEventWait(threadNode.evp->event); + + epicsMutexMustLock(pmsg->mutex); + + if (!threadNode.eventSent) + ellDelete(&pmsg->receiveQueue, &threadNode.link); + ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + + epicsMutexUnlock(pmsg->mutex); + + if (threadNode.eventSent && (threadNode.size <= size) && + status == epicsEventOK) + return threadNode.size; + return -1; +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueTryReceive(epicsMessageQueueId pmsg, void *message, + unsigned int size) +{ + return myReceive(pmsg, message, size, 0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueReceive(epicsMessageQueueId pmsg, void *message, + unsigned int size) +{ + return myReceive(pmsg, message, size, -1); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueReceiveWithTimeout(epicsMessageQueueId pmsg, void *message, + unsigned int size, double timeout) +{ + return myReceive(pmsg, message, size, timeout); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueuePending(epicsMessageQueueId pmsg) +{ + char *myInPtr, *myOutPtr; + int nmsg; + + epicsMutexMustLock(pmsg->mutex); + myInPtr = (char *)pmsg->inPtr; + myOutPtr = (char *)pmsg->outPtr; + if (pmsg->full) + nmsg = pmsg->capacity; + else if (myInPtr >= myOutPtr) + nmsg = (myInPtr - myOutPtr) / pmsg->slotSize; + else + nmsg = pmsg->capacity - (myOutPtr - myInPtr) / pmsg->slotSize; + epicsMutexUnlock(pmsg->mutex); + return nmsg; +} + +epicsShareFunc void epicsShareAPI +epicsMessageQueueShow(epicsMessageQueueId pmsg, int level) +{ + printf("Message Queue Used:%d Slots:%lu", epicsMessageQueuePending(pmsg), pmsg->capacity); + if (level >= 1) + printf(" Maximum size:%lu", pmsg->maxMessageSize); + printf("\n"); +} diff --git a/modules/libcom/src/osi/os/default/osdMessageQueue.h b/modules/libcom/src/osi/os/default/osdMessageQueue.h new file mode 100644 index 000000000..dabf6a71e --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdMessageQueue.h @@ -0,0 +1,3 @@ +/* + * Nothing needed for default implementation + */ diff --git a/modules/libcom/src/osi/os/default/osdNetIntf.c b/modules/libcom/src/osi/os/default/osdNetIntf.c new file mode 100644 index 000000000..62247bc5d --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdNetIntf.c @@ -0,0 +1,352 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + * Date: 04-05-94 + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "epicsAssert.h" +#include "errlog.h" +#include "epicsThread.h" + +#ifdef DEBUG +# define ifDepenDebugPrintf(argsInParen) printf argsInParen +#else +# define ifDepenDebugPrintf(argsInParen) +#endif + +static osiSockAddr osiLocalAddrResult; +static epicsThreadOnceId osiLocalAddrId = EPICS_THREAD_ONCE_INIT; + +/* + * Determine the size of an ifreq structure + * Made difficult by the fact that addresses larger than the structure + * size may be returned from the kernel. + */ +static size_t ifreqSize ( struct ifreq *pifreq ) +{ + size_t size; + + size = ifreq_size ( pifreq ); + if ( size < sizeof ( *pifreq ) ) { + size = sizeof ( *pifreq ); + } + return size; +} + +/* + * Move to the next ifreq structure + */ +static struct ifreq * ifreqNext ( struct ifreq *pifreq ) +{ + struct ifreq *ifr; + + ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq ); + ifDepenDebugPrintf( ("ifreqNext() pifreq %p, size 0x%x, ifr 0x%p\n", pifreq, (unsigned)ifreqSize (pifreq), ifr) ); + return ifr; +} + + +/* + * osiSockDiscoverBroadcastAddresses () + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) +{ + static const unsigned nelem = 100; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pIfreqListEnd; + struct ifreq *pifreq; + struct ifreq *pnextifreq; + osiSockAddrNode *pNewNode; + + if ( pMatchAddr->sa.sa_family == AF_INET ) { + if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); + return; + } + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_port = htons ( 0 ); + pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + ellAdd ( pList, &pNewNode->node ); + return; + } + } + + /* + * use pool so that we avoid using too much stack space + * + * nelem is set to the maximum interfaces + * on one machine here + */ + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); + if (!pIfreqList) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory to complete request\n"); + return; + } + + ifconf.ifc_len = nelem * sizeof(*pifreq); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); + if (status < 0 || ifconf.ifc_len == 0) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration (%d)\n", status); + free (pIfreqList); + return; + } + + pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList); + pIfreqListEnd--; + + for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + uint32_t current_ifreqsize; + + /* + * find the next ifreq + */ + pnextifreq = ifreqNext (pifreq); + + /* determine ifreq size */ + current_ifreqsize = ifreqSize ( pifreq ); + /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ + memmove(pIfreqList, pifreq, current_ifreqsize); + + ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n", + pIfreqList->ifr_name, + (unsigned)ifreq_size(pifreq), + (unsigned)current_ifreqsize)); + + /* + * If its not an internet interface then dont use it + */ + if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) ); + continue; + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + if ( pMatchAddr->sa.sa_family != AF_UNSPEC ) { + if ( pMatchAddr->sa.sa_family != AF_INET ) { + continue; + } + if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) { + struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; + if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) ); + continue; + } + } + } + + status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); + if ( status ) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" flags: %x\n", pIfreqList->ifr_name, pIfreqList->ifr_flags) ); + + /* + * dont bother with interfaces that have been disabled + */ + if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", pIfreqList->ifr_name) ); + continue; + } + + /* + * dont use the loop back interface + */ + if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) ); + continue; + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); + free ( pIfreqList ); + return; + } + + /* + * If this is an interface that supports + * broadcast fetch the broadcast address. + * + * Otherwise if this is a point to point + * interface then use the destination address. + * + * Otherwise CA will not query through the + * interface. + */ + if ( pIfreqList->ifr_flags & IFF_BROADCAST ) { + osiSockAddr baddr; + status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList); + if ( status ) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); + free ( pNewNode ); + continue; + } + baddr.sa = pIfreqList->ifr_broadaddr; + if (baddr.ia.sin_family==AF_INET && baddr.ia.sin_addr.s_addr != INADDR_ANY) { + pNewNode->addr.sa = pIfreqList->ifr_broadaddr; + ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); + } else { + ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); + free ( pNewNode ); + continue; + } + } +#if defined (IFF_POINTOPOINT) + else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) { + status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList); + if ( status ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) ); + free ( pNewNode ); + continue; + } + pNewNode->addr.sa = pIfreqList->ifr_dstaddr; + } +#endif + else { + ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) ); + free ( pNewNode ); + continue; + } + + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", pIfreqList->ifr_name) ); + + /* + * LOCK applied externally + */ + ellAdd ( pList, &pNewNode->node ); + } + + free ( pIfreqList ); +} + +/* + * osiLocalAddr () + */ +static void osiLocalAddrOnce (void *raw) +{ + SOCKET *psocket = raw; + const unsigned nelem = 100; + osiSockAddr addr; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pifreq; + struct ifreq *pIfreqListEnd; + struct ifreq *pnextifreq; + + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.sa.sa_family = AF_UNSPEC; + + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) ); + if ( ! pIfreqList ) { + errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" ); + goto fail; + } + + ifconf.ifc_len = nelem * sizeof ( *pIfreqList ); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl ( *psocket, SIOCGIFCONF, &ifconf ); + if ( status < 0 || ifconf.ifc_len == 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n", + sockErrBuf ); + goto fail; + } + + pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req ); + pIfreqListEnd--; + + for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + osiSockAddr addrCpy; + uint32_t current_ifreqsize; + + /* + * find the next if req + */ + pnextifreq = ifreqNext ( pifreq ); + + /* determine ifreq size */ + current_ifreqsize = ifreqSize ( pifreq ); + /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ + memmove(pIfreqList, pifreq, current_ifreqsize); + + if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): interface %s was not AF_INET\n", pIfreqList->ifr_name) ); + continue; + } + + addrCpy.sa = pIfreqList->ifr_addr; + + status = socket_ioctl ( *psocket, SIOCGIFFLAGS, pIfreqList ); + if ( status < 0 ) { + errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pIfreqList->ifr_name ); + continue; + } + + if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s was down\n", pIfreqList->ifr_name) ); + continue; + } + + /* + * dont use the loop back interface + */ + if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): ignoring loopback interface: %s\n", pIfreqList->ifr_name) ); + continue; + } + + ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pIfreqList->ifr_name) ); + + osiLocalAddrResult = addrCpy; + free ( pIfreqList ); + return; + } + + errlogPrintf ( + "osiLocalAddr(): only loopback found\n"); +fail: + /* fallback to loopback */ + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + osiLocalAddrResult = addr; + + free ( pIfreqList ); +} + + +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +{ + epicsThreadOnce(&osiLocalAddrId, osiLocalAddrOnce, &socket); + return osiLocalAddrResult; +} diff --git a/modules/libcom/src/osi/os/default/osdPoolStatus.c b/modules/libcom/src/osi/os/default/osdPoolStatus.c new file mode 100644 index 000000000..bb764b2ad --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdPoolStatus.c @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + * + * @@@@@ not implemented @@@@@ + * + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + return 1; +} diff --git a/modules/libcom/src/osi/os/default/osdPoolStatus.h b/modules/libcom/src/osi/os/default/osdPoolStatus.h new file mode 100644 index 000000000..efb2cfab8 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdPoolStatus.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdPoolStatush +#define osdPoolStatush + +#endif /* osdPoolStatush */ diff --git a/modules/libcom/src/osi/os/default/osdSignal.cpp b/modules/libcom/src/osi/os/default/osdSignal.cpp new file mode 100644 index 000000000..08dfa023c --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * Copyright (c) 2002 The University of Chicago, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * All NOOPs if the os isnt POSIX + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} + diff --git a/modules/libcom/src/osi/os/default/osdSpin.c b/modules/libcom/src/osi/os/default/osdSpin.c new file mode 100644 index 000000000..6e9acb158 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdSpin.c @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsSpin.h" + +/* + * Default: EPICS MUTEX IMPLEMENTATION + */ + +typedef struct epicsSpin { + epicsMutexId lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + spin->lock = epicsMutexCreate(); + if (!spin->lock) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if(!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + epicsMutexDestroy(spin->lock); + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + epicsMutexLockStatus status; + + status = epicsMutexLock(spin->lock); + if (status != epicsMutexLockOK) { + errlogPrintf("epicsSpinLock(%p): epicsMutexLock returned %s\n", spin, + status == epicsMutexLockTimeout ? + "epicsMutexLockTimeout" : "epicsMutexLockError"); + } +} + +int epicsSpinTryLock(epicsSpinId spin) { + epicsMutexLockStatus status; + + status = epicsMutexTryLock(spin->lock); + if (status == epicsMutexLockOK) return 0; + if (status == epicsMutexLockTimeout) return 1; + + errlogPrintf("epicsSpinTryLock(%p): epicsMutexTryLock returned epicsMutexLockError\n", spin); + return 2; +} + +void epicsSpinUnlock(epicsSpinId spin) { + epicsMutexUnlock(spin->lock); +} diff --git a/modules/libcom/src/osi/os/default/osdThreadExtra.c b/modules/libcom/src/osi/os/default/osdThreadExtra.c new file mode 100644 index 000000000..0a7c7ae95 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdThreadExtra.c @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Ralph Lange Date: 26 Jun 2012 */ + +/* Null default thread hooks for all platforms that do not do anything special */ + +#define epicsExportSharedSymbols +#include "epicsThread.h" + +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; diff --git a/modules/libcom/src/osi/os/default/osdThreadHooks.c b/modules/libcom/src/osi/os/default/osdThreadHooks.c new file mode 100644 index 000000000..cbe28a4ff --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdThreadHooks.c @@ -0,0 +1,140 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Authors: Ralph Lange & Andrew Johnson */ + +/* Secure hooks for epicsThread */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "ellLib.h" +#include "epicsMutex.h" +#include "epicsThread.h" + +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +typedef struct epicsThreadHook { + ELLNODE node; + EPICS_THREAD_HOOK_ROUTINE func; +} epicsThreadHook; + +static ELLLIST hookList = ELLLIST_INIT; +static epicsMutexId hookLock; + + +static void threadHookOnce(void *arg) +{ + hookLock = epicsMutexMustCreate(); + + if (epicsThreadHookDefault) { + static epicsThreadHook defHook = {ELLNODE_INIT, NULL}; + + defHook.func = epicsThreadHookDefault; + ellAdd(&hookList, &defHook.node); + } +} + +static void threadHookInit(void) +{ + static epicsThreadOnceId flag = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce(&flag, threadHookOnce, NULL); +} + +epicsShareFunc int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook) +{ + epicsThreadHook *pHook; + + if (!hook) return 0; + threadHookInit(); + + pHook = calloc(1, sizeof(epicsThreadHook)); + if (!pHook) { + fprintf(stderr, "epicsThreadHookAdd: calloc failed\n"); + return -1; + } + pHook->func = hook; + + if (epicsMutexLock(hookLock) == epicsMutexLockOK) { + ellAdd(&hookList, &pHook->node); + epicsMutexUnlock(hookLock); + return 0; + } + fprintf(stderr, "epicsThreadHookAdd: Locking problem\n"); + return -1; +} + +epicsShareFunc int epicsThreadHookDelete(EPICS_THREAD_HOOK_ROUTINE hook) +{ + if (!hook) return 0; + threadHookInit(); + + if (epicsMutexLock(hookLock) == epicsMutexLockOK) { + epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); + + while (pHook) { + if (hook == pHook->func) { + ellDelete(&hookList, &pHook->node); + break; + } + pHook = (epicsThreadHook *) ellNext(&pHook->node); + } + epicsMutexUnlock(hookLock); + return 0; + } + fprintf(stderr, "epicsThreadHookAdd: Locking problem\n"); + return -1; +} + +epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id) +{ + if (epicsThreadHookMain) + epicsThreadHookMain(id); +} + +epicsShareFunc void osdThreadHooksRun(epicsThreadId id) +{ + threadHookInit(); + + if (epicsMutexLock(hookLock) == epicsMutexLockOK) { + epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); + + while (pHook) { + pHook->func(id); + pHook = (epicsThreadHook *) ellNext(&pHook->node); + } + epicsMutexUnlock(hookLock); + } + else { + fprintf(stderr, "osdThreadHooksRun: Locking problem\n"); + } +} + +epicsShareFunc void epicsThreadHooksShow(void) +{ + threadHookInit(); + + if (epicsMutexLock(hookLock) == epicsMutexLockOK) { + epicsThreadHook *pHook = (epicsThreadHook *) ellFirst(&hookList); + + while (pHook) { + printf(" %p\n", pHook->func); + pHook = (epicsThreadHook *) ellNext(&pHook->node); + } + epicsMutexUnlock(hookLock); + } + else { + fprintf(stderr, "epicsThreadHooksShow: Locking problem\n"); + } +} diff --git a/modules/libcom/src/osi/os/default/osdVME.h b/modules/libcom/src/osi/os/default/osdVME.h new file mode 100644 index 000000000..1b65b3184 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdVME.h @@ -0,0 +1,13 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * OS-dependent VME support + */ diff --git a/modules/libcom/src/osi/os/default/osdWireConfig.h b/modules/libcom/src/osi/os/default/osdWireConfig.h new file mode 100644 index 000000000..e8def865e --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdWireConfig.h @@ -0,0 +1,64 @@ +/* + * Default version of osdWireConfig.h that might + * work on UNIX like systems that define + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +/* This file must be usable from both C and C++ */ + +/* if compilation fails because this wasnt found then you may need to define an OS + specific osdWireConfig.h */ +#include + +#ifdef __BYTE_ORDER +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +# elif __BYTE_ORDER == __BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to run on the specified __BYTE_ORDER +# endif +#else +# ifdef BYTE_ORDER +# if BYTE_ORDER == LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +# elif BYTE_ORDER == BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to run on the specified BYTE_ORDER +# endif +# else +# error doesnt specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed? +# endif +#endif + +#ifdef __FLOAT_WORD_ORDER +# if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE +# elif __FLOAT_WORD_ORDER == __BIG_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to specified __FLOAT_WORD_ORDER +# endif +#else +# ifdef FLOAT_WORD_ORDER +# if FLOAT_WORD_ORDER == LITTLE_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE +# elif FLOAT_WORD_ORDER == BIG_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to specified FLOAT_WORD_ORDER +# endif +# else + /* assume that if neither __FLOAT_WORD_ORDER nor FLOAT_WORD_ORDER are + defined then weird fp ordered archs like arm nwfp aren't supported */ +# define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER +# endif +#endif + +#endif /* ifdef osdWireConfig_h */ diff --git a/modules/libcom/src/osi/os/default/osdWireFormat.h b/modules/libcom/src/osi/os/default/osdWireFormat.h new file mode 100644 index 000000000..385c9bd69 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdWireFormat.h @@ -0,0 +1,240 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireFormat +#define osdWireFormat + +#ifdef __SUNPRO_CC +# include +#else +# include +#endif + +#include "epicsEndian.h" + +// +// The default assumption is that the local floating point format is +// IEEE and that these routines only need to perform byte swapping +// as a side effect of copying an aligned operand into an unaligned +// network byte stream. OS specific code can provide a alternative +// for this file if that assumption is wrong. +// + +// +// EPICS_CONVERSION_REQUIRED is set if either the byte order +// or the floating point word order are not exactly big endian. +// This can be set by hand above for a specific architecture +// should there be an architecture that is a weird middle endian +// ieee floating point format that is also big endian integer. +// +#if EPICS_BYTE_ORDER != EPICS_ENDIAN_BIG || EPICS_FLOAT_WORD_ORDER != EPICS_BYTE_ORDER +# if ! defined ( EPICS_CONVERSION_REQUIRED ) +# define EPICS_CONVERSION_REQUIRED +# endif +#endif + +// +// We still use a big endian wire format for CA consistent with the internet, +// but inconsistent with the vast majority of CPUs +// + +template <> +inline void WireGet < epicsFloat64 > ( + const epicsUInt8 * pWireSrc, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union { + epicsFloat64 _f; + epicsUInt32 _u[2]; + } tmp; +# if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + WireGet ( pWireSrc, tmp._u[1] ); + WireGet ( pWireSrc + 4, tmp._u[0] ); +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + WireGet ( pWireSrc, tmp._u[0] ); + WireGet ( pWireSrc + 4, tmp._u[1] ); +# else +# error unsupported floating point word order +# endif + dst = tmp._f; +} + +#if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) +template <> +inline void WireGet < epicsOldString > ( + const epicsUInt8 * pWireSrc, epicsOldString & dst ) +{ + memcpy ( dst, pWireSrc, sizeof ( dst ) ); +} +#else +inline void WireGet ( + const epicsUInt8 * pWireSrc, epicsOldString & dst ) +{ + memcpy ( dst, pWireSrc, sizeof ( dst ) ); +} +#endif + +template <> +inline void WireSet < epicsFloat64 > ( + const epicsFloat64 & src, epicsUInt8 * pWireDst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union { + epicsFloat64 _f; + epicsUInt32 _u[2]; + } tmp; + tmp._f = src; +# if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + WireSet ( tmp._u[1], pWireDst ); + WireSet ( tmp._u[0], pWireDst + 4 ); +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + WireSet ( tmp._u[0], pWireDst ); + WireSet ( tmp._u[1], pWireDst + 4 ); +# else +# error unsupported floating point word order +# endif +} + +#if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) +template <> +inline void WireSet < epicsOldString > ( + const epicsOldString & src, epicsUInt8 * pWireDst ) +{ + memcpy ( pWireDst, src, sizeof ( src ) ); +} +#else +inline void WireSet ( + const epicsOldString & src, epicsUInt8 * pWireDst ) +{ + memcpy ( pWireDst, src, sizeof ( src ) ); +} +#endif + +template <> +inline void AlignedWireGet < epicsUInt16 > ( + const epicsUInt16 & src, epicsUInt16 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error unsupported endian type +# endif +} + +template <> +inline void AlignedWireGet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt32 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error unsupported endian type +# endif +} + +template <> +inline void AlignedWireGet < epicsFloat64 > ( + const epicsFloat64 & src, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union Swapper { + epicsUInt32 _u[2]; + epicsFloat64 _f; + }; +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER + dst = src; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + Swapper tmp; + tmp._f = src; + AlignedWireGet ( tmp._u[0], tmp._u[0] ); + AlignedWireGet ( tmp._u[1], tmp._u[1] ); + dst = tmp._f; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + Swapper srcu, dstu; + srcu._f = src; + AlignedWireGet ( srcu._u[1], dstu._u[0] ); + AlignedWireGet ( srcu._u[0], dstu._u[1] ); + dst = dstu._f; +# else +# error unsupported floating point word order +# endif +} + +template <> +inline void AlignedWireSet < epicsUInt16 > + ( const epicsUInt16 & src, epicsUInt16 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error undefined endian type +# endif +} + +template <> +inline void AlignedWireSet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt32 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error undefined endian type +# endif +} + +template <> +inline void AlignedWireSet < epicsFloat64 > ( + const epicsFloat64 & src, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union Swapper { + epicsUInt32 _u[2]; + epicsFloat64 _f; + }; +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER + dst = src; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + Swapper tmp; + tmp._f = src; + AlignedWireSet ( tmp._u[0], tmp._u[0] ); + AlignedWireSet ( tmp._u[1], tmp._u[1] ); + dst = tmp._f; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + Swapper srcu, dstu; + srcu._f = src; + AlignedWireSet ( srcu._u[1], dstu._u[0] ); + AlignedWireSet ( srcu._u[0], dstu._u[1] ); + dst = dstu._f; +# else +# error unsupported floating point word order +# endif +} + +#endif // osdWireFormat diff --git a/modules/libcom/src/osi/os/freebsd/osdSock.h b/modules/libcom/src/osi/os/freebsd/osdSock.h new file mode 100644 index 000000000..b402ec120 --- /dev/null +++ b/modules/libcom/src/osi/os/freebsd/osdSock.h @@ -0,0 +1,81 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +#ifndef IPPORT_USERRESERVED +#define IPPORT_USERRESERVED 5000 +#endif + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; +typedef int osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof(pifreq->ifr_name)) +#else +# define ifreq_size(pifreq) sizeof(*pifreq) +#endif + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/freebsd/osdTime.h b/modules/libcom/src/osi/os/freebsd/osdTime.h new file mode 100644 index 000000000..941426c72 --- /dev/null +++ b/modules/libcom/src/osi/os/freebsd/osdTime.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +/* + * We need this include file since the POSIX version + * causes `struct timespec' to be defined in more than one place. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/modules/libcom/src/osi/os/freebsd/osiFileName.h b/modules/libcom/src/osi/os/freebsd/osiFileName.h new file mode 100644 index 000000000..83204c70f --- /dev/null +++ b/modules/libcom/src/osi/os/freebsd/osiFileName.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/freebsd/osiUnistd.h b/modules/libcom/src/osi/os/freebsd/osiUnistd.h new file mode 100644 index 000000000..c64e57a6c --- /dev/null +++ b/modules/libcom/src/osi/os/freebsd/osiUnistd.h @@ -0,0 +1,22 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ diff --git a/modules/libcom/src/osi/os/iOS/epicsMath.h b/modules/libcom/src/osi/os/iOS/epicsMath.h new file mode 100644 index 000000000..493583f70 --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/epicsMath.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#define finite(x) isfinite(x) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/iOS/osdEnv.c b/modules/libcom/src/osi/os/iOS/osdEnv.c new file mode 100644 index 000000000..a32cce5ef --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osdEnv.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include +#include +#include +#include +#include "epicsFindSymbol.h" +#include + +/* + * Set the value of an environment variable + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + iocshEnvClear(name); + setenv(name, value, 1); +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/iOS/osdMonotonic.c b/modules/libcom/src/osi/os/iOS/osdMonotonic.c new file mode 100644 index 000000000..dfce9dc85 --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osdMonotonic.c @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "dbDefs.h" +#include "errlog.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +/* see https://developer.apple.com/library/content/qa/qa1398/_index.html */ +static mach_timebase_info_data_t tbinfo; + +void osdMonotonicInit(void) +{ + (void)mach_timebase_info(&tbinfo); +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return 1e-9 * tbinfo.numer / tbinfo.denom; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + uint64_t val = mach_absolute_time(); + return val * tbinfo.numer / tbinfo.denom; +} diff --git a/modules/libcom/src/osi/os/iOS/osdSock.h b/modules/libcom/src/osi/os/iOS/osdSock.h new file mode 100644 index 000000000..08849a8d8 --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osdSock.h @@ -0,0 +1,73 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Eric Norum + */ + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; +typedef int osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/iOS/osdSockAddrReuse.cpp b/modules/libcom/src/osi/os/iOS/osdSockAddrReuse.cpp new file mode 100644 index 000000000..9f64e331a --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osdSockAddrReuse.cpp @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEPORT?\n"); + } +} diff --git a/modules/libcom/src/osi/os/iOS/osdTime.h b/modules/libcom/src/osi/os/iOS/osdTime.h new file mode 100644 index 000000000..953ec31b8 --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osdTime.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void convertDoubleToWakeTime(double timeout, + struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/modules/libcom/src/osi/os/iOS/osiFileName.h b/modules/libcom/src/osi/os/iOS/osiFileName.h new file mode 100644 index 000000000..f7dd62878 --- /dev/null +++ b/modules/libcom/src/osi/os/iOS/osiFileName.h @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Eric Norum + */ + +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/posix/README b/modules/libcom/src/osi/os/posix/README new file mode 100644 index 000000000..59f3d9524 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/README @@ -0,0 +1,13 @@ +A knowledge of pthreads is necessary to understand osdThread.c and osdSem.c. + +The following are good references + +Programming with POSIX Threads, David R. Butenhof, Addison-weslet, 1997 + +Multithreaded Programming with Pthreads, Bil Lewis & Daniel J. Berg, +Sun Microsystems, 1998 + +Pthreads Programming, Bradford Nichols etc, O'Reilly & Associates, 1996 + +The implementation of semMutex is based on the example code by Bil Leeis +that os available via the WWW. diff --git a/modules/libcom/src/osi/os/posix/epicsAtomicOSD.cpp b/modules/libcom/src/osi/os/posix/epicsAtomicOSD.cpp new file mode 100644 index 000000000..1cc227fcd --- /dev/null +++ b/modules/libcom/src/osi/os/posix/epicsAtomicOSD.cpp @@ -0,0 +1,74 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * johill@lanl.gov + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsAtomic.h" + +/* + * Slow, but probably correct on all systems. + * Useful only if something more efficient isn`t + * provided based on knowledge of the compiler + * or OS + * + * A statically initialized pthread mutex doesn`t + * need to be destroyed + * + * !!!!! + * !!!!! WARNING + * !!!!! + * !!!!! Do not use this implementation on systems where + * !!!!! code runs at interrupt context. If so, then + * !!!!! an implementation must be provided that is based + * !!!!! on a compiler intrinsic or an interrupt lock and or + * !!!!! a spin lock primitive + * !!!!! + */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +void epicsAtomicLock ( EpicsAtomicLockKey * ) +{ + unsigned countDown = 1000u; + int status; + while ( true ) { + status = pthread_mutex_lock ( & mutex ); + if ( status == 0 ) return; + assert ( status == EINTR ); + static const useconds_t retryDelayUSec = 100000; + usleep ( retryDelayUSec ); + countDown--; + assert ( countDown ); + } +} + +void epicsAtomicUnlock ( EpicsAtomicLockKey * ) +{ + const int status = pthread_mutex_unlock ( & mutex ); + assert ( status == 0 ); +} + + +// Slow, but probably correct on all systems. +// Useful only if something more efficient isn`t +// provided based on knowledge of the compiler +// or OS +void epicsAtomicMemoryBarrierFallback (void) +{ + EpicsAtomicLockKey key; + epicsAtomicLock ( & key ); + epicsAtomicUnlock ( & key ); +} diff --git a/modules/libcom/src/osi/os/posix/epicsAtomicOSD.h b/modules/libcom/src/osi/os/posix/epicsAtomicOSD.h new file mode 100644 index 000000000..892d2dcb5 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/epicsAtomicOSD.h @@ -0,0 +1,54 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef epicsAtomicOSD_h +#define epicsAtomicOSD_h + +#include + +#define EPICS_ATOMIC_OS_NAME "POSIX" + +typedef struct EpicsAtomicLockKey {} EpicsAtomicLockKey; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); +epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); +epicsShareFunc void epicsAtomicMemoryBarrierFallback ( void ); + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + epicsAtomicMemoryBarrierFallback(); +} +#endif + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + epicsAtomicMemoryBarrierFallback(); +} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/modules/libcom/src/osi/os/posix/epicsMath.h b/modules/libcom/src/osi/os/posix/epicsMath.h new file mode 100644 index 000000000..4e558676c --- /dev/null +++ b/modules/libcom/src/osi/os/posix/epicsMath.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef isfinite +# undef finite +# define finite(x) isfinite((double)(x)) +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/posix/epicsTempFile.cpp b/modules/libcom/src/osi/os/posix/epicsTempFile.cpp new file mode 100644 index 000000000..19b5fb4bc --- /dev/null +++ b/modules/libcom/src/osi/os/posix/epicsTempFile.cpp @@ -0,0 +1,37 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "epicsTempFile.h" + +extern "C" +epicsShareFunc void epicsShareAPI epicsTempName ( + char * pNameBuf, size_t nameBufLength ) +{ + if ( nameBufLength ) { + pNameBuf[0] = '\0'; + char nameBuf[L_tmpnam]; + if ( tmpnam ( nameBuf ) ) { + if ( nameBufLength > strlen ( nameBuf ) ) { + strncpy ( pNameBuf, nameBuf, nameBufLength ); + } + } + } +} + + +extern "C" +epicsShareFunc FILE * epicsShareAPI epicsTempFile ( void ) +{ + return tmpfile (); +} + diff --git a/modules/libcom/src/osi/os/posix/osdElfFindAddr.c b/modules/libcom/src/osi/os/posix/osdElfFindAddr.c new file mode 100644 index 000000000..bc67ae9ea --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdElfFindAddr.c @@ -0,0 +1,659 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _POSIX_MAPPED_FILES +#include +#endif + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsStackTrace.h" +#include "epicsStackTracePvt.h" + +/* This routine is provided by osiClockTime.c */ +epicsShareExtern void ClockTime_GetProgramStart(epicsTimeStamp *pDest); + +#define FIND_ADDR_DEBUG 0 + +/* + * On some systems (linux, solaris) dladdr doesn't find local symbols + * or symbols in the main executable. + * Hence, we want to use dladdr() to find the file name + * where a symbol is defined and if not more information is available + * then proceed to lookup symbols in the ELF symbol tables. + */ + +/* Macros to handle elf32 vs. elf64 access to unions etc. */ + +#define FLD(c,s,f) (ELFCLASS32==c ? s.e32.f : s.e64.f ) +#define ARR(c,s,i,f) (ELFCLASS32==c ? s.e32[i].f : s.e64[i].f) + +/* Elf header */ +typedef union Ehdr_ { + Elf32_Ehdr e32; + Elf64_Ehdr e64; +} Ehdr; + +/* Section header */ +typedef union Shdr_ { + Elf32_Shdr e32; + Elf64_Shdr e64; +} Shdr; + +/* Elf symbol */ +typedef union Sym_ { + void *raw; + Elf32_Sym *e32; + Elf64_Sym *e64; +} Sym; + +/* Memory mapped portion of a file; we must + * keep additional information because the + * map's starting address + length must be + * page-aligned (man mmap). + */ +typedef struct MMap_ { + void *addr; + off_t off; /* offset into the map where 'real' data start */ + size_t len; + size_t max; /* max offset: legal data from addr+off .. addr+off+max-1 */ + void (*freeMap)(struct MMap_*); /* 'method' to destroy the mapping */ +} *MMap; + +/* Structure describing symbol information + * contained in a file. + * We keep these around (so that the file + * doesn't have to be opened + parsed every + * time we do a lookup). + */ +typedef struct ESyms_ { + struct ESyms_ *next; /* linked list; one struct per executable */ + const char *fname; /* file name */ + int fd; /* file descriptor */ + uintptr_t addr; /* address where file is loaded */ + MMap symMap; + MMap strMap; + size_t nsyms; + uint8_t eclss; +} *ESyms; + +/* LOCKING NOTE: if the ELF symbol facility is ever expanded to be truly used + * in a multithreaded way then proper multiple-readers, single-writer locking + * should be implemented. + */ + +/* Linked list where we keep all our ESyms */ +static ESyms elfs = 0; + +static epicsMutexId listMtx; +static epicsThreadOnceId listMtxInitId = EPICS_THREAD_ONCE_INIT; + +static void listMtxInit(void *unused) +{ + listMtx = epicsMutexMustCreate(); +} + +static void +elfsLockWrite() +{ + epicsThreadOnce(&listMtxInitId, listMtxInit, 0); + epicsMutexMustLock(listMtx); +} + +static void +elfsUnlockWrite() +{ + epicsMutexUnlock(listMtx); +} + +static void +freeMap(MMap m) +{ + if ( m ) { + m->freeMap(m); + free(m); + } +} + +/* Helper to read exactly 'sz' bytes into 'buf' + * RETURNS: # chars read or negative value on error. + */ +static ssize_t +do_read(int fd, void *buf, ssize_t sz) +{ + ssize_t got; + char *ptr=(char*)buf; + + while ( sz > 0 ) { + if ( (got=read(fd,ptr,sz)) <= 0 ) { + return got; + } + ptr+=got; + sz -=got; + } + return ptr-(char*)buf; +} + +/* Elf file access -- can either be with mmap or by sequential read */ + +#ifdef _POSIX_MAPPED_FILES +/* Destructor for data that is mmap()ed */ +static void +freeMapMmap(MMap m) +{ + if ( MAP_FAILED != m->addr ) + munmap( m->addr, m->len ); +} + +/* Obtain section data with mmap() */ +static MMap +getscn_mmap(int fd, uint8_t c, Shdr *shdr_p) +{ + off_t n; + MMap rval = 0; + size_t pgsz = sysconf(_SC_PAGESIZE); + + if ( 0 == (n = (off_t)FLD(c,(*shdr_p),sh_size)) ) { + errlogPrintf("elfRead - getscn() -- no section data\n"); + goto bail; + } + + if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { + errlogPrintf("elfRead - getscn() -- no memory for section map\n"); + goto bail; + } + + rval->freeMap = freeMapMmap; + + rval->off = (off_t) (FLD(c,(*shdr_p),sh_offset) & (pgsz-1)); + rval->len = (n + rval->off + (pgsz - 1)) & ~(pgsz - 1); + rval->max = rval->len - rval->off; + + if ( MAP_FAILED == (rval->addr = mmap(0, rval->len, PROT_READ, MAP_SHARED, fd, (off_t) (FLD(c,(*shdr_p),sh_offset) & ~(pgsz-1)))) ) { + errlogPrintf("elfRead - getscn() -- mapping section contents: %s\n", strerror(errno)); + goto bail; + } + + return rval; + +bail: + freeMap(rval); + return 0; +} +#else +static MMap getscn_mmap(int fd, uint8_t c, Shrd *shdr_p) +{ + return 0; +} +#endif + +/* Destructor for data that is read into a malloc()ed buffer */ +static void +freeMapMalloc(MMap m) +{ + free(m->addr); +} + +/* Read section data into a malloc()ed buffer */ +static MMap +getscn_read(int fd, uint8_t c, Shdr *shdr_p) +{ + ssize_t n; + MMap rval = 0; + + if ( 0 == (n = (ssize_t) FLD(c,(*shdr_p),sh_size)) ) { + errlogPrintf("elfRead - getscn() -- no section data\n"); + goto bail; + } + + if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { + errlogPrintf("elfRead - getscn() -- no memory for section map\n"); + goto bail; + } + + rval->freeMap = freeMapMalloc; + + if ( ! (rval->addr = malloc(n)) ) { + errlogPrintf("elfRead - getscn() -- no memory for section data\n"); + goto bail; + } + + rval->off = 0; + rval->len = n; + rval->max = rval->len - rval->off; + + /* seek to symbol table contents */ + if ( (off_t)-1 == lseek(fd, (off_t) FLD(c,(*shdr_p),sh_offset), SEEK_SET) ) { + errlogPrintf("elfRead - getscn() -- seeking to sh_offset: %s\n", strerror(errno)); + goto bail; + } + + if ( n != do_read(fd, rval->addr, n) ) { + errlogPrintf("elfRead - getscn() -- reading section contents: %s\n", strerror(errno)); + goto bail; + } + + return rval; + +bail: + freeMap(rval); + return 0; +} + +static MMap +getscn(int fd, uint8_t c, Shdr *shdr_p) +{ + MMap rval = getscn_mmap(fd, c, shdr_p); + + if ( ! rval ) + rval = getscn_read(fd, c, shdr_p); + + return rval; +} + +/* Release resources but keep filename so that + * a file w/o symbol table is not read over and over again. + */ +static void +elfSymsRelease(ESyms es) +{ + if ( es ) { + freeMap(es->symMap); + es->symMap = 0; + freeMap(es->strMap); + es->strMap = 0; + if ( es->fd >= 0 ) + close(es->fd); + es->fd = -1; + es->nsyms = 0; + } +} + +static ESyms +elfRead(const char *fname, uintptr_t fbase) +{ + int i; + Ehdr ehdr; + Shdr shdr; + uint8_t c; + ESyms es; + ssize_t idx,n; + const char *cp; + struct stat stat_b; + + if ( !(es = (ESyms) malloc(sizeof(*es))) ) { + /* no memory -- give up */ + return 0; + } + + memset(es, 0, sizeof(*es)); + es->fd = -1; + es->fname = fname; + + if ( (es->fd = open(fname, O_RDONLY)) < 0 ) { + errlogPrintf("elfRead() -- unable to open file: %s\n", strerror(errno)); + goto bail; + } + + if ( EI_NIDENT != do_read(es->fd, &ehdr, EI_NIDENT) ) { + errlogPrintf("elfRead() -- unable to read ELF e_ident: %s\n", strerror(errno)); + goto bail; + } + + if ( ELFMAG0 != ehdr.e32.e_ident[EI_MAG0] + || ELFMAG1 != ehdr.e32.e_ident[EI_MAG1] + || ELFMAG2 != ehdr.e32.e_ident[EI_MAG2] + || ELFMAG3 != ehdr.e32.e_ident[EI_MAG3] ) { + errlogPrintf("bad ELF magic number\n"); + goto bail; + } + + if ( EV_CURRENT != ehdr.e32.e_ident[EI_VERSION] ) { + errlogPrintf("bad ELF version\n"); + goto bail; + } + + switch ( (es->eclss = c = ehdr.e32.e_ident[EI_CLASS]) ) { + default: + errlogPrintf("bad ELF class\n"); + goto bail; + + case ELFCLASS32: + n = sizeof(Elf32_Ehdr); + break; + case ELFCLASS64: + n = sizeof(Elf64_Ehdr); + break; + } + n -= EI_NIDENT; + + if ( 0 == fstat(es->fd, &stat_b) ) { + epicsTimeStamp progStartStamp; + time_t progStartTime; + + ClockTime_GetProgramStart(&progStartStamp); + epicsTimeToTime_t(&progStartTime, &progStartStamp); + if ( stat_b.st_mtime >= progStartTime ) { + errlogPrintf("elfRead() -- WARNING: '%s' was modified after program start -- symbol information may be inaccurate or invalid\n", fname); + } + } + + /* read rest */ + if ( n != do_read(es->fd, ehdr.e32.e_ident + EI_NIDENT, n) ) { + errlogPrintf("elfRead() -- unable to read ELF ehdr: %s\n", strerror(errno)); + goto bail; + } + + /* seek to section header table */ + if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { + errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); + goto bail; + } + + n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); + + for ( i = 0; ifd, &shdr, n) ) { + errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); + goto bail; + } + if ( SHT_SYMTAB == FLD(c,shdr,sh_type) ) + break; + } + + if ( i>=FLD(c,ehdr,e_shnum) ) { + /* no SYMTAB -- try dynamic symbols */ + + if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { + errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); + goto bail; + } + + for ( i = 0; ifd, &shdr, n) ) { + errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); + goto bail; + } + if ( SHT_DYNSYM == FLD(c,shdr,sh_type) ) + break; + } + } + + if ( i>=FLD(c,ehdr,e_shnum) ) { + errlogPrintf("elfRead() -- no symbol table found\n"); + goto bail; + } + + if ( 0 == (n = (ssize_t) FLD(c,shdr,sh_size)) ) { + errlogPrintf("elfRead() -- no symbol table data\n"); + goto bail; + } + + if ( !(es->symMap = getscn(es->fd, c, &shdr)) ) { + errlogPrintf("elfRead() -- unable to read ELF symtab\n"); + goto bail; + } + + es->nsyms = n / (ELFCLASS32==c ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym)); + + /* find and read string table */ + + n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); + + /* seek to section header table */ + if ( (off_t)-1 == lseek(es->fd, (off_t) (FLD(c,ehdr,e_shoff) + n * FLD(c,shdr,sh_link)), SEEK_SET) ) { + errlogPrintf("elfRead() -- unable to lseek to ELF e_shoff: %s\n", strerror(errno)); + goto bail; + } + + if ( n != do_read(es->fd, &shdr, n) ) { + errlogPrintf("elfRead() -- unable to read ELF strtab section header: %s\n", strerror(errno)); + goto bail; + } + + if ( !(es->strMap = getscn(es->fd,c,&shdr)) ) { + errlogPrintf("elfRead() -- unable to read ELF strtab\n"); + goto bail; + } + + /* Make sure there is a terminating NUL - unfortunately, memrchr is not portable */ + cp = (char*)es->strMap->addr + es->strMap->off; + for ( idx = es->strMap->max - 1; i >= 0; i-- ) { + if ( !cp[i] ) + break; + } + es->strMap->max = idx + 1; + + switch ( FLD(c,ehdr,e_type) ) { + case ET_EXEC: + /* Symbols in an executable already has absolute addresses */ + es->addr = 0; + break; + case ET_DYN: + /* Symbols in an shared library are relative to base address */ + es->addr = fbase; + break; + default: + errlogPrintf("dlLookupAddr(): Unexpected ELF object file type %u\n", FLD(c,ehdr,e_type)); + goto bail; + } + + return es; + +bail: + elfSymsRelease(es); + return es; +} + +/* Destroy a cached ELF symbol table */ +static void +elfSymsDestroy(ESyms es) +{ + if ( es ) { + elfSymsRelease(es); + free(es); + } +} + +/* Destroy all cached ELF symbol tables + * + * However - w/o proper locking for read access + * this must not be used. Otherwise, readers + * will hold stale pointers... + * + * We leave the commented code here to show + * how the tables can be torn down. + +void +elfSymTblFlush() +{ +ESyms es; + + elfsLockWrite(); + while ( (es = elfs) ) { + elfs = es->next; + es->next = 0; + elfsUnlockWrite(); + elfSymsDestroy(es); + elfsLockWrite(); + } + elfsUnlockWrite(); +} + +*/ + + +/* This routine must be called with the write-lock held */ +static ESyms +elfSymsFind(const char *fname) +{ + ESyms es; + + for ( es=elfs; es && strcmp(fname, es->fname); es = es->next ) + /* nothing else to do */; + return es; +} + +int +epicsFindAddr(void *addr, epicsSymbol *sym_p) +{ + Dl_info inf; + ESyms es,nes = 0; + uintptr_t minoff,off; + size_t i; + Sym sym; + Sym nearest; + const char *strtab; + uint8_t c; + size_t idx; + + if ( ! dladdr(addr, &inf) || (!inf.dli_fname && !inf.dli_sname) ) { + sym_p->f_nam = 0; + sym_p->s_nam = 0; + /* unable to lookup */ + return 0; + } + + sym_p->f_nam = inf.dli_fname; + + /* If the symbol is in the main executable then solaris' dladdr returns bogus info */ +#ifndef __sun + if ( (sym_p->s_nam = inf.dli_sname) ) { + sym_p->s_val = inf.dli_saddr; + /* Have a symbol name - just use it and be done */ + return 0; + } +#endif + + /* No symbol info; try to access ELF file and ready symbol table from there */ + + elfsLockWrite(); + + /* See if we have loaded this file already */ + es = elfSymsFind(inf.dli_fname); + + if ( !es ) { + + elfsUnlockWrite(); + + if ( ! (nes = elfRead(inf.dli_fname, (uintptr_t)inf.dli_fbase)) ) { + /* this path can only be taken if there is no memory for '*nes' */ + return 0; + } + + elfsLockWrite(); + + /* Has someone else intervened and already added this file while we were reading ? */ + es = elfSymsFind(inf.dli_fname); + + if ( es ) { + /* will undo our work in the unlikely event... */ + } else { + nes->next = elfs; + es = elfs = nes; + nes = 0; + } + } + + elfsUnlockWrite(); + + /* Undo our work in the unlikely event that it was redundant */ + if ( nes ) + elfSymsDestroy( nes ); + + nearest.raw = 0; + minoff = (uintptr_t)-1LL; + + if ( es->nsyms ) { + c = es->eclss; + sym.raw = (char*)es->symMap->addr + es->symMap->off; + strtab = (char*)es->strMap->addr + es->strMap->off; + + /* Do a brute-force search through the symbol table; if this is executed + * very often then it would be worthwhile constructing a sorted list of + * symbol addresses but for the stack trace we don't care... + */ +#if (FIND_ADDR_DEBUG & 1) + printf("Looking for %p\n", addr); +#endif + + if ( ELFCLASS32 == c ) { + for ( i=0; insyms; i++ ) { + if ( STT_FUNC != ELF32_ST_TYPE(sym.e32[i].st_info) ) + continue; + /* don't bother about undefined symbols */ + if ( 0 == sym.e32[i].st_shndx ) + continue; +#if (FIND_ADDR_DEBUG & 1) + printf("Trying: %s (0x%lx)\n", strtab + sym.e32[i].st_name, (unsigned long)(sym.e32[i].st_value + es->addr)); +#endif + if ( (uintptr_t)addr >= (uintptr_t)sym.e32[i].st_value + es->addr ) { + off = (uintptr_t)addr - ((uintptr_t)sym.e32[i].st_value + es->addr); + if ( off < minoff ) { + minoff = off; + nearest.e32 = &sym.e32[i]; + } + } + } + } else { + for ( i=0; insyms; i++ ) { + if ( STT_FUNC != ELF64_ST_TYPE(sym.e64[i].st_info) ) + continue; + /* don't bother about undefined symbols */ + if ( 0 == sym.e64[i].st_shndx ) + continue; +#if (FIND_ADDR_DEBUG & 1) + printf("Trying: %s (0x%llx)\n", strtab + sym.e64[i].st_name, (unsigned long long)(sym.e64[i].st_value + es->addr)); +#endif + if ( (uintptr_t)addr >= (uintptr_t)sym.e64[i].st_value + es->addr ) { + off = (uintptr_t)addr - ((uintptr_t)sym.e64[i].st_value + es->addr); + if ( off < minoff ) { + minoff = off; + nearest.e64 = &sym.e64[i]; + } + } + } + } + } + + if ( nearest.raw && ( (idx = ARR(c,nearest,0,st_name)) < es->strMap->max ) ) { + sym_p->s_nam = strtab + idx; + sym_p->s_val = (char*) ARR(c, nearest, 0, st_value) + es->addr; + } + + return 0; +} + +int epicsFindAddrGetFeatures(void) +{ + /* The static information given here may not be correct; + * it also depends on + * - compilation (frame pointer optimization) + * - linkage (static vs. dynamic) + * - stripping + */ + return EPICS_STACKTRACE_LCL_SYMBOLS + | EPICS_STACKTRACE_GBL_SYMBOLS + | EPICS_STACKTRACE_DYN_SYMBOLS; +} diff --git a/modules/libcom/src/osi/os/posix/osdEvent.c b/modules/libcom/src/osi/os/posix/osdEvent.c new file mode 100644 index 000000000..f133ed028 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdEvent.c @@ -0,0 +1,158 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/posix/osdEvent.c */ + +/* Author: Marty Kraimer Date: 13AUG1999 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsTime.h" +#include "errlog.h" + +struct epicsEventOSD { + pthread_mutex_t mutex; + pthread_cond_t cond; + int isFull; +}; + +#define printStatus(status, routine, func) \ + errlogPrintf("%s: %s failed: %s\n", (func), (routine), strerror(status)) + +#define checkStatus(status, routine, func) \ + if (status) { \ + printStatus(status, routine, func); \ + } + +#define checkStatusReturn(status, routine, func) \ + if (status) { \ + printStatus(status, routine, func); \ + return epicsEventError; \ + } + + +epicsShareFunc epicsEventId epicsEventCreate(epicsEventInitialState init) +{ + epicsEventId pevent = malloc(sizeof(*pevent)); + + if (pevent) { + int status = pthread_mutex_init(&pevent->mutex, 0); + + pevent->isFull = (init == epicsEventFull); + if (status) { + printStatus(status, "pthread_mutex_init", "epicsEventCreate"); + } else { + status = pthread_cond_init(&pevent->cond, 0); + if (!status) + return pevent; + printStatus(status, "pthread_cond_init", "epicsEventCreate"); + status = pthread_mutex_destroy(&pevent->mutex); + checkStatus(status, "pthread_mutex_destroy", "epicsEventCreate"); + } + free(pevent); + } + return NULL; +} + +epicsShareFunc void epicsEventDestroy(epicsEventId pevent) +{ + int status = pthread_mutex_destroy(&pevent->mutex); + + checkStatus(status, "pthread_mutex_destroy", "epicsEventDestroy"); + status = pthread_cond_destroy(&pevent->cond); + checkStatus(status, "pthread_cond_destroy", "epicsEventDestroy"); + free(pevent); +} + +epicsShareFunc epicsEventStatus epicsEventTrigger(epicsEventId pevent) +{ + int status = pthread_mutex_lock(&pevent->mutex); + + checkStatusReturn(status, "pthread_mutex_lock", "epicsEventTrigger"); + if (!pevent->isFull) { + pevent->isFull = 1; + status = pthread_cond_signal(&pevent->cond); + checkStatus(status, "pthread_cond_signal", "epicsEventTrigger"); + } + status = pthread_mutex_unlock(&pevent->mutex); + checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventTrigger"); + return epicsEventOK; +} + +epicsShareFunc epicsEventStatus epicsEventWait(epicsEventId pevent) +{ + epicsEventStatus result = epicsEventOK; + int status = pthread_mutex_lock(&pevent->mutex); + + checkStatusReturn(status, "pthread_mutex_lock", "epicsEventWait"); + while (!pevent->isFull) { + status = pthread_cond_wait(&pevent->cond, &pevent->mutex); + if (status) { + printStatus(status, "pthread_cond_wait", "epicsEventWait"); + result = epicsEventError; + goto release; + } + } + pevent->isFull = 0; + result = epicsEventOK; +release: + status = pthread_mutex_unlock(&pevent->mutex); + checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventWait"); + return result; +} + +epicsShareFunc epicsEventStatus epicsEventWaitWithTimeout(epicsEventId pevent, + double timeout) +{ + epicsEventStatus result = epicsEventOK; + int status = pthread_mutex_lock(&pevent->mutex); + + checkStatusReturn(status, "pthread_mutex_lock", "epicsEventWaitWithTimeout"); + if (!pevent->isFull) { + struct timespec wakeTime; + + convertDoubleToWakeTime(timeout, &wakeTime); + while (!status && !pevent->isFull) { + status = pthread_cond_timedwait(&pevent->cond, &pevent->mutex, + &wakeTime); + } + if (status) { + result = (status == ETIMEDOUT) ? + epicsEventWaitTimeout : epicsEventError; + goto release; + } + } + pevent->isFull = 0; +release: + status = pthread_mutex_unlock(&pevent->mutex); + checkStatusReturn(status, "pthread_mutex_unlock", "epicsEventWaitWithTimeout"); + return result; +} + +epicsShareFunc epicsEventStatus epicsEventTryWait(epicsEventId id) +{ + return epicsEventWaitWithTimeout(id, 0.0); +} + +epicsShareFunc void epicsEventShow(epicsEventId pevent, unsigned int level) +{ + printf("epicsEvent %p: %s\n", pevent, + pevent->isFull ? "full" : "empty"); + if (level > 0) + printf(" pthread_mutex = %p, pthread_cond = %p\n", + &pevent->mutex, &pevent->cond); +} diff --git a/modules/libcom/src/osi/os/posix/osdEvent.h b/modules/libcom/src/osi/os/posix/osdEvent.h new file mode 100644 index 000000000..e2f8b9af9 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdEvent.h @@ -0,0 +1,10 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* for a pure posix implementation no osdEvent.h definitions are needed*/ diff --git a/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp new file mode 100644 index 000000000..ae13d28fc --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp @@ -0,0 +1,18 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsStackTracePvt.h" + +int epicsBackTrace(void **buf, int buf_sz) +{ + return backtrace(buf, buf_sz); +} diff --git a/modules/libcom/src/osi/os/posix/osdFindSymbol.c b/modules/libcom/src/osi/os/posix/osdFindSymbol.c new file mode 100644 index 000000000..acfc405e5 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdFindSymbol.c @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/posix/osdFindSymbol.c */ + +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return dlopen(name, RTLD_LAZY | RTLD_GLOBAL); +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return dlerror(); +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return dlsym(0, name); +} diff --git a/modules/libcom/src/osi/os/posix/osdMonotonic.c b/modules/libcom/src/osi/os/posix/osdMonotonic.c new file mode 100644 index 000000000..00c9c2871 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdMonotonic.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "dbDefs.h" +#include "errlog.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +static clockid_t osdMonotonicID; +static epicsUInt64 osdMonotonicResolution; + +void osdMonotonicInit(void) +{ + unsigned i; +clockid_t ids[] = { +#ifdef CLOCK_MONOTONIC_RAW + CLOCK_MONOTONIC_RAW, /* Linux specific */ +#endif +#ifdef CLOCK_HIGHRES + CLOCK_HIGHRES, /* solaris specific */ +#endif +#ifdef CLOCK_MONOTONIC + CLOCK_MONOTONIC, /* Linux, RTEMS, and probably others */ +#endif + /* fallback and vxWorks, not actually monotonic, but always available */ + CLOCK_REALTIME + }; + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "cantProceed.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsAssert.h" + +#define checkStatus(status,message) \ + if((status)) { \ + errlogPrintf("epicsMutex %s failed: error %s\n", \ + (message), strerror((status))); \ + } +#define checkStatusQuit(status,message,method) \ + if(status) { \ + errlogPrintf("epicsMutex %s failed: error %s\n", \ + (message), strerror((status))); \ + cantProceed((method)); \ + } + +static int mutexLock(pthread_mutex_t *id) +{ + int status; + + while ((status = pthread_mutex_lock(id)) == EINTR) { + errlogPrintf("pthread_mutex_lock returned EINTR. Violates SUSv3\n"); + } + return status; +} + +/* Until these can be demonstrated to work leave them undefined*/ +/* On solaris 8 _POSIX_THREAD_PRIO_INHERIT fails*/ +#undef _POSIX_THREAD_PROCESS_SHARED +#undef _POSIX_THREAD_PRIO_INHERIT + +/* Two completely different implementations are provided below + * If support is available for PTHREAD_MUTEX_RECURSIVE then + * only pthread_mutex is used. + * If support is not available for PTHREAD_MUTEX_RECURSIVE then + * a much more complicated solution is required + */ + + +#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 +typedef struct epicsMutexOSD { + pthread_mutex_t lock; + pthread_mutexattr_t mutexAttr; +} epicsMutexOSD; + +epicsMutexOSD * epicsMutexOsdCreate(void) { + epicsMutexOSD *pmutex; + int status; + + pmutex = calloc(1, sizeof(*pmutex)); + if(!pmutex) + goto fail; + + status = pthread_mutexattr_init(&pmutex->mutexAttr); + if (status) + goto fail; + +#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 + status = pthread_mutexattr_setprotocol(&pmutex->mutexAttr, + PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); +#endif /*_POSIX_THREAD_PRIO_INHERIT*/ + + status = pthread_mutexattr_settype(&pmutex->mutexAttr, + PTHREAD_MUTEX_RECURSIVE); + checkStatus(status, "pthread_mutexattr_settype"); + if (status) + goto fail; + + status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); + if (status) + goto dattr; + return pmutex; + +dattr: + pthread_mutexattr_destroy(&pmutex->mutexAttr); +fail: + free(pmutex); + return NULL; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_mutex_destroy(&pmutex->lock); + checkStatus(status, "pthread_mutex_destroy"); + status = pthread_mutexattr_destroy(&pmutex->mutexAttr); + checkStatus(status, "pthread_mutexattr_destroy"); + free(pmutex); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) +{ + int status; + + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + if(status) { + errlogMessage("epicsMutex pthread_mutex_lock failed: error epicsMutexOsdLock\n"); + return epicsMutexLockError; + } + return epicsMutexLockOK; +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) +{ + int status; + + if (!pmutex) return epicsMutexLockError; + status = pthread_mutex_trylock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + if (status == EBUSY) return epicsMutexLockTimeout; + if(status) { + errlogMessage("epicsMutex pthread_mutex_trylock failed: error epicsMutexOsdTryLock"); + return epicsMutexLockError; + } + return epicsMutexLockOK; +} + +void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level) +{ +} + +#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ + +typedef struct epicsMutexOSD { + pthread_mutex_t lock; + pthread_mutexattr_t mutexAttr; + pthread_cond_t waitToBeOwner; +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 + pthread_condattr_t condAttr; +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + int count; + int owned; /* TRUE | FALSE */ + pthread_t ownerTid; +} epicsMutexOSD; + +epicsMutexOSD * epicsMutexOsdCreate(void) { + epicsMutexOSD *pmutex; + int status; + + pmutex = calloc(1, sizeof(*pmutex)); + if(!pmutex) + return NULL; + + status = pthread_mutexattr_init(&pmutex->mutexAttr); + if(status) + goto fail; + +#if defined(_POSIX_THREAD_PRIO_INHERIT) && _POSIX_THREAD_PRIO_INHERIT > 0 + status = pthread_mutexattr_setprotocol( + &pmutex->mutexAttr,PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); +#endif /*_POSIX_THREAD_PRIO_INHERIT*/ + + status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); + if(status) + goto dattr; + +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 + status = pthread_condattr_init(&pmutex->condAttr); + checkStatus(status, "pthread_condattr_init"); + status = pthread_condattr_setpshared(&pmutex->condAttr, + PTHREAD_PROCESS_PRIVATE); + checkStatus(status, "pthread_condattr_setpshared"); + status = pthread_cond_init(&pmutex->waitToBeOwner, &pmutex->condAttr); +#else + status = pthread_cond_init(&pmutex->waitToBeOwner, 0); +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + if(status) + goto dmutex; + + return pmutex; + +dmutex: + pthread_mutex_destroy(&pmutex->lock); +dattr: + pthread_mutexattr_destroy(&pmutex->mutexAttr); +fail: + free(pmutex); + return NULL; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_cond_destroy(&pmutex->waitToBeOwner); + checkStatus(status, "pthread_cond_destroy"); +#if defined(_POSIX_THREAD_PROCESS_SHARED) && _POSIX_THREAD_PROCESS_SHARED > 0 + status = pthread_condattr_destroy(&pmutex->condAttr); +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + status = pthread_mutex_destroy(&pmutex->lock); + checkStatus(status, "pthread_mutex_destroy"); + status = pthread_mutexattr_destroy(&pmutex->mutexAttr); + checkStatus(status, "pthread_mutexattr_destroy"); + free(pmutex); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) +{ + int status; + + status = mutexLock(&pmutex->lock); + checkStatus(status, "pthread_mutex_lock epicsMutexOsdUnlock"); + if(status) + return; + + if ((pmutex->count <= 0) || (pmutex->ownerTid != pthread_self())) { + pthread_mutex_unlock(&pmutex->lock); + checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); + errlogPrintf("epicsMutexOsdUnlock but caller is not owner\n"); + cantProceed("epicsMutexOsdUnlock but caller is not owner"); + return; + } + + pmutex->count--; + if (pmutex->count == 0) { + pmutex->owned = 0; + pmutex->ownerTid = 0; + status = pthread_cond_signal(&pmutex->waitToBeOwner); + checkStatusQuit(status, "pthread_cond_signal epicsMutexOsdUnlock", "epicsMutexOsdUnlock"); + } + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatus(status, "pthread_mutex_unlock epicsMutexOsdUnlock"); +} + +static int condWait(pthread_cond_t *condId, pthread_mutex_t *mutexId) +{ + int status; + + while ((status = pthread_cond_wait(condId, mutexId)) == EINTR) { + errlogPrintf("pthread_cond_wait returned EINTR. Violates SUSv3\n"); + } + return status; +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) +{ + pthread_t tid = pthread_self(); + int status; + + if (!pmutex || !tid) return epicsMutexLockError; + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + checkStatus(status, "pthread_mutex_lock epicsMutexOsdLock"); + if(status) + return epicsMutexLockError; + + while (pmutex->owned && !pthread_equal(pmutex->ownerTid, tid)) + condWait(&pmutex->waitToBeOwner, &pmutex->lock); + pmutex->ownerTid = tid; + pmutex->owned = 1; + pmutex->count++; + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatus(status, "pthread_mutex_unlock epicsMutexOsdLock"); + if(status) + return epicsMutexLockError; + return epicsMutexLockOK; +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) +{ + pthread_t tid = pthread_self(); + epicsMutexLockStatus result; + int status; + + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + checkStatus(status, "pthread_mutex_lock epicsMutexOsdTryLock"); + if(status) + return epicsMutexLockError; + + if (!pmutex->owned || pthread_equal(pmutex->ownerTid, tid)) { + pmutex->ownerTid = tid; + pmutex->owned = 1; + pmutex->count++; + result = epicsMutexLockOK; + } else { + result = epicsMutexLockTimeout; + } + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatus(status, "pthread_mutex_unlock epicsMutexOsdTryLock"); + if(status) + return epicsMutexLockError; + return result; +} + +void epicsMutexOsdShow(struct epicsMutexOSD *pmutex,unsigned int level) +{ + printf("ownerTid %p count %d owned %d\n", + (void *)pmutex->ownerTid, pmutex->count, pmutex->owned); +} +#endif /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ diff --git a/modules/libcom/src/osi/os/posix/osdMutex.h b/modules/libcom/src/osi/os/posix/osdMutex.h new file mode 100644 index 000000000..cc416384a --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdMutex.h @@ -0,0 +1,10 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* for a pure posix implementation no osdMutex.h definitions are needed*/ diff --git a/modules/libcom/src/osi/os/posix/osdProcess.c b/modules/libcom/src/osi/os/posix/osdProcess.c new file mode 100644 index 000000000..a08871465 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdProcess.c @@ -0,0 +1,121 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" +#include "epicsAssert.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + struct passwd *p; + + p = getpwuid ( getuid () ); + if ( p && p->pw_name ) { + size_t len = strlen ( p->pw_name ); + unsigned uiLength; + + if ( len > UINT_MAX || len <= 0 ) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, p->pw_name, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; + } + else { + return osiGetUserNameFail; + } +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + int status; + + /* + * create a duplicate process + */ + status = fork (); + if (status < 0) { + return osiSpawnDetachedProcessFail; + } + + /* + * return to the caller + * in the initiating (parent) process + */ + if (status) { + return osiSpawnDetachedProcessSuccess; + } + + /* + * This is executed only by the new child process. + * Close all open files except for STDIO, so they will not + * be inherited by the new program. + */ + { + int fd, maxfd = sysconf ( _SC_OPEN_MAX ); + for ( fd = 0; fd <= maxfd; fd++ ) { + if (fd==STDIN_FILENO) continue; + if (fd==STDOUT_FILENO) continue; + if (fd==STDERR_FILENO) continue; + close (fd); + } + } + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + /* + * Drop real-time SCHED_FIFO priority + */ + { + struct sched_param p; + + p.sched_priority = 0; + status = sched_setscheduler(0, SCHED_OTHER, &p); + } +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + /* + * Run the specified executable + */ + status = execlp (pBaseExecutableName, pBaseExecutableName, (char *)NULL); + if ( status < 0 ) { + fprintf ( stderr, "**** The executable \"%s\" couldn't be located\n", pBaseExecutableName ); + fprintf ( stderr, "**** because of errno = \"%s\".\n", strerror (errno) ); + fprintf ( stderr, "**** You may need to modify your PATH environment variable.\n" ); + fprintf ( stderr, "**** Unable to start \"%s\" process.\n", pProcessName); + } + /* Don't run our parent's atexit() handlers */ + _exit ( -1 ); +} diff --git a/modules/libcom/src/osi/os/posix/osdSignal.cpp b/modules/libcom/src/osi/os/posix/osdSignal.cpp new file mode 100644 index 000000000..3f8deb4fb --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdSignal.cpp @@ -0,0 +1,57 @@ +/*************************************************************************\ + * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: J. Hill, A. Johnson + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +static void ignoreIfDefault(int signum, const char *name) +{ + struct sigaction curAction; + int status = sigaction(signum, NULL, &curAction); + + if (status >= 0 && + curAction.sa_handler == SIG_DFL) { + curAction.sa_handler = SIG_IGN; + status = sigaction(signum, &curAction, NULL); + } + if (status < 0) { + fprintf(stderr, "%s: sigaction failed for %s, %s\n", + __FILE__, name, strerror(errno)); + } +} + +/* + * epicsSignalInstallSigHupIgnore () + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore (void) +{ + ignoreIfDefault(SIGHUP, "SIGHUP"); +} + +/* + * epicsSignalInstallSigPipeIgnore () + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore (void) +{ + ignoreIfDefault(SIGPIPE, "SIGPIPE"); +} + +/* Disabled */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm + ( struct epicsThreadOSD * /* threadId */ ) {} diff --git a/modules/libcom/src/osi/os/posix/osdSock.c b/modules/libcom/src/osi/os/posix/osdSock.c new file mode 100644 index 000000000..9f8146142 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdSock.c @@ -0,0 +1,183 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdSock.c */ +/* + * Author: Jeff Hill + * Date: 04-05-94 + * + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "osiSock.h" +#include "epicsAssert.h" +#include "errlog.h" + +/* + * Protect some routines which are not thread-safe + */ +static epicsMutexId infoMutex; +static void createInfoMutex (void *unused) +{ + infoMutex = epicsMutexMustCreate (); +} +static void lockInfo (void) +{ + static epicsThreadOnceId infoMutexOnceFlag = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce (&infoMutexOnceFlag, createInfoMutex, NULL); + epicsMutexMustLock (infoMutex); +} +static void unlockInfo (void) +{ + epicsMutexUnlock (infoMutex); +} + +/* + * NOOP + */ +int osiSockAttach() +{ + return 1; +} + +/* + * NOOP + */ +void osiSockRelease() +{ +} + +/* + * this version sets the file control flags so that + * the socket will be closed if the user uses exec() + * as is the case with third party tools such as TCL/TK + */ +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + SOCKET sock = socket ( domain, type, protocol ); + if ( sock < 0 ) { + sock = INVALID_SOCKET; + } + else { + int status = fcntl ( sock, F_SETFD, FD_CLOEXEC ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketCreate: failed to " + "fcntl FD_CLOEXEC because \"%s\"\n", + buf ); + close ( sock ); + sock = INVALID_SOCKET; + } + } + return sock; +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + int newSock = accept ( sock, pAddr, addrlen ); + if ( newSock < 0 ) { + newSock = INVALID_SOCKET; + } + else { + int status = fcntl ( newSock, F_SETFD, FD_CLOEXEC ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketCreate: failed to " + "fcntl FD_CLOEXEC because \"%s\"\n", + buf ); + close ( newSock ); + newSock = INVALID_SOCKET; + } + } + return newSock; +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = close ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", + buf ); + } +} + +/* + * ipAddrToHostName + * On many systems, gethostbyaddr must be protected by a + * mutex since the routine is not thread-safe. + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + struct hostent *ent; + int ret = 0; + + if (bufSize<1) { + return 0; + } + + lockInfo (); + ent = gethostbyaddr((const char *) pAddr, sizeof (*pAddr), AF_INET); + if (ent) { + strncpy (pBuf, ent->h_name, bufSize); + pBuf[bufSize-1] = '\0'; + ret = strlen (pBuf); + } + unlockInfo (); + return ret; +} + +/* + * hostToIPAddr () + * On many systems, gethostbyname must be protected by a + * mutex since the routine is not thread-safe. + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA) +{ + struct hostent *phe; + int ret = -1; + + lockInfo (); + phe = gethostbyname (pHostName); + if (phe && phe->h_addr_list[0]) { + if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { + struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; + + *pIPA = *pInAddrIn; + ret = 0; + } + } + unlockInfo (); + return ret; +} + + + diff --git a/modules/libcom/src/osi/os/posix/osdSockAddrReuse.cpp b/modules/libcom/src/osi/os/posix/osdSockAddrReuse.cpp new file mode 100644 index 000000000..3a3f78ebd --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdSockAddrReuse.cpp @@ -0,0 +1,49 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnableAddressReuseDuringTimeWaitState: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/modules/libcom/src/osi/os/posix/osdSpin.c b/modules/libcom/src/osi/os/posix/osdSpin.c new file mode 100644 index 000000000..aa61a6c90 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdSpin.c @@ -0,0 +1,180 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "cantProceed.h" +#include "epicsSpin.h" + +/* POSIX spinlocks may be subject to priority inversion + * and so can't be guaranteed safe in situations where + * threads have different priorities, and thread + * preemption can't be disabled. + */ +#if defined(DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING) +#if defined(_POSIX_SPIN_LOCKS) && (_POSIX_SPIN_LOCKS > 0) +# define USE_PSPIN +#endif +#endif + +#define checkStatus(status,message) \ + if ((status)) { \ + errlogPrintf("epicsSpin %s failed: error %s\n", \ + (message), strerror((status))); \ + } + +#ifdef USE_PSPIN + +/* + * POSIX SPIN LOCKS IMPLEMENTATION + */ + +typedef struct epicsSpin { + pthread_spinlock_t lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + int status; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + status = pthread_spin_init(&spin->lock, PTHREAD_PROCESS_PRIVATE); + checkStatus(status, "pthread_spin_init"); + if (status) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +void epicsSpinDestroy(epicsSpinId spin) { + int status; + + status = pthread_spin_destroy(&spin->lock); + checkStatus(status, "pthread_spin_destroy"); + + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int status; + + status = pthread_spin_lock(&spin->lock); + checkStatus(status, "pthread_spin_lock"); + if (status) + cantProceed(NULL); +} + +int epicsSpinTryLock(epicsSpinId spin) { + int status; + + status = pthread_spin_trylock(&spin->lock); + if (status == EBUSY) + return 1; + checkStatus(status, "pthread_spin_trylock"); + return status; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int status; + + status = pthread_spin_unlock(&spin->lock); + checkStatus(status, "pthread_spin_unlock"); +} + +#else /* USE_PSPIN */ + +/* + * POSIX MUTEX IMPLEMENTATION + */ + +typedef struct epicsSpin { + pthread_mutex_t lock; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + epicsSpin *spin; + int status; + + spin = calloc(1, sizeof(*spin)); + if (!spin) + goto fail; + + status = pthread_mutex_init(&spin->lock, NULL); + checkStatus(status, "pthread_mutex_init"); + if (status) + goto fail; + + return spin; + +fail: + free(spin); + return NULL; +} + +void epicsSpinDestroy(epicsSpinId spin) { + int status; + + status = pthread_mutex_destroy(&spin->lock); + checkStatus(status, "pthread_mutex_destroy"); + + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int status; + + status = pthread_mutex_lock(&spin->lock); + checkStatus(status, "pthread_mutex_lock"); + if (status) + cantProceed(NULL); +} + +int epicsSpinTryLock(epicsSpinId spin) { + int status; + + status = pthread_mutex_trylock(&spin->lock); + if (status == EBUSY) + return 1; + checkStatus(status, "pthread_mutex_trylock"); + return status; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int status; + + status = pthread_mutex_unlock(&spin->lock); + checkStatus(status, "pthread_mutex_unlock"); +} + +#endif /* USE_PSPIN */ + + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if(!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} diff --git a/modules/libcom/src/osi/os/posix/osdStdio.c b/modules/libcom/src/osi/os/posix/osdStdio.c new file mode 100644 index 000000000..2a23cee71 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdStdio.c @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#define epicsExportSharedSymbols +#include + +epicsShareFunc int epicsShareAPI epicsSnprintf( + char *str, size_t size, const char *format, ...) +{ + int nchars; + va_list pvar; + + va_start(pvar,format); + nchars = epicsVsnprintf(str,size,format,pvar); + va_end (pvar); + return(nchars); +} + +epicsShareFunc int epicsShareAPI epicsVsnprintf( + char *str, size_t size, const char *format, va_list ap) +{ + return vsnprintf ( str, size, format, ap ); +} diff --git a/modules/libcom/src/osi/os/posix/osdStrtod.h b/modules/libcom/src/osi/os/posix/osdStrtod.h new file mode 100644 index 000000000..502889a9d --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with working strtod() routine + */ +#define epicsStrtod strtod diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c new file mode 100644 index 000000000..755390eed --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -0,0 +1,914 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This is a posix implementation of epicsThread */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_POSIX_MEMLOCK) && _POSIX_MEMLOCK > 0 +#include +#endif + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsString.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsExit.h" + +epicsShareFunc void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level); +epicsShareFunc void osdThreadHooksRun(epicsThreadId id); +epicsShareFunc void osdThreadHooksRunMain(epicsThreadId id); + +static int mutexLock(pthread_mutex_t *id) +{ + int status; + + while(1) { + status = pthread_mutex_lock(id); + if(status!=EINTR) return status; + fprintf(stderr,"pthread_mutex_lock returned EINTR. Violates SUSv3\n"); + } +} + +#if defined DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#endif + +typedef struct commonAttr{ + pthread_attr_t attr; + struct sched_param schedParam; + int maxPriority; + int minPriority; + int schedPolicy; + int usePolicy; +} commonAttr; + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 +typedef struct { + int min_pri, max_pri; + int policy; + int ok; +} priAvailable; +#endif + +static pthread_key_t getpthreadInfo; +static pthread_mutex_t onceLock; +static pthread_mutex_t listLock; +static ELLLIST pthreadList = ELLLIST_INIT; +static commonAttr *pcommonAttr = 0; +static int epicsThreadOnceCalled = 0; + +static epicsThreadOSD *createImplicit(void); + +#define checkStatus(status,message) \ +if((status)) {\ + errlogPrintf("%s error %s\n",(message),strerror((status))); \ +} + +#define checkStatusQuit(status,message,method) \ +if(status) { \ + errlogPrintf("%s error %s\n",(message),strerror((status))); \ + cantProceed((method)); \ +} + +/* The following are for use by once, which is only invoked from epicsThreadInit*/ +/* Until epicsThreadInit completes errlogInit will not work */ +/* It must also be used by init_threadInfo otherwise errlogInit could get */ +/* called recursively */ +#define checkStatusOnce(status,message) \ +if((status)) {\ + fprintf(stderr,"%s error %s\n",(message),strerror((status))); } + +#define checkStatusOnceQuit(status,message,method) \ +if(status) { \ + fprintf(stderr,"%s error %s",(message),strerror((status))); \ + fprintf(stderr," %s\n",method); \ + fprintf(stderr,"epicsThreadInit cant proceed. Program exiting\n"); \ + exit(-1);\ +} + + +epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId pthreadInfo) +{ +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + double maxPriority,minPriority,slope,oss; + + if(pcommonAttr->maxPriority==pcommonAttr->minPriority) + return(pcommonAttr->maxPriority); + maxPriority = (double)pcommonAttr->maxPriority; + minPriority = (double)pcommonAttr->minPriority; + slope = (maxPriority - minPriority)/100.0; + oss = (double)pthreadInfo->osiPriority * slope + minPriority; + return((int)oss); +#else + return 0; +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +} + +static void setSchedulingPolicy(epicsThreadOSD *pthreadInfo,int policy) +{ +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + int status; + + if(!pcommonAttr->usePolicy) return; + + status = pthread_attr_getschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + checkStatusOnce(status,"pthread_attr_getschedparam"); + pthreadInfo->schedParam.sched_priority = epicsThreadGetPosixPriority(pthreadInfo); + pthreadInfo->schedPolicy = policy; + status = pthread_attr_setschedpolicy( + &pthreadInfo->attr,policy); + checkStatusOnce(status,"pthread_attr_setschedpolicy"); + status = pthread_attr_setschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + checkStatusOnce(status,"pthread_attr_setschedparam"); + status = pthread_attr_setinheritsched( + &pthreadInfo->attr,PTHREAD_EXPLICIT_SCHED); + checkStatusOnce(status,"pthread_attr_setinheritsched"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +} + +static epicsThreadOSD * create_threadInfo(const char *name) +{ + epicsThreadOSD *pthreadInfo; + + /* sizeof(epicsThreadOSD) includes one byte for the '\0' */ + pthreadInfo = calloc(1,sizeof(*pthreadInfo) + strlen(name)); + if(!pthreadInfo) + return NULL; + pthreadInfo->suspendEvent = epicsEventCreate(epicsEventEmpty); + if(!pthreadInfo->suspendEvent){ + free(pthreadInfo); + return NULL; + } + strcpy(pthreadInfo->name, name); + return pthreadInfo; +} + +static epicsThreadOSD * init_threadInfo(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadOSD *pthreadInfo; + int status; + + pthreadInfo = create_threadInfo(name); + if(!pthreadInfo) + return NULL; + pthreadInfo->createFunc = funptr; + pthreadInfo->createArg = parm; + status = pthread_attr_init(&pthreadInfo->attr); + checkStatusOnce(status,"pthread_attr_init"); + if(status) return 0; + status = pthread_attr_setdetachstate( + &pthreadInfo->attr, PTHREAD_CREATE_DETACHED); + checkStatusOnce(status,"pthread_attr_setdetachstate"); +#if defined (_POSIX_THREAD_ATTR_STACKSIZE) +#if ! defined (OSITHREAD_USE_DEFAULT_STACK) + status = pthread_attr_setstacksize( &pthreadInfo->attr,(size_t)stackSize); + checkStatusOnce(status,"pthread_attr_setstacksize"); +#endif /*OSITHREAD_USE_DEFAULT_STACK*/ +#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ + status = pthread_attr_setscope(&pthreadInfo->attr,PTHREAD_SCOPE_PROCESS); + if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); + pthreadInfo->osiPriority = priority; + return(pthreadInfo); +} + +static void free_threadInfo(epicsThreadOSD *pthreadInfo) +{ + int status; + + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","free_threadInfo"); + if(pthreadInfo->isOnThreadList) ellDelete(&pthreadList,&pthreadInfo->node); + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","free_threadInfo"); + epicsEventDestroy(pthreadInfo->suspendEvent); + status = pthread_attr_destroy(&pthreadInfo->attr); + checkStatusQuit(status,"pthread_attr_destroy","free_threadInfo"); + free(pthreadInfo); +} + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 +/* + * The actually available range priority range (at least under linux) + * may be restricted by resource limitations (but that is ignored + * by sched_get_priority_max()). See bug #835138 which is fixed by + * this code. + */ + +static int try_pri(int pri, int policy) +{ +struct sched_param schedp; + + schedp.sched_priority = pri; + return pthread_setschedparam(pthread_self(), policy, &schedp); +} + +static void* +find_pri_range(void *arg) +{ +priAvailable *prm = arg; +int min = sched_get_priority_min(prm->policy); +int max = sched_get_priority_max(prm->policy); +int low, try; + + if ( -1 == min || -1 == max ) { + /* something is very wrong; maintain old behavior + * (warning message if sched_get_priority_xxx() fails + * and use default policy's sched_priority [even if + * that is likely to cause epicsThreadCreate to fail + * because that priority is not suitable for SCHED_FIFO]). + */ + prm->min_pri = prm->max_pri = -1; + return 0; + } + + + if ( try_pri(min, prm->policy) ) { + /* cannot create thread at minimum priority; + * probably no permission to use SCHED_FIFO + * at all. However, we still must return + * a priority range accepted by the SCHED_FIFO + * policy. Otherwise, epicsThreadCreate() cannot + * detect the unsufficient permission (EPERM) + * and fall back to a non-RT thread (because + * pthread_attr_setschedparam would fail with + * EINVAL due to the bad priority). + */ + prm->min_pri = prm->max_pri = min; + return 0; + } + + + /* Binary search through available priorities. + * The actually available range may be restricted + * by resource limitations (but that is ignored + * by sched_get_priority_max() [linux]). + */ + low = min; + + while ( low < max ) { + try = (max+low)/2; + if ( try_pri(try, prm->policy) ) { + max = try; + } else { + low = try + 1; + } + } + + prm->min_pri = min; + prm->max_pri = try_pri(max, prm->policy) ? max-1 : max; + prm->ok = 1; + + return 0; +} + +static void findPriorityRange(commonAttr *a_p) +{ +priAvailable arg; +pthread_t id; +void *dummy; +int status; + + arg.policy = a_p->schedPolicy; + arg.ok = 0; + + status = pthread_create(&id, 0, find_pri_range, &arg); + checkStatusQuit(status, "pthread_create","epicsThreadInit"); + + status = pthread_join(id, &dummy); + checkStatusQuit(status, "pthread_join","epicsThreadInit"); + + a_p->minPriority = arg.min_pri; + a_p->maxPriority = arg.max_pri; + a_p->usePolicy = arg.ok; +} +#endif + + +static void once(void) +{ + epicsThreadOSD *pthreadInfo; + int status; + + pthread_key_create(&getpthreadInfo,0); + status = pthread_mutex_init(&onceLock,0); + checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); + status = pthread_mutex_init(&listLock,0); + checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); + pcommonAttr = calloc(1,sizeof(commonAttr)); + if(!pcommonAttr) checkStatusOnceQuit(errno,"calloc","epicsThreadInit"); + status = pthread_attr_init(&pcommonAttr->attr); + checkStatusOnceQuit(status,"pthread_attr_init","epicsThreadInit"); + status = pthread_attr_setdetachstate( + &pcommonAttr->attr, PTHREAD_CREATE_DETACHED); + checkStatusOnce(status,"pthread_attr_setdetachstate"); + status = pthread_attr_setscope(&pcommonAttr->attr,PTHREAD_SCOPE_PROCESS); + if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + status = pthread_attr_setschedpolicy( + &pcommonAttr->attr,SCHED_FIFO); + checkStatusOnce(status,"pthread_attr_setschedpolicy"); + status = pthread_attr_getschedpolicy( + &pcommonAttr->attr,&pcommonAttr->schedPolicy); + checkStatusOnce(status,"pthread_attr_getschedpolicy"); + status = pthread_attr_getschedparam( + &pcommonAttr->attr,&pcommonAttr->schedParam); + checkStatusOnce(status,"pthread_attr_getschedparam"); + + findPriorityRange(pcommonAttr); + + if(pcommonAttr->maxPriority == -1) { + pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority; + fprintf(stderr,"sched_get_priority_max failed set to %d\n", + pcommonAttr->maxPriority); + } + if(pcommonAttr->minPriority == -1) { + pcommonAttr->minPriority = pcommonAttr->schedParam.sched_priority; + fprintf(stderr,"sched_get_priority_min failed set to %d\n", + pcommonAttr->maxPriority); + } + + if (errVerbose) { + fprintf(stderr, "LRT: min priority: %d max priority %d\n", + pcommonAttr->minPriority, pcommonAttr->maxPriority); + } + +#else + if(errVerbose) fprintf(stderr,"task priorities are not implemented\n"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0); + assert(pthreadInfo!=NULL); + status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); + checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit"); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","epicsThreadInit"); + ellAdd(&pthreadList,&pthreadInfo->node); + pthreadInfo->isOnThreadList = 1; + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadInit"); + status = atexit(epicsExitCallAtExits); + checkStatusOnce(status,"atexit"); + osdThreadHooksRunMain(pthreadInfo); + epicsThreadOnceCalled = 1; +} + +static void * start_routine(void *arg) +{ + epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg; + int status; + sigset_t blockAllSig; + + sigfillset(&blockAllSig); + pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL); + status = pthread_setspecific(getpthreadInfo,arg); + checkStatusQuit(status,"pthread_setspecific","start_routine"); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","start_routine"); + ellAdd(&pthreadList,&pthreadInfo->node); + pthreadInfo->isOnThreadList = 1; + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","start_routine"); + osdThreadHooksRun(pthreadInfo); + + (*pthreadInfo->createFunc)(pthreadInfo->createArg); + + epicsExitCallAtThreadExits (); + free_threadInfo(pthreadInfo); + return(0); +} + +static void epicsThreadInit(void) +{ + static pthread_once_t once_control = PTHREAD_ONCE_INIT; + int status = pthread_once(&once_control,once); + checkStatusQuit(status,"pthread_once","epicsThreadInit"); +} + +epicsShareFunc +void epicsThreadRealtimeLock(void) +{ +#if defined(_POSIX_MEMLOCK) && _POSIX_MEMLOCK > 0 + if (pcommonAttr->maxPriority > pcommonAttr->minPriority) { + int status = mlockall(MCL_CURRENT | MCL_FUTURE); + + if (status) { + fprintf(stderr, "epicsThreadRealtimeLock " + "Warning: Unable to lock the virtual address space.\n" + "VM page faults may harm real-time performance.\n"); + } + } +#endif +} + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) +{ +#if defined (OSITHREAD_USE_DEFAULT_STACK) + return 0; +#elif defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE > 0 + #define STACK_SIZE(f) (f * 0x10000 * sizeof(void *)) + static const unsigned stackSizeTable[epicsThreadStackBig+1] = { + STACK_SIZE(1), STACK_SIZE(2), STACK_SIZE(4) + }; + if (stackSizeClassepicsThreadStackBig) { + errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +#else + return 0; +#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ +} + +epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE &threadOnceComplete + int status; + + epicsThreadInit(); + status = mutexLock(&onceLock); + if(status) { + fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n", + strerror(status)); + exit(-1); + } + + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + func(arg); + status = mutexLock(&onceLock); + checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + epicsThreadSleep(epicsThreadSleepQuantum()); + status = mutexLock(&onceLock); + checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); + } + } + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce"); +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadOSD *pthreadInfo; + int status; + sigset_t blockAllSig, oldSig; + + epicsThreadInit(); + assert(pcommonAttr); + sigfillset(&blockAllSig); + pthread_sigmask(SIG_SETMASK,&blockAllSig,&oldSig); + pthreadInfo = init_threadInfo(name,priority,stackSize,funptr,parm); + if(pthreadInfo==0) return 0; + pthreadInfo->isEpicsThread = 1; + setSchedulingPolicy(pthreadInfo,SCHED_FIFO); + pthreadInfo->isRealTimeScheduled = 1; + status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr, + start_routine,pthreadInfo); + if(status==EPERM){ + /* Try again without SCHED_FIFO*/ + free_threadInfo(pthreadInfo); + pthreadInfo = init_threadInfo(name,priority,stackSize,funptr,parm); + if(pthreadInfo==0) return 0; + pthreadInfo->isEpicsThread = 1; + status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr, + start_routine,pthreadInfo); + } + checkStatusOnce(status,"pthread_create"); + if(status) { + free_threadInfo(pthreadInfo); + return 0; + } + status = pthread_sigmask(SIG_SETMASK,&oldSig,NULL); + checkStatusOnce(status,"pthread_sigmask"); + return(pthreadInfo); +} + +/* + * Create dummy context for threads not created by epicsThreadCreate(). + */ +static epicsThreadOSD *createImplicit(void) +{ + epicsThreadOSD *pthreadInfo; + char name[64]; + pthread_t tid; + int status; + + tid = pthread_self(); + sprintf(name, "non-EPICS_%ld", (long)tid); + pthreadInfo = create_threadInfo(name); + assert(pthreadInfo); + pthreadInfo->tid = tid; + pthreadInfo->osiPriority = 0; + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + { + struct sched_param param; + int policy; + if(pthread_getschedparam(tid,&policy,¶m) == 0) + pthreadInfo->osiPriority = + (param.sched_priority - pcommonAttr->minPriority) * 100.0 / + (pcommonAttr->maxPriority - pcommonAttr->minPriority + 1); + } +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); + checkStatus(status,"pthread_setspecific createImplicit"); + if(status){ + free_threadInfo(pthreadInfo); + return NULL; + } + return pthreadInfo; +} + +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void) +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + pthreadInfo->isSuspended = 1; + epicsEventWait(pthreadInfo->suspendEvent); +} + +epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadOSD *pthreadInfo) +{ + assert(epicsThreadOnceCalled); + pthreadInfo->isSuspended = 0; + epicsEventSignal(pthreadInfo->suspendEvent); +} + +epicsShareFunc void epicsShareAPI epicsThreadExitMain(void) +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + if(pthreadInfo->createFunc) { + errlogPrintf("called from non-main thread\n"); + cantProceed("epicsThreadExitMain"); + } + else { + free_threadInfo(pthreadInfo); + pthread_exit(0); + } +} + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(epicsThreadId pthreadInfo) +{ + assert(epicsThreadOnceCalled); + return(pthreadInfo->osiPriority); +} + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void) +{ + epicsThreadInit(); + return(epicsThreadGetPriority(epicsThreadGetIdSelf())); +} + +epicsShareFunc void epicsShareAPI epicsThreadSetPriority(epicsThreadId pthreadInfo,unsigned int priority) +{ +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + int status; +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + assert(epicsThreadOnceCalled); + assert(pthreadInfo); + if(!pthreadInfo->isEpicsThread) { + fprintf(stderr,"epicsThreadSetPriority called by non epics thread\n"); + return; + } + pthreadInfo->osiPriority = priority; + if(!pthreadInfo->isRealTimeScheduled) return; + +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + if(!pcommonAttr->usePolicy) return; + pthreadInfo->schedParam.sched_priority = epicsThreadGetPosixPriority(pthreadInfo); + status = pthread_attr_setschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + if(errVerbose) checkStatus(status,"pthread_attr_setschedparam"); + status = pthread_setschedparam( + pthreadInfo->tid, pthreadInfo->schedPolicy, &pthreadInfo->schedParam); + if(errVerbose) checkStatus(status,"pthread_setschedparam"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +} + +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow( + unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + int diff; + diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; + if(diff<0) diff = -diff; + if(diff>1 && diff <100) newPriority -= 100/(diff+1); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove( + unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 + int diff; + diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; + if(diff<0) diff = -diff; + if(diff>1 && diff <100) newPriority += 100/(diff+1); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsShareFunc int epicsShareAPI epicsThreadIsEqual(epicsThreadId p1, epicsThreadId p2) +{ + assert(epicsThreadOnceCalled); + assert(p1); + assert(p2); + return(pthread_equal(p1->tid,p2->tid)); +} + +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId pthreadInfo) { + assert(epicsThreadOnceCalled); + assert(pthreadInfo); + return(pthreadInfo->isSuspended ? 1 : 0); +} + +epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds) +{ + struct timespec delayTime; + struct timespec remainingTime; + double nanoseconds; + + if (seconds > 0) { + delayTime.tv_sec = seconds; + nanoseconds = (seconds - delayTime.tv_sec) *1e9; + delayTime.tv_nsec = nanoseconds; + } + else { + delayTime.tv_sec = 0; + delayTime.tv_nsec = 0; + } + while (nanosleep(&delayTime, &remainingTime) == -1 && + errno == EINTR) + delayTime = remainingTime; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void) { + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + assert ( pthreadInfo ); + return(pthreadInfo); +} + +epicsShareFunc pthread_t epicsThreadGetPosixThreadId ( epicsThreadId threadId ) +{ + return threadId->tid; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name) { + epicsThreadOSD *pthreadInfo; + int status; + + assert(epicsThreadOnceCalled); + status = mutexLock(&listLock); + checkStatus(status,"pthread_mutex_lock epicsThreadGetId"); + if(status) + return NULL; + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + if(strcmp(name,pthreadInfo->name) == 0) break; + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatus(status,"pthread_mutex_unlock epicsThreadGetId"); + + return(pthreadInfo); +} + +epicsShareFunc const char epicsShareAPI *epicsThreadGetNameSelf() +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + return(pthreadInfo->name); +} + +epicsShareFunc void epicsShareAPI epicsThreadGetName(epicsThreadId pthreadInfo, char *name, size_t size) +{ + assert(epicsThreadOnceCalled); + strncpy(name, pthreadInfo->name, size-1); + name[size-1] = '\0'; +} + +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) +{ + epicsThreadOSD *pthreadInfo; + int status; + + epicsThreadInit(); + status = mutexLock(&listLock); + checkStatus(status, "pthread_mutex_lock epicsThreadMap"); + if (status) + return; + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while (pthreadInfo) { + func(pthreadInfo); + pthreadInfo = (epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatus(status, "pthread_mutex_unlock epicsThreadMap"); +} + +epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) +{ + epicsThreadOSD *pthreadInfo; + int status; + + epicsThreadInit(); + epicsThreadShow(0,level); + status = mutexLock(&listLock); + checkStatus(status,"pthread_mutex_lock epicsThreadShowAll"); + if(status) + return; + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + epicsThreadShowInfo(pthreadInfo,level); + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll"); +} + +epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsigned int level) +{ + epicsThreadOSD *pthreadInfo; + int status; + int found = 0; + + epicsThreadInit(); + if(!showThread) { + epicsThreadShowInfo(0,level); + return; + } + status = mutexLock(&listLock); + checkStatus(status,"pthread_mutex_lock epicsThreadShowAll"); + if(status) + return; + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + if (((epicsThreadId)pthreadInfo == showThread) + || ((epicsThreadId)pthreadInfo->tid == showThread)) { + found = 1; + epicsThreadShowInfo(pthreadInfo,level); + } + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatus(status,"pthread_mutex_unlock epicsThreadShowAll"); + if(status) return; + if (!found) + printf("Thread %#lx (%lu) not found.\n", (unsigned long)showThread, (unsigned long)showThread); +} + +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void) +{ + pthread_key_t *key; + int status; + + epicsThreadInit(); + key = calloc(1,sizeof(pthread_key_t)); + if(!key) + return NULL; + status = pthread_key_create(key,0); + checkStatus(status,"pthread_key_create epicsThreadPrivateCreate"); + if(status) + return NULL; + return((epicsThreadPrivateId)key); +} + +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id) +{ + pthread_key_t *key = (pthread_key_t *)id; + int status; + + assert(epicsThreadOnceCalled); + status = pthread_key_delete(*key); + checkStatusQuit(status,"pthread_key_delete","epicsThreadPrivateDelete"); + free((void *)key); +} + +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet (epicsThreadPrivateId id, void *value) +{ + pthread_key_t *key = (pthread_key_t *)id; + int status; + + assert(epicsThreadOnceCalled); + if(errVerbose && !value) + errlogPrintf("epicsThreadPrivateSet: setting value of 0\n"); + status = pthread_setspecific(*key,value); + checkStatusQuit(status,"pthread_setspecific","epicsThreadPrivateSet"); +} + +epicsShareFunc void epicsShareAPI *epicsThreadPrivateGet(epicsThreadPrivateId id) +{ + pthread_key_t *key = (pthread_key_t *)id; + + assert(epicsThreadOnceCalled); + return pthread_getspecific(*key); +} + +epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum () +{ + double hz; + hz = sysconf ( _SC_CLK_TCK ); + if(hz<0) + return 0.0; + return 1.0 / hz; +} + +epicsShareFunc int epicsThreadGetCPUs(void) +{ + long ret; +#ifdef _SC_NPROCESSORS_ONLN + ret = sysconf(_SC_NPROCESSORS_ONLN); + if (ret > 0) + return ret; +#endif +#ifdef _SC_NPROCESSORS_CONF + ret = sysconf(_SC_NPROCESSORS_CONF); + if (ret > 0) + return ret; +#endif + return 1; +} diff --git a/modules/libcom/src/osi/os/posix/osdThread.h b/modules/libcom/src/osi/os/posix/osdThread.h new file mode 100644 index 000000000..3a80b537c --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdThread.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef osdThreadh +#define osdThreadh + +#include + +#include "shareLib.h" +#include "ellLib.h" +#include "epicsEvent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct epicsThreadOSD { + ELLNODE node; + pthread_t tid; + pthread_attr_t attr; + struct sched_param schedParam; + int schedPolicy; + EPICSTHREADFUNC createFunc; + void *createArg; + epicsEventId suspendEvent; + int isSuspended; + int isEpicsThread; + int isRealTimeScheduled; + int isOnThreadList; + unsigned int osiPriority; + char name[1]; /* actually larger */ +} epicsThreadOSD; + +epicsShareFunc pthread_t epicsThreadGetPosixThreadId(epicsThreadId id); +epicsShareFunc int epicsThreadGetPosixPriority(epicsThreadId id); + +#ifdef __cplusplus +} +#endif + +#endif /* osdThreadh */ diff --git a/modules/libcom/src/osi/os/posix/osdThreadExtra.c b/modules/libcom/src/osi/os/posix/osdThreadExtra.c new file mode 100644 index 000000000..af622dc08 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdThreadExtra.c @@ -0,0 +1,45 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This is part of the posix implementation of epicsThread */ + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" + +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; + +void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level) +{ + if(!pthreadInfo) { + fprintf(epicsGetStdout()," NAME EPICS ID " + "PTHREAD ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int policy; + int priority = 0; + + if(pthreadInfo->tid) { + int status; + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); + if(!status) priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->tid, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended?"SUSPEND":"OK"); + } +} + diff --git a/modules/libcom/src/osi/os/posix/osdTime.cpp b/modules/libcom/src/osi/os/posix/osdTime.cpp new file mode 100644 index 000000000..1952babd9 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdTime.cpp @@ -0,0 +1,127 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "osiSock.h" + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +#ifdef CLOCK_REALTIME + #include "osiClockTime.h" + + #define TIME_INIT ClockTime_Init(CLOCKTIME_NOSYNC) +#else + /* Some posix systems may not have CLOCK_REALTIME */ + + #define TIME_INIT generalTimeCurrentTpRegister("GetTimeOfDay", \ + LAST_RESORT_PRIORITY, osdTimeGetCurrent) + + extern "C" { + static int osdTimeGetCurrent (epicsTimeStamp *pDest) + { + struct timeval tv; + struct timezone tz; + + if (gettimeofday (&tv, &tz)) + return errno; + + *pDest = epicsTime(tv); + return epicsTimeOK; + } + } // extern "C" +#endif + +#ifdef __CYGWIN__ +int clock_settime(clockid_t clock, const timespec *tp) +{ + return -EFAULT; +} +#endif + + +static int timeRegister(void) +{ + TIME_INIT; + + osdMonotonicInit(); + return 1; +} +static int done = timeRegister(); + + +int epicsTime_gmtime ( const time_t *pAnsiTime, + struct tm *pTM ) +{ + struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return errno; + } +} + +int epicsTime_localtime ( const time_t *clock, + struct tm *result ) +{ + struct tm * pRet = localtime_r ( clock, result ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return errno; + } +} + +extern "C" epicsShareFunc void + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime) +{ + struct timespec now, wait; + int status; + + if (timeout < 0.0) + timeout = 0.0; + else if (timeout > 60 * 60 * 24 * 3652.5) + timeout = 60 * 60 * 24 * 3652.5; /* 10 years */ + +#ifdef CLOCK_REALTIME + status = clock_gettime(CLOCK_REALTIME, &now); +#else + { + struct timeval tv; + struct timezone tz; + status = gettimeofday(&tv, &tz); + now.tv_sec = tv.tv_sec; + now.tv_nsec = tv.tv_usec * 1000; + } +#endif + if (status) { + perror("convertDoubleToWakeTime"); + cantProceed("convertDoubleToWakeTime"); + } + + wait.tv_sec = static_cast< time_t >(timeout); + wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9); + + wakeTime->tv_sec = now.tv_sec + wait.tv_sec; + wakeTime->tv_nsec = now.tv_nsec + wait.tv_nsec; + if (wakeTime->tv_nsec >= 1000000000L) { + wakeTime->tv_nsec -= 1000000000L; + ++wakeTime->tv_sec; + } +} diff --git a/modules/libcom/src/osi/os/posix/osdTime.h b/modules/libcom/src/osi/os/posix/osdTime.h new file mode 100644 index 000000000..d1da77696 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osdTime.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeff Hill + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#if !defined(_POSIX_TIMERS) || _POSIX_TIMERS < 0 + struct timespec { + time_t tv_sec; /* seconds since some epoch */ + long tv_nsec; /* nanoseconds within the second */ + }; +#endif /* !_POSIX_TIMERS */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/modules/libcom/src/osi/os/posix/osiUnistd.h b/modules/libcom/src/osi/os/posix/osiUnistd.h new file mode 100644 index 000000000..5121261b6 --- /dev/null +++ b/modules/libcom/src/osi/os/posix/osiUnistd.h @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include diff --git a/modules/libcom/src/osi/os/posix/systemCallIntMech.cpp b/modules/libcom/src/osi/os/posix/systemCallIntMech.cpp new file mode 100644 index 000000000..cdac1d0cb --- /dev/null +++ b/modules/libcom/src/osi/os/posix/systemCallIntMech.cpp @@ -0,0 +1,22 @@ + +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeff Hill + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ + return esscimqi_socketBothShutdownRequired; +} diff --git a/modules/libcom/src/osi/os/solaris/epicsAtomicOSD.h b/modules/libcom/src/osi/os/solaris/epicsAtomicOSD.h new file mode 100644 index 000000000..1fa23bd88 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/epicsAtomicOSD.h @@ -0,0 +1,185 @@ + +/*************************************************************************\ +* Copyright (c) 2011 LANS LLC, as Operator of +* Los Alamos National Laboratory. +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include "shareLib.h" + +#ifndef epicsAtomicOSD_h +#define epicsAtomicOSD_h + +#define EPICS_ATOMIC_OS_NAME "Solaris" + +#if defined ( __SunOS_5_10 ) + +/* + * atomic.h exists only in Solaris 10 or higher + */ +#include + +#include "epicsAssert.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + membar_consumer (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + membar_producer (); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_INTT +#define EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ) +{ + STATIC_ASSERT ( sizeof ( int ) == sizeof ( unsigned ) ); + unsigned * const pTarg = ( unsigned * ) pTarget; + return ( int ) atomic_cas_uint ( pTarg, ( unsigned ) oldVal, + ( unsigned ) newVal ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_SIZET +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( + size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_cas_ulong ( pTarg, oldVal, newVal ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_PTRT +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( + EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, + EpicsAtomicPtrT newVal ) +{ + return atomic_cas_ptr ( pTarget, oldVal, newVal ); +} +#endif + +#ifndef EPICS_ATOMIC_INCR_INTT +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_inc_uint_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_INCR_SIZET +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_inc_ulong_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_INTT +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_dec_uint_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_dec_ulong_nv ( pTarg ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_INTT +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + STATIC_ASSERT ( sizeof ( unsigned ) == sizeof ( int ) ); + unsigned * const pTarg = ( unsigned * ) ( pTarget ); + return ( int ) atomic_add_int_nv ( pTarg, delta ); +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, + size_t delta ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + return ( size_t ) atomic_add_long_nv ( pTarg, ( long ) delta ); +} +#endif + +#ifndef EPICS_ATOMIC_SUB_SIZET +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, + size_t delta ) +{ + STATIC_ASSERT ( sizeof ( ulong_t ) == sizeof ( size_t ) ); + ulong_t * const pTarg = ( ulong_t * ) pTarget; + long sdelta = ( long ) delta; + return ( size_t ) atomic_add_long_nv ( pTarg, -sdelta ); +} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* ifdef __SunOS_5_10 */ + +typedef struct EpicsAtomicLockKey { + char dummy; +} EpicsAtomicLockKey; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsAtomicLock ( struct EpicsAtomicLockKey * ); +epicsShareFunc void epicsAtomicUnlock ( struct EpicsAtomicLockKey * ); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/modules/libcom/src/osi/os/solaris/epicsMath.h b/modules/libcom/src/osi/os/solaris/epicsMath.h new file mode 100644 index 000000000..87d4986a7 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/epicsMath.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne, LLC as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsMath_H +#define INC_epicsMath_H + +#include +#include +#include + +#ifndef isinf +# define isinf(x) (((x)==(x)) && !finite((x))) +/* same as (!isnan(x) && !finite(x)) */ +#endif + +#ifndef isnan +# define isnan(x) ((x) != (x)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsMath_H */ diff --git a/modules/libcom/src/osi/os/solaris/osdBackTrace.cpp b/modules/libcom/src/osi/os/solaris/osdBackTrace.cpp new file mode 100644 index 000000000..55d5e3648 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdBackTrace.cpp @@ -0,0 +1,46 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2014 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsStackTracePvt.h" + +struct wlk { + void **buf; + int max; + int cur; +}; + + +extern "C" { + +static int +walker(uintptr_t addr, int sig, void *arg) +{ +struct wlk *w_p = (struct wlk *)arg; + if ( w_p->cur < w_p->max ) + w_p->buf[w_p->cur++] = (void*)addr; + return 0; +} + +} + +int epicsBackTrace(void **buf, int buf_sz) +{ +ucontext_t u; +struct wlk d; + d.buf = buf; + d.max = buf_sz; + d.cur = 0; + if ( getcontext(&u) ) + return -1; + walkcontext( &u, walker, &d ); + return d.cur; +} diff --git a/modules/libcom/src/osi/os/solaris/osdFindAddr.c b/modules/libcom/src/osi/os/solaris/osdFindAddr.c new file mode 100644 index 000000000..84d17d96f --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdFindAddr.c @@ -0,0 +1,10 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2011, 2014 + */ + +#include "osdElfFindAddr.c" diff --git a/modules/libcom/src/osi/os/solaris/osdSock.h b/modules/libcom/src/osi/os/solaris/osdSock.h new file mode 100644 index 000000000..96ea2a983 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdSock.h @@ -0,0 +1,89 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Solaris specific socket include + */ + +#ifndef osdSockH +#define osdSockH + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; + +#if SOLARIS > 6 || defined ( _SOCKLEN_T ) + typedef uint32_t osiSocklen_t; +#else + typedef int osiSocklen_t; +#endif +typedef char osiSockOptMcastLoop_t; + +#define DOES_NOT_ACCEPT_ZERO_LENGTH_UDP + +#define FD_IN_FDSET(FD) ((FD)=0) + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EACCES EACCES +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/solaris/osdStrtod.h b/modules/libcom/src/osi/os/solaris/osdStrtod.h new file mode 100644 index 000000000..b5fda31c3 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdStrtod.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); + +#ifdef __cplusplus +} +#endif diff --git a/modules/libcom/src/osi/os/solaris/osdWireConfig.h b/modules/libcom/src/osi/os/solaris/osdWireConfig.h new file mode 100644 index 000000000..277fe395e --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdWireConfig.h @@ -0,0 +1,26 @@ +/* + * Solaris version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +#include + +#if defined ( _LITTLE_ENDIAN ) +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#elif defined ( _BIG_ENDIAN ) +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +#else +# error EPICS hasnt been ported to byte order specified by on Solaris +#endif + +/* for now, assume that Solaris doesnt run on weird arch like ARM NWFP */ +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ + diff --git a/modules/libcom/src/osi/os/solaris/osiFileName.h b/modules/libcom/src/osi/os/solaris/osiFileName.h new file mode 100644 index 000000000..b79203992 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osiFileName.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/vxWorks/atReboot.cpp b/modules/libcom/src/osi/os/vxWorks/atReboot.cpp new file mode 100644 index 000000000..2f755c581 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/atReboot.cpp @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* atReboot.cpp */ + +/* Author: Marty Kraimer Date: 30AUG2003 */ + +#include + +#include "epicsFindSymbol.h" +#include "epicsExit.h" + +extern "C" { + +typedef int (*sysAtReboot_t)(void(func)(void)); + +void atRebootRegister(void) +{ + sysAtReboot_t sysAtReboot = (sysAtReboot_t) epicsFindSymbol("_sysAtReboot"); + + if (sysAtReboot) { + STATUS status = sysAtReboot(epicsExitCallAtExits); + + if (status) { + printf("atReboot: sysAtReboot returned error %d\n", status); + } + } else { + printf("BSP routine sysAtReboot() not found, epicsExit() will not be\n" + "called by reboot. For reduced functionality, call\n" + " rebootHookAdd(epicsExitCallAtExits)\n"); + } +} + +} diff --git a/modules/libcom/src/osi/os/vxWorks/camacLib.h b/modules/libcom/src/osi/os/vxWorks/camacLib.h new file mode 100644 index 000000000..a30ab5659 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/camacLib.h @@ -0,0 +1,57 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* camacLib.h -- Prototypes for camacLib.o + * + * Marty Wise + * 10/11/93 + * + */ + +/********************************/ +/* GLOBAL DATA */ +/********************************/ +extern int debug_hook; + +extern struct glob_dat { + int total; + int read_error[5]; + int write_error[5]; + int cmd_error[5]; + int total_err; + int lam_count[12]; +} debug_dat; + + +/********************************/ +/* FUNCTION PROTOTYPES */ +/********************************/ + +void cdreg(int *ext, int b, int c, int n, int a); +void cfsa(int f, int ext, int *dat, int *q); +void cssa(int f, int ext, short *dat, int *q); +void ccci(int ext, int l); +void cccz(int ext); +void cccc(int ext); +void ccinit(int b); +void ctci(int ext, int *l); +void cgreg(int ext, int *b, int *c, int *n, int *a); +void cfmad(int f, int extb[2], int *intc, int cb[4]); +void cfubc(int f, int ext, int *intc, int cb[4]); +void cfubc(int f, int ext, int *intc, int cb[4]); +void csmad(int f, int extb[2], short *intc, int cb[4]); +void ctcd(int ext, int *l); +void cccd(int ext, int l); +void csga(int fa[], int exta[], unsigned short intc[], int qa[], int cb[4]); +void cfga(int fa[], int exta[], int intc[], int qa[], int cb[4]); +void cfubr(int f, int ext, int intc[], int cb[4]); +void csubc(int f, int ext, unsigned short *intc, int cb[4]); +void csubr(int f, int ext, int intc[], int cb[4]); +void print_reg(int ext); + diff --git a/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c b/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c new file mode 100644 index 000000000..82bec603d --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/devLibVMEOSD.c @@ -0,0 +1,502 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Archictecture dependent support for common device driver resources + * + * Author: Jeff Hill + * Date: 10-30-98 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsFindSymbol.h" +#include "devLibVME.h" +#include "errlog.h" + +typedef void myISR (void *pParam); + +#if CPU_FAMILY != PPC +/* + * A list of the names of the unexpected interrupt handlers + * ( some of these are provided by wrs ) + */ +static char *defaultHandlerNames[] = { + "excStub", + "excIntStub", + "unsolicitedHandlerEPICS"}; + +static myISR *defaultHandlerAddr[NELEMENTS(defaultHandlerNames)]; +#endif + +static myISR *isrFetch(unsigned vectorNumber); + +/* + * this routine needs to be in the symbol table + * (i.e. not static) for this code to work correctly + */ +void unsolicitedHandlerEPICS(int vectorNumber); + +/* + * this is in veclist.c + */ +int cISRTest(void (*)(), void (**)(), void **); + +/* + * Make sure that the CR/CSR addressing mode is defined. + * (it may not be in older versions of vxWorks) + */ +#ifndef VME_AM_CSR +# define VME_AM_CSR (0x2f) +#endif + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ + +#define EPICSAddrTypeNoConvert -1 + +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert, + VME_AM_CSR + }; + +#if CPU_FAMILY != PPC +static void initHandlerAddrList(void); +#endif + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +static long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +static void *devA24Malloc(size_t size); +static void devA24Free(void *pBlock); +static long devInit(void) { return 0;} + +static long vxDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter); + +static long vxDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +); + +static long vxDevEnableInterruptLevelVME (unsigned level); + +static long vxDevDisableInterruptLevelVME (unsigned level); + +static int vxDevInterruptInUseVME (unsigned vectorNumber); + +/* + * used by dynamic bind in devLib.c + */ +static devLibVME vxVirtualOS = { + vxDevMapAddr, vxDevReadProbe, vxDevWriteProbe, + vxDevConnectInterruptVME, vxDevDisconnectInterruptVME, + vxDevEnableInterruptLevelVME, vxDevDisableInterruptLevelVME, + devA24Malloc,devA24Free,devInit,vxDevInterruptInUseVME +}; +devLibVME *pdevLibVME = &vxVirtualOS; + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on vxWorks + */ +static long vxDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + pFunction, + (int) parameter); + if (status<0) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * vxDevDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on vxWorks + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +static long vxDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + int status; + +# if CPU_FAMILY == PPC + return S_dev_vecInstlFail; +# endif + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + unsolicitedHandlerEPICS, + (int) vectorNumber); + if(status<0){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +static long vxDevEnableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntEnable (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * enable ISA interrupt level + */ +long devEnableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntEnablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * disable ISA interrupt level + */ +long devDisableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntDisablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } +# else + return S_dev_intEnFail; +# endif + + return 0; +} + +/* + * disable VME interrupt level + */ +static long vxDevDisableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntDisable (level); + if (s!=OK) { + return S_dev_intDissFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * vxDevMapAddr () + */ +static long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = sysBusToLocalAdrs (EPICStovxWorksAddrType[addrType], + (char *) logicalAddress, (char **)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isn't present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + status = vxMemProbe ((char *)ptr, VX_READ, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isn't present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + status = vxMemProbe ((char *)ptr, VX_WRITE, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +static myISR *isrFetch(unsigned vectorNumber) +{ + myISR *psub; + myISR *pCISR; + void *pParam; + int s; + + /* + * fetch the handler or C stub attached at this vector + */ + psub = (myISR *) intVecGet((FUNCPTR *)INUM_TO_IVEC(vectorNumber)); + + if ( psub ) { + /* + * from libvxWorks/veclist.c + * + * checks to see if it is a C ISR + * and if so finds the function pointer and + * the parameter passed + */ + s = cISRTest(psub, &pCISR, &pParam); + if(!s){ + psub = pCISR; + } + } + + return psub; +} + +/* + * determine if a VME interrupt vector is in use + */ +static int vxDevInterruptInUseVME (unsigned vectorNumber) +{ +#if CPU_FAMILY == PPC + return FALSE; +#else + { + static int init; + int i; + myISR *psub; + + if (!init) { + initHandlerAddrList(); + init = TRUE; + } + + psub = isrFetch (vectorNumber); + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i +#endif + +#include "vxWorks.h" /* obtain the version of vxWorks */ +#include "epicsAssert.h" + +/* + * With vxWorks 6.6 and later we need to use vxAtomicLib + * to implement this functionality correctly on SMP systems + */ +#if _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 + +#include +#include + +#define EPICS_ATOMIC_OS_NAME "VX-ATOMICLIB" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) +{ + VX_MEM_BARRIER_R (); +} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) +{ + VX_MEM_BARRIER_W (); +} +#endif + +/* + * we make the probably correct guess that if ULONG_MAX + * is the same as UINT_MAX then sizeof ( atomic_t ) + * will be the same as sizeof ( size_t ) + * + * if ULONG_MAX != UINT_MAX then its 64 bit vxWorks and + * WRS doesnt not supply at this time the atomic interface + * for 8 byte integers that is needed - so that architecture + * receives the lock synchronized version + */ +#if ULONG_MAX == UINT_MAX + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( size_t ) ); +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( EpicsAtomicPtrT ) ); + + +#ifndef EPICS_ATOMIC_INCR_SIZET +#define EPICS_ATOMIC_INCR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicIncrSizeT ( size_t * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicInc ( pTarg ); + return 1 + ( size_t ) ( oldVal ); +} +#endif + +#ifndef EPICS_ATOMIC_DECR_SIZET +#define EPICS_ATOMIC_DECR_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicDecrSizeT ( size_t * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicDec ( pTarg ); + return ( ( size_t ) oldVal ) - 1u; +} +#endif + +#ifndef EPICS_ATOMIC_ADD_SIZET +#define EPICS_ATOMIC_ADD_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicAddSizeT ( size_t * pTarget, size_t delta ) +{ + /* + * vxAtomicLib doc indicates that vxAtomicAdd is + * implemented using signed arithmetic, but it + * does not change the end result because twos + * complement addition is used in either case + */ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta ); + return delta + ( size_t ) oldVal; +} +#endif + +#ifndef EPICS_ATOMIC_SUB_SIZET +#define EPICS_ATOMIC_SUB_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicSubSizeT ( size_t * pTarget, size_t delta ) +{ + /* + * vxAtomicLib doc indicates that vxAtomicSub is + * implemented using signed arithmetic, but it + * does not change the end result because twos + * complement subtraction is used in either case + */ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicSub ( pTarg, (atomic_t) delta ); + return ( ( size_t ) oldVal ) - delta; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_SIZET +#define EPICS_ATOMIC_CAS_SIZET +EPICS_ATOMIC_INLINE size_t epicsAtomicCmpAndSwapSizeT ( size_t * pTarget, + size_t oldVal, size_t newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return ( size_t ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} +#endif + +#ifndef EPICS_ATOMIC_CAS_PTRT +#define EPICS_ATOMIC_CAS_PTRT +EPICS_ATOMIC_INLINE EpicsAtomicPtrT epicsAtomicCmpAndSwapPtrT ( EpicsAtomicPtrT * pTarget, + EpicsAtomicPtrT oldVal, EpicsAtomicPtrT newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return (EpicsAtomicPtrT) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} +#endif + +#else /* ULONG_MAX == UINT_MAX */ + +/* + * if its 64 bit SMP vxWorks and the compiler doesnt + * have an intrinsic then maybe there isnt any way to + * implement these without using a global lock because + * size_t is maybe bigger than atomic_t + * + * I dont yet have access to vxWorks manuals for + * 64 bit systems so this is still undecided, but is + * defaulting now to a global lock + */ + +#endif /* ULONG_MAX == UINT_MAX */ + +STATIC_ASSERT ( sizeof ( atomic_t ) == sizeof ( int ) ); + +#ifndef EPICS_ATOMIC_INCR_INTT +#define EPICS_ATOMIC_INCR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicIncrIntT ( int * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicInc ( pTarg ); + return 1 + ( int ) oldVal; +} +#endif + +#ifndef EPICS_ATOMIC_DECR_INTT +#define EPICS_ATOMIC_DECR_INTT +EPICS_ATOMIC_INLINE int epicsAtomicDecrIntT ( int * pTarget ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicDec ( pTarg ); + return ( ( int ) oldVal ) - 1; +} +#endif + +#ifndef EPICS_ATOMIC_ADD_INTT +#define EPICS_ATOMIC_ADD_INTT +EPICS_ATOMIC_INLINE int epicsAtomicAddIntT ( int * pTarget, int delta ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + const atomic_t oldVal = vxAtomicAdd ( pTarg, (atomic_t) delta ); + return delta + ( int ) oldVal; +} +#endif + +#ifndef EPICS_ATOMIC_CAS_INTT +#define EPICS_ATOMIC_CAS_INTT +EPICS_ATOMIC_INLINE int epicsAtomicCmpAndSwapIntT ( int * pTarget, + int oldVal, int newVal ) +{ + atomic_t * const pTarg = ( atomic_t * ) ( pTarget ); + return ( int ) vxCas ( pTarg, (atomic_t) oldVal, (atomic_t) newVal ); +} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#else /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#include "vxLib.h" +#include "intLib.h" + +#define EPICS_ATOMIC_OS_NAME "VX-INTLIB" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef EPICS_ATOMIC_LOCK +#define EPICS_ATOMIC_LOCK + +typedef struct EpicsAtomicLockKey { int m_key; } EpicsAtomicLockKey; + +EPICS_ATOMIC_INLINE void epicsAtomicLock ( EpicsAtomicLockKey * pKey ) +{ + pKey->m_key = intLock (); +} + +EPICS_ATOMIC_INLINE void epicsAtomicUnlock ( EpicsAtomicLockKey * pKey ) +{ + intUnlock ( pKey->m_key ); +} +#endif + +#ifndef EPICS_ATOMIC_READ_MEMORY_BARRIER +#define EPICS_ATOMIC_READ_MEMORY_BARRIER +/* + * no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system + * (we are not protecting against multiple access to memory mapped IO) + */ +EPICS_ATOMIC_INLINE void epicsAtomicReadMemoryBarrier (void) {} +#endif + +#ifndef EPICS_ATOMIC_WRITE_MEMORY_BARRIER +#define EPICS_ATOMIC_WRITE_MEMORY_BARRIER +/* + * no need for memory barrior since prior to vxWorks 6.6 it is a single cpu system + * (we are not protecting against multiple access to memory mapped IO) + */ +EPICS_ATOMIC_INLINE void epicsAtomicWriteMemoryBarrier (void) {} +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif /* _WRS_VXWORKS_MAJOR * 100 + _WRS_VXWORKS_MINOR >= 606 */ + +#include "epicsAtomicDefault.h" + +#endif /* epicsAtomicOSD_h */ + diff --git a/modules/libcom/src/osi/os/vxWorks/epicsDynLink.c b/modules/libcom/src/osi/os/vxWorks/epicsDynLink.c new file mode 100644 index 000000000..df954782a --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/epicsDynLink.c @@ -0,0 +1,94 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * On 68K targets, all symbols have an underscore prepended to their name. + * This code permits both standards to work, as long as you're not looking + * for a symbol name that actually begins with an underscore. + */ + +#include + +#include "epicsDynLink.h" + +#if _WRS_VXWORKS_MAJOR < 6 || _WRS_VXWORKS_MINOR < 9 + +static int symNoUnderscore(SYMTAB_ID symTblId) +{ + static int init = 0; + static int noUnderscore = 0; + + if (!init) { + char name[] = "symFindByNameEPICS"; + char *pSymValue; + SYM_TYPE type; + + if (symFindByName(symTblId, name, &pSymValue, &type) == OK) + noUnderscore = 1; + init = 1; + } + return noUnderscore; +} + +STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, + SYM_TYPE *pType) +{ + if (name[0] == '_' && symNoUnderscore(symTblId)) + name++; + + return symFindByName(symTblId, name, ppvalue, pType); +} + +STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, + char **ppvalue, SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) +{ + if (name[0] == '_' && symNoUnderscore(symTblId)) + name++; + + return symFindByNameAndType(symTblId, name, ppvalue, pType, sType, mask); +} + +#else /* VxWorks 6.9 deprecated the symFindBy routines */ + +STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **ppvalue, + SYM_TYPE *pType) +{ + SYMBOL_DESC symDesc; + STATUS status; + + memset(&symDesc, 0, sizeof(SYMBOL_DESC)); + symDesc.mask = SYM_FIND_BY_NAME; + symDesc.name = name + (name[0] == '_'); + status = symFind(sysSymTbl, &symDesc); + if (!status) { + *ppvalue = symDesc.value; + *pType = symDesc.type; + } + return status; +} + +STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, + char **ppvalue, SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) +{ + SYMBOL_DESC symDesc; + STATUS status; + + memset(&symDesc, 0, sizeof(SYMBOL_DESC)); + symDesc.mask = SYM_FIND_BY_NAME | SYM_FIND_BY_TYPE; + symDesc.name = name + (name[0] == '_'); + symDesc.type = sType; + symDesc.typeMask = mask; + status = symFind(sysSymTbl, &symDesc); + if (!status) { + *ppvalue = symDesc.value; + *pType = symDesc.type; + } + return status; +} + +#endif diff --git a/modules/libcom/src/osi/os/vxWorks/epicsDynLink.h b/modules/libcom/src/osi/os/vxWorks/epicsDynLink.h new file mode 100644 index 000000000..581260a49 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/epicsDynLink.h @@ -0,0 +1,40 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * These routines will eventually need to be made OS independent + * (currently this is vxWorks specific) + */ + +#ifndef epicsDynLinkh +#define epicsDynLinkh + +#include "vxWorks.h" +#include "symLib.h" +#include "sysSymTbl.h" +#include "compilerDependencies.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Use epicsFindSymbol() instead of these */ + +STATUS symFindByNameEPICS(SYMTAB_ID symTblId, char *name, char **pvalue, + SYM_TYPE *pType) EPICS_DEPRECATED; + +STATUS symFindByNameAndTypeEPICS(SYMTAB_ID symTblId, char *name, char **pvalue, + SYM_TYPE *pType, SYM_TYPE sType, SYM_TYPE mask) EPICS_DEPRECATED; + +#ifdef __cplusplus +} +#endif + +#endif /* ifdef epicsDynLinkh */ + diff --git a/modules/libcom/src/osi/os/vxWorks/epicsMMIO.h b/modules/libcom/src/osi/os/vxWorks/epicsMMIO.h new file mode 100644 index 000000000..b12c71ad9 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/epicsMMIO.h @@ -0,0 +1,174 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2006 The Regents of the University of California, +* as Operator of Los Alamos National Laboratory. +* Copyright (c) 2006 The Board of Trustees of the Leland Stanford Junior +* University, as Operator of the Stanford Linear Accelerator Center. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Original Author: Eric Bjorklund (was called mrfSyncIO.h) + * Author: Michael Davidsaver + */ + +#ifndef EPICSMMIO_H +#define EPICSMMIO_H + +#if (CPU_FAMILY != PPC) && (CPU_FAMILY != I80X86) +# include "epicsMMIODef.h" +#else + +/**************************************************************************************************/ +/* Required Header Files */ +/**************************************************************************************************/ + +/* This is needed on vxWorks 6.8 */ +#ifndef _VSB_CONFIG_FILE +# define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> +#endif + +#include /* vxWorks common definitions */ +#include /* vxWorks System Library Definitions */ +#include /* vxWorks Version Definitions */ + +#include /* EPICS Common Type Definitions */ +#include /* EPICS Byte Order Definitions */ +#include + +/*===================== + * vxAtomicLib.h (which defines the memory barrier macros) + * is available on vxWorks 6.6 and above. + */ + +#if _WRS_VXWORKS_MAJOR > 6 +# include +#elif _WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR >= 6 +# include +#endif + +static EPICS_ALWAYS_INLINE +epicsUInt16 +bswap16(epicsUInt16 value) +{ + return (((epicsUInt16)(value) & 0x00ff) << 8) | + (((epicsUInt16)(value) & 0xff00) >> 8); +} + +static EPICS_ALWAYS_INLINE +epicsUInt32 +bswap32(epicsUInt32 value) +{ + return (((epicsUInt32)(value) & 0x000000ff) << 24) | + (((epicsUInt32)(value) & 0x0000ff00) << 8) | + (((epicsUInt32)(value) & 0x00ff0000) >> 8) | + (((epicsUInt32)(value) & 0xff000000) >> 24); +} + +#if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG +# define be16_to_cpu(X) (epicsUInt16)(X) +# define be32_to_cpu(X) (epicsUInt32)(X) +# define le16_to_cpu(X) bswap16(X) +# define le32_to_cpu(X) bswap32(X) + +#elif EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE +# define be16_to_cpu(X) bswap16(X) +# define be32_to_cpu(X) bswap32(X) +# define le16_to_cpu(X) (epicsUInt16)(X) +# define le32_to_cpu(X) (epicsUInt32)(X) + +#else +# error Unable to determine native byte order +#endif + +#if CPU_FAMILY == PPC + +/* All PowerPC BSPs that I have studied implement these functions + * with the same definition, byte-swapping the data and adding a + * sync and/or eieio instruction as necessary on that CPU board. + * They do *not* all implement the sys{In/Out}{Byte/Word/Long} + * functions to do the same thing though, so we can't use them. + */ +#ifdef __cplusplus +extern "C" { +#endif +UINT8 sysPciInByte(UINT8 *addr); +void sysPciOutByte(UINT8 *addr, UINT8 data); +UINT16 sysPciInWord(UINT16 *addr); +void sysPciOutWord(UINT16 *addr, UINT16 data); +UINT32 sysPciInLong (UINT32 *addr); +void sysPciOutLong (UINT32 *addr, UINT32 data); +#ifdef __cplusplus +} +#endif + +#define ioread8(address) sysPciInByte((UINT8 *)(address)) +#define iowrite8(address,data) sysPciOutByte((UINT8 *)(address), (epicsUInt8)(data)) + +#define nat_ioread16(address) bswap16(sysPciInWord((UINT16 *)(address))) +#define nat_ioread32(address) bswap32(sysPciInLong((UINT32 *)(address))) + +#define nat_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), bswap16(data)) +#define nat_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), bswap32(data)) + +#define be_ioread16(address) bswap16(sysPciInWord((UINT16 *)(address))) +#define be_ioread32(address) bswap32(sysPciInLong((UINT32 *)(address))) + +#define be_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), bswap16(data)) +#define be_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), bswap32(data)) + +#define le_ioread16(address) sysPciInWord((UINT16 *)(address)) +#define le_ioread32(address) sysPciInLong((UINT32 *)(address)) + +#define le_iowrite16(address,data) sysPciOutWord((UINT16 *)(address), (data)) +#define le_iowrite32(address,data) sysPciOutLong((UINT32 *)(address), (data)) + +#else /* CPU_FAMILY == I80X86 */ + +/* All Intel BSPs should implement the sys{In/Out}{Byte/Word/Long} + * functions, which are declared in the sysLib.h header. + */ + +#define ioread8(address) sysInByte ((epicsUInt32)(address)) +#define iowrite8(address,data) sysOutByte ((epicsUInt32)(address), (epicsUInt8)(data)) + +#define nat_ioread16(address) sysInWord ((epicsUInt32)(address)) +#define nat_ioread32(address) sysInLong ((epicsUInt32)(address)) + +#define nat_iowrite16(address,data) sysOutWord((epicsUInt32)(address),(data)) +#define nat_iowrite32(address,data) sysOutLong((epicsUInt32)(address),(data)) + +#define be_ioread16(address) be16_to_cpu (sysInWord ((epicsUInt32)(address))) +#define be_ioread32(address) be32_to_cpu (sysInLong ((epicsUInt32)(address))) + +#define be_iowrite16(address,data) sysOutWord ((epicsUInt32)(address), be16_to_cpu((epicsUInt16)(data))) +#define be_iowrite32(address,data) sysOutLong ((epicsUInt32)(address), be32_to_cpu((epicsUInt32)(data))) + +#define le_ioread16(address) le16_to_cpu (sysInWord ((epicsUInt32)(address))) +#define le_ioread32(address) le32_to_cpu (sysInLong ((epicsUInt32)(address))) + +#define le_iowrite16(address,data) sysOutWord ((epicsUInt32)(address), le16_to_cpu((epicsUInt16)(data))) +#define le_iowrite32(address,data) sysOutLong ((epicsUInt32)(address), le32_to_cpu((epicsUInt32)(data))) + +#endif /* I80X86 */ + + +#ifndef VX_MEM_BARRIER_R +# define VX_MEM_BARRIER_R() do{}while(0) +#endif +#ifndef VX_MEM_BARRIER_W +# define VX_MEM_BARRIER_W() do{}while(0) +#endif +#ifndef VX_MEM_BARRIER_RW +# define VX_MEM_BARRIER_RW() do{}while(0) +#endif + +#define rbarr() VX_MEM_BARRIER_R() +#define wbarr() VX_MEM_BARRIER_W() +#define rwbarr() VX_MEM_BARRIER_RW() + +#endif /* CPU_FAMILY */ +#endif /* EPICSMMIO_H */ diff --git a/modules/libcom/src/osi/os/vxWorks/epicsMath.h b/modules/libcom/src/osi/os/vxWorks/epicsMath.h new file mode 100644 index 000000000..e973d2d9c --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/epicsMath.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include +#include + +/* private/mathP.h defines NAN as 4, and uses its value in the + * isNan() macro. We need mathP.h for isInf(), but can create + * our own isnan() test. epicsMath.cpp requires that NAN either + * be undef or yield the NaN value, so this solves the issue. + */ +#undef NAN + +#define isnan(D) (!(D == D)) +#define isinf(D) isInf(D) +#define finite(D) (!isnan(D) && !isInf(D)) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/modules/libcom/src/osi/os/vxWorks/logMsgToErrlog.cpp b/modules/libcom/src/osi/os/vxWorks/logMsgToErrlog.cpp new file mode 100644 index 000000000..b44881cd0 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/logMsgToErrlog.cpp @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * route vxWorks logMsg messages into the EPICS logging system + * + * Author: Jeff Hill + * + */ + +#include +#include + +#include +#include +#include + +#include "errlog.h" + +// vxCommonLibrary calls logMsgToErrlog so that logMsgToErrlog gets loaded +extern "C" { + int logMsgToErrlog(); +} + +int logMsgToErrlog() { return 0;} + +static class errlogDevTimeInit +{ +public: + errlogDevTimeInit (); +} errlogDevInstance; + +static int errlogOpen ( DEV_HDR *, const char *, int ) +{ + return OK; +} + +static int errlogWrite ( DEV_HDR *, const char * pInBuf, int nbytes ) +{ + errlogPrintfNoConsole ( "%.*s", nbytes, pInBuf ); + return nbytes; +} + +errlogDevTimeInit::errlogDevTimeInit () +{ + int errlogNo = iosDrvInstall ( + 0, // create not supported + 0, // remove not supported + reinterpret_cast < FUNCPTR > ( errlogOpen ), + 0, // close is a noop + 0, // read not supported + reinterpret_cast < FUNCPTR > ( errlogWrite ), + 0 // ioctl not supported + ); + if ( errlogNo == ERROR ) { + errlogPrintf ( + "Unable to install driver routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + DEV_HDR * pDev = static_cast < DEV_HDR * > ( calloc ( 1, sizeof ( *pDev ) ) ); + if ( ! pDev ) { + errlogPrintf ( + "Unable to create driver data structure for routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + int status = iosDevAdd ( pDev, "/errlog/", errlogNo ); + if ( status < 0 ) { + errlogPrintf ( + "Unable to install device routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + free ( pDev ); + return; + } + int fd = open ( "/errlog/any", O_WRONLY, 0 ); + if ( fd < 0 ) { + errlogPrintf ( + "Unable to open fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + status = logFdAdd ( fd ); + if ( status != OK) { + errlogPrintf ( + "Unable to install fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + close ( fd ); + return; + } +} + diff --git a/modules/libcom/src/osi/os/vxWorks/module_types.h b/modules/libcom/src/osi/os/vxWorks/module_types.h new file mode 100644 index 000000000..07372c37a --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/module_types.h @@ -0,0 +1,494 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* module_types.h */ +/* + * Author: Bob Dalesio + * Date: 12-07-88 + */ + +#ifndef INCLmodule_typesh +#define INCLmodule_typesh + +/* Device module types */ +/* + * all devices have corresponding entries in ~operator/db/src/menus.c + * changes must be made in both areas to keep the database and drivers in sync + */ +/* & in comment indicates tested with card 0 */ +/* % in comment indicates tested with card other than card 0 */ +/* # in comment indicates that the Nth card has been tested */ +/* !! never been tested */ + +/* + * @# If any changes are made to this file, check the procedures + * ab_card, and vme_card in signallist.c, and get_address in sigmenu.c. + */ + +#ifdef MODULE_TYPES_INIT +#define MODULE_TYPES_DEF(MT_DEF_PARM) MT_DEF_PARM +#else +#define MODULE_TYPES_DEF(MT_DEF_PARM) extern MT_DEF_PARM; +#endif + +/* Number of columns used in io_report. */ +#define IOR_MAX_COLS 4 + +/* I/O types */ +#define IO_AI 0 +#define IO_AO 1 +#define IO_BI 2 +#define IO_BO 3 +#define IO_SM 4 +#define IO_WF 5 +#define IO_TIMER 6 +#define MAX_IO_TYPE IO_TIMER + +/* bus types */ +/* must correspond to the values in link types */ +/* these defines are in ~gta/dbcon/h/link.h */ + + +/* equates for the Allen-Bradley cards. */ +#define AB_BASE_ADDR 0xc00000 /* base addr of first AB6008SV */ +#define AB_MAX_LINKS 2 /* number of serial links from VME */ +#define AB_MAX_ADAPTERS 8 /* number of physical adapters on a link */ +#define AB_MAX_CARDS 16 /* max number of IO cards per adapter */ +#define AB_CARD_ADAPTER 16 /* cards per logical adapter */ +#define AB_CHAN_CARD 16 /* max channels per card */ + +/* analog inputs */ +#define AB1771IL 0 /* &% Allen-Bradley low level analog input */ +#define AB1771IFE 1 /* &% Allen-Bradley low level analog input */ +#define AB1771IXE 2 /* &% Allen-Bradley millivolt input */ +#define XY566SE 3 /* & Xycom 12-bit Single Ended Scanned*/ +#define XY566DI 4 /* &% Xycom 12-bit Differential Scanned */ +#define XY566DIL 5 /* &% Xycom 12-bit Differential Latched */ +#define VXI_AT5_AI 6 /* % AT-5 VXI module's Analog Inputs */ +#define AB1771IFE_SE 7 /* % A-B IFE in 16 single-ended input mode */ +#define AB1771IFE_4to20MA 8 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define DVX2502 9 /* &% DVX_2502 128 chan 16 bit differential */ +#define AB1771IFE_0to5V 10 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define KSCV215 11 /* % KSC V215 VXI 16 bit differential */ +#define AB1771IrPlatinum 12 /* % A-B RTD Platinum */ +#define AB1771IrCopper 13 /* % A-B RTD Copper */ +#define MAX_AI_TYPES AB1771IrCopper +MODULE_TYPES_DEF(short ai_num_cards[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={12,12,12, 4, 4, 6,32,12,12, 1, 12, 32, 12,12}; +#endif +MODULE_TYPES_DEF(short ai_num_channels[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 8, 8, 8,32,16,16, 8,16, 8, 127, 8, 32,6,6}; +#endif +MODULE_TYPES_DEF(short ai_interruptable[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(short ai_bus[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 4, 2, 2, 2, 2, 4, 4, 2, 4, 2,4,4}; +#endif +MODULE_TYPES_DEF(unsigned short ai_addrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0,0x6000,0x7000,0xe000, 0xc014,0,0, 0xff00, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(long ai_memaddrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0,0,0,0x000000,0x040000,0x0c0000, 0,0,0, 0x100000, 0, 0,0,0}; +#endif + +/* analog outputs */ +#define AB1771OFE 0 /* &% Allen-Bradley 12 bit Analog Output */ +#define VMI4100 1 /* & VMIC VMIVME 4100 */ +#define ZIO085 2 /* & Ziomek 085 */ +#define VXI_AT5_AO 3 /* !! AT-5 VXI modules analog outputs */ +#define MAX_AO_TYPES VXI_AT5_AO +MODULE_TYPES_DEF(short ao_num_cards[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {12, 4, 1, 32}; +#endif +MODULE_TYPES_DEF(short ao_num_channels[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = { 4, 16, 32, 16}; +#endif +MODULE_TYPES_DEF(short ao_interruptable[MAX_AO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0, 0, 1}; +#endif +MODULE_TYPES_DEF(short ao_bus[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short ao_addrs[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0x4100,0x0800, 0xc000}; +#endif + +/* binary inputs */ +#define ABBI_08_BIT 0 /* &% Allen-Bradley generic Binary In 8 bit */ +#define ABBI_16_BIT 1 /* &% Allen-Bradley generic Binary In 16 bit */ +#define BB910 2 /* & BURR BROWN MPV 910 (relay) */ +#define XY210 3 /* &% XYcom 32 bit binary in */ +#define VXI_AT5_BI 4 /* !! AT-5 VXI modules binary inputs */ +#define HPE1368A_BI 5 /* !! HP E1368A video switch */ +#define AT8_FP10S_BI 6 /* !! AT8 FP10 slave fast protect */ +#define XY240_BI 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BI_TYPES XY240_BI +MODULE_TYPES_DEF(short bi_num_cards[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 12, 12, 4, 4, 32, 32, 8, 2}; +#endif +MODULE_TYPES_DEF(short bi_num_channels[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bi_interruptable[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 1, 1, 0, 0, 1, 1, 1, 1}; +#endif +MODULE_TYPES_DEF(short bi_bus[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short bi_addrs[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xb800,0xa000, 0xc000, 0xc000, 0x0e00, 0xd000}; +#endif + +/* binary outputs */ +#define ABBO_08_BIT 0 /* &% Allen-Bradley 8 bit binary out */ +#define ABBO_16_BIT 1 /* &% Allen-Bradley 16 bit binary out */ +#define BB902 2 /* &% BURR BROWN MPV 902 (relay) */ +#define XY220 3 /* &% XYcom 32 bit binary out */ +#define VXI_AT5_BO 4 /* !! AT-5 VXI modules binary outputs */ +#define HPE1368A_BO 5 /* !! HP E1368A video switch */ +#define AT8_FP10M_BO 6 /* !! AT8 FP10 master fast protect */ +#define XY240_BO 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BO_TYPES XY240_BO +MODULE_TYPES_DEF(short bo_num_cards[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={12, 12, 4, 1, 32, 32, 2, 2}; +#endif +MODULE_TYPES_DEF(short bo_num_channels[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bo_interruptable[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 0, 0, 0, 0, 1, 0, 0, 1 }; +#endif +MODULE_TYPES_DEF(short bo_bus[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short bo_addrs[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xd800,0xc800, 0xc000, 0xc000, 0x0c00, 0xd000}; +#endif + +/* stepper motor drivers */ +#define CM57_83E 0 /* & Compumotor 57-83E motor controller */ +#define OMS_6AXIS 1 /* & OMS six axis motor controller */ +#define MAX_SM_TYPES OMS_6AXIS +MODULE_TYPES_DEF(short sm_num_cards[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 8 }; +#endif +MODULE_TYPES_DEF(short sm_num_channels[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 8}; +#endif +MODULE_TYPES_DEF(short sm_interruptable[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0 }; +#endif +MODULE_TYPES_DEF(short sm_bus[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short sm_addrs[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0x8000, 0xfc00 }; +#endif + +/* waveforms */ +#define XY566WF 0 /* & Xycom 566 as a waveform */ +#define CAMAC_THING 1 /* !! CAMAC waveform digitizer */ +#define JGVTR1 2 /* & Joerger transient recorder */ +#define COMET 3 /* !! COMET transient recorder */ +#define MAX_WF_TYPES COMET +MODULE_TYPES_DEF(short wf_num_cards[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={4, 4, 8, 4}; +#endif +MODULE_TYPES_DEF(short wf_num_channels[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={1, 1, 1, 4}; +#endif +MODULE_TYPES_DEF(short wf_interruptable[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = {0, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(short wf_bus[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={2, 3, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short wf_addrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x9000, 0, 0xB000, 0xbc00}; +#endif +MODULE_TYPES_DEF(unsigned short wf_armaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {0x5400, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(long wf_memaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x080000, 0, 0xb80000, 0xe0000000}; +#endif + + +/* timing cards */ +#define MZ8310 0 /* &% Mizar Timing Module */ +#define DG535 1 /* !! GPIB timing instrument */ +#define VXI_AT5_TIME 2 /* !! AT-5 VXI modules timing channels */ +#define MAX_TM_TYPES VXI_AT5_TIME +MODULE_TYPES_DEF(short tm_num_cards[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 4, 1, 32 }; +#endif +MODULE_TYPES_DEF(short tm_num_channels[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={10, 1, 10}; +#endif +MODULE_TYPES_DEF(short tm_interruptable[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 0, 1 }; +#endif +MODULE_TYPES_DEF(short tm_bus[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 5, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short tm_addrs[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0xf800, 0, 0xc000 }; +#endif + +/* AT830X clock cards */ +MODULE_TYPES_DEF(long AT830X_1_addrs ) +#ifdef MODULE_TYPES_INIT + = 0x0400; +#endif +MODULE_TYPES_DEF(short AT830X_1_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif +MODULE_TYPES_DEF(long AT830X_addrs ) +#ifdef MODULE_TYPES_INIT + = 0xaa0000; +#endif +MODULE_TYPES_DEF(short AT830X_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif + +/* + * system controller cards. + * (driver looks for only one card) + */ +MODULE_TYPES_DEF(long xy010ScA16Base) +#ifdef MODULE_TYPES_INIT + = 0x0000; +#endif +/* + * limit the size of the VXI logical address space + * + * = + 0xc000 + * + * LA VME address + * 0 + * EPICS_VXI_LA_COUNT + (EPICS_VXI_LA_COUNT-1)*64 + */ +MODULE_TYPES_DEF(unsigned char EPICS_VXI_LA_COUNT) +#ifdef MODULE_TYPES_INIT + = 32; +#endif + +/* + * + * address ranges for VXI A24 and A32 devices + * + */ +MODULE_TYPES_DEF(char *EPICS_VXI_A24_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x900000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A24_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x100000; +#endif +MODULE_TYPES_DEF(char *EPICS_VXI_A32_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x90000000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A32_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x10000000; +#endif + + +/****************************************************************************** + * + * Interrupt vector locations used by the MV167 CPU board. + * These are defined in mv167.h + * + * PCC2_INT_VEC_BASE 0x40 PCC interrupt vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE0 0x50 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE1 0x60 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * + * INT_VEC_CD2400_A 0x90 int vec for channel A + * INT_VEC_CD2400_B 0x94 int vec for channel B + * INT_VEC_CD2400_C 0x98 int vec for channel C + * INT_VEC_CD2400_D 0x9c int vec for channel D + * + * LANC_IRQ_LEVEL 3 LNANC IRQ level + * MPCC_IRQ_LEVEL 4 serial comm IRQ level + * SYS_CLK_LEVEL 6 interrupt level for sysClk + * AUX_CLK_LEVEL 5 interrupt level for auxClk + * SCSI_IRQ_LEVEL 2 scsi interrupt level + * + ******************************************************************************/ + +/* interrupt vector allocation - one for each XY566 DIL card */ +MODULE_TYPES_DEF(int AI566_VNUM) +#ifdef MODULE_TYPES_INIT + =0xf8; /* Xycom 566 Differential Latched */ +#endif + +/* interrupt vector allocation - one for each DVX card */ +MODULE_TYPES_DEF(int DVX_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd0; +#endif + +/* stepper motor interrupt vector - one for each motor */ +MODULE_TYPES_DEF(int MD_INT_BASE) +#ifdef MODULE_TYPES_INIT + =0xf0; /* base of the motor int vector */ +#endif + +/* I reserve from here up to num_cards * 4 interrupting chans/card - joh */ +MODULE_TYPES_DEF(int MZ8310_INT_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xe8; +#endif + +/* Allen-Bradley Serial Driver - MAX_AB_LINKS number of vectors */ +MODULE_TYPES_DEF(int AB_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0x60; +#endif + +/* only one interrupt vector allocated for all Joerger VTR1 boards joh */ +MODULE_TYPES_DEF(int JGVTR1_INT_VEC) +#ifdef MODULE_TYPES_INIT + =0xe0; +#endif + +/* AT830X_1 cards have 1 intr vector for each AT830X_1_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_1_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd4; +#endif + +/* AT830X cards have 1 intr vector for each AT830X_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd6; +#endif + +/* AT8 fast protect interrupt vector base */ +MODULE_TYPES_DEF(int AT8FP_IVEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xa2; +#endif + + +MODULE_TYPES_DEF(int AT8FPM_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + =0xaa; +#endif + + +/****************************************************************************** + * + * Addresses and IRQ information used by the XVME402 bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1800; /* the first address of link 0's region */ +#endif +#define BB_NUM_LINKS 4 /* max number of BB ports allowed */ +MODULE_TYPES_DEF(int BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xa0; /* vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int BB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + = 5; /* IRQ level */ +#endif + +/****************************************************************************** + * + * Information for the PEP modular Bitbus boards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short PEP_BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1c00; +#endif +MODULE_TYPES_DEF(int PEP_BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xe8; +#endif + +/****************************************************************************** + * + * Addresses and IRQ information used by the NI1014 and NI1014D bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short NIGPIB_SHORT_OFF) +#ifdef MODULE_TYPES_INIT + = 0x5000;/* First address of link 0's region */ +#endif + /* Each link uses 0x0200 bytes */ +#define NIGPIB_NUM_LINKS 4 /* Max number of NI GPIB ports allowed */ +MODULE_TYPES_DEF(int NIGPIB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 100; /* Vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int NIGPIB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + =5; /* IRQ level */ +#endif + +#if 0 /* JRW */ +#define NI1014_LINK_NUM_BASE 0 +#endif + +/* + * nothing after this endif + */ +#endif /*INCLmodule_typesh*/ diff --git a/modules/libcom/src/osi/os/vxWorks/osdEnv.c b/modules/libcom/src/osi/os/vxWorks/osdEnv.c new file mode 100644 index 000000000..c81f49316 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdEnv.c @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsFindSymbol.h" +#include "epicsStdio.h" +#include "errlog.h" +#include "iocsh.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + iocshEnvClear(name); + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf(-1L, __FILE__, __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, value, strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + envShow (0); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdEvent.c b/modules/libcom/src/osi/os/vxWorks/osdEvent.c new file mode 100644 index 000000000..875d4e6f6 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdEvent.c @@ -0,0 +1,74 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* os/vxWorks/osdEvent.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include +#include +#include +#include +#include + +#include "epicsEvent.h" + +/* The following not defined in any vxWorks header */ +int sysClkRateGet(void); + +epicsEventId epicsEventCreate(epicsEventInitialState initialState) +{ + return (epicsEventId) semBCreate(SEM_Q_FIFO, + (initialState == epicsEventEmpty) ? SEM_EMPTY : SEM_FULL); +} + +void epicsEventDestroy(epicsEventId id) +{ + semDelete((SEM_ID)id); +} + +epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut) +{ + int rate = sysClkRateGet(); + int status; + int ticks; + + if (timeOut <= 0.0) { + ticks = 0; + } else if (timeOut >= (double) INT_MAX / rate) { + ticks = WAIT_FOREVER; + } else { + ticks = timeOut * rate; + if (ticks <= 0) + ticks = 1; + } + status = semTake((SEM_ID)id, ticks); + if (status == OK) + return epicsEventOK; + if (errno == S_objLib_OBJ_TIMEOUT || + (errno == S_objLib_OBJ_UNAVAILABLE && ticks == 0)) + return epicsEventWaitTimeout; + return epicsEventError; +} + +epicsEventStatus epicsEventTryWait(epicsEventId id) +{ + int status = semTake((SEM_ID)id, NO_WAIT); + + if (status == OK) + return epicsEventOK; + if (errno == S_objLib_OBJ_UNAVAILABLE) + return epicsEventWaitTimeout; + return epicsEventError; +} + +void epicsEventShow(epicsEventId id, unsigned int level) +{ + semShow((SEM_ID)id,level); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdEvent.h b/modules/libcom/src/osi/os/vxWorks/osdEvent.h new file mode 100644 index 000000000..b08b4d98f --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdEvent.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* os/vxWorks/osdEvent.h */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include + +#define epicsEventTrigger(ID) \ + (semGive((SEM_ID)(ID)) == OK ? epicsEventOK : epicsEventError) + +#define epicsEventWait(ID) \ + (semTake((SEM_ID)(ID), WAIT_FOREVER) == OK ? epicsEventOK : epicsEventError) diff --git a/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c b/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c new file mode 100644 index 000000000..da17c5bf3 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdFindSymbol.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/osdFindSymbol */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbmf.h" +#include "epicsString.h" +#include "epicsFindSymbol.h" + +static char *errmsg = NULL; +static char *oldmsg = NULL; + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + MODULE_ID m = 0; + int fd; + + if (oldmsg) { + free(oldmsg); + oldmsg = NULL; + } + if (errmsg) { + free(errmsg); + errmsg = NULL; + } + + fd = open(name, O_RDONLY, 0); + if (fd != ERROR) { + m = loadModule(fd, GLOBAL_SYMBOLS); + close(fd); + } + + if (!m) { + errmsg = epicsStrDup(strerror(errno)); + } + return m; +} + +epicsShareFunc const char *epicsLoadError(void) +{ + if (oldmsg) + free(oldmsg); + + oldmsg = errmsg; + errmsg = NULL; + return oldmsg; +} + +void *epicsFindSymbol(const char *name) +{ + STATUS status; + +#if _WRS_VXWORKS_MAJOR < 6 || _WRS_VXWORKS_MINOR < 9 + char *pvalue; + SYM_TYPE type; + + status = symFindByName(sysSymTbl, (char *)name, &pvalue, &type); + if (!status) + return pvalue; + + if (name[0] == '_' ) { + status = symFindByName(sysSymTbl, (char *)(name+1), &pvalue, &type); + } +#if CPU_FAMILY == MC680X0 + else { + char *pname = dbmfMalloc(strlen(name) + 2); + + pname[0] = '_'; + strcpy(pname + 1, name); + status = symFindByName(sysSymTbl, pname, &pvalue, &type); + dbmfFree(pname); + } +#endif + if (!status) + return pvalue; + +#else + + SYMBOL_DESC symDesc; + + memset(&symDesc, 0, sizeof(SYMBOL_DESC)); + symDesc.mask = SYM_FIND_BY_NAME; + symDesc.name = (char *) name; + status = symFind(sysSymTbl, &symDesc); + if (!status) + return symDesc.value; + + if (name[0] == '_') { + symDesc.name++; + status = symFind(sysSymTbl, &symDesc); + if (!status) + return symDesc.value; + } + /* No need to prepend an '_'; 68K-only, no longer supported */ +#endif + return 0; +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdInterrupt.c b/modules/libcom/src/osi/os/vxWorks/osdInterrupt.c new file mode 100644 index 000000000..3ac9e9e83 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdInterrupt.c @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/osdInterrupt.c */ + +/* Author: Marty Kraimer Date: 28JAN2000 */ + +#include +#include +#include + +#include "epicsInterrupt.h" + +int epicsInterruptLock() {return(intLock());} + +void epicsInterruptUnlock(int key) {intUnlock(key);} + +int epicsInterruptIsInterruptContext() {return(intContext());} + +void epicsInterruptContextMessage(const char *message) +{ + logMsg((char *)message,0,0,0,0,0,0); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdInterrupt.h b/modules/libcom/src/osi/os/vxWorks/osdInterrupt.h new file mode 100644 index 000000000..ad99926de --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdInterrupt.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/osdInterrupt.h */ + +/* Author: Marty Kraimer Date: 28JAN2000 */ + +/*osdInterrupt.h not needed */ diff --git a/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.cpp b/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.cpp new file mode 100644 index 000000000..89c2745ed --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.cpp @@ -0,0 +1,54 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#define epicsExportSharedSymbols +#include +#include "epicsMessageQueue.h" + +extern "C" int sysClkRateGet(void); + +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout) +{ + int ticks; + + if (timeout<=0.0) { + ticks = 0; + } else { + ticks = (int)(timeout*sysClkRateGet()); + if(ticks<=0) ticks = 1; + } + return msgQSend((MSG_Q_ID)id, (char *)message, messageSize, ticks, MSG_PRI_NORMAL); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout) +{ + int ticks; + + if (timeout<=0.0) { + ticks = 0; + } else { + ticks = (int)(timeout*sysClkRateGet()); + if(ticks<=0) ticks = 1; + } + return msgQReceive((MSG_Q_ID)id, (char *)message, size, ticks); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.h b/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.h new file mode 100644 index 000000000..87b35bd38 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdMessageQueue.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Very thin shims around vxWorks routines + */ +#include +#include + +#define epicsMessageQueueCreate(c,s) ((epicsMessageQueueId)msgQCreate((c),(s),MSG_Q_FIFO)) +#define epicsMessageQueueDestroy(q) (msgQDelete((MSG_Q_ID)(q))) + +#define epicsMessageQueueTrySend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), NO_WAIT, MSG_PRI_NORMAL)) +#define epicsMessageQueueSend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), WAIT_FOREVER, MSG_PRI_NORMAL)) + +#define epicsMessageQueueTryReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), NO_WAIT)) +#define epicsMessageQueueReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), WAIT_FOREVER)) + +#define epicsMessageQueuePending(q) (msgQNumMsgs((MSG_Q_ID)(q))) +#define epicsMessageQueueShow(q,l) (msgQShow((MSG_Q_ID)(q),(l))) diff --git a/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c b/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c new file mode 100644 index 000000000..e0c6b2278 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdMonotonic.c @@ -0,0 +1,113 @@ +/*************************************************************************\ +* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTypes.h" +#include "epicsTime.h" + + +#define NS_PER_SEC 1000000000 + +union timebase { + UINT32 u32[2]; /* vxTimeBaseGet() */ + INT64 i64; /* pentiumTscGet64() */ + UINT64 u64; /* epicsMonotonicGet() */ +}; + + +#if CPU_FAMILY == PPC +#include +#include "epicsFindSymbol.h" + +/* On PowerPC the timebase counter runs at a rate related to the + * bus clock and its frequency should always fit into a UINT32. + */ + +static epicsUInt32 ticksPerSec; + +#define TIMEBASEGET(TB) \ + vxTimeBaseGet(&TB.u32[0], &TB.u32[1]) + +void osdMonotonicInit(void) +{ + typedef epicsUInt32 (*sysTimeBaseFreq_t)(void); + sysTimeBaseFreq_t sysTimeBaseFreq = + (sysTimeBaseFreq_t) epicsFindSymbol("_sysTimeBaseFreq"); + + if (sysTimeBaseFreq) { + ticksPerSec = sysTimeBaseFreq(); + + if (!ticksPerSec) + printf("Warning: Failed to set up monotonic time source.\n"); + /* Warn here only if the BSP routine exists but returned 0 */ + } + else + ticksPerSec = 0; /* Warn on first use */ +} + + +#elif CPU_FAMILY == I80X86 + +#include +#include + +/* On Intel the timebase counter frequency is returned by the OS as a + * UINT64. Some CPUs may count at multi-GHz rates so we need 64 bits. + */ + +static epicsUInt64 ticksPerSec; + +#define TIMEBASEGET(TB) \ + pentiumTscGet64(&TB.i64) + +void osdMonotonicInit(void) +{ + ticksPerSec = vxCpuIdGetFreq(); + + if (!ticksPerSec) + printf("Warning: Failed to set up monotonic time source.\n"); +} + + +#else + #error This CPU family not supported yet! +#endif + + +epicsUInt64 epicsMonotonicResolution(void) +{ + if (!ticksPerSec) + return 0; + + return NS_PER_SEC / ticksPerSec; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + union timebase tbNow; + + if (!ticksPerSec) { + static int warned = 0; + + if (!warned) { + printf("Warning: Monotonic time source is not available.\n"); + warned = 1; + } + return 0; + } + + TIMEBASEGET(tbNow); + /* Using a long double for the calculation below to preserve + * as many bits in the mantissa as possible. + */ + return ((long double) tbNow.u64) * NS_PER_SEC / ticksPerSec; +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdMutex.c b/modules/libcom/src/osi/os/vxWorks/osdMutex.c new file mode 100644 index 000000000..38db2ab83 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdMutex.c @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* os/vxWorks/osdMutex.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include +#include +#include +#include + +/* The following not defined in an vxWorks header */ +int sysClkRateGet(void); + + +#include "epicsMutex.h" + +struct epicsMutexOSD * epicsMutexOsdCreate(void) +{ + return((struct epicsMutexOSD *) + semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY)); +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +{ + semDelete((SEM_ID)id); +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +{ + int status; + status = semTake((SEM_ID)id,NO_WAIT); + if(status==OK) return(epicsMutexLockOK); + if(errno==S_objLib_OBJ_UNAVAILABLE) return(epicsMutexLockTimeout); + return(epicsMutexLockError); +} + +void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +{ + semShow((SEM_ID)id,level); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdMutex.h b/modules/libcom/src/osi/os/vxWorks/osdMutex.h new file mode 100644 index 000000000..3ea951a8f --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdMutex.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* os/vxWorks/osdMutex.h */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include + +/* If the macro is replaced by inline it is necessary to say + static __inline__ + but then a warning message appears everywhere osdMutex.h is included +*/ + +#define epicsMutexOsdUnlock(ID) semGive((SEM_ID)(ID)) + +#define epicsMutexOsdLock(ID) \ +(semTake((SEM_ID)(ID),WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError) diff --git a/modules/libcom/src/osi/os/vxWorks/osdPoolStatus.c b/modules/libcom/src/osi/os/vxWorks/osdPoolStatus.c new file mode 100644 index 000000000..06447c1f7 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdPoolStatus.c @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "osiPoolStatus.h" + +/* + * It turns out that memPartInfoGet() and memFindMax() are very CPU intensive on vxWorks + * so we must spawn off a thread that periodically polls. Although this isnt 100% safe, I + * dont see what else to do. + * + * It takes about 30 uS to call memPartInfoGet() on a pcPentium I vxWorks system. + * + * joh + */ + +static epicsThreadOnceId osdMaxBlockOnceler = EPICS_THREAD_ONCE_INIT; + +static size_t osdMaxBlockSize = 0; + +static void osdSufficentSpaceInPoolQuery () +{ + osdMaxBlockSize = (size_t) memFindMax (); +} + +static void osdSufficentSpaceInPoolPoll ( void *pArgIn ) +{ + while ( 1 ) { + epicsThreadSleep ( 1.0 ); + osdSufficentSpaceInPoolQuery (); + } +} + +static void osdSufficentSpaceInPoolInit ( void *pArgIn ) +{ + epicsThreadId id; + + osdSufficentSpaceInPoolQuery (); + + id = epicsThreadCreate ( "poolPoll", epicsThreadPriorityMedium, + epicsThreadGetStackSize ( epicsThreadStackSmall ), + osdSufficentSpaceInPoolPoll, 0 ); +} + +/* + * osiSufficentSpaceInPool () + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + epicsThreadOnce ( &osdMaxBlockOnceler, osdSufficentSpaceInPoolInit, 0 ); + + if ( UINT_MAX - 100000u >= contiguousBlockSize ) { + return ( osdMaxBlockSize > 100000 + contiguousBlockSize ); + } + else { + return 0; + } +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdProcess.c b/modules/libcom/src/osi/os/vxWorks/osdProcess.c new file mode 100644 index 000000000..2347a407c --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdProcess.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include + +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + char pName[MAX_IDENTITY_LEN]; + unsigned uiLength; + size_t len; + + remCurIdGet ( pName, NULL ); + len = strlen ( pName ); + + if (len>UINT_MAX || len<=0) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, pName, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + return osiSpawnDetachedProcessNoSupport; +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdReadline.c b/modules/libcom/src/osi/os/vxWorks/osdReadline.c new file mode 100644 index 000000000..dee24fd64 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdReadline.c @@ -0,0 +1,100 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Eric Norum Date: 12DEC2001 */ + +/* + * This file is included by epicsReadline.c which has already included the + * headers stdio.h, stdlib.h, errno.h, envDefs.h and epicsReadline.h + */ + +#include +#include + +/* FIXME: Remove line-lenth limitation */ +#define LEDLIB_LINESIZE 1000 + +#ifndef _WRS_VXWORKS_MAJOR +typedef int LED_ID; +#endif + +struct osdContext { + LED_ID ledId; + char line[LEDLIB_LINESIZE]; +}; + +/* + * Create a command-line context + */ +static void +osdReadlineBegin(struct readlineContext *context) +{ + struct osdContext *osd = malloc(sizeof *osd); + + if (osd != NULL) { + osd->ledId = (LED_ID) ERROR; + if (context->in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 1) + i = 1; + + osd->ledId = ledOpen(fileno(stdin), fileno(stdout), i); + if (osd->ledId == (LED_ID) ERROR) { + context->in = stdin; + printf("Warning -- Unabled to allocate space for command-line history.\n"); + printf("Warning -- Command-line editting disabled.\n"); + } + } + context->osd = osd; + } +} + +/* + * Read a line of input + */ +static char * +osdReadline (const char *prompt, struct readlineContext *context) +{ + struct osdContext *osd = context->osd; + int i; + + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + if (osd->ledId != (LED_ID) ERROR) { + i = ledRead(osd->ledId, osd->line, LEDLIB_LINESIZE-1); + if (i < 0) + return NULL; + } + else { + if (fgets(osd->line, LEDLIB_LINESIZE, context->in) == NULL) + return NULL; + i = strlen(osd->line); + } + if ((i >= 1) && (osd->line[i-1] == '\n')) + osd->line[i-1] = '\0'; + else + osd->line[i] = '\0'; + return osd->line; +} + +/* + * Destroy a command-line context + */ +static void +osdReadlineEnd (struct readlineContext *context) +{ + LED_ID ledId = context->osd->ledId; + + if (ledId != (LED_ID) ERROR) + ledClose(ledId); + free(context->osd); +} + diff --git a/modules/libcom/src/osi/os/vxWorks/osdSignal.cpp b/modules/libcom/src/osi/os/vxWorks/osdSignal.cpp new file mode 100644 index 000000000..1157854aa --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdSignal.cpp @@ -0,0 +1,20 @@ +/*************************************************************************\ + * Copyright (c) 2002 The University of Chicago, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * NOOP + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.c b/modules/libcom/src/osi/os/vxWorks/osdSock.c new file mode 100644 index 000000000..a0f5e8ec5 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdSock.c @@ -0,0 +1,129 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include + +#ifndef vxWorks +#error this is a vxWorks specific source code +#endif + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "osiSock.h" + +int osiSockAttach() +{ + return 1; +} + +void osiSockRelease() +{ +} + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + SOCKET sock = socket ( domain, type, protocol ); + if ( sock < 0 ) { + sock = INVALID_SOCKET; + } + return sock; +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + int newSock = accept ( sock, pAddr, addrlen ); + if ( newSock < 0 ) { + newSock = INVALID_SOCKET; + } + return newSock; +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = close ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", + buf ); + } +} + +/* + * ipAddrToHostName + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + int status; + int errnoCopy = errno; + unsigned len; + + if (bufSize<1) { + return 0; + } + + if (bufSize>MAXHOSTNAMELEN) { + status = hostGetByAddr ((int)pAddr->s_addr, pBuf); + if (status==OK) { + pBuf[MAXHOSTNAMELEN] = '\0'; + len = strlen (pBuf); + } + else { + len = 0; + } + } + else { + char name[MAXHOSTNAMELEN+1]; + status = hostGetByAddr (pAddr->s_addr, name); + if (status==OK) { + strncpy (pBuf, name, bufSize); + pBuf[bufSize-1] = '\0'; + len = strlen (pBuf); + } + else { + len = 0; + } + } + + errno = errnoCopy; + + return len; +} + +/* + * hostToIPAddr () + */ +epicsShareFunc int epicsShareAPI +hostToIPAddr(const char *pHostName, struct in_addr *pIPA) +{ + int addr = hostGetByName ( (char *) pHostName ); + if ( addr == ERROR ) { + return -1; + } + pIPA->s_addr = (unsigned long) addr; + + /* + * success + */ + return 0; +} + diff --git a/modules/libcom/src/osi/os/vxWorks/osdSock.h b/modules/libcom/src/osi/os/vxWorks/osdSock.h new file mode 100644 index 000000000..d949b25f1 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdSock.h @@ -0,0 +1,108 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * vxWorks specific socket include + */ + +#ifndef osdSockH +#define osdSockH + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#ifndef _VSB_CONFIG_FILE +# define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*This following is not defined in any vxWorks header files*/ +int sysClkRateGet(void); + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +/* + * it is quite lame on WRS's part to assume that + * a ptr is always the same as an int + */ +#define socket_ioctl(A,B,C) ioctl(A,B,(int)C) +typedef int osiSockIoctl_t; +typedef int osiSocklen_t; +typedef int osiSockOptMcastLoop_t; + +#define FD_IN_FDSET(FD) ((FD)=0) + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EACCES EACCES +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7F000001 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +#if defined(_SIZEOF_ADDR_IFREQ) +# define ifreq_size(pifreq) _SIZEOF_ADDR_IFREQ(*pifreq) +#elif ( defined (BSD) && ( BSD >= 44 ) ) +# define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) +#else +# define ifreq_size(pifreq) sizeof(*pifreq) +#endif + +#endif /*osdSockH*/ diff --git a/modules/libcom/src/osi/os/vxWorks/osdSpin.c b/modules/libcom/src/osi/os/vxWorks/osdSpin.c new file mode 100644 index 000000000..a68cdd374 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdSpin.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2013 Brookhaven Science Assoc. as Operator of Brookhaven +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: Ralph Lange + * Andrew Johnson + * Michael Davidsaver + * + * Inspired by Linux UP spinlocks implemention + * include/linux/spinlock_api_up.h + */ + +/* + * vxWorks (single CPU): LOCK INTERRUPT and DISABLE PREEMPTION + * + * CAVEAT: + * This implementation will not compile on vxWorks SMP architectures. + * These architectures provide spinlocks, which must be used instead. + * + */ + +/* This is needed for vxWorks 6.x to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include + +#include "cantProceed.h" +#include "epicsSpin.h" + +typedef struct epicsSpin { + int key; + unsigned int locked; +} epicsSpin; + +epicsSpinId epicsSpinCreate(void) { + return calloc(1, sizeof(epicsSpin)); +} + +epicsSpinId epicsSpinMustCreate(void) +{ + epicsSpinId ret = epicsSpinCreate(); + if (!ret) + cantProceed("epicsSpinMustCreate: epicsSpinCreate failed."); + return ret; +} + +void epicsSpinDestroy(epicsSpinId spin) { + free(spin); +} + +void epicsSpinLock(epicsSpinId spin) { + int key = intLock(); + + if (!intContext()) + taskLock(); + if (spin->locked) { + intUnlock(key); + if (!intContext()) { + taskUnlock(); + logMsg("epicsSpinLock(%p): Deadlock.\n", + (int) spin, 0, 0, 0, 0, 0); + cantProceed("Recursive lock, missed unlock or block when locked."); + } + else { + logMsg("epicsSpinLock(%p): Deadlock in ISR.\n" + "Recursive lock, missed unlock or block when locked.\n", + (int) spin, 0, 0, 0, 0, 0); + } + return; + } + spin->key = key; + spin->locked = 1; +} + +int epicsSpinTryLock(epicsSpinId spin) { + int key = intLock(); + + if (!intContext()) + taskLock(); + if (spin->locked) { + intUnlock(key); + if (!intContext()) + taskUnlock(); + return 1; + } + spin->key = key; + spin->locked = 1; + return 0; +} + +void epicsSpinUnlock(epicsSpinId spin) { + int key = spin->key; + + if (!spin->locked) { + logMsg("epicsSpinUnlock(%p): not locked\n", + (int) spin, 0, 0, 0, 0, 0); + return; + } + spin->key = spin->locked = 0; + intUnlock(key); + if (!intContext()) + taskUnlock(); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdStdio.c b/modules/libcom/src/osi/os/vxWorks/osdStdio.c new file mode 100644 index 000000000..20664a1a4 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdStdio.c @@ -0,0 +1,57 @@ +/* osdStdio.c */ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include "epicsStdio.h" +#include "dbDefs.h" + +struct outStr_s { + char *str; + size_t free; +}; + +static STATUS outRoutine(char *buffer, size_t nchars, int outarg) { + struct outStr_s *poutStr = (struct outStr_s *) outarg; + size_t free = poutStr->free; + size_t len; + + if (free < 1) { /*let fioFormatV continue to count length*/ + return OK; + } else if (free > 1) { + len = min(free-1, nchars); + strncpy(poutStr->str, buffer, len); + poutStr->str += len; + poutStr->free -= len; + } + /*make sure final string is null terminated*/ + *poutStr->str = 0; + return OK; +} + +int epicsVsnprintf(char *str, size_t size, const char *format, va_list ap) { + struct outStr_s outStr; + + outStr.str = str; + outStr.free = size; + return fioFormatV(format, ap, outRoutine, (int) &outStr); +} + +int epicsSnprintf(char *str, size_t size, const char *format, ...) +{ + size_t nchars; + va_list pvar; + + va_start(pvar,format); + nchars = epicsVsnprintf(str,size,format,pvar); + va_end (pvar); + return(nchars); +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdStrtod.h b/modules/libcom/src/osi/os/vxWorks/osdStrtod.h new file mode 100644 index 000000000..91c47d70e --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdStrtod.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * This header is included by epicsString.h and epicsStdlib.h + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +double epicsStrtod(const char *str, char **endp); + +/* + * VxWorks doesn't provide these routines, so for now we do + */ + +long long int strtoll(const char *nptr, char **endptr, int base); +unsigned long long int strtoull(const char *nptr, char **endptr, int base); + +#ifdef __cplusplus +} +#endif diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.c b/modules/libcom/src/osi/os/vxWorks/osdThread.c new file mode 100644 index 000000000..ce01ea609 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdThread.c @@ -0,0 +1,457 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/epicsThread.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "errlog.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsAssert.h" +#include "vxLib.h" +#include "epicsExit.h" + +epicsShareFunc void osdThreadHooksRun(epicsThreadId id); + +#if CPU_FAMILY == MC680X0 +#define ARCH_STACK_FACTOR 1 +#elif CPU_FAMILY == SPARC +#define ARCH_STACK_FACTOR 2 +#else +#define ARCH_STACK_FACTOR 2 +#endif +static const unsigned stackSizeTable[epicsThreadStackBig+1] = + {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR}; + +/* Table and lock for epicsThreadMap() */ +#define ID_LIST_CHUNK 512 +static int *taskIdList = 0; +int taskIdListSize = 0; +static SEM_ID epicsThreadListMutex = 0; + +/* This routine is found in atReboot.cpp */ +extern void atRebootRegister(void); + +/* definitions for implementation of epicsThreadPrivate */ +static void **papTSD = 0; +static int nepicsThreadPrivate = 0; + +static SEM_ID epicsThreadOnceMutex = 0; + +/* Just map osi 0 to 99 into vx 100 to 199 */ +/* remember that for vxWorks lower number means higher priority */ +/* vx = 100 + (99 -osi) = 199 - osi*/ +/* osi = 199 - vx */ + +static unsigned int getOsiPriorityValue(int ossPriority) +{ + if ( ossPriority < 100 ) { + return epicsThreadPriorityMax; + } + else if ( ossPriority > 199 ) { + return epicsThreadPriorityMin; + } + else { + return ( 199u - (unsigned int) ossPriority ); + } +} + +static int getOssPriorityValue(unsigned int osiPriority) +{ + if ( osiPriority > 99 ) { + return 100; + } + else { + return ( 199 - (signed int) osiPriority ); + } +} + +#define THREAD_SEM_FLAGS (SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY) +static void epicsThreadInit(void) +{ + static int lock = 0; + static int done = 0; + + if (done) return; + + while(!vxTas(&lock)) + taskDelay(1); + + if (!done) { + epicsThreadOnceMutex = semMCreate(THREAD_SEM_FLAGS); + assert(epicsThreadOnceMutex); + epicsThreadListMutex = semMCreate(THREAD_SEM_FLAGS); + assert(epicsThreadListMutex); + taskIdList = calloc(ID_LIST_CHUNK, sizeof(int)); + assert(taskIdList); + taskIdListSize = ID_LIST_CHUNK; + atRebootRegister(); + done = 1; + } + lock = 0; +} + +void epicsThreadRealtimeLock(void) +{} + +unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) +{ + + if (stackSizeClassepicsThreadStackBig) { + errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +} + +struct epicsThreadOSD {}; + /* Strictly speaking this should be a WIND_TCB, but we only need it to + * be able to create an epicsThreadId that is guaranteed never to be + * the same as any current TID, and since TIDs are pointers this works. + */ + +void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE &threadOnceComplete + int result; + + epicsThreadInit(); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + semGive(epicsThreadOnceMutex); + func(arg); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + semGive(epicsThreadOnceMutex); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + semGive(epicsThreadOnceMutex); + epicsThreadSleep(epicsThreadSleepQuantum()); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + } + } + semGive(epicsThreadOnceMutex); +} + +static void createFunction(EPICSTHREADFUNC func, void *parm) +{ + int tid = taskIdSelf(); + + taskVarAdd(tid,(int *)(char *)&papTSD); + /*Make sure that papTSD is still 0 after that call to taskVarAdd*/ + papTSD = 0; + osdThreadHooksRun((epicsThreadId)tid); + (*func)(parm); + epicsExitCallAtThreadExits (); + free(papTSD); + taskVarDelete(tid,(int *)(char *)&papTSD); +} + +#ifdef ALTIVEC + #define TASK_FLAGS (VX_FP_TASK | VX_ALTIVEC_TASK) +#else + #define TASK_FLAGS (VX_FP_TASK) +#endif +epicsThreadId epicsThreadCreate(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + int tid; + + epicsThreadInit(); + if(stackSize<100) { + errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,stackSize); + return(0); + } + tid = taskSpawn((char *)name,getOssPriorityValue(priority), + TASK_FLAGS, stackSize, + (FUNCPTR)createFunction,(int)funptr,(int)parm, + 0,0,0,0,0,0,0,0); + if(tid==ERROR) { + errlogPrintf("epicsThreadCreate %s failure %s\n", + name,strerror(errno)); + return(0); + } + return((epicsThreadId)tid); +} + +void epicsThreadSuspendSelf() +{ + STATUS status; + + status = taskSuspend(taskIdSelf()); + if(status) errlogPrintf("epicsThreadSuspendSelf failed\n"); +} + +void epicsThreadResume(epicsThreadId id) +{ + int tid = (int)id; + STATUS status; + + status = taskResume(tid); + if(status) errlogPrintf("epicsThreadResume failed\n"); +} + +void epicsThreadExitMain(void) +{ + errlogPrintf("epicsThreadExitMain was called for vxWorks. Why?\n"); +} + +unsigned int epicsThreadGetPriority(epicsThreadId id) +{ + int tid = (int)id; + STATUS status; + int priority = 0; + + status = taskPriorityGet(tid,&priority); + if(status) errlogPrintf("epicsThreadGetPriority failed\n"); + return(getOsiPriorityValue(priority)); +} + +unsigned int epicsThreadGetPrioritySelf(void) +{ + return(epicsThreadGetPriority(epicsThreadGetIdSelf())); +} + +void epicsThreadSetPriority(epicsThreadId id,unsigned int osip) +{ + int tid = (int)id; + STATUS status; + int priority = 0; + + priority = getOssPriorityValue(osip); + status = taskPrioritySet(tid,priority); + if(status) errlogPrintf("epicsThreadSetPriority failed\n"); +} + +epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow( + unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove( + unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + + newPriority = priority + 1; + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +int epicsThreadIsEqual(epicsThreadId id1, epicsThreadId id2) +{ + return((id1==id2) ? 1 : 0); +} + +int epicsThreadIsSuspended(epicsThreadId id) +{ + int tid = (int)id; + return((int)taskIsSuspended(tid)); +} + +void epicsThreadSleep(double seconds) +{ + STATUS status; + int ticks; + + if (seconds > 0.0) { + seconds *= sysClkRateGet(); + seconds += 0.99999999; /* 8 9s here is optimal */ + ticks = (seconds >= INT_MAX) ? INT_MAX : (int) seconds; + } + else { /* seconds <= 0 or NAN */ + ticks = 0; + } + status = taskDelay(ticks); + if(status) errlogPrintf("epicsThreadSleep\n"); +} + +epicsThreadId epicsThreadGetIdSelf(void) +{ + return((epicsThreadId)taskIdSelf()); +} + +epicsThreadId epicsThreadGetId(const char *name) +{ + int tid = taskNameToId((char *)name); + return((epicsThreadId)(tid==ERROR?0:tid)); +} + +const char *epicsThreadGetNameSelf(void) +{ + return taskName(taskIdSelf()); +} + +void epicsThreadGetName (epicsThreadId id, char *name, size_t size) +{ + int tid = (int)id; + strncpy(name,taskName(tid),size-1); + name[size-1] = '\0'; +} + +epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) +{ + int noTasks = 0; + int i; + int result; + + result = semTake(epicsThreadListMutex, WAIT_FOREVER); + assert(result == OK); + while (noTasks == 0) { + noTasks = taskIdListGet(taskIdList, taskIdListSize); + if (noTasks == taskIdListSize) { + taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); + assert(taskIdList); + taskIdListSize += ID_LIST_CHUNK; + noTasks = 0; + } + } + for (i = 0; i < noTasks; i++) { + func ((epicsThreadId)taskIdList[i]); + } + semGive(epicsThreadListMutex); +} + +void epicsThreadShowAll(unsigned int level) +{ + taskShow(0,2); +} + +void epicsThreadShow(epicsThreadId id, unsigned int level) +{ + int tid = (int)id; + + if (level > 1) level = 1; + if (tid) + taskShow(tid, level); +} + +/* The following algorithm was thought of by Andrew Johnson APS/ASD . + * The basic idea is to use a single vxWorks task variable. + * The task variable is papTSD, which is an array of pointers to the TSD + * The array size is equal to the number of epicsThreadPrivateIds created + 1 + * when epicsThreadPrivateSet is called. + * Until the first call to epicsThreadPrivateCreate by a application papTSD=0 + * After first call papTSD[0] is value of nepicsThreadPrivate when + * epicsThreadPrivateSet was last called by the thread. This is also + * the value of epicsThreadPrivateId. + * The algorithm allows for epicsThreadPrivateCreate being called after + * the first call to epicsThreadPrivateSet. + */ +epicsThreadPrivateId epicsThreadPrivateCreate() +{ + static int lock = 0; + epicsThreadPrivateId id; + + epicsThreadInit(); + /*lock is necessary because ++nepicsThreadPrivate may not be indivisible*/ + while(!vxTas(&lock)) taskDelay(1); + id = (epicsThreadPrivateId)++nepicsThreadPrivate; + lock = 0; + return(id); +} + +void epicsThreadPrivateDelete(epicsThreadPrivateId id) +{ + /*nothing to delete */ + return; +} + +/* + * No mutex is necessary here because by definition + * thread variables are local to a single thread. + */ +void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) +{ + int int_id = (int)id; + int nepicsThreadPrivateOld = 0; + + if (papTSD) nepicsThreadPrivateOld = (int)papTSD[0]; + + if (!papTSD || nepicsThreadPrivateOld < int_id) { + void **papTSDold = papTSD; + int i; + + papTSD = callocMustSucceed(int_id + 1,sizeof(void *), + "epicsThreadPrivateSet"); + papTSD[0] = (void *)(int_id); + for (i = 1; i <= nepicsThreadPrivateOld; i++) { + papTSD[i] = papTSDold[i]; + } + free (papTSDold); + } + papTSD[int_id] = pvt; +} + +void *epicsThreadPrivateGet(epicsThreadPrivateId id) +{ + int int_id = (int)id; + + /* + * If epicsThreadPrivateGet() is called before epicsThreadPrivateSet() + * for any particular thread, the value returned is always NULL. + */ + if ( papTSD && int_id <= (int) papTSD[0] ) { + return papTSD[int_id]; + } + return NULL; +} + +double epicsThreadSleepQuantum () +{ + double HZ = sysClkRateGet (); + return 1.0 / HZ; +} + +epicsShareFunc int epicsThreadGetCPUs(void) +{ + return 1; +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.h b/modules/libcom/src/osi/os/vxWorks/osdThread.h new file mode 100644 index 000000000..2ee9f2d46 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdThread.h @@ -0,0 +1,13 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef osdThreadh +#define osdThreadh + +#endif /* osdThreadh */ diff --git a/modules/libcom/src/osi/os/vxWorks/osdThreadExtra.c b/modules/libcom/src/osi/os/vxWorks/osdThreadExtra.c new file mode 100644 index 000000000..0a7c7ae95 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdThreadExtra.c @@ -0,0 +1,16 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Ralph Lange Date: 26 Jun 2012 */ + +/* Null default thread hooks for all platforms that do not do anything special */ + +#define epicsExportSharedSymbols +#include "epicsThread.h" + +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookDefault; +epicsShareDef EPICS_THREAD_HOOK_ROUTINE epicsThreadHookMain; diff --git a/modules/libcom/src/osi/os/vxWorks/osdTime.cpp b/modules/libcom/src/osi/os/vxWorks/osdTime.cpp new file mode 100644 index 000000000..fdfefb704 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdTime.cpp @@ -0,0 +1,118 @@ +/*************************************************************************\ +* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "osiNTPTime.h" +#include "osiClockTime.h" +#include "generalTimeSup.h" +#include "envDefs.h" + +#define NTP_REQUEST_TIMEOUT 4 /* seconds */ + +static char sntp_sync_task[] = "ipsntps"; +static char ntp_daemon[] = "ipntpd"; + +static const char *pserverAddr = NULL; +extern char* sysBootLine; + +static int timeRegister(void) +{ + /* If TIMEZONE not defined, set it from EPICS_TIMEZONE */ + if (getenv("TIMEZONE") == NULL) { + const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); + if (timezone == NULL) { + printf("timeRegister: No Time Zone Information\n"); + } else { + epicsEnvSet("TIMEZONE", timezone); + } + } + + // Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider + bool useNTP = getenv("EPICS_TS_FORCE_NTPTIME") != NULL; + + if (!useNTP && + (taskNameToId(sntp_sync_task) != ERROR || + taskNameToId(ntp_daemon) != ERROR)) { + // A VxWorks 6 SNTP/NTP sync task is running + struct timespec clockNow; + + useNTP = clock_gettime(CLOCK_REALTIME, &clockNow) != OK || + clockNow.tv_sec < BUILD_TIME; + // Assumes VxWorks and the host OS have the same epoch + } + + if (useNTP) { + // Start NTP first so it can be used to sync SysTime + NTPTime_Init(100); + ClockTime_Init(CLOCKTIME_SYNC); + } else { + ClockTime_Init(CLOCKTIME_NOSYNC); + } + osdMonotonicInit(); + return 1; +} +static int done = timeRegister(); + + +// Routines for NTPTime provider + +int osdNTPGet(struct timespec *ts) +{ + return sntpcTimeGet(const_cast(pserverAddr), + NTP_REQUEST_TIMEOUT * sysClkRateGet(), ts); +} + +void osdNTPInit(void) +{ + pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); + if (!pserverAddr) { /* use the boot host */ + BOOT_PARAMS bootParms; + static char host_addr[BOOT_ADDR_LEN]; + + bootStringToStruct(sysBootLine, &bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr, bootParms.had, BOOT_ADDR_LEN); + pserverAddr = host_addr; + } +} + +void osdNTPReport(void) +{ + if (pserverAddr) + printf("NTP Server = %s\n", pserverAddr); +} + + +// Other Time Routines + +// vxWorks localtime_r returns different things in different versions. +// It can't fail though, so we just ignore the return value. +int epicsTime_localtime(const time_t *clock, struct tm *result) +{ + localtime_r(clock, result); + return epicsTimeOK; +} + +// vxWorks gmtime_r returns different things in different versions. +// It can't fail though, so we just ignore the return value. +int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + gmtime_r(pAnsiTime, pTM); + return epicsTimeOK; +} diff --git a/modules/libcom/src/osi/os/vxWorks/osdTime.h b/modules/libcom/src/osi/os/vxWorks/osdTime.h new file mode 100644 index 000000000..a816c7ae3 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdTime.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Following needed for struct timeval */ +#include + +#ifndef osdTimeh +#define osdTimeh + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void osdNTPInit(void); +int osdNTPGet(struct timespec *); +void osdNTPReport(void); + +#define osdTickRateGet sysClkRateGet +#define osdTickGet tickGet + +#ifdef __cplusplus +} +#endif +#endif /* ifndef osdTimeh */ diff --git a/modules/libcom/src/osi/os/vxWorks/osdVME.h b/modules/libcom/src/osi/os/vxWorks/osdVME.h new file mode 100644 index 000000000..b9d031a04 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdVME.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * OS-dependent VME support + */ +#include diff --git a/modules/libcom/src/osi/os/vxWorks/osdWireConfig.h b/modules/libcom/src/osi/os/vxWorks/osdWireConfig.h new file mode 100644 index 000000000..408ec6758 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osdWireConfig.h @@ -0,0 +1,26 @@ +/* + * vxWorks version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +#include +#include + +#if _BYTE_ORDER == _LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#elif _BYTE_ORDER == _BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +#else +# error EPICS hasnt been ported to _BYTE_ORDER specified by vxWorks +#endif + +/* for now, assume that vxWorks doesnt run on weird arch like ARM NWFP */ +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ diff --git a/modules/libcom/src/osi/os/vxWorks/osiFileName.h b/modules/libcom/src/osi/os/vxWorks/osiFileName.h new file mode 100644 index 000000000..3a6448d54 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/osiFileName.h @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/modules/libcom/src/osi/os/vxWorks/strtoll.c b/modules/libcom/src/osi/os/vxWorks/strtoll.c new file mode 100644 index 000000000..54e0a9522 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/strtoll.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#include +#include +#include +#include + +#ifndef LLONG_MAX +#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL +#define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1) +#endif + +/* + * Convert a string to a long long integer. + * + * Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +long long +strtoll(const char * __restrict nptr, char ** __restrict endptr, int base) +{ + const char *s; + unsigned long long acc; + char c; + unsigned long long cutoff; + int neg, any, cutlim; + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X') && + ((s[1] >= '0' && s[1] <= '9') || + (s[1] >= 'A' && s[1] <= 'F') || + (s[1] >= 'a' && s[1] <= 'f'))) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for quads is + * [-9223372036854775808..9223372036854775807] and the input base + * is 10, cutoff will be set to 922337203685477580 and cutlim to + * either 7 (neg==0) or 8 (neg==1), meaning that if we have + * accumulated a value > 922337203685477580, or equal but the + * next digit is > 7 (or 8), the number is too big, and we will + * return a range error. + * + * Set 'any' if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX + : LLONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + for ( ; ; c = *s++) { + 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 + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LLONG_MIN : LLONG_MAX; + errno = ERANGE; + } else if (!any) { +noconv: + errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} diff --git a/modules/libcom/src/osi/os/vxWorks/strtoull.c b/modules/libcom/src/osi/os/vxWorks/strtoull.c new file mode 100644 index 000000000..197a45bcf --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/strtoull.c @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + */ + +#include +#include +#include +#include + +#ifndef ULLONG_MAX +#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL +#endif + +/* + * Convert a string to an unsigned long long integer. + * + * Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long long +strtoull(const char * __restrict nptr, char ** __restrict endptr, int base) +{ + const char *s; + unsigned long long acc; + char c; + unsigned long long cutoff; + int neg, any, cutlim; + + /* + * See strtoq for comments as to the logic used. + */ + s = nptr; + do { + c = *s++; + } while (isspace((unsigned char)c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X') && + ((s[1] >= '0' && s[1] <= '9') || + (s[1] >= 'A' && s[1] <= 'F') || + (s[1] >= 'a' && s[1] <= 'f'))) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + acc = any = 0; + if (base < 2 || base > 36) + goto noconv; + + cutoff = ULLONG_MAX / base; + cutlim = ULLONG_MAX % base; + for ( ; ; c = *s++) { + 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 + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULLONG_MAX; + errno = ERANGE; + } else if (!any) { +noconv: + errno = EINVAL; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (char *)(any ? s - 1 : nptr); + return (acc); +} diff --git a/modules/libcom/src/osi/os/vxWorks/task_params.h b/modules/libcom/src/osi/os/vxWorks/task_params.h new file mode 100644 index 000000000..7b2055157 --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/task_params.h @@ -0,0 +1,184 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Parameters for tasks on IOC */ +/* + * Authors: Andy Kozubal, Jeff Hill, and Bob Dalesio + * Date: 2-24-89 + */ + +#ifndef INCtaskLibh +#include +#endif + +#define VXTASKIDSELF 0 + +/* Task Names */ +#define EVENTSCAN_NAME "scanEvent" +#define SCANONCE_NAME "scanOnce" +#define SMCMD_NAME "smCommand" +#define SMRESP_NAME "smResponse" +#define ABDONE_NAME "abDone" +#define ABSCAN_NAME "abScan" +#define ABCOS_NAME "abBiCosScanner" +#define MOMENTARY_NAME "momentary" +#define WFDONE_NAME "wfDone" +#define SEQUENCER_NAME "sequencer" +#define BKPT_CONT_NAME "bkptCont" +#define SCANNER_NAME "scanner" +#define REQ_SRVR_NAME "CA_TCP" +#define CA_CLIENT_NAME "CA_client" +#define CA_EVENT_NAME "CA_event" +#define CAST_SRVR_NAME "CA_UDP" +#define CA_REPEATER_NAME "CA_repeater" +#define CA_ONLINE_NAME "CA_online" +#define TASKWD_NAME "taskwd" +#define SMIOTEST_NAME "smInout" +#define SMROTTEST_NAME "smRotate" +#define EVENT_PEND_NAME "event_task" +#define XY240_NAME "xy_240_scan" +#define GPIBLINK_NAME "gpibLink" +#define BBLINK_NAME "bbLinkTask" +#define BBTXLINK_NAME "bbTx" +#define BBRXLINK_NAME "bbRx" +#define BBWDTASK_NAME "bbwd" +#define ERRLOG_NAME "errlog" +#define LOG_RESTART_NAME "logRestart" + +/* Task priorities */ +#define SCANONCE_PRI 129 /* scan one time */ +/*DO NOT RUN ANY RECORD PROCESSING TASKS AT HIGHER PRIORITY THAN _netTask=50*/ +#define CALLBACK_PRI_LOW 140 /* callback task - generall callback task */ +#define CALLBACK_PRI_MEDIUM 135 /* callback task - generall callback task */ +#define CALLBACK_PRI_HIGH 128 /* callback task - generall callback task */ +#define EVENTSCAN_PRI 129 /* Event Scanner - Runs on a global event */ +#define SMCMD_PRI 120 /* Stepper Motor Command Task - Waits for cmds */ +#define SMRESP_PRI 121 /* Stepper Motor Resp Task - Waits for resps */ +#define ABCOS_PRI 121 /* Allen-Bradley Binary Input COS io_event wakeup */ +#define ABDONE_PRI 122 /* Allen-Bradley Resp Task - Interrupt Driven */ +#define ABSCAN_PRI 123 /* Allen-Bradley Scan Task - Base Rate .1 secs */ +#define BBLINK_PRI 124 +#define BBWDTASK_PRI 123 /* BitBus watchdog task */ +#define BBRXLINK_PRI 124 /* BitBus link task */ +#define BBTXLINK_PRI 125 /* BitBus link task */ +#define GPIBLINK_PRI 125 /* GPIB link task */ +#define MOMENTARY_PRI 126 /* Momentary output - posted from watchdog */ +#define WFDONE_PRI 127 /* Waveform Task - Base Rate of .1 second */ +#define PERIODSCAN_PRI 139 /* Periodic Scanners - Slowest rate */ +#define DB_CA_PRI 149 /*database to channel access*/ +#define SEQUENCER_PRI 151 +#define XY240_PRI 160 /* xy 240 dio scanner */ +#define SCANNER_PRI 170 +#define REQ_SRVR_PRI 181 /* Channel Access TCP request server*/ +#define CA_CLIENT_PRI 180 /* Channel Access clients */ +#define CA_REPEATER_PRI 181 /* Channel Access repeater */ +#define ERRLOG_PRI CA_REPEATER_PRI /*error logger task*/ +#define CAST_SRVR_PRI 182 /* Channel Access broadcast server */ +#define CA_ONLINE_PRI 183 /* Channel Access server online notify */ +#define TASKWD_PRI 200 /* Watchdog Scan Task - runs every 6 seconds */ +#define SMIOTEST_PRI 205 /* Stepper Mtr in/out test - runs every .1 sec */ +#define SMROTTEST_PRI 205 /* Stepper Mtr rotate test - runs every .1 sec */ +#define LOG_RESTART_PRI 200 /* Log server connection watch dog */ + +/* Task delay times (seconds) */ +#define TASKWD_DELAY 6 + +/* Task delay times (tics) */ +#define ABSCAN_RATE (sysClkRateGet()/6) +#define SEQUENCER_DELAY (sysClkRateGet()/5) +#define SCANNER_DELAY (sysClkRateGet()/5) +#define CA_ONLINE_DELAY (sysClkRateGet()*15) +#define LOG_RESTART_DELAY (sysClkRateGet()*30) + +/* Task creation options */ +#define ERRLOG_OPT VX_FP_TASK +#define EVENTSCAN_OPT VX_FP_TASK +#define SCANONCE_OPT VX_FP_TASK +#define CALLBACK_OPT VX_FP_TASK +#define SMCMD_OPT VX_FP_TASK +#define SMRESP_OPT VX_FP_TASK +#define ABDONE_OPT VX_FP_TASK +#define ABCOS_OPT VX_FP_TASK +#define ABSCAN_OPT VX_FP_TASK +#define MOMENTARY_OPT VX_FP_TASK +#define PERIODSCAN_OPT VX_FP_TASK +#define WFDONE_OPT VX_FP_TASK +#define SEQUENCER_OPT VX_FP_TASK | VX_STDIO +#define BKPT_CONT_OPT VX_FP_TASK +#define SCANNER_OPT VX_FP_TASK +#define REQ_SRVR_OPT VX_FP_TASK +#define CAST_SRVR_OPT VX_FP_TASK +#define CA_CLIENT_OPT VX_FP_TASK +#define CA_REPEATER_OPT VX_FP_TASK +#define CA_ONLINE_OPT VX_FP_TASK +#define TASKWD_OPT VX_FP_TASK +#define SMIOTEST_OPT VX_FP_TASK +#define SMROTTEST_OPT VX_FP_TASK +#define EVENT_PEND_OPT VX_FP_TASK +#define GPIBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBTXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBRXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBWDTASK_OPT VX_FP_TASK|VX_STDIO +#define DB_CA_OPT (VX_FP_TASK | VX_STDIO) +#define XY_240_OPT (0) /* none */ +#define LOG_RESTART_OPT (VX_FP_TASK) + + +/* + * Task stack sizes + * + * (original stack sizes are appropriate for the 68k) + * ARCH_STACK_FACTOR allows scaling the stacks on particular + * processor architectures + */ +#if CPU_FAMILY == MC680X0 +#define ARCH_STACK_FACTOR 1 +#elif CPU_FAMILY == SPARC +#define ARCH_STACK_FACTOR 2 +#else +#define ARCH_STACK_FACTOR 2 +#endif + +#define ERRLOG_STACK (4000*ARCH_STACK_FACTOR) +#define EVENTSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define SCANONCE_STACK (11000*ARCH_STACK_FACTOR) +#define CALLBACK_STACK (11000*ARCH_STACK_FACTOR) +#define SMCMD_STACK (3000*ARCH_STACK_FACTOR) +#define SMRESP_STACK (3000*ARCH_STACK_FACTOR) +#define ABCOS_STACK (3000*ARCH_STACK_FACTOR) +#define ABDONE_STACK (3000*ARCH_STACK_FACTOR) +#define ABSCAN_STACK (3000*ARCH_STACK_FACTOR) +#define MOMENTARY_STACK (2000*ARCH_STACK_FACTOR) +#define PERIODSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define WFDONE_STACK (9000*ARCH_STACK_FACTOR) +#define SEQUENCER_STACK (5500*ARCH_STACK_FACTOR) +#define BKPT_CONT_STACK (11000*ARCH_STACK_FACTOR) +#define SCANNER_STACK (3048*ARCH_STACK_FACTOR) +#define RSP_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define REQ_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CAST_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CA_CLIENT_STACK (11000*ARCH_STACK_FACTOR) +#define CA_REPEATER_STACK (5096*ARCH_STACK_FACTOR) +#define CA_ONLINE_STACK (3048*ARCH_STACK_FACTOR) +#define TASKWD_STACK (2000*ARCH_STACK_FACTOR) +#define SMIOTEST_STACK (2000*ARCH_STACK_FACTOR) +#define SMROTTEST_STACK (2000*ARCH_STACK_FACTOR) +#define EVENT_PEND_STACK (5096*ARCH_STACK_FACTOR) +#define TIMESTAMP_STACK (4000*ARCH_STACK_FACTOR) +#define GPIBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBRXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBTXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBWDTASK_STACK (5000*ARCH_STACK_FACTOR) +#define DB_CA_STACK (11000*ARCH_STACK_FACTOR) +#define XY_240_STACK (4096*ARCH_STACK_FACTOR) +#define LOG_RESTART_STACK (0x1000*ARCH_STACK_FACTOR) + diff --git a/modules/libcom/src/osi/os/vxWorks/veclist.c b/modules/libcom/src/osi/os/vxWorks/veclist.c new file mode 100644 index 000000000..2a4e617ef --- /dev/null +++ b/modules/libcom/src/osi/os/vxWorks/veclist.c @@ -0,0 +1,208 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * list fuctions attached to the interrupt vector table + * + * Created 28Mar89 Jeffrey O. Hill + * johill@lanl.gov + * (505) 665 1831 + * + */ + +#include "vxWorks.h" +#include "stdio.h" +#include "string.h" +#include "intLib.h" +#include "vxLib.h" +#include "iv.h" +#include "ctype.h" +#include "sysSymTbl.h" + +/* + * + * VME bus dependent + * + */ +#define NVEC 0x100 + +static char *ignore_list[] = {"_excStub","_excIntStub"}; + +int veclist(int); +int cISRTest(FUNCPTR proutine, FUNCPTR *ppisr, void **pparam); +static void *fetch_pointer(unsigned char *); + + +/* + * + * veclist() + * + */ +int veclist(int all) +{ + int vec; + int value; + SYM_TYPE type; + char name[MAX_SYS_SYM_LEN]; + char function_type[10]; + FUNCPTR proutine; + FUNCPTR pCISR; + int cRoutine; + void *pparam; + int status; + unsigned i; + + for(vec=0; vec +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "errlog.h" +#include "epicsGeneralTime.h" +#include "generalTimeSup.h" +#include "iocsh.h" +#include "osiClockTime.h" +#include "taskwd.h" + +#define NSEC_PER_SEC 1000000000 +#define ClockTimeSyncInterval_value 60.0 + + +static struct { + int synchronize; + int synchronized; + epicsEventId loopEvent; + epicsTimeStamp startTime; + epicsTimeStamp syncTime; + double ClockTimeSyncInterval; + int syncFromPriority; + epicsMutexId lock; +} ClockTimePvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + +#if defined(CLOCK_REALTIME) && !defined(_WIN32) +/* This code is not used on systems without Posix CLOCK_REALTIME such + * as Darwin, but the only way to detect that is from the OS headers, + * so the Makefile can't exclude building this file on those systems. + */ + +/* Forward references */ + +static int ClockTimeGetCurrent(epicsTimeStamp *pDest); +static void ClockTimeSync(void *dummy); + + +/* ClockTime_Report iocsh command */ +static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; +static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs}; +static void ReportCallFunc(const iocshArgBuf *args) +{ + ClockTime_Report(args[0].ival); +} + +/* ClockTime_Shutdown iocsh command */ +static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL}; +static void ShutdownCallFunc(const iocshArgBuf *args) +{ + ClockTime_Shutdown(NULL); +} + + +/* Initialization */ + +static void ClockTime_InitOnce(void *pfirst) +{ + *(int *) pfirst = 1; + + ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); + ClockTimePvt.lock = epicsMutexCreate(); + ClockTimePvt.ClockTimeSyncInterval = 1.0; /* First sync */ + + epicsAtExit(ClockTime_Shutdown, NULL); + + /* Register the iocsh commands */ + iocshRegister(&ReportFuncDef, ReportCallFunc); + iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); + + /* Register as a time provider */ + generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY, + ClockTimeGetCurrent); +} + +void ClockTime_Init(int synchronize) +{ + int firstTime = 0; + + epicsThreadOnce(&onceId, ClockTime_InitOnce, &firstTime); + + if (synchronize == CLOCKTIME_SYNC) { + if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) { + /* Start synchronizing */ + ClockTimePvt.synchronize = synchronize; + + epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + ClockTimeSync, NULL); + } + else { + /* No change, sync thread should already be running */ + } + } + else { + if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) { + /* Turn off synchronization thread */ + ClockTime_Shutdown(NULL); + } + else { + /* No synchronization thread */ + if (firstTime) + ClockTimeGetCurrent(&ClockTimePvt.startTime); + } + } +} + + +/* Shutdown */ + +void ClockTime_Shutdown(void *dummy) +{ + ClockTimePvt.synchronize = CLOCKTIME_NOSYNC; + epicsEventSignal(ClockTimePvt.loopEvent); +} + +void ClockTime_GetProgramStart(epicsTimeStamp *pDest) +{ + *pDest = ClockTimePvt.startTime; +} + + +/* Synchronization thread */ + +static void ClockTimeSync(void *dummy) +{ + taskwdInsert(0, NULL, NULL); + + for (epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, + ClockTimePvt.ClockTimeSyncInterval); + ClockTimePvt.synchronize == CLOCKTIME_SYNC; + epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, + ClockTimePvt.ClockTimeSyncInterval)) { + epicsTimeStamp timeNow; + int priority; + + if (generalTimeGetExceptPriority(&timeNow, &priority, + LAST_RESORT_PRIORITY) == epicsTimeOK) { + struct timespec clockNow; + + epicsTimeToTimespec(&clockNow, &timeNow); + if (clock_settime(CLOCK_REALTIME, &clockNow)) { + errlogPrintf("ClockTimeSync: clock_settime failed\n"); + continue; + } + + epicsMutexMustLock(ClockTimePvt.lock); + if (!ClockTimePvt.synchronized) { + ClockTimePvt.startTime = timeNow; + ClockTimePvt.synchronized = 1; + } + ClockTimePvt.syncFromPriority = priority; + ClockTimePvt.syncTime = timeNow; + epicsMutexUnlock(ClockTimePvt.lock); + + ClockTimePvt.ClockTimeSyncInterval = ClockTimeSyncInterval_value; + } + } + + ClockTimePvt.synchronized = 0; + taskwdRemove(0); +} + + +/* Time Provider Routine */ + +static int ClockTimeGetCurrent(epicsTimeStamp *pDest) +{ + struct timespec clockNow; + + /* If a Hi-Res clock is available and works, use it */ + #ifdef CLOCK_REALTIME_HR + clock_gettime(CLOCK_REALTIME_HR, &clockNow) && + #endif + clock_gettime(CLOCK_REALTIME, &clockNow); + + if (!ClockTimePvt.synchronized && + clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) { + clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400; + clockNow.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &clockNow); + errlogPrintf("WARNING: OS Clock time was read before being set.\n" + "Using 1990-01-02 00:00:00.000000 UTC\n"); + } + + epicsTimeFromTimespec(pDest, &clockNow); + return 0; +} + +#endif /* CLOCK_REALTIME */ + +/* Allow the following report routine to be compiled anyway + * to avoid getting a build warning from ranlib. + */ + +/* Status Report */ + +int ClockTime_Report(int level) +{ + char timebuf[32]; + + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("OS Clock driver not %s.\n", +#ifdef CLOCK_REALTIME + "initialized" +#else + "available" +#endif /* CLOCK_REALTIME */ + ); + } + else if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) { + int synchronized, syncFromPriority; + epicsTimeStamp startTime, syncTime; + + epicsMutexMustLock(ClockTimePvt.lock); + synchronized = ClockTimePvt.synchronized; + syncFromPriority = ClockTimePvt.syncFromPriority; + startTime = ClockTimePvt.startTime; + syncTime = ClockTimePvt.syncTime; + epicsMutexUnlock(ClockTimePvt.lock); + + if (synchronized) { + printf("OS Clock driver is synchronized to a priority=%d provider\n", + syncFromPriority); + if (level) { + epicsTimeToStrftime(timebuf, sizeof(timebuf), + "%Y-%m-%d %H:%M:%S.%06f", &startTime); + printf("Initial sync was at %s\n", timebuf); + epicsTimeToStrftime(timebuf, sizeof(timebuf), + "%Y-%m-%d %H:%M:%S.%06f", &syncTime); + printf("Last successful sync was at %s\n", timebuf); + } + printf("Syncronization interval = %.0f seconds\n", + ClockTimePvt.ClockTimeSyncInterval); + } + else + printf("OS Clock driver is *not* synchronized\n"); + } + else { + epicsTimeToStrftime(timebuf, sizeof(timebuf), + "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime); + printf("Program started at %s\n", timebuf); + printf("OS Clock synchronization thread not running.\n"); + } + return 0; +} diff --git a/modules/libcom/src/osi/osiClockTime.h b/modules/libcom/src/osi/osiClockTime.h new file mode 100644 index 000000000..17eacab3e --- /dev/null +++ b/modules/libcom/src/osi/osiClockTime.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiClockTime_H +#define INC_osiClockTime_H + +#define CLOCKTIME_NOSYNC 0 +#define CLOCKTIME_SYNC 1 + +#ifdef __cplusplus +extern "C" { +#endif + +void ClockTime_Init(int synchronize); +void ClockTime_Shutdown(void *dummy); +int ClockTime_Report(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/osi/osiNTPTime.c b/modules/libcom/src/osi/osiNTPTime.c new file mode 100644 index 000000000..d22df861b --- /dev/null +++ b/modules/libcom/src/osi/osiNTPTime.c @@ -0,0 +1,284 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Original Author: Marty Kraimer + * Date: 16JUN2000 + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsTypes.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "epicsGeneralTime.h" +#include "generalTimeSup.h" +#include "iocsh.h" +#include "osdTime.h" +#include "osiNTPTime.h" +#include "taskwd.h" + +#define NSEC_PER_SEC 1000000000 +#define NTPTimeSyncInterval 60.0 +#define NTPTimeSyncRetries 4 + + +static struct { + int synchronize; + int synchronized; + epicsEventId loopEvent; + int syncsFailed; + epicsMutexId lock; + epicsTimeStamp syncTime; + epicsUInt32 syncTick; + epicsTimeStamp clockTime; + epicsUInt32 clockTick; + epicsUInt32 ticksToSkip; + double tickRate; +} NTPTimePvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + +/* Forward references */ + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest); +static void NTPTimeSync(void *dummy); + + +/* NTPTime_Report iocsh command */ +static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; +static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs}; +static void ReportCallFunc(const iocshArgBuf *args) +{ + NTPTime_Report(args[0].ival); +} + +/* NTPTime_Shutdown iocsh command */ +static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL}; +static void ShutdownCallFunc(const iocshArgBuf *args) +{ + NTPTime_Shutdown(NULL); +} + + +/* Initialization */ + +static void NTPTime_InitOnce(void *pprio) +{ + struct timespec timespecNow; + + NTPTimePvt.synchronize = 1; + NTPTimePvt.synchronized = 0; + NTPTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); + NTPTimePvt.syncsFailed = 0; + NTPTimePvt.lock = epicsMutexCreate(); + + /* Initialize OS-dependent code */ + osdNTPInit(); + + /* Try to sync with NTP server */ + if (!osdNTPGet(×pecNow)) { + NTPTimePvt.syncTick = osdTickGet(); + if (timespecNow.tv_sec > POSIX_TIME_AT_EPICS_EPOCH && epicsTimeOK == + epicsTimeFromTimespec(&NTPTimePvt.syncTime, ×pecNow)) { + NTPTimePvt.clockTick = NTPTimePvt.syncTick; + NTPTimePvt.clockTime = NTPTimePvt.syncTime; + NTPTimePvt.synchronized = 1; + } + } + + /* Start the sync thread */ + epicsThreadCreate("NTPTimeSync", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + NTPTimeSync, NULL); + + epicsAtExit(NTPTime_Shutdown, NULL); + + /* Register the iocsh commands */ + iocshRegister(&ReportFuncDef, ReportCallFunc); + iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); + + /* Finally register as a time provider */ + generalTimeRegisterCurrentProvider("NTP", *(int *)pprio, NTPTimeGetCurrent); +} + +void NTPTime_Init(int priority) +{ + epicsThreadOnce(&onceId, NTPTime_InitOnce, &priority); +} + + +/* Shutdown */ + +void NTPTime_Shutdown(void *dummy) +{ + NTPTimePvt.synchronize = 0; + epicsEventSignal(NTPTimePvt.loopEvent); +} + + +/* Synchronization thread */ + +static void NTPTimeSync(void *dummy) +{ + taskwdInsert(0, NULL, NULL); + + for (epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval); + NTPTimePvt.synchronize; + epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval)) { + int status; + struct timespec timespecNow; + epicsTimeStamp timeNow; + epicsUInt32 tickNow; + double diff; + double ntpDelta; + + status = osdNTPGet(×pecNow); + tickNow = osdTickGet(); + + if (status) { + if (++NTPTimePvt.syncsFailed > NTPTimeSyncRetries && + NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: NTP requests failing - %s\n", + strerror(errno)); + NTPTimePvt.synchronized = 0; + } + continue; + } + + if (timespecNow.tv_sec <= POSIX_TIME_AT_EPICS_EPOCH || + epicsTimeFromTimespec(&timeNow, ×pecNow) != epicsTimeOK) { + errlogPrintf("NTPTimeSync: Bad time received from NTP server\n"); + NTPTimePvt.synchronized = 0; + continue; + } + + ntpDelta = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.syncTime); + if (ntpDelta <= 0.0 && NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: NTP time not increasing, delta = %g\n", + ntpDelta); + NTPTimePvt.synchronized = 0; + continue; + } + + NTPTimePvt.syncsFailed = 0; + if (!NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: Sync recovered.\n"); + } + + epicsMutexMustLock(NTPTimePvt.lock); + diff = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.clockTime); + if (diff >= 0.0) { + NTPTimePvt.ticksToSkip = 0; + } else { /* dont go back in time */ + NTPTimePvt.ticksToSkip = -diff * osdTickRateGet(); + } + NTPTimePvt.clockTick = tickNow; + NTPTimePvt.clockTime = timeNow; + NTPTimePvt.synchronized = 1; + epicsMutexUnlock(NTPTimePvt.lock); + + NTPTimePvt.tickRate = (tickNow - NTPTimePvt.syncTick) / ntpDelta; + NTPTimePvt.syncTick = tickNow; + NTPTimePvt.syncTime = timeNow; + } + + NTPTimePvt.synchronized = 0; + taskwdRemove(0); +} + + +/* Time Provider Routine */ + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest) +{ + epicsUInt32 tickNow; + epicsUInt32 ticksSince; + + if (!NTPTimePvt.synchronized) + return S_time_unsynchronized; + + epicsMutexMustLock(NTPTimePvt.lock); + + tickNow = osdTickGet(); + ticksSince = tickNow - NTPTimePvt.clockTick; + + if (NTPTimePvt.ticksToSkip <= ticksSince) { + if (NTPTimePvt.ticksToSkip) { + ticksSince -= NTPTimePvt.ticksToSkip; + NTPTimePvt.ticksToSkip = 0; + } + + if (ticksSince) { + epicsUInt32 ticksPerSecond = osdTickRateGet(); + epicsUInt32 nsecsPerTick = NSEC_PER_SEC / ticksPerSecond; + epicsUInt32 secsSince = ticksSince / ticksPerSecond; + + ticksSince -= secsSince * ticksPerSecond; + NTPTimePvt.clockTime.nsec += ticksSince * nsecsPerTick; + if (NTPTimePvt.clockTime.nsec >= NSEC_PER_SEC) { + secsSince++; + NTPTimePvt.clockTime.nsec -= NSEC_PER_SEC; + } + NTPTimePvt.clockTime.secPastEpoch += secsSince; + NTPTimePvt.clockTick = tickNow; + } + } + + *pDest = NTPTimePvt.clockTime; + + epicsMutexUnlock(NTPTimePvt.lock); + return 0; +} + + +/* Status Report */ + +int NTPTime_Report(int level) +{ + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("NTP driver not initialized\n"); + } else if (NTPTimePvt.synchronize) { + printf("NTP driver %s synchronized with server\n", + NTPTimePvt.synchronized ? "is" : "is *not*"); + if (NTPTimePvt.syncsFailed) { + printf("Last successful sync was %.1f minutes ago\n", + NTPTimePvt.syncsFailed * NTPTimeSyncInterval / 60.0); + } + if (level) { + char lastSync[32]; + + epicsTimeToStrftime(lastSync, sizeof(lastSync), + "%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime); + printf("Syncronization interval = %.1f seconds\n", + NTPTimeSyncInterval); + printf("Last synchronized at %s\n", + lastSync); + printf("Current OS tick rate = %u Hz\n", + osdTickRateGet()); + printf("Measured tick rate = %.3f Hz\n", + NTPTimePvt.tickRate); + osdNTPReport(); + } + } else { + printf("NTP synchronization thread not running.\n"); + } + return 0; +} diff --git a/modules/libcom/src/osi/osiNTPTime.h b/modules/libcom/src/osi/osiNTPTime.h new file mode 100644 index 000000000..21b29d118 --- /dev/null +++ b/modules/libcom/src/osi/osiNTPTime.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiNTPTime_H +#define INC_osiNTPTime_H + +#ifdef __cplusplus +extern "C" { +#endif + +void NTPTime_Init(int priority); +void NTPTime_Shutdown(void *dummy); +int NTPTime_Report(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/osi/osiPoolStatus.h b/modules/libcom/src/osi/osiPoolStatus.h new file mode 100644 index 000000000..b0207f185 --- /dev/null +++ b/modules/libcom/src/osi/osiPoolStatus.h @@ -0,0 +1,42 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiPoolStatus_H +#define INC_osiPoolStatus_H + +/* + * Author: Jeff Hill + * + * Functions which interrogate the state of the system wide pool + * + */ + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * tests to see if there is sufficent space for a block of the requested size + * along with whatever additional free space is necessary to keep the system running + * reliably + * + * this routine is called quite frequently so an efficent implementation is important + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ); + +#ifdef __cplusplus +} +#endif + +#include "osdPoolStatus.h" + +#endif /* INC_osiPoolStatus_H */ diff --git a/modules/libcom/src/osi/osiProcess.h b/modules/libcom/src/osi/osiProcess.h new file mode 100644 index 000000000..eabff1e72 --- /dev/null +++ b/modules/libcom/src/osi/osiProcess.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiProcess_H +#define INC_osiProcess_H + +/* + * Operating System Independent Interface to Process Environment + * + * Author: Jeff Hill + * + */ +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum osiGetUserNameReturn { + osiGetUserNameFail, + osiGetUserNameSuccess} osiGetUserNameReturn; +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSize); + +/* + * Spawn detached process with named executable, but return + * osiSpawnDetachedProcessNoSupport if the local OS does not + * support heavy weight processes. + */ +typedef enum osiSpawnDetachedProcessReturn { + osiSpawnDetachedProcessFail, + osiSpawnDetachedProcessSuccess, + osiSpawnDetachedProcessNoSupport} osiSpawnDetachedProcessReturn; + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_osiProcess_H */ diff --git a/modules/libcom/src/osi/osiSock.c b/modules/libcom/src/osi/osiSock.c new file mode 100644 index 000000000..8b4634caf --- /dev/null +++ b/modules/libcom/src/osi/osiSock.c @@ -0,0 +1,189 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * socket support library generic code + * + * 7-1-97 -joh- + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsSignal.h" +#include "epicsStdio.h" +#include "osiSock.h" + +#define nDigitsDottedIP 4u +#define chunkSize 8u + +#define makeMask(NBITS) ( ( 1u << ( (unsigned) NBITS) ) - 1u ) + +/* + * sockAddrAreIdentical() + * (returns true if addresses are identical) + */ +int epicsShareAPI sockAddrAreIdentical + ( const osiSockAddr *plhs, const osiSockAddr *prhs ) +{ + int match; + + if ( plhs->sa.sa_family != prhs->sa.sa_family ) { + match = 0; + } + else if ( plhs->sa.sa_family != AF_INET ) { + match = 0; + } + else if ( plhs->ia.sin_addr.s_addr != prhs->ia.sin_addr.s_addr ) { + match = 0; + } + else if ( plhs->ia.sin_port != prhs->ia.sin_port ) { + match = 0; + } + else { + match = 1; + } + return match; +} + +/* + * sockAddrToA() + * (convert socket address to ASCII host name) + */ +unsigned epicsShareAPI sockAddrToA ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) +{ + if ( bufSize < 1 ) { + return 0; + } + + if ( paddr->sa_family != AF_INET ) { + static const char * pErrStr = ""; + unsigned len = strlen ( pErrStr ); + if ( len < bufSize ) { + strcpy ( pBuf, pErrStr ); + return len; + } + else { + strncpy ( pBuf, "", bufSize-1 ); + pBuf[bufSize-1] = '\0'; + return bufSize-1; + } + } + else { + const struct sockaddr_in * paddr_in = + (const struct sockaddr_in *) paddr; + return ipAddrToA ( paddr_in, pBuf, bufSize ); + } +} + +/* + * ipAddrToA() + * (convert IP address to ASCII host name) + */ +unsigned epicsShareAPI ipAddrToA ( + const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ) +{ + unsigned len = ipAddrToHostName ( + & paddr->sin_addr, pBuf, bufSize ); + if ( len == 0 ) { + len = ipAddrToDottedIP ( paddr, pBuf, bufSize ); + } + else { + unsigned reducedSize = bufSize - len; + int status = epicsSnprintf ( + &pBuf[len], reducedSize, ":%hu", + ntohs (paddr->sin_port) ); + if ( status > 0 ) { + unsigned portSize = (unsigned) status; + if ( portSize < reducedSize ) { + len += portSize; + } + } + } + return len; +} + +/* + * sockAddrToDottedIP () + */ +unsigned epicsShareAPI sockAddrToDottedIP ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) +{ + if ( paddr->sa_family != AF_INET ) { + const char * pErrStr = ""; + unsigned errStrLen = strlen ( pErrStr ); + if ( errStrLen < bufSize ) { + strcpy ( pBuf, pErrStr ); + return errStrLen; + } + else { + unsigned reducedSize = bufSize - 1u; + strncpy ( pBuf, pErrStr, reducedSize ); + pBuf[reducedSize] = '\0'; + return reducedSize; + } + } + else { + const struct sockaddr_in *paddr_in = ( const struct sockaddr_in * ) paddr; + return ipAddrToDottedIP ( paddr_in, pBuf, bufSize ); + } +} + +/* + * ipAddrToDottedIP () + */ +unsigned epicsShareAPI ipAddrToDottedIP ( + const struct sockaddr_in *paddr, char *pBuf, unsigned bufSize ) +{ + static const char * pErrStr = ""; + unsigned chunk[nDigitsDottedIP]; + unsigned addr = ntohl ( paddr->sin_addr.s_addr ); + unsigned strLen; + unsigned i; + int status; + + if ( bufSize == 0u ) { + return 0u; + } + + for ( i = 0; i < nDigitsDottedIP; i++ ) { + chunk[i] = addr & makeMask ( chunkSize ); + addr >>= chunkSize; + } + + /* + * inet_ntoa() isnt used because it isnt thread safe + * (and the replacements are not standardized) + */ + status = epicsSnprintf ( + pBuf, bufSize, "%u.%u.%u.%u:%hu", + chunk[3], chunk[2], chunk[1], chunk[0], + ntohs ( paddr->sin_port ) ); + if ( status > 0 ) { + strLen = ( unsigned ) status; + if ( strLen < bufSize - 1 ) { + return strLen; + } + } + strLen = strlen ( pErrStr ); + if ( strLen < bufSize ) { + strcpy ( pBuf, pErrStr ); + return strLen; + } + else { + strncpy ( pBuf, pErrStr, bufSize ); + pBuf[bufSize-1] = '\0'; + return bufSize - 1u; + } +} + diff --git a/modules/libcom/src/osi/osiSock.h b/modules/libcom/src/osi/osiSock.h new file mode 100644 index 000000000..061619e89 --- /dev/null +++ b/modules/libcom/src/osi/osiSock.h @@ -0,0 +1,208 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * socket support library API def + * + * 7-1-97 -joh- + */ +#ifndef osiSockh +#define osiSockh + +#include "shareLib.h" +#include "osdSock.h" +#include "ellLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sockaddr; +struct sockaddr_in; +struct in_addr; + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ); +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ); +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( + SOCKET ); +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ); +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ); + +/* + * Fortunately, on most systems the combination of a shutdown of both + * directions and or a signal is sufficent to interrupt a blocking send, + * receive, or connect call. For odd ball systems this is stubbed out in the + * osi area. + */ +enum epicsSocketSystemCallInterruptMechanismQueryInfo { + esscimqi_socketCloseRequired, + esscimqi_socketBothShutdownRequired, + esscimqi_socketSigAlarmRequired /* NO LONGER USED/SUPPORTED */ +}; +epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery (); + + +/* + * convert socket address to ASCII in this order + * 1) look for matching host name and typically add trailing IP port + * 2) failing that, convert to raw ascii address (typically this is a + * dotted IP address with trailing port) + * 3) failing that, writes "" into pBuf + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI sockAddrToA ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); + +/* + * convert IP address to ASCII in this order + * 1) look for matching host name and add trailing port + * 2) convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToA ( + const struct sockaddr_in * pInetAddr, char * pBuf, unsigned bufSize ); + +/* + * sockAddrToDottedIP () + * typically convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI sockAddrToDottedIP ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); + +/* + * ipAddrToDottedIP () + * convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToDottedIP ( + const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ); + +/* + * convert inet address to a host name string + * + * returns the number of character elements stored in buffer not + * including the null termination. This will be zero if a matching + * host name cant be found. + * + * there are many OS specific implementation stubs for this routine + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName ( + const struct in_addr * pAddr, char * pBuf, unsigned bufSize ); + +/* + * attempt to convert ASCII string to an IP address in this order + * 1) look for traditional doted ip with optional port + * 2) look for raw number form of ip address with optional port + * 3) look for valid host name with optional port + */ +epicsShareFunc int epicsShareAPI aToIPAddr + ( const char * pAddrString, unsigned short defaultPort, struct sockaddr_in * pIP); + +/* + * attempt to convert ASCII host name string with optional port to an IP address + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA); +/* + * attach to BSD socket library + */ +epicsShareFunc int epicsShareAPI osiSockAttach (void); /* returns T if success, else F */ + +/* + * release BSD socket library + */ +epicsShareFunc void epicsShareAPI osiSockRelease (void); + +/* + * convert socket error numbers to a string + */ +epicsShareFunc void epicsSocketConvertErrorToString ( + char * pBuf, unsigned bufSize, int error ); +epicsShareFunc void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ); + +typedef union osiSockAddr { + struct sockaddr_in ia; + struct sockaddr sa; +} osiSockAddr; + +typedef struct osiSockAddrNode { + ELLNODE node; + osiSockAddr addr; +} osiSockAddrNode; + +/* + * sockAddrAreIdentical() + * (returns true if addresses are identical) + */ +epicsShareFunc int epicsShareAPI sockAddrAreIdentical + ( const osiSockAddr * plhs, const osiSockAddr * prhs ); + +/* + * osiSockDiscoverBroadcastAddresses () + * Returns the broadcast addresses of each network interface found. + * + * This routine is provided with the address of an ELLLIST, a socket, + * a destination port number, and a match address. When the + * routine returns there will be one additional entry + * (an osiSockAddrNode) in the list for each network interface found that + * is up and isnt a loop back interface (match addr is INADDR_ANY), + * or only the interfaces that match the specified addresses (match addr + * is other than INADDR_ANY). If the interface supports broadcasting + * then add its broadcast address to the list. If the interface is a + * point to point link then add the destination address of the point to + * point link to the list. + * + * Any mutex locking required to protect pList is applied externally. + * + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr); + +/* + * osiLocalAddr () + * Returns the osiSockAddr of the first non-loopback interface found + * that is operational (up flag is set). If no valid address can be + * located then return an osiSockAddr with the address family set to + * unspecified (AF_UNSPEC). + * + * Unfortunately in EPICS 3.13 beta 11 and before the CA + * repeater would not always allow the loopback address + * as a local client address so current clients alternate + * between the address of the first non-loopback interface + * found and the loopback addresss when subscribing with + * the CA repeater until all CA repeaters have been updated + * to current code. After all CA repeaters have been restarted + * this osi interface can be eliminated. + */ +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef osiSockh */ diff --git a/modules/libcom/src/osi/osiWireFormat.h b/modules/libcom/src/osi/osiWireFormat.h new file mode 100644 index 000000000..4d7e6717f --- /dev/null +++ b/modules/libcom/src/osi/osiWireFormat.h @@ -0,0 +1,257 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osiWireFormat +#define osiWireFormat + +#include "epicsTypes.h" + +// +// With future CA protocols user defined payload composition will be +// supported and we will need to move away from a naturally aligned +// protocol (because pad byte overhead will probably be excessive when +// maintaining 8 byte natural alignment if the user isnt thinking about +// placing like sized elements together). +// +// Nevertheless, the R3.14 protocol continues to be naturally aligned, +// and all of the fields within the DBR_XXXX types are naturally aligned. +// Therefore we support here two wire transfer interfaces (naturally +// aligned and otherwise) because there are important optimizations +// specific to each of them. +// +// At some point in the future the naturally aligned interfaces might +// be eliminated (or unbundled from base) should they be no-longer needed. +// + +template < class T > +void WireGet ( const epicsUInt8 * pWireSrc, T & ); + +template < class T > +void WireSet ( const T &, epicsUInt8 * pWireDst ); + +template < class T > +void AlignedWireGet ( const T &, T & ); + +template < class T > +void AlignedWireSet ( const T &, T & ); + +template < class T > +class AlignedWireRef { +public: + AlignedWireRef ( T & ref ); + operator T () const; + AlignedWireRef < T > & operator = ( const T & ); +private: + T & _ref; + AlignedWireRef ( const AlignedWireRef & ); + AlignedWireRef & operator = ( const AlignedWireRef & ); +}; + +template < class T > +class AlignedWireRef < const T > { +public: + AlignedWireRef ( const T & ref ); + operator T () const; +private: + const T & _ref; + AlignedWireRef ( const AlignedWireRef & ); + AlignedWireRef & operator = ( const AlignedWireRef & ); +}; + +template < class T > +inline AlignedWireRef < T > :: AlignedWireRef ( T & ref ) : + _ref ( ref ) +{ +} + +template < class T > +inline AlignedWireRef < T > :: operator T () const +{ + T tmp; + AlignedWireGet ( _ref, tmp ); + return tmp; +} + +template < class T > +inline AlignedWireRef < T > & AlignedWireRef < T > :: operator = ( const T & src ) +{ + AlignedWireSet ( src, _ref ); + return *this; +} + +template < class T > +inline AlignedWireRef < const T > :: AlignedWireRef ( const T & ref ) : + _ref ( ref ) +{ +} + +template < class T > +inline AlignedWireRef < const T > :: operator T () const +{ + T tmp; + AlignedWireGet ( _ref, tmp ); + return tmp; +} + +// may be useful when creating support for little endian +inline epicsUInt16 byteSwap ( const epicsUInt16 & src ) +{ + return static_cast < epicsUInt16 > + ( ( src << 8u ) | ( src >> 8u ) ); +} + +// may be useful when creating support for little endian +inline epicsUInt32 byteSwap ( const epicsUInt32 & src ) +{ + epicsUInt32 tmp0 = byteSwap ( + static_cast < epicsUInt16 > ( src >> 16u ) ); + epicsUInt32 tmp1 = byteSwap ( + static_cast < epicsUInt16 > ( src ) ); + return static_cast < epicsUInt32 > + ( ( tmp1 << 16u ) | tmp0 ); +} + +template < class T > union WireAlias; + +template <> +union WireAlias < epicsInt8 > { + epicsUInt8 _u; + epicsInt8 _o; +}; + +template <> +union WireAlias < epicsInt16 > { + epicsUInt16 _u; + epicsInt16 _o; +}; + +template <> +union WireAlias < epicsInt32 > { + epicsUInt32 _u; + epicsInt32 _o; +}; + +template <> +union WireAlias < epicsFloat32 > { + epicsUInt32 _u; + epicsFloat32 _o; +}; + +// +// Missaligned unsigned wire format get/set can be implemented generically +// w/o performance penalty. Attempts to improve this on architectures that +// dont have alignement requirements will probably get into trouble with +// over-aggressive optimization under strict aliasing rules. +// + +template < class T > +inline void WireGet ( const epicsUInt8 * pWireSrc, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > tmp; + WireGet ( pWireSrc, tmp._u ); + dst = tmp._o; +} + +template <> +inline void WireGet < epicsUInt8 > ( + const epicsUInt8 * pWireSrc, epicsUInt8 & dst ) +{ + dst = pWireSrc[0]; +} + +template <> +inline void WireGet < epicsUInt16 > ( + const epicsUInt8 * pWireSrc, epicsUInt16 & dst ) +{ + dst = static_cast < epicsUInt16 > ( + ( pWireSrc[0] << 8u ) | pWireSrc[1] ); +} + +template <> +inline void WireGet < epicsUInt32 > ( + const epicsUInt8 * pWireSrc, epicsUInt32 & dst ) +{ + dst = static_cast < epicsUInt32 > ( + ( pWireSrc[0] << 24u ) | + ( pWireSrc[1] << 16u ) | + ( pWireSrc[2] << 8u ) | + pWireSrc[3] ); +} + +template < class T > +inline void WireSet ( const T & src, epicsUInt8 * pWireDst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > tmp; + tmp._o = src; + WireSet ( tmp._u, pWireDst ); +} + +template <> +inline void WireSet < epicsUInt8 > ( + const epicsUInt8 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = src; +} + +template <> +inline void WireSet < epicsUInt16 > ( + const epicsUInt16 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = static_cast < epicsUInt8 > ( src >> 8u ); + pWireDst[1] = static_cast < epicsUInt8 > ( src ); +} + +template <> +inline void WireSet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = static_cast < epicsUInt8 > ( src >> 24u ); + pWireDst[1] = static_cast < epicsUInt8 > ( src >> 16u ); + pWireDst[2] = static_cast < epicsUInt8 > ( src >> 8u ); + pWireDst[3] = static_cast < epicsUInt8 > ( src ); +} + +template < class T > +inline void AlignedWireGet ( const T & src, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > srcu, dstu; + srcu._o = src; + AlignedWireGet ( srcu._u, dstu._u ); + dst = dstu._o; +} + +template < class T > +inline void AlignedWireSet ( const T & src, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > srcu, dstu; + srcu._o = src; + AlignedWireSet ( srcu._u, dstu._u ); + dst = dstu._o; +} + +#include "osdWireFormat.h" + +#endif // osiWireFormat diff --git a/modules/libcom/src/pool/Makefile b/modules/libcom/src/pool/Makefile new file mode 100644 index 000000000..efaf66729 --- /dev/null +++ b/modules/libcom/src/pool/Makefile @@ -0,0 +1,16 @@ +#************************************************************************* +# Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/pool + +INC += epicsThreadPool.h + +Com_SRCS += poolJob.c +Com_SRCS += threadPool.c + diff --git a/modules/libcom/src/pool/epicsThreadPool.h b/modules/libcom/src/pool/epicsThreadPool.h new file mode 100644 index 000000000..3416bb23a --- /dev/null +++ b/modules/libcom/src/pool/epicsThreadPool.h @@ -0,0 +1,160 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* General purpose worker thread pool manager + * mdavidsaver@bnl.gov + */ +#ifndef EPICSTHREADPOOL_H +#define EPICSTHREADPOOL_H + +#include +#include + +#include "shareLib.h" +#include "errMdef.h" + +#define S_pool_jobBusy (M_pool| 1) /*Job already queued or running*/ +#define S_pool_jobIdle (M_pool| 2) /*Job was not queued or running*/ +#define S_pool_noPool (M_pool| 3) /*Job not associated with a pool*/ +#define S_pool_paused (M_pool| 4) /*Pool not currently accepting jobs*/ +#define S_pool_noThreads (M_pool| 5) /*Can't create worker thread*/ +#define S_pool_timeout (M_pool| 6) /*Pool still busy after timeout*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned int initialThreads; + unsigned int maxThreads; + unsigned int workerStack; + unsigned int workerPriority; +} epicsThreadPoolConfig; + +typedef struct epicsThreadPool epicsThreadPool; + +/* Job function call modes */ +typedef enum { + /* Normal run of job */ + epicsJobModeRun, + + /* Thread pool is being destroyed. + * A chance to cleanup the job immediately with epicsJobDestroy(). + * If ignored, the job is orphaned (dissociated from the thread pool) + * and epicsJobDestroy() must be called later. + */ + epicsJobModeCleanup +} epicsJobMode; + +typedef void (*epicsJobFunction)(void* arg, epicsJobMode mode); + +typedef struct epicsJob epicsJob; + +/* Pool operations */ + +/* Initialize a pool config with default values. + * This much be done to preserve future compatibility + * when new options are added. + */ +epicsShareFunc void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *); + +/* fetch or create a thread pool which can be shared with other users. + * may return NULL for allocation failures + */ +epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts); +epicsShareFunc void epicsThreadPoolReleaseShared(epicsThreadPool *pool); + +/* If opts is NULL then defaults are used. + * The opts pointer is not stored by this call, and may exist on the stack. + */ +epicsShareFunc epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts); + +/* Blocks until all worker threads have stopped. + * Any jobs still attached to this pool receive a callback with EPICSJOB_CLEANUP + * and are then orphaned. + */ +epicsShareFunc void epicsThreadPoolDestroy(epicsThreadPool *); + +/* pool control options */ +typedef enum { + epicsThreadPoolQueueAdd, /* val==0 causes epicsJobQueue to fail, 1 is default */ + epicsThreadPoolQueueRun /* val==0 prevents workers from running jobs, 1 is default */ +} epicsThreadPoolOption; + +epicsShareFunc void epicsThreadPoolControl(epicsThreadPool* pool, + epicsThreadPoolOption opt, + unsigned int val); + +/* Block until job queue is emptied and no jobs are running. + * Useful after calling epicsThreadPoolControl() with option epicsThreadPoolQueueAdd=0 + * + * timeout<0 waits forever, timeout==0 polls, timeout>0 waits at most one timeout period + * Returns 0 for success or non-zero on error (timeout is ETIMEOUT) + */ +epicsShareFunc int epicsThreadPoolWait(epicsThreadPool* pool, double timeout); + + +/* Per job operations */ + +/* Special flag for epicsJobCreate(). + * When passed as the third argument "user" + * the argument passed to the job callback + * will be the epicsJob* + */ +#define EPICSJOB_SELF epicsJobArgSelfMagic +epicsShareExtern void* epicsJobArgSelfMagic; + +/* Creates, but does not add, a new job. + * If pool is NULL then the job is not associated with any pool and + * epicsJobMove() must be called before epicsJobQueue(). + * Safe to call from a running job function. + * Returns a new job pointer, or NULL on error. + */ +epicsShareFunc epicsJob* epicsJobCreate(epicsThreadPool* pool, + epicsJobFunction cb, + void* user); + +/* Cancel and free a job structure. Does not block. + * Job may not be immediately free'd. + * Safe to call from a running job function. + */ +epicsShareFunc void epicsJobDestroy(epicsJob*); + +/* Move the job to a different pool. + * If pool is NULL then the job will no longer be associated + * with any pool. + * Not thread safe. Job must not be running or queued. + * returns 0 on success, non-zero on error. + */ +epicsShareFunc int epicsJobMove(epicsJob* job, epicsThreadPool* pool); + +/* Adds the job to the run queue + * Safe to call from a running job function. + * returns 0 for success, non-zero on error. + */ +epicsShareFunc int epicsJobQueue(epicsJob*); + +/* Remove a job from the run queue if it is queued. + * Safe to call from a running job function. + * returns 0 if job was queued and now is not. + * 1 if job already ran, is running, or was not queued before, + * Other non-zero on error + */ +epicsShareFunc int epicsJobUnqueue(epicsJob*); + + +/* Mostly useful for debugging */ + +epicsShareFunc void epicsThreadPoolReport(epicsThreadPool *pool, FILE *fd); + +/* Current number of active workers. May be less than the maximum */ +epicsShareFunc unsigned int epicsThreadPoolNThreads(epicsThreadPool *); + +#ifdef __cplusplus +} +#endif + +#endif // EPICSTHREADPOOL_H diff --git a/modules/libcom/src/pool/poolJob.c b/modules/libcom/src/pool/poolJob.c new file mode 100644 index 000000000..8e86fb768 --- /dev/null +++ b/modules/libcom/src/pool/poolJob.c @@ -0,0 +1,327 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include + +#define epicsExportSharedSymbols + +#include "dbDefs.h" +#include "errlog.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsInterrupt.h" + +#include "epicsThreadPool.h" +#include "poolPriv.h" + +void *epicsJobArgSelfMagic = &epicsJobArgSelfMagic; + +static +void workerMain(void *arg) +{ + epicsThreadPool *pool = arg; + unsigned int nrun, ocnt; + + /* workers are created with counts + * in the running, sleeping, and (possibly) waking counters + */ + + epicsMutexMustLock(pool->guard); + pool->threadsAreAwake++; + pool->threadsSleeping--; + + while (1) { + ELLNODE *cur; + + pool->threadsAreAwake--; + pool->threadsSleeping++; + epicsMutexUnlock(pool->guard); + + epicsEventMustWait(pool->workerWakeup); + + epicsMutexMustLock(pool->guard); + pool->threadsSleeping--; + pool->threadsAreAwake++; + + if (pool->threadsWaking==0) + continue; + + pool->threadsWaking--; + + CHECKCOUNT(pool); + + if (pool->shutdown) + break; + + if (pool->pauserun) + continue; + + /* more threads to wakeup */ + if (pool->threadsWaking) { + epicsEventSignal(pool->workerWakeup); + } + + while ((cur=ellGet(&pool->jobs)) != NULL) { + epicsJob *job = CONTAINER(cur, epicsJob, jobnode); + + assert(job->queued && !job->running); + + job->queued=0; + job->running=1; + + epicsMutexUnlock(pool->guard); + (*job->func)(job->arg, epicsJobModeRun); + epicsMutexMustLock(pool->guard); + + if (job->freewhendone) { + job->dead=1; + free(job); + } + else { + job->running=0; + /* job may be re-queued from within callback */ + if (job->queued) + ellAdd(&pool->jobs, &job->jobnode); + else + ellAdd(&pool->owned, &job->jobnode); + } + } + + if (pool->observerCount) + epicsEventSignal(pool->observerWakeup); + } + + pool->threadsAreAwake--; + pool->threadsRunning--; + + nrun = pool->threadsRunning; + ocnt = pool->observerCount; + epicsMutexUnlock(pool->guard); + + if (ocnt) + epicsEventSignal(pool->observerWakeup); + + if (nrun) + epicsEventSignal(pool->workerWakeup); /* pass along */ + else + epicsEventSignal(pool->shutdownEvent); +} + +int createPoolThread(epicsThreadPool *pool) +{ + epicsThreadId tid; + + tid = epicsThreadCreate("PoolWorker", + pool->conf.workerPriority, + pool->conf.workerStack, + &workerMain, + pool); + if (!tid) + return S_pool_noThreads; + + pool->threadsRunning++; + pool->threadsSleeping++; + return 0; +} + +epicsJob* epicsJobCreate(epicsThreadPool *pool, + epicsJobFunction func, + void *arg) +{ + epicsJob *job = calloc(1, sizeof(*job)); + + if (!job) + return NULL; + + if (arg == &epicsJobArgSelfMagic) + arg = job; + + job->pool = NULL; + job->func = func; + job->arg = arg; + + epicsJobMove(job, pool); + + return job; +} + +void epicsJobDestroy(epicsJob *job) +{ + epicsThreadPool *pool; + if (!job || !job->pool) { + free(job); + return; + } + pool = job->pool; + + epicsMutexMustLock(pool->guard); + + assert(!job->dead); + + epicsJobUnqueue(job); + + if (job->running || job->freewhendone) { + job->freewhendone = 1; + } + else { + ellDelete(&pool->owned, &job->jobnode); + job->dead = 1; + free(job); + } + + epicsMutexUnlock(pool->guard); +} + +int epicsJobMove(epicsJob *job, epicsThreadPool *newpool) +{ + epicsThreadPool *pool = job->pool; + + /* remove from current pool */ + if (pool) { + epicsMutexMustLock(pool->guard); + + if (job->queued || job->running) { + epicsMutexUnlock(pool->guard); + return S_pool_jobBusy; + } + + ellDelete(&pool->owned, &job->jobnode); + + epicsMutexUnlock(pool->guard); + } + + pool = job->pool = newpool; + + /* add to new pool */ + if (pool) { + epicsMutexMustLock(pool->guard); + + ellAdd(&pool->owned, &job->jobnode); + + epicsMutexUnlock(pool->guard); + } + + return 0; +} + +int epicsJobQueue(epicsJob *job) +{ + int ret = 0; + epicsThreadPool *pool = job->pool; + + if (!pool) + return S_pool_noPool; + + epicsMutexMustLock(pool->guard); + + assert(!job->dead); + + if (pool->pauseadd) { + ret = S_pool_paused; + goto done; + } + else if (job->freewhendone) { + ret = S_pool_jobBusy; + goto done; + } + else if (job->queued) { + goto done; + } + + job->queued = 1; + /* Job may be queued from within a callback */ + if (!job->running) { + ellDelete(&pool->owned, &job->jobnode); + ellAdd(&pool->jobs, &job->jobnode); + } + else { + /* some worker will find it again before sleeping */ + goto done; + } + + /* Since we hold the lock, we can be certain that all awake worker are + * executing work functions. The current thread may be a worker. + * We prefer to wakeup a new worker rather then wait for a busy worker to + * finish. However, after we initiate a wakeup there will be a race + * between the worker waking up, and a busy worker finishing. + * Thus we can't avoid spurious wakeups. + */ + + if (pool->threadsRunning >= pool->conf.maxThreads) { + /* all workers created... */ + /* ... but some are sleeping, so wake one up */ + if (pool->threadsWaking < pool->threadsSleeping) { + pool->threadsWaking++; + epicsEventSignal(pool->workerWakeup); + } + /*else one of the running workers will find this job before sleeping */ + CHECKCOUNT(pool); + + } + else { + /* could create more workers so + * will either create a new worker, or wakeup an existing worker + */ + + if (pool->threadsWaking >= pool->threadsSleeping) { + /* all sleeping workers have already been woken. + * start a new worker for this job + */ + if (createPoolThread(pool) && pool->threadsRunning == 0) { + /* oops, we couldn't lazy create our first worker + * so this job would never run! + */ + ret = S_pool_noThreads; + job->queued = 0; + /* if threadsRunning==0 then no jobs can be running */ + assert(!job->running); + ellDelete(&pool->jobs, &job->jobnode); + ellAdd(&pool->owned, &job->jobnode); + } + } + if (ret == 0) { + pool->threadsWaking++; + epicsEventSignal(pool->workerWakeup); + } + CHECKCOUNT(pool); + } + +done: + epicsMutexUnlock(pool->guard); + return ret; +} + +int epicsJobUnqueue(epicsJob *job) +{ + int ret = S_pool_jobIdle; + epicsThreadPool *pool = job->pool; + + if (!pool) + return S_pool_noPool; + + epicsMutexMustLock(pool->guard); + + assert(!job->dead); + + if (job->queued) { + if (!job->running) { + ellDelete(&pool->jobs, &job->jobnode); + ellAdd(&pool->owned, &job->jobnode); + } + job->queued = 0; + ret = 0; + } + + epicsMutexUnlock(pool->guard); + + return ret; +} + diff --git a/modules/libcom/src/pool/poolPriv.h b/modules/libcom/src/pool/poolPriv.h new file mode 100644 index 000000000..237cedf45 --- /dev/null +++ b/modules/libcom/src/pool/poolPriv.h @@ -0,0 +1,99 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef POOLPRIV_H +#define POOLPRIV_H + +#include "epicsThreadPool.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsMutex.h" + +struct epicsThreadPool { + ELLNODE sharedNode; + size_t sharedCount; + + ELLLIST jobs; /* run queue */ + ELLLIST owned; /* unqueued jobs. */ + + /* Worker state counters. + * The life cycle of a worker is + * Wakeup -> Awake -> Sleeping + * Newly created workers go into the wakeup state + */ + + /* # of running workers which are not waiting for a wakeup event */ + unsigned int threadsAreAwake; + /* # of sleeping workers which need to be awakened */ + unsigned int threadsWaking; + /* # of workers waiting on the workerWakeup event */ + unsigned int threadsSleeping; + /* # of threads started and not stopped */ + unsigned int threadsRunning; + + /* # of observers waiting on pool events */ + unsigned int observerCount; + + epicsEventId workerWakeup; + epicsEventId shutdownEvent; + + epicsEventId observerWakeup; + + /* Disallow epicsJobQueue */ + unsigned int pauseadd:1; + /* Prevent workers from running new jobs */ + unsigned int pauserun:1; + /* Prevent further changes to pool options */ + unsigned int freezeopt:1; + /* tell workers to exit */ + unsigned int shutdown:1; + + epicsMutexId guard; + + /* copy of config passed when created */ + epicsThreadPoolConfig conf; +}; + +/* Called after manipulating counters to check that invariants are preserved */ +#define CHECKCOUNT(pPool) do { \ + if (!(pPool)->shutdown) { \ + assert((pPool)->threadsAreAwake + (pPool)->threadsSleeping == (pPool)->threadsRunning); \ + assert((pPool)->threadsWaking <= (pPool)->threadsSleeping); \ + } \ +} while(0) + +/* When created a job is idle. queued and running are false + * and jobnode is in the thread pool's owned list. + * + * When the job is added, the queued flag is set and jobnode + * is in the jobs list. + * + * When the job starts running the queued flag is cleared and + * the running flag is set. jobnode is not in any list + * (held locally by worker). + * + * When the job has finished running, the running flag is cleared. + * The queued flag may be set if the job re-added itself. + * Based on the queued flag jobnode is added to the appropriate + * list. + */ +struct epicsJob { + ELLNODE jobnode; + epicsJobFunction func; + void *arg; + epicsThreadPool *pool; + + unsigned int queued:1; + unsigned int running:1; + unsigned int freewhendone:1; /* lazy delete of running job */ + unsigned int dead:1; /* flag to catch use of freed objects */ +}; + +int createPoolThread(epicsThreadPool *pool); + +#endif // POOLPRIV_H diff --git a/modules/libcom/src/pool/threadPool.c b/modules/libcom/src/pool/threadPool.c new file mode 100644 index 000000000..3cfd06606 --- /dev/null +++ b/modules/libcom/src/pool/threadPool.c @@ -0,0 +1,403 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include + +#define epicsExportSharedSymbols + +#include "dbDefs.h" +#include "errlog.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsInterrupt.h" +#include "cantProceed.h" + +#include "epicsThreadPool.h" +#include "poolPriv.h" + + +void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *opts) +{ + memset(opts, 0, sizeof(*opts)); + opts->maxThreads = epicsThreadGetCPUs(); + opts->workerStack = epicsThreadGetStackSize(epicsThreadStackSmall); + + if (epicsThreadLowestPriorityLevelAbove(epicsThreadPriorityCAServerHigh, &opts->workerPriority) + != epicsThreadBooleanStatusSuccess) + opts->workerPriority = epicsThreadPriorityMedium; +} + +epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts) +{ + size_t i; + epicsThreadPool *pool; + + /* caller likely didn't initialize the options structure */ + if (opts && opts->maxThreads == 0) { + errlogMessage("Error: epicsThreadPoolCreate() options provided, but not initialized"); + return NULL; + } + + pool = calloc(1, sizeof(*pool)); + if (!pool) + return NULL; + + if (opts) + memcpy(&pool->conf, opts, sizeof(*opts)); + else + epicsThreadPoolConfigDefaults(&pool->conf); + + if (pool->conf.initialThreads > pool->conf.maxThreads) + pool->conf.initialThreads = pool->conf.maxThreads; + + pool->workerWakeup = epicsEventCreate(epicsEventEmpty); + pool->shutdownEvent = epicsEventCreate(epicsEventEmpty); + pool->observerWakeup = epicsEventCreate(epicsEventEmpty); + pool->guard = epicsMutexCreate(); + + if (!pool->workerWakeup || !pool->shutdownEvent || + !pool->observerWakeup || !pool->guard) + goto cleanup; + + ellInit(&pool->jobs); + ellInit(&pool->owned); + + epicsMutexMustLock(pool->guard); + + for (i = 0; i < pool->conf.initialThreads; i++) { + createPoolThread(pool); + } + + if (pool->threadsRunning == 0 && pool->conf.initialThreads != 0) { + epicsMutexUnlock(pool->guard); + errlogPrintf("Error: Unable to create any threads for thread pool\n"); + goto cleanup; + + } + else if (pool->threadsRunning < pool->conf.initialThreads) { + errlogPrintf("Warning: Unable to create all threads for thread pool (%u/%u)\n", + pool->threadsRunning, pool->conf.initialThreads); + } + + epicsMutexUnlock(pool->guard); + + return pool; + +cleanup: + if (pool->workerWakeup) + epicsEventDestroy(pool->workerWakeup); + if (pool->shutdownEvent) + epicsEventDestroy(pool->shutdownEvent); + if (pool->observerWakeup) + epicsEventDestroy(pool->observerWakeup); + if (pool->guard) + epicsMutexDestroy(pool->guard); + + free(pool); + return NULL; +} + +static +void epicsThreadPoolControlImpl(epicsThreadPool *pool, epicsThreadPoolOption opt, unsigned int val) +{ + if (pool->freezeopt) + return; + + if (opt == epicsThreadPoolQueueAdd) { + pool->pauseadd = !val; + } + else if (opt == epicsThreadPoolQueueRun) { + if (!val && !pool->pauserun) + pool->pauserun = 1; + + else if (val && pool->pauserun) { + int jobs = ellCount(&pool->jobs); + pool->pauserun = 0; + + if (jobs) { + int wakeable = pool->threadsSleeping - pool->threadsWaking; + + /* first try to give jobs to sleeping workers */ + if (wakeable) { + int wakeup = jobs > wakeable ? wakeable : jobs; + assert(wakeup > 0); + jobs -= wakeup; + pool->threadsWaking += wakeup; + epicsEventSignal(pool->workerWakeup); + CHECKCOUNT(pool); + } + } + while (jobs-- && pool->threadsRunning < pool->conf.maxThreads) { + if (createPoolThread(pool) == 0) { + pool->threadsWaking++; + epicsEventSignal(pool->workerWakeup); + } + else + break; /* oops, couldn't create worker */ + } + CHECKCOUNT(pool); + } + } + /* unknown options ignored */ + +} + +void epicsThreadPoolControl(epicsThreadPool *pool, epicsThreadPoolOption opt, unsigned int val) +{ + epicsMutexMustLock(pool->guard); + epicsThreadPoolControlImpl(pool, opt, val); + epicsMutexUnlock(pool->guard); +} + +int epicsThreadPoolWait(epicsThreadPool *pool, double timeout) +{ + int ret = 0; + epicsMutexMustLock(pool->guard); + + while (ellCount(&pool->jobs) > 0 || pool->threadsAreAwake > 0) { + pool->observerCount++; + epicsMutexUnlock(pool->guard); + + if (timeout < 0.0) { + epicsEventMustWait(pool->observerWakeup); + } + else { + switch (epicsEventWaitWithTimeout(pool->observerWakeup, timeout)) { + case epicsEventWaitError: + cantProceed("epicsThreadPoolWait: failed to wait for Event"); + break; + case epicsEventWaitTimeout: + ret = S_pool_timeout; + break; + case epicsEventWaitOK: + ret = 0; + break; + } + } + + epicsMutexMustLock(pool->guard); + pool->observerCount--; + + if (pool->observerCount) + epicsEventSignal(pool->observerWakeup); + + if (ret != 0) + break; + } + + epicsMutexUnlock(pool->guard); + return ret; +} + +void epicsThreadPoolDestroy(epicsThreadPool *pool) +{ + unsigned int nThr; + ELLLIST notify; + ELLNODE *cur; + + if (!pool) + return; + + ellInit(¬ify); + + epicsMutexMustLock(pool->guard); + + /* run remaining queued jobs */ + epicsThreadPoolControlImpl(pool, epicsThreadPoolQueueAdd, 0); + epicsThreadPoolControlImpl(pool, epicsThreadPoolQueueRun, 1); + nThr = pool->threadsRunning; + pool->freezeopt = 1; + + epicsMutexUnlock(pool->guard); + + epicsThreadPoolWait(pool, -1.0); + /* At this point all queued jobs have run */ + + epicsMutexMustLock(pool->guard); + + pool->shutdown = 1; + /* wakeup all */ + if (pool->threadsWaking < pool->threadsSleeping) { + pool->threadsWaking = pool->threadsSleeping; + epicsEventSignal(pool->workerWakeup); + } + + ellConcat(¬ify, &pool->owned); + ellConcat(¬ify, &pool->jobs); + + epicsMutexUnlock(pool->guard); + + if (nThr && epicsEventWait(pool->shutdownEvent) != epicsEventWaitOK){ + errlogMessage("epicsThreadPoolDestroy: wait error"); + return; + } + + /* all workers are now shutdown */ + + /* notify remaining jobs that pool is being destroyed */ + while ((cur = ellGet(¬ify)) != NULL) { + epicsJob *job = CONTAINER(cur, epicsJob, jobnode); + + job->running = 1; + job->func(job->arg, epicsJobModeCleanup); + job->running = 0; + if (job->freewhendone) + free(job); + else + job->pool = NULL; /* orphan */ + } + + epicsEventDestroy(pool->workerWakeup); + epicsEventDestroy(pool->shutdownEvent); + epicsEventDestroy(pool->observerWakeup); + epicsMutexDestroy(pool->guard); + + free(pool); +} + + +void epicsThreadPoolReport(epicsThreadPool *pool, FILE *fd) +{ + ELLNODE *cur; + epicsMutexMustLock(pool->guard); + + fprintf(fd, "Thread Pool with %u/%u threads\n" + " running %d jobs with %u threads\n", + pool->threadsRunning, + pool->conf.maxThreads, + ellCount(&pool->jobs), + pool->threadsAreAwake); + if (pool->pauseadd) + fprintf(fd, " Inhibit queueing\n"); + if (pool->pauserun) + fprintf(fd, " Pause workers\n"); + if (pool->shutdown) + fprintf(fd, " Shutdown in progress\n"); + + for (cur = ellFirst(&pool->jobs); cur; cur = ellNext(cur)) { + epicsJob *job = CONTAINER(cur, epicsJob, jobnode); + + fprintf(fd, " job %p func: %p, arg: %p ", + job, job->func, + job->arg); + if (job->queued) + fprintf(fd, "Queued "); + if (job->running) + fprintf(fd, "Running "); + if (job->freewhendone) + fprintf(fd, "Free "); + fprintf(fd, "\n"); + } + + epicsMutexUnlock(pool->guard); +} + +unsigned int epicsThreadPoolNThreads(epicsThreadPool *pool) +{ + unsigned int ret; + + epicsMutexMustLock(pool->guard); + ret = pool->threadsRunning; + epicsMutexUnlock(pool->guard); + + return ret; +} + +static +ELLLIST sharedPools = ELLLIST_INIT; + +static +epicsMutexId sharedPoolsGuard; + +static +epicsThreadOnceId sharedPoolsOnce = EPICS_THREAD_ONCE_INIT; + +static +void sharedPoolsInit(void* unused) +{ + sharedPoolsGuard = epicsMutexMustCreate(); +} + +epicsShareFunc epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts) +{ + ELLNODE *node; + epicsThreadPool *cur; + epicsThreadPoolConfig defopts; + size_t N = epicsThreadGetCPUs(); + + if (!opts) { + epicsThreadPoolConfigDefaults(&defopts); + opts = &defopts; + } + /* shared pools must have a minimum allowed number of workers. + * Use the number of CPU cores + */ + if (opts->maxThreads < N) + opts->maxThreads = N; + + epicsThreadOnce(&sharedPoolsOnce, &sharedPoolsInit, NULL); + + epicsMutexMustLock(sharedPoolsGuard); + + for (node = ellFirst(&sharedPools); node; node = ellNext(node)) { + cur = CONTAINER(node, epicsThreadPool, sharedNode); + + /* Must have exactly the requested priority + * At least the requested max workers + * and at least the requested stack size + */ + if (cur->conf.workerPriority != opts->workerPriority) + continue; + if (cur->conf.maxThreads < opts->maxThreads) + continue; + if (cur->conf.workerStack < opts->workerStack) + continue; + + cur->sharedCount++; + assert(cur->sharedCount > 0); + epicsMutexUnlock(sharedPoolsGuard); + + epicsMutexMustLock(cur->guard); + *opts = cur->conf; + epicsMutexUnlock(cur->guard); + return cur; + } + + cur = epicsThreadPoolCreate(opts); + if (!cur) { + epicsMutexUnlock(sharedPoolsGuard); + return NULL; + } + cur->sharedCount = 1; + + ellAdd(&sharedPools, &cur->sharedNode); + epicsMutexUnlock(sharedPoolsGuard); + return cur; +} + +epicsShareFunc void epicsThreadPoolReleaseShared(epicsThreadPool *pool) +{ + if (!pool) + return; + + epicsMutexMustLock(sharedPoolsGuard); + + assert(pool->sharedCount > 0); + + pool->sharedCount--; + + if (pool->sharedCount == 0) { + ellDelete(&sharedPools, &pool->sharedNode); + epicsThreadPoolDestroy(pool); + } + + epicsMutexUnlock(sharedPoolsGuard); +} diff --git a/modules/libcom/src/ring/Makefile b/modules/libcom/src/ring/Makefile new file mode 100644 index 000000000..ec2ecbd2d --- /dev/null +++ b/modules/libcom/src/ring/Makefile @@ -0,0 +1,15 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/ring +#following needed for locating epicsRingPointer.h and epicsRingBytes.h +INC += epicsRingPointer.h +INC += epicsRingBytes.h +Com_SRCS += epicsRingPointer.cpp +Com_SRCS += epicsRingBytes.c diff --git a/modules/libcom/src/ring/epicsRingBytes.c b/modules/libcom/src/ring/epicsRingBytes.c new file mode 100644 index 000000000..cb7e52e83 --- /dev/null +++ b/modules/libcom/src/ring/epicsRingBytes.c @@ -0,0 +1,226 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Marty Kraimer Date: 15JUL99 + * Eric Norum + * Ralph Lange + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsSpin.h" +#include "dbDefs.h" +#include "epicsRingBytes.h" + +/* + * Need at least one extra byte to be able to distinguish a completely + * full buffer from a completely empty one. Allow for a little extra + * space to try and keep good alignment and avoid multiple calls to + * memcpy for a single put/get operation. + */ +#define SLOP 16 + +typedef struct ringPvt { + epicsSpinId lock; + volatile int nextPut; + volatile int nextGet; + int size; + volatile char buffer[1]; /* actually larger */ +}ringPvt; + +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) +{ + ringPvt *pring = malloc(sizeof(ringPvt) + size + SLOP); + if(!pring) + return NULL; + pring->size = size + SLOP; + pring->nextGet = 0; + pring->nextPut = 0; + pring->lock = 0; + return((void *)pring); +} + +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesLockedCreate(int size) +{ + ringPvt *pring = (ringPvt *)epicsRingBytesCreate(size); + if(!pring) + return NULL; + pring->lock = epicsSpinCreate(); + return((void *)pring); +} + +epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + if (pring->lock) epicsSpinDestroy(pring->lock); + free((void *)pring); +} + +epicsShareFunc int epicsShareAPI epicsRingBytesGet( + epicsRingBytesId id, char *value,int nbytes) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet, nextPut, size; + int count; + + if (pring->lock) epicsSpinLock(pring->lock); + nextGet = pring->nextGet; + nextPut = pring->nextPut; + size = pring->size; + + if (nextGet <= nextPut) { + count = nextPut - nextGet; + if (count < nbytes) + nbytes = count; + if (nbytes) + memcpy (value, (void *)&pring->buffer[nextGet], nbytes); + nextGet += nbytes; + } + else { + count = size - nextGet; + if (count > nbytes) + count = nbytes; + memcpy (value, (void *)&pring->buffer[nextGet], count); + nextGet += count; + if (nextGet == size) { + int nLeft = nbytes - count; + if (nLeft > nextPut) + nLeft = nextPut; + memcpy (value+count, (void *)&pring->buffer[0], nLeft); + nextGet = nLeft; + nbytes = count + nLeft; + } + else { + nbytes = count; + } + } + pring->nextGet = nextGet; + + if (pring->lock) epicsSpinUnlock(pring->lock); + return nbytes; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesPut( + epicsRingBytesId id, char *value,int nbytes) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet, nextPut, size; + int freeCount, copyCount, topCount; + + if (pring->lock) epicsSpinLock(pring->lock); + nextGet = pring->nextGet; + nextPut = pring->nextPut; + size = pring->size; + + if (nextPut < nextGet) { + freeCount = nextGet - nextPut - SLOP; + if (nbytes > freeCount) { + if (pring->lock) epicsSpinUnlock(pring->lock); + return 0; + } + if (nbytes) + memcpy ((void *)&pring->buffer[nextPut], value, nbytes); + nextPut += nbytes; + } + else { + freeCount = size - nextPut + nextGet - SLOP; + if (nbytes > freeCount) { + if (pring->lock) epicsSpinUnlock(pring->lock); + return 0; + } + topCount = size - nextPut; + copyCount = (nbytes > topCount) ? topCount : nbytes; + if (copyCount) + memcpy ((void *)&pring->buffer[nextPut], value, copyCount); + nextPut += copyCount; + if (nextPut == size) { + int nLeft = nbytes - copyCount; + if (nLeft) + memcpy ((void *)&pring->buffer[0], value+copyCount, nLeft); + nextPut = nLeft; + } + } + pring->nextPut = nextPut; + + if (pring->lock) epicsSpinUnlock(pring->lock); + return nbytes; +} + +epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + if (pring->lock) epicsSpinLock(pring->lock); + pring->nextGet = pring->nextPut; + if (pring->lock) epicsSpinUnlock(pring->lock); +} + +epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet, nextPut; + + if (pring->lock) epicsSpinLock(pring->lock); + nextGet = pring->nextGet; + nextPut = pring->nextPut; + if (pring->lock) epicsSpinUnlock(pring->lock); + + if (nextPut < nextGet) + return nextGet - nextPut - SLOP; + else + return pring->size - nextPut + nextGet - SLOP; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet, nextPut; + int used; + + if (pring->lock) epicsSpinLock(pring->lock); + nextGet = pring->nextGet; + nextPut = pring->nextPut; + if (pring->lock) epicsSpinUnlock(pring->lock); + + used = nextPut - nextGet; + if (used < 0) used += pring->size; + + return used; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + return pring->size - SLOP; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + int isEmpty; + + if (pring->lock) epicsSpinLock(pring->lock); + isEmpty = (pring->nextPut == pring->nextGet); + if (pring->lock) epicsSpinUnlock(pring->lock); + + return isEmpty; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) +{ + return (epicsRingBytesFreeBytes(id) <= 0); +} diff --git a/modules/libcom/src/ring/epicsRingBytes.h b/modules/libcom/src/ring/epicsRingBytes.h new file mode 100644 index 000000000..011829bfe --- /dev/null +++ b/modules/libcom/src/ring/epicsRingBytes.h @@ -0,0 +1,54 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Marty Kraimer Date: 15JUL99 + * Eric Norum + * Ralph Lange + */ + +#ifndef INCepicsRingBytesh +#define INCepicsRingBytesh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +typedef void *epicsRingBytesId; + +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); +/* Same, but secured by a spinlock */ +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesLockedCreate(int nbytes); +epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesGet( + epicsRingBytesId id, char *value,int nbytes); +epicsShareFunc int epicsShareAPI epicsRingBytesPut( + epicsRingBytesId id, char *value,int nbytes); +epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); + +#ifdef __cplusplus +} +#endif + +/* NOTES + If there is only one writer it is not necessary to lock for put + If there is a single reader it is not necessary to lock for puts + + epicsRingBytesLocked uses a spinlock. +*/ + +#endif /* INCepicsRingBytesh */ diff --git a/modules/libcom/src/ring/epicsRingPointer.cpp b/modules/libcom/src/ring/epicsRingPointer.cpp new file mode 100644 index 000000000..9c144cec1 --- /dev/null +++ b/modules/libcom/src/ring/epicsRingPointer.cpp @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Marty Kraimer Date: 13OCT2000 + * Ralph Lange + */ + + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsRingPointer.h" +typedef epicsRingPointer voidPointer; + + +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size) +{ + voidPointer *pvoidPointer = new voidPointer(size, false); + return(reinterpret_cast(pvoidPointer)); +} + +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerLockedCreate(int size) +{ + voidPointer *pvoidPointer = new voidPointer(size, true); + return(reinterpret_cast(pvoidPointer)); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + delete pvoidPointer; +} + +epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return pvoidPointer->pop(); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id, void *p) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->push(p) ? 1 : 0)); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + pvoidPointer->flush(); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getFree()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getUsed()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getSize()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->isEmpty()) ? 1 : 0); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->isFull()) ? 1 : 0); +} diff --git a/modules/libcom/src/ring/epicsRingPointer.h b/modules/libcom/src/ring/epicsRingPointer.h new file mode 100644 index 000000000..48d62036d --- /dev/null +++ b/modules/libcom/src/ring/epicsRingPointer.h @@ -0,0 +1,201 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Marty Kraimer Date: 15JUL99 + * Ralph Lange + */ + +#ifndef INCepicsRingPointerh +#define INCepicsRingPointerh + +/* NOTES + * If there is only one writer it is not necessary to lock push + * If there is a single reader it is not necessary to lock pop + * + * epicsRingPointerLocked uses a spinlock. + */ + +#include "epicsSpin.h" +#include "shareLib.h" + +#ifdef __cplusplus +template +class epicsRingPointer { +public: /* Functions */ + epicsRingPointer(int size, bool locked); + ~epicsRingPointer(); + bool push(T *p); + T* pop(); + void flush(); + int getFree() const; + int getUsed() const; + int getSize() const; + bool isEmpty() const; + bool isFull() const; + +private: /* Prevent compiler-generated member functions */ + /* default constructor, copy constructor, assignment operator */ + epicsRingPointer(); + epicsRingPointer(const epicsRingPointer &); + epicsRingPointer& operator=(const epicsRingPointer &); + +private: /* Data */ + epicsSpinId lock; + volatile int nextPush; + volatile int nextPop; + int size; + T * volatile * buffer; +}; + +extern "C" { +#endif /*__cplusplus */ + +typedef void *epicsRingPointerId; + +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); +/* Same, but secured by a spinlock */ +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerLockedCreate(int size); +epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id); +/*ringPointerPush returns (0,1) if p (was not, was) put on ring*/ +epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id,void *p); +/*ringPointerPop returns 0 if ring is empty*/ +epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) ; +epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); + +/* This routine was incorrectly named in previous releases */ +#define epicsRingPointerSize epicsRingPointerGetSize + +#ifdef __cplusplus +} +#endif +/* END OF DECLARATIONS */ + +/* INLINE FUNCTIONS */ + +/* Algorithm note + * Space is allocated for one additional element. + * A put request is rejected if the it would cause nextPush to equal nextPop + * The algorithm does not require locking puts for a single writer + * or locking of gets for a single reader + */ +#ifdef __cplusplus + +template +inline epicsRingPointer::epicsRingPointer(int sz, bool locked) : + lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1]) +{ + if (locked) + lock = epicsSpinCreate(); +} + +template +inline epicsRingPointer::~epicsRingPointer() +{ + if (lock) epicsSpinDestroy(lock); + delete [] buffer; +} + +template +inline bool epicsRingPointer::push(T *p) +{ + if (lock) epicsSpinLock(lock); + int next = nextPush; + int newNext = next + 1; + if(newNext>=size) newNext=0; + if (newNext == nextPop) { + if (lock) epicsSpinUnlock(lock); + return(false); + } + buffer[next] = p; + nextPush = newNext; + if (lock) epicsSpinUnlock(lock); + return(true); +} + +template +inline T* epicsRingPointer::pop() +{ + if (lock) epicsSpinLock(lock); + int next = nextPop; + if (next == nextPush) { + if (lock) epicsSpinUnlock(lock); + return(0); + } + T*p = buffer[next]; + ++next; + if(next >=size) next = 0; + nextPop = next; + if (lock) epicsSpinUnlock(lock); + return(p); +} + +template +inline void epicsRingPointer::flush() +{ + if (lock) epicsSpinLock(lock); + nextPop = 0; + nextPush = 0; + if (lock) epicsSpinUnlock(lock); +} + +template +inline int epicsRingPointer::getFree() const +{ + if (lock) epicsSpinLock(lock); + int n = nextPop - nextPush - 1; + if (n < 0) n += size; + if (lock) epicsSpinUnlock(lock); + return n; +} + +template +inline int epicsRingPointer::getUsed() const +{ + if (lock) epicsSpinLock(lock); + int n = nextPush - nextPop; + if (n < 0) n += size; + if (lock) epicsSpinUnlock(lock); + return n; +} + +template +inline int epicsRingPointer::getSize() const +{ + return(size-1); +} + +template +inline bool epicsRingPointer::isEmpty() const +{ + bool isEmpty; + if (lock) epicsSpinLock(lock); + isEmpty = (nextPush == nextPop); + if (lock) epicsSpinUnlock(lock); + return isEmpty; +} + +template +inline bool epicsRingPointer::isFull() const +{ + if (lock) epicsSpinLock(lock); + int count = nextPush - nextPop +1; + if (lock) epicsSpinUnlock(lock); + return((count == 0) || (count == size)); +} + +#endif /* __cplusplus */ + +#endif /* INCepicsRingPointerh */ diff --git a/modules/libcom/src/taskwd/Makefile b/modules/libcom/src/taskwd/Makefile new file mode 100644 index 000000000..af738f0d6 --- /dev/null +++ b/modules/libcom/src/taskwd/Makefile @@ -0,0 +1,12 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/taskwd +INC += taskwd.h +Com_SRCS += taskwd.c diff --git a/modules/libcom/src/taskwd/taskwd.c b/modules/libcom/src/taskwd/taskwd.c new file mode 100644 index 000000000..44ddc779c --- /dev/null +++ b/modules/libcom/src/taskwd/taskwd.c @@ -0,0 +1,430 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* taskwd.c */ + +/* tasks and subroutines for a general purpose task watchdog */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "dbDefs.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsStdioRedirect.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "valgrind/valgrind.h" +#include "errlog.h" +#include "ellLib.h" +#include "errMdef.h" +#include "taskwd.h" + +struct tNode { + ELLNODE node; + epicsThreadId tid; + TASKWDFUNC callback; + void *usr; + int suspended; +}; + +struct mNode { + ELLNODE node; + const taskwdMonitor *funcs; + void *usr; +}; + +struct aNode { + void *key; + TASKWDANYFUNC callback; + void *usr; +}; + +union twdNode { + struct tNode t; + struct mNode m; + struct aNode a; +}; + +/* Registered Tasks */ +static epicsMutexId tLock; +static ELLLIST tList = ELLLIST_INIT; + +/* Active Monitors */ +static epicsMutexId mLock; +static ELLLIST mList = ELLLIST_INIT; + +/* Free List */ +static epicsMutexId fLock; +static ELLLIST fList = ELLLIST_INIT; + +/* Watchdog task control */ +static volatile enum { + twdctlInit, twdctlRun, twdctlDisable, twdctlExit +} twdCtl; +static epicsEventId loopEvent; +static epicsEventId exitEvent; + +/* Task delay times (seconds) */ +#define TASKWD_DELAY 6.0 + + +/* forward definitions */ +static union twdNode *allocNode(void); +static void freeNode(union twdNode *); + +/* Initialization, lazy */ + +static void twdTask(void *arg) +{ + struct tNode *pt; + struct mNode *pm; + + while (twdCtl != twdctlExit) { + if (twdCtl == twdctlRun) { + epicsMutexMustLock(tLock); + pt = (struct tNode *)ellFirst(&tList); + while (pt) { + int susp = epicsThreadIsSuspended(pt->tid); + if (susp != pt->suspended) { + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->notify) { + pm->funcs->notify(pm->usr, pt->tid, susp); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + if (susp) { + char tName[40]; + epicsThreadGetName(pt->tid, tName, sizeof(tName)); + errlogPrintf("Thread %s (%p) suspended\n", + tName, (void *)pt->tid); + if (pt->callback) { + pt->callback(pt->usr); + } + } + pt->suspended = susp; + } + pt = (struct tNode *)ellNext(&pt->node); + } + epicsMutexUnlock(tLock); + } + epicsEventWaitWithTimeout(loopEvent, TASKWD_DELAY); + } + epicsEventSignal(exitEvent); +} + + +static void twdShutdown(void *arg) +{ + ELLNODE *cur; + twdCtl = twdctlExit; + epicsEventSignal(loopEvent); + epicsEventWait(exitEvent); + while ((cur = ellGet(&fList)) != NULL) { + VALGRIND_MEMPOOL_FREE(&fList, cur); + free(cur); + } + VALGRIND_DESTROY_MEMPOOL(&fList); +} + +static void twdInitOnce(void *arg) +{ + epicsThreadId tid; + + tLock = epicsMutexMustCreate(); + mLock = epicsMutexMustCreate(); + fLock = epicsMutexMustCreate(); + ellInit(&fList); + VALGRIND_CREATE_MEMPOOL(&fList, 0, 0); + + twdCtl = twdctlRun; + loopEvent = epicsEventMustCreate(epicsEventEmpty); + exitEvent = epicsEventMustCreate(epicsEventEmpty); + + tid = epicsThreadCreate("taskwd", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + twdTask, NULL); + if (tid == 0) + cantProceed("Failed to spawn task watchdog thread\n"); + + epicsAtExit(twdShutdown, NULL); +} + +void taskwdInit(void) +{ + static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; + epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL); +} + + +/* For tasks to be monitored */ + +void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr) +{ + struct tNode *pt; + struct mNode *pm; + + taskwdInit(); + if (tid == 0) + tid = epicsThreadGetIdSelf(); + + pt = &allocNode()->t; + pt->tid = tid; + pt->callback = callback; + pt->usr = usr; + pt->suspended = FALSE; + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->insert) { + pm->funcs->insert(pm->usr, tid); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + epicsMutexMustLock(tLock); + ellAdd(&tList, (void *)pt); + epicsMutexUnlock(tLock); +} + +void taskwdRemove(epicsThreadId tid) +{ + struct tNode *pt; + struct mNode *pm; + char tName[40]; + + taskwdInit(); + + if (tid == 0) + tid = epicsThreadGetIdSelf(); + + epicsMutexMustLock(tLock); + pt = (struct tNode *)ellFirst(&tList); + while (pt != NULL) { + if (tid == pt->tid) { + ellDelete(&tList, (void *)pt); + epicsMutexUnlock(tLock); + freeNode((union twdNode *)pt); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->remove) { + pm->funcs->remove(pm->usr, tid); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + return; + } + pt = (struct tNode *)ellNext(&pt->node); + } + epicsMutexUnlock(tLock); + + epicsThreadGetName(tid, tName, sizeof(tName)); + errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n", + tName, (void *)tid); +} + + +/* Monitoring API */ + +void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr) +{ + struct mNode *pm; + + if (funcs == NULL) return; + + taskwdInit(); + + pm = &allocNode()->m; + pm->funcs = funcs; + pm->usr = usr; + + epicsMutexMustLock(mLock); + ellAdd(&mList, (void *)pm); + epicsMutexUnlock(mLock); +} + +void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr) +{ + struct mNode *pm; + + if (funcs == NULL) return; + + taskwdInit(); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs == funcs && pm->usr == usr) { + ellDelete(&mList, (void *)pm); + freeNode((union twdNode *)pm); + epicsMutexUnlock(mLock); + return; + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + errlogPrintf("taskwdMonitorDel: Unregistered!\n"); +} + + +/* Support old API for backwards compatibility */ + +static void anyNotify(void *usr, epicsThreadId tid, int suspended) +{ + struct aNode *pa = (struct aNode *)usr; + + if (suspended) { + pa->callback(pa->usr, tid); + } +} + +static taskwdMonitor anyFuncs = { + NULL, anyNotify, NULL +}; + +void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr) +{ + struct mNode *pm; + struct aNode *pa; + + if (callback == NULL) return; + + taskwdInit(); + + pa = &allocNode()->a; + pa->key = key; + pa->callback = callback; + pa->usr = usr; + + pm = &allocNode()->m; + pm->funcs = &anyFuncs; + pm->usr = pa; + + epicsMutexMustLock(mLock); + ellAdd(&mList, (void *)pm); + epicsMutexUnlock(mLock); +} + +void taskwdAnyRemove(void *key) +{ + struct mNode *pm; + struct aNode *pa; + + taskwdInit(); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs == &anyFuncs) { + pa = (struct aNode *)pm->usr; + if (pa->key == key) { + ellDelete(&mList, (void *)pm); + freeNode((union twdNode *)pa); + freeNode((union twdNode *)pm); + epicsMutexUnlock(mLock); + return; + } + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + errlogPrintf("taskwdAnyRemove: Unregistered key %p\n", key); +} + + +/* Report function */ + +epicsShareFunc void taskwdShow(int level) +{ + struct tNode *pt; + int mCount, fCount, tCount; + char tName[40]; + + epicsMutexMustLock(mLock); + mCount = ellCount(&mList); + epicsMutexUnlock(mLock); + + epicsMutexMustLock(fLock); + fCount = ellCount(&fList); + epicsMutexUnlock(fLock); + + epicsMutexMustLock(tLock); + tCount = ellCount(&tList); + printf("%d monitors, %d threads registered, %d free nodes\n", + mCount, tCount, fCount); + if (level) { + printf("%16.16s %9s %12s %12s %12s\n", + "THREAD NAME", "STATE", "EPICS TID", "CALLBACK", "USR ARG"); + pt = (struct tNode *)ellFirst(&tList); + while (pt != NULL) { + epicsThreadGetName(pt->tid, tName, sizeof(tName)); + printf("%16.16s %9s %12p %12p %12p\n", + tName, pt->suspended ? "Suspended" : "Ok ", + (void *)pt->tid, (void *)pt->callback, pt->usr); + pt = (struct tNode *)ellNext(&pt->node); + } + } + epicsMutexUnlock(tLock); +} + + +/* Free list management */ + +static union twdNode *newNode(void) +{ + union twdNode *pn; + + epicsMutexMustLock(fLock); + pn = (union twdNode *)ellGet(&fList); + if (pn) { + VALGRIND_MEMPOOL_FREE(&fList, pn); + } + epicsMutexUnlock(fLock); + if (!pn) + pn = calloc(1, sizeof(union twdNode)); + if (pn) + VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(*pn)); + return pn; +} + +static union twdNode *allocNode(void) +{ + union twdNode *pn = newNode(); + while (!pn) { + errlogPrintf("Thread taskwd suspending: out of memory\n"); + epicsThreadSuspendSelf(); + pn = newNode(); + } + return pn; +} + +static void freeNode(union twdNode *pn) +{ + VALGRIND_MEMPOOL_FREE(&fList, pn); + VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE)); + epicsMutexMustLock(fLock); + ellAdd(&fList, (void *)pn); + epicsMutexUnlock(fLock); +} diff --git a/modules/libcom/src/taskwd/taskwd.h b/modules/libcom/src/taskwd/taskwd.h new file mode 100644 index 000000000..4d5274d56 --- /dev/null +++ b/modules/libcom/src/taskwd/taskwd.h @@ -0,0 +1,66 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* General purpose task watchdog */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#ifndef INC_taskwd_H +#define INC_taskwd_H + +#include "epicsThread.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Initialization, optional */ +epicsShareFunc void taskwdInit(void); + + +/* For tasks to be monitored */ +typedef void (*TASKWDFUNC)(void *usr); + +epicsShareFunc void taskwdInsert(epicsThreadId tid, + TASKWDFUNC callback, void *usr); +epicsShareFunc void taskwdRemove(epicsThreadId tid); + + +/* Monitoring API */ +typedef struct { + void (*insert)(void *usr, epicsThreadId tid); + void (*notify)(void *usr, epicsThreadId tid, int suspended); + void (*remove)(void *usr, epicsThreadId tid); +} taskwdMonitor; + +epicsShareFunc void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr); +epicsShareFunc void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr); + + +/* Old monitoring API, deprecated */ +typedef void (*TASKWDANYFUNC)(void *usr, epicsThreadId tid); + +epicsShareFunc void taskwdAnyInsert(void *key, + TASKWDANYFUNC callback, void *usr); +epicsShareFunc void taskwdAnyRemove(void *key); + + +/* Report function */ +epicsShareFunc void taskwdShow(int level); + + +#ifdef __cplusplus +} +#endif + +#endif /* INC_taskwd_H */ diff --git a/modules/libcom/src/timer/Makefile b/modules/libcom/src/timer/Makefile new file mode 100644 index 000000000..51094d069 --- /dev/null +++ b/modules/libcom/src/timer/Makefile @@ -0,0 +1,17 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/timer +INC += epicsTimer.h +Com_SRCS += epicsTimer.cpp +Com_SRCS += timer.cpp +Com_SRCS += timerQueue.cpp +Com_SRCS += timerQueueActive.cpp +Com_SRCS += timerQueueActiveMgr.cpp +Com_SRCS += timerQueuePassive.cpp diff --git a/modules/libcom/src/timer/epicsTimer.cpp b/modules/libcom/src/timer/epicsTimer.cpp new file mode 100644 index 000000000..e55280e7e --- /dev/null +++ b/modules/libcom/src/timer/epicsTimer.cpp @@ -0,0 +1,276 @@ +/*************************************************************************\ +* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "epicsTimer.h" +#include "epicsGuard.h" +#include "timerPrivate.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +template class tsFreeList < epicsTimerForC, 0x20 >; + +epicsTimer::~epicsTimer () {} + +epicsTimerQueueNotify::~epicsTimerQueueNotify () {} + +epicsTimerNotify::~epicsTimerNotify () {} + +void epicsTimerNotify::show ( unsigned /* level */ ) const {} + +epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) : + timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn ) +{ +} + +epicsTimerForC::~epicsTimerForC () +{ +} + +void epicsTimerForC::destroy () +{ + timerQueue & queueTmp = this->queue; + this->~epicsTimerForC (); + queueTmp.timerForCFreeList.release ( this ); +} + +epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & ) +{ + ( *this->pCallBack ) ( this->pPrivate ); + return noRestart; +} + +epicsTimerQueueActiveForC :: + epicsTimerQueueActiveForC ( RefMgr & refMgr, + bool okToShare, unsigned priority ) : + timerQueueActive ( refMgr, okToShare, priority ) +{ + timerQueueActive::start(); +} + +epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () +{ +} + +void epicsTimerQueueActiveForC::release () +{ + _refMgr->release ( *this ); +} + +epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) : + timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), + pRescheduleCallback ( pRescheduleCallbackIn ), + pSleepQuantumCallback ( pSleepQuantumCallbackIn ), + pPrivate ( pPrivateIn ) +{ +} + +epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () +{ +} + +void epicsTimerQueuePassiveForC::reschedule () +{ + (*this->pRescheduleCallback) ( this->pPrivate ); +} + +double epicsTimerQueuePassiveForC::quantum () +{ + return (*this->pSleepQuantumCallback) ( this->pPrivate ); +} + +void epicsTimerQueuePassiveForC::destroy () +{ + delete this; +} + +epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) : + delay ( - DBL_MAX ) +{ + if ( restart != noRestart ) { + throw std::logic_error + ( "timer restart was requested without specifying a delay?" ); + } +} + +epicsShareFunc epicsTimerNotify::expireStatus::expireStatus + ( restart_t restartIn, const double & expireDelaySec ) : + delay ( expireDelaySec ) +{ + if ( restartIn != epicsTimerNotify::restart ) { + throw std::logic_error + ( "no timer restart was requested, but a delay was specified?" ); + } + if ( this->delay < 0.0 || !finite(this->delay) ) { + throw std::logic_error + ( "timer restart was requested, but a negative delay was specified?" ); + } +} + +epicsShareFunc bool epicsTimerNotify::expireStatus::restart () const +{ + return this->delay >= 0.0 && finite(this->delay); +} + +epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const +{ + if ( this->delay < 0.0 || !finite(this->delay) ) { + throw std::logic_error + ( "no timer restart was requested, but you are asking for a restart delay?" ); + } + return this->delay; +} + +extern "C" epicsTimerQueuePassiveId epicsShareAPI + epicsTimerQueuePassiveCreate ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) +{ + try { + return new epicsTimerQueuePassiveForC ( + pRescheduleCallbackIn, + pSleepQuantumCallbackIn, + pPrivateIn ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI + epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId pQueue ) +{ + pQueue->destroy (); +} + +extern "C" double epicsShareAPI + epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) +{ + try { + return pQueue->process ( epicsTime::getCurrent() ); + } + catch ( ... ) { + return 1.0; + } +} + +extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( + epicsTimerQueuePassiveId pQueue, epicsTimerCallback pCallback, void *pArg ) +{ + try { + return & pQueue->createTimerForC ( pCallback, pArg ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( + epicsTimerQueuePassiveId /* pQueue */, epicsTimerId pTmr ) +{ + pTmr->destroy (); +} + +extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( + epicsTimerQueuePassiveId pQueue, unsigned int level ) +{ + pQueue->show ( level ); +} + +extern "C" epicsTimerQueueId epicsShareAPI + epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) +{ + try { + epicsSingleton < timerQueueActiveMgr > :: reference ref = + timerQueueMgrEPICS.getReference (); + epicsTimerQueueActiveForC & tmr = + ref->allocate ( ref, okToShare ? true : false, threadPriority ); + return &tmr; + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId pQueue ) +{ + pQueue->release (); +} + +extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( + epicsTimerQueueId pQueue, epicsTimerCallback pCallback, void *pArg ) +{ + try { + return & pQueue->createTimerForC ( pCallback, pArg ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI epicsTimerQueueShow ( + epicsTimerQueueId pQueue, unsigned int level ) +{ + pQueue->show ( level ); +} + +extern "C" void epicsShareAPI epicsTimerQueueDestroyTimer ( + epicsTimerQueueId /* pQueue */, epicsTimerId pTmr ) +{ + pTmr->destroy (); +} + +extern "C" void epicsShareAPI epicsTimerStartTime ( + epicsTimerId pTmr, const epicsTimeStamp *pTime ) +{ + pTmr->start ( *pTmr, *pTime ); +} + +extern "C" void epicsShareAPI epicsTimerStartDelay ( + epicsTimerId pTmr, double delaySeconds ) +{ + pTmr->start ( *pTmr, delaySeconds ); +} + +extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr ) +{ + pTmr->cancel (); +} + +extern "C" double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId pTmr ) +{ + return pTmr->getExpireDelay (); +} + +extern "C" void epicsShareAPI epicsTimerShow ( + epicsTimerId pTmr, unsigned int level ) +{ + pTmr->timer::show ( level ); +} + diff --git a/modules/libcom/src/timer/epicsTimer.h b/modules/libcom/src/timer/epicsTimer.h new file mode 100644 index 000000000..72270f273 --- /dev/null +++ b/modules/libcom/src/timer/epicsTimer.h @@ -0,0 +1,185 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTimer.h */ + +/* Authors: Marty Kraimer, Jeff Hill */ + +#ifndef epicsTimerH +#define epicsTimerH + +#include + +#include "shareLib.h" +#include "epicsTime.h" +#include "epicsThread.h" + +#ifdef __cplusplus + +/* + * Notes: + * 1) epicsTimer does not hold its lock when calling callbacks. + */ + +/* code using a timer must implement epicsTimerNotify */ +class epicsShareClass epicsTimerNotify { +public: + enum restart_t { noRestart, restart }; + class expireStatus { + public: + epicsShareFunc expireStatus ( restart_t ); + epicsShareFunc expireStatus ( restart_t, const double & expireDelaySec ); + epicsShareFunc bool restart () const; + epicsShareFunc double expirationDelay () const; + private: + double delay; + }; + + virtual ~epicsTimerNotify () = 0; + /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ + virtual expireStatus expire ( const epicsTime & currentTime ) = 0; + virtual void show ( unsigned int level ) const; +}; + +class epicsShareClass epicsTimer { +public: + /* calls cancel (see warning below) and then destroys the timer */ + virtual void destroy () = 0; + virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0; + virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0; + /* WARNING: A deadlock will occur if you hold a lock while + * calling this function that you also take within the timer + * expiration callback. + */ + virtual void cancel () = 0; + struct expireInfo { + expireInfo ( bool active, const epicsTime & expireTime ); + bool active; + epicsTime expireTime; + }; + virtual expireInfo getExpireInfo () const = 0; + double getExpireDelay (); + virtual void show ( unsigned int level ) const = 0; +protected: + virtual ~epicsTimer () = 0; /* protected => delete() must not be called */ +}; + +class epicsTimerQueue { +public: + virtual epicsTimer & createTimer () = 0; + virtual void show ( unsigned int level ) const = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueue () = 0; +}; + +class epicsTimerQueueActive + : public epicsTimerQueue { +public: + static epicsShareFunc epicsTimerQueueActive & allocate ( + bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); + virtual void release () = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueueActive () = 0; +}; + +class epicsTimerQueueNotify { +public: + /* called when a new timer is inserted into the queue and the */ + /* delay to the next expire has changed */ + virtual void reschedule () = 0; + /* if there is a quantum in the scheduling of timer intervals */ + /* return this quantum in seconds. If unknown then return zero. */ + virtual double quantum () = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueueNotify () = 0; +}; + +class epicsTimerQueuePassive + : public epicsTimerQueue { +public: + static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); + epicsShareFunc virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */ + virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ +}; + +inline epicsTimer::expireInfo::expireInfo ( bool activeIn, + const epicsTime & expireTimeIn ) : + active ( activeIn ), expireTime ( expireTimeIn ) +{ +} + +inline double epicsTimer::getExpireDelay () +{ + epicsTimer::expireInfo info = this->getExpireInfo (); + if ( info.active ) { + double delay = info.expireTime - epicsTime::getCurrent (); + if ( delay < 0.0 ) { + delay = 0.0; + } + return delay; + } + return - DBL_MAX; +} + +extern "C" { +#endif /* __cplusplus */ + +typedef struct epicsTimerForC * epicsTimerId; +typedef void ( *epicsTimerCallback ) ( void *pPrivate ); + +/* thread managed timer queue */ +typedef struct epicsTimerQueueActiveForC * epicsTimerQueueId; +epicsShareFunc epicsTimerQueueId epicsShareAPI + epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueRelease ( epicsTimerQueueId ); +epicsShareFunc epicsTimerId epicsShareAPI + epicsTimerQueueCreateTimer ( epicsTimerQueueId queueid, + epicsTimerCallback callback, void *arg ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueDestroyTimer ( epicsTimerQueueId queueid, epicsTimerId id ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueShow ( epicsTimerQueueId id, unsigned int level ); + +/* passive timer queue */ +typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId; +typedef void ( * epicsTimerQueueNotifyReschedule ) ( void * pPrivate ); +typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate ); +epicsShareFunc epicsTimerQueuePassiveId epicsShareAPI + epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, void *pPrivate ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId ); +epicsShareFunc epicsTimerId epicsShareAPI + epicsTimerQueuePassiveCreateTimer ( + epicsTimerQueuePassiveId queueid, epicsTimerCallback pCallback, void *pArg ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveDestroyTimer ( epicsTimerQueuePassiveId queueid, epicsTimerId id ); +epicsShareFunc double epicsShareAPI + epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); + +/* timer */ +epicsShareFunc void epicsShareAPI + epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); +epicsShareFunc void epicsShareAPI + epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); +epicsShareFunc void epicsShareAPI + epicsTimerCancel ( epicsTimerId id ); +epicsShareFunc double epicsShareAPI + epicsTimerGetExpireDelay ( epicsTimerId id ); +epicsShareFunc void epicsShareAPI + epicsTimerShow ( epicsTimerId id, unsigned int level ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* epicsTimerH */ diff --git a/modules/libcom/src/timer/timer.cpp b/modules/libcom/src/timer/timer.cpp new file mode 100644 index 000000000..35d6e47bf --- /dev/null +++ b/modules/libcom/src/timer/timer.cpp @@ -0,0 +1,243 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" +#include "errlog.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class tsFreeList < timer, 0x20 >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +timer::timer ( timerQueue & queueIn ) : + queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 ) +{ +} + +timer::~timer () +{ + this->cancel (); +} + +void timer::destroy () +{ + timerQueue & queueTmp = this->queue; + this->~timer (); + queueTmp.timerFreeList.release ( this ); +} + +void timer::start ( epicsTimerNotify & notify, double delaySeconds ) +{ + this->start ( notify, epicsTime::getCurrent () + delaySeconds ); +} + +void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) +{ + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + this->privateStart ( notify, expire ); +} + +void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) +{ + this->pNotify = & notify; + this->exp = expire - ( this->queue.notify.quantum () / 2.0 ); + + bool reschedualNeeded = false; + if ( this->curState == stateActive ) { + // above expire time and notify will override any restart parameters + // that may be returned from the timer expire callback + return; + } + else if ( this->curState == statePending ) { + this->queue.timerList.remove ( *this ); + if ( this->queue.timerList.first() == this && + this->queue.timerList.count() > 0 ) { + reschedualNeeded = true; + } + } + +# ifdef DEBUG + unsigned preemptCount=0u; +# endif + + // + // insert into the pending queue + // + // Finds proper time sorted location using a linear search. + // + // **** this should use a binary tree ???? + // + tsDLIter < timer > pTmr = this->queue.timerList.lastIter (); + while ( true ) { + if ( ! pTmr.valid () ) { + // + // add to the beginning of the list + // + this->queue.timerList.push ( *this ); + reschedualNeeded = true; + break; + } + if ( pTmr->exp <= this->exp ) { + // + // add after the item found that expires earlier + // + this->queue.timerList.insertAfter ( *this, *pTmr ); + break; + } +# ifdef DEBUG + preemptCount++; +# endif + --pTmr; + } + + this->curState = timer::statePending; + + if ( reschedualNeeded ) { + this->queue.notify.reschedule (); + } + +# if defined(DEBUG) && 0 + this->show ( 10u ); + this->queue.show ( 10u ); +# endif + + debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n", + typeid ( this->notify ).name (), + expire - epicsTime::getCurrent (), + this, preemptCount ) ); +} + +void timer::cancel () +{ + bool reschedual = false; + bool wakeupCancelBlockingThreads = false; + { + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + this->pNotify = 0; + if ( this->curState == statePending ) { + this->queue.timerList.remove ( *this ); + this->curState = stateLimbo; + if ( this->queue.timerList.first() == this && + this->queue.timerList.count() > 0 ) { + reschedual = true; + } + } + else if ( this->curState == stateActive ) { + this->queue.cancelPending = true; + this->curState = timer::stateLimbo; + if ( this->queue.processThread != epicsThreadGetIdSelf() ) { + // make certain timer expire() does not run after cancel () returns, + // but dont require that lock is applied while calling expire() + while ( this->queue.cancelPending && + this->queue.pExpireTmr == this ) { + epicsGuardRelease < epicsMutex > autoRelease ( locker ); + this->queue.cancelBlockingEvent.wait (); + } + // in case other threads are waiting + wakeupCancelBlockingThreads = true; + } + } + } + if ( reschedual ) { + this->queue.notify.reschedule (); + } + if ( wakeupCancelBlockingThreads ) { + this->queue.cancelBlockingEvent.signal (); + } +} + +epicsTimer::expireInfo timer::getExpireInfo () const +{ + // taking a lock here guarantees that users will not + // see brief intervals when a timer isnt active because + // it is is canceled when start is called + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + if ( this->curState == statePending || this->curState == stateActive ) { + return expireInfo ( true, this->exp ); + } + return expireInfo ( false, epicsTime() ); +} + +void timer::show ( unsigned int level ) const +{ + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + double delay; + if ( this->curState == statePending || this->curState == stateActive ) { + try { + delay = this->exp - epicsTime::getCurrent(); + } + catch ( ... ) { + delay = - DBL_MAX; + } + } + else { + delay = -DBL_MAX; + } + const char *pStateName; + if ( this->curState == statePending ) { + pStateName = "pending"; + } + else if ( this->curState == stateActive ) { + pStateName = "active"; + } + else if ( this->curState == stateLimbo ) { + pStateName = "limbo"; + } + else { + pStateName = "corrupt"; + } + printf ( "timer, state = %s, delay = %f\n", + pStateName, delay ); + if ( level >= 1u && this->pNotify ) { + this->pNotify->show ( level - 1u ); + } +} + +void timer::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void epicsTimerForC::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/modules/libcom/src/timer/timerPrivate.h b/modules/libcom/src/timer/timerPrivate.h new file mode 100644 index 000000000..55291309c --- /dev/null +++ b/modules/libcom/src/timer/timerPrivate.h @@ -0,0 +1,277 @@ +/*************************************************************************\ +* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef epicsTimerPrivate_h +#define epicsTimerPrivate_h + +#include + +#include "tsFreeList.h" +#include "epicsSingleton.h" +#include "tsDLList.h" +#include "epicsTimer.h" +#include "compilerDependencies.h" + +#ifdef DEBUG +# define debugPrintf(ARGSINPAREN) printf ARGSINPAREN +#else +# define debugPrintf(ARGSINPAREN) +#endif + +template < class T > class epicsGuard; + +class timer : public epicsTimer, public tsDLNode < timer > { +public: + void destroy (); + void start ( class epicsTimerNotify &, const epicsTime & ); + void start ( class epicsTimerNotify &, double delaySeconds ); + void cancel (); + expireInfo getExpireInfo () const; + void show ( unsigned int level ) const; + void * operator new ( size_t size, tsFreeList < timer, 0x20 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & )) +protected: + timer ( class timerQueue & ); + ~timer (); + timerQueue & queue; +private: + enum state { statePending = 45, stateActive = 56, stateLimbo = 78 }; + epicsTime exp; // experation time + state curState; // current state + epicsTimerNotify * pNotify; // callback + void privateStart ( epicsTimerNotify & notify, const epicsTime & ); + timer & operator = ( const timer & ); + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + void operator delete ( void * ); + friend class timerQueue; +}; + +struct epicsTimerForC : public epicsTimerNotify, public timer { +public: + void destroy (); +protected: + epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn ); + ~epicsTimerForC (); + void * operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList < epicsTimerForC, 0x20 > & )) +private: + epicsTimerCallback pCallBack; + void * pPrivate; + expireStatus expire ( const epicsTime & currentTime ); + epicsTimerForC & operator = ( const epicsTimerForC & ); + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + void operator delete ( void * ); + friend class timerQueue; +}; + +using std :: type_info; + +class timerQueue : public epicsTimerQueue { +public: + timerQueue ( epicsTimerQueueNotify ¬ify ); + virtual ~timerQueue (); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + double process ( const epicsTime & currentTime ); + void show ( unsigned int level ) const; +private: + tsFreeList < timer, 0x20 > timerFreeList; + tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList; + mutable epicsMutex mutex; + epicsEvent cancelBlockingEvent; + tsDLList < timer > timerList; + epicsTimerQueueNotify & notify; + timer * pExpireTmr; + epicsThreadId processThread; + epicsTime exceptMsgTimeStamp; + bool cancelPending; + static const double exceptMsgMinPeriod; + void printExceptMsg ( const char * pName, + const type_info & type ); + timerQueue ( const timerQueue & ); + timerQueue & operator = ( const timerQueue & ); + friend class timer; + friend struct epicsTimerForC; +}; + +class timerQueueActiveMgrPrivate { +public: + timerQueueActiveMgrPrivate (); +protected: + virtual ~timerQueueActiveMgrPrivate () = 0; +private: + unsigned referenceCount; + friend class timerQueueActiveMgr; +}; + +class timerQueueActiveMgr; + +class timerQueueActive : public epicsTimerQueueActive, + public epicsThreadRunable, public epicsTimerQueueNotify, + public timerQueueActiveMgrPrivate { +public: + typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; + timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); + void start (); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + void show ( unsigned int level ) const; + bool sharingOK () const; + unsigned threadPriority () const; +protected: + ~timerQueueActive (); + RefMgr _refMgr; +private: + timerQueue queue; + epicsEvent rescheduleEvent; + epicsEvent exitEvent; + epicsThread thread; + const double sleepQuantum; + bool okToShare; + bool exitFlag; + bool terminateFlag; + void run (); + void reschedule (); + double quantum (); + void _printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ); + epicsTimerQueue & getEpicsTimerQueue (); + timerQueueActive ( const timerQueueActive & ); + timerQueueActive & operator = ( const timerQueueActive & ); +}; + +class timerQueueActiveMgr { +public: + typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis; + timerQueueActiveMgr (); + ~timerQueueActiveMgr (); + epicsTimerQueueActiveForC & allocate ( RefThis &, bool okToShare, + unsigned threadPriority = epicsThreadPriorityMin + 10 ); + void release ( epicsTimerQueueActiveForC & ); +private: + epicsMutex mutex; + tsDLList < epicsTimerQueueActiveForC > sharedQueueList; + timerQueueActiveMgr ( const timerQueueActiveMgr & ); + timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); +}; + +extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; + +class timerQueuePassive : public epicsTimerQueuePassive { +public: + timerQueuePassive ( epicsTimerQueueNotify & ); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + void show ( unsigned int level ) const; + double process ( const epicsTime & currentTime ); +protected: + timerQueue queue; + ~timerQueuePassive (); + epicsTimerQueue & getEpicsTimerQueue (); + timerQueuePassive ( const timerQueuePassive & ); + timerQueuePassive & operator = ( const timerQueuePassive & ); +}; + +struct epicsTimerQueuePassiveForC : + public epicsTimerQueueNotify, public timerQueuePassive { +public: + epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, + void * pPrivate ); + void destroy (); +protected: + ~epicsTimerQueuePassiveForC (); +private: + epicsTimerQueueNotifyReschedule pRescheduleCallback; + epicsTimerQueueNotifyQuantum pSleepQuantumCallback; + void * pPrivate; + static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; + void reschedule (); + double quantum (); +}; + +struct epicsTimerQueueActiveForC : public timerQueueActive, + public tsDLNode < epicsTimerQueueActiveForC > { +public: + epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority ); + void release (); + void * operator new ( size_t ); + void operator delete ( void * ); +protected: + virtual ~epicsTimerQueueActiveForC (); +private: + epicsTimerQueueActiveForC ( const epicsTimerQueueActiveForC & ); + epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); +}; + +inline bool timerQueueActive::sharingOK () const +{ + return this->okToShare; +} + +inline unsigned timerQueueActive::threadPriority () const +{ + return thread.getPriority (); +} + +inline void * timer::operator new ( size_t size, + tsFreeList < timer, 0x20 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void timer::operator delete ( void * pCadaver, + tsFreeList < timer, 0x20 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * epicsTimerForC::operator new ( size_t size, + tsFreeList < epicsTimerForC, 0x20 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void epicsTimerForC::operator delete ( void * pCadaver, + tsFreeList < epicsTimerForC, 0x20 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * epicsTimerQueueActiveForC::operator new ( size_t size ) +{ + return ::operator new ( size ); +} + +inline void epicsTimerQueueActiveForC::operator delete ( void * pCadaver ) +{ + ::operator delete ( pCadaver ); +} + +#endif // epicsTimerPrivate_h + diff --git a/modules/libcom/src/timer/timerQueue.cpp b/modules/libcom/src/timer/timerQueue.cpp new file mode 100644 index 000000000..5a798d4d4 --- /dev/null +++ b/modules/libcom/src/timer/timerQueue.cpp @@ -0,0 +1,225 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" +#include "errlog.h" + +const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds + +epicsTimerQueue::~epicsTimerQueue () {} + +timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : + notify ( notifyIn ), + pExpireTmr ( 0 ), + processThread ( 0 ), + exceptMsgTimeStamp ( + epicsTime :: getCurrent () - exceptMsgMinPeriod ), + cancelPending ( false ) +{ +} + +timerQueue::~timerQueue () +{ + timer *pTmr; + while ( ( pTmr = this->timerList.get () ) ) { + pTmr->curState = timer::stateLimbo; + } +} + +void timerQueue :: + printExceptMsg ( const char * pName, const type_info & type ) +{ + char date[64]; + double delay; + try { + epicsTime cur = epicsTime :: getCurrent (); + delay = cur - this->exceptMsgTimeStamp; + cur.strftime ( date, sizeof ( date ), + "%a %b %d %Y %H:%M:%S.%f" ); + if ( delay >= exceptMsgMinPeriod ) { + this->exceptMsgTimeStamp = cur; + } + } + catch ( ... ) { + delay = DBL_MAX; + strcpy ( date, "UKN DATE" ); + } + if ( delay >= exceptMsgMinPeriod ) { + // we dont touch the typeid for the timer expiration + // notify interface here because they might have + // destroyed the timer during its callback + errlogPrintf ( + "timerQueue: Unexpected C++ exception \"%s\" " + "with type \"%s\" during timer expiration " + "callback at %s\n", + pName, + type.name (), + date ); + errlogFlush (); + } +} + +double timerQueue::process ( const epicsTime & currentTime ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( this->pExpireTmr ) { + // if some other thread is processing the queue + // (or if this is a recursive call) + timer * pTmr = this->timerList.first (); + if ( pTmr ) { + double delay = pTmr->exp - currentTime; + if ( delay < 0.0 ) { + delay = 0.0; + } + return delay; + } + else { + return DBL_MAX; + } + } + + // + // Tag current epired tmr so that we can detect if call back + // is in progress when canceling the timer. + // + if ( this->timerList.first () ) { + if ( currentTime >= this->timerList.first ()->exp ) { + this->pExpireTmr = this->timerList.first (); + this->timerList.remove ( *this->pExpireTmr ); + this->pExpireTmr->curState = timer::stateActive; + this->processThread = epicsThreadGetIdSelf (); +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + } + else { + double delay = this->timerList.first ()->exp - currentTime; + debugPrintf ( ( "no activity process %f to next\n", delay ) ); + return delay; + } + } + else { + return DBL_MAX; + } + +# ifdef DEBUG + unsigned N = 0u; +# endif + + double delay = DBL_MAX; + while ( true ) { + epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; + this->pExpireTmr->pNotify = 0; + epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); + + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + + debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", + N++, typeid ( this->pExpireTmr->notify ).name (), + currentTime - this->pExpireTmr->exp ) ); + try { + expStat = pTmpNotify->expire ( currentTime ); + } + catch ( std::exception & except ) { + printExceptMsg ( except.what (), typeid ( except ) ); + } + catch ( ... ) { + printExceptMsg ( "non-standard exception", typeid ( void ) ); + } + } + + // + // only restart if they didnt cancel() the timer + // while the call back was running + // + if ( this->cancelPending ) { + // 1) if another thread is canceling then cancel() waits for + // the event below + // 2) if this thread is canceling in the timer callback then + // dont touch timer or notify here because the cancel might + // have occurred because they destroyed the timer in the + // callback + this->cancelPending = false; + this->cancelBlockingEvent.signal (); + } + else { + this->pExpireTmr->curState = timer::stateLimbo; + if ( this->pExpireTmr->pNotify ) { + // pNotify was cleared above so if it is valid now we know that + // someone has started the timer from another thread and that + // predominates over the restart parameters from expire. + this->pExpireTmr->privateStart ( + *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); + } + else if ( expStat.restart() ) { + // restart as nec + this->pExpireTmr->privateStart ( + *pTmpNotify, currentTime + expStat.expirationDelay() ); + } + } + this->pExpireTmr = 0; + + if ( this->timerList.first () ) { + if ( currentTime >= this->timerList.first ()->exp ) { + this->pExpireTmr = this->timerList.first (); + this->timerList.remove ( *this->pExpireTmr ); + this->pExpireTmr->curState = timer::stateActive; +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + } + else { + delay = this->timerList.first ()->exp - currentTime; + this->processThread = 0; + break; + } + } + else { + this->processThread = 0; + delay = DBL_MAX; + break; + } + } + return delay; +} + +epicsTimer & timerQueue::createTimer () +{ + return * new ( this->timerFreeList ) timer ( * this ); +} + +epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) +{ + return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); +} + +void timerQueue::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); + if ( level >= 1u ) { + tsDLIterConst < timer > iter = this->timerList.firstIter (); + while ( iter.valid () ) { + iter->show ( level - 1u ); + ++iter; + } + } +} diff --git a/modules/libcom/src/timer/timerQueueActive.cpp b/modules/libcom/src/timer/timerQueueActive.cpp new file mode 100644 index 000000000..5d8b72951 --- /dev/null +++ b/modules/libcom/src/timer/timerQueueActive.cpp @@ -0,0 +1,155 @@ +/*************************************************************************\ +* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#define epicsExportSharedSymbols +#include "timerPrivate.h" +#include "errlog.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class epicsSingleton < timerQueueActiveMgr >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; + +epicsTimerQueueActive::~epicsTimerQueueActive () {} + +epicsTimerQueueActive & epicsTimerQueueActive::allocate ( bool okToShare, unsigned threadPriority ) +{ + epicsSingleton < timerQueueActiveMgr >::reference pMgr = + timerQueueMgrEPICS.getReference (); + return pMgr->allocate ( pMgr, okToShare, threadPriority ); +} + +timerQueueActive :: + timerQueueActive ( RefMgr & refMgr, + bool okToShareIn, unsigned priority ) : + _refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue", + epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), + sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), + exitFlag ( false ), terminateFlag ( false ) +{ +} + +void timerQueueActive::start () +{ + this->thread.start (); +} + +timerQueueActive::~timerQueueActive () +{ + this->terminateFlag = true; + this->rescheduleEvent.signal (); + while ( ! this->exitFlag ) { + this->exitEvent.wait ( 1.0 ); + } + // in case other threads are waiting here also + this->exitEvent.signal (); +} + +void timerQueueActive :: _printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ) +{ + char date[64]; + try { + epicsTime cur = epicsTime :: getCurrent (); + cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + } + catch ( ... ) { + strcpy ( date, "" ); + } + errlogPrintf ( + "timerQueueActive: Unexpected C++ exception \"%s\" with type \"%s\" " + "while processing timer queue, at %s\n", + pExceptionContext, pExceptionTypeName, date ); +} + + +void timerQueueActive :: run () +{ + this->exitFlag = false; + while ( ! this->terminateFlag ) { + try { + double delay = this->queue.process ( epicsTime::getCurrent() ); + debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) ); + this->rescheduleEvent.wait ( delay ); + } + catch ( std :: exception & except ) { + _printLastChanceExceptionMessage ( + typeid ( except ).name (), except.what () ); + epicsThreadSleep ( 10.0 ); + } + catch ( ... ) { + _printLastChanceExceptionMessage ( + "catch ( ... )", "Non-standard C++ exception" ); + epicsThreadSleep ( 10.0 ); + } + } + this->exitFlag = true; + this->exitEvent.signal (); // no access to queue after exitEvent signal +} + +epicsTimer & timerQueueActive::createTimer () +{ + return this->queue.createTimer(); +} + +epicsTimerForC & timerQueueActive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) +{ + return this->queue.createTimerForC ( pCallback, pArg ); +} + +void timerQueueActive::reschedule () +{ + this->rescheduleEvent.signal (); +} + +double timerQueueActive::quantum () +{ + return this->sleepQuantum; +} + +void timerQueueActive::show ( unsigned int level ) const +{ + printf ( "EPICS threaded timer queue at %p\n", + static_cast ( this ) ); + if ( level > 0u ) { + // specifying level one here avoids recursive + // show callback + this->thread.show ( 1u ); + this->queue.show ( level - 1u ); + printf ( "reschedule event\n" ); + this->rescheduleEvent.show ( level - 1u ); + printf ( "exit event\n" ); + this->exitEvent.show ( level - 1u ); + printf ( "exitFlag = %c, terminateFlag = %c\n", + this->exitFlag ? 'T' : 'F', + this->terminateFlag ? 'T' : 'F' ); + } +} + +epicsTimerQueue & timerQueueActive::getEpicsTimerQueue () +{ + return static_cast < epicsTimerQueue &> ( * this ); +} + diff --git a/modules/libcom/src/timer/timerQueueActiveMgr.cpp b/modules/libcom/src/timer/timerQueueActiveMgr.cpp new file mode 100644 index 000000000..d6349a84c --- /dev/null +++ b/modules/libcom/src/timer/timerQueueActiveMgr.cpp @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" + +timerQueueActiveMgr::timerQueueActiveMgr () +{ +} + +timerQueueActiveMgr::~timerQueueActiveMgr () +{ + epicsGuard < epicsMutex > locker ( this->mutex ); +} + +epicsTimerQueueActiveForC & timerQueueActiveMgr :: + allocate ( RefThis & refThis, bool okToShare, unsigned threadPriority ) +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + if ( okToShare ) { + tsDLIter < epicsTimerQueueActiveForC > iter = this->sharedQueueList.firstIter (); + while ( iter.valid () ) { + if ( iter->threadPriority () == threadPriority ) { + assert ( iter->timerQueueActiveMgrPrivate::referenceCount < UINT_MAX ); + iter->timerQueueActiveMgrPrivate::referenceCount++; + return *iter; + } + iter++; + } + } + + epicsTimerQueueActiveForC & queue = + * new epicsTimerQueueActiveForC ( refThis, okToShare, threadPriority ); + queue.timerQueueActiveMgrPrivate::referenceCount = 1u; + if ( okToShare ) { + this->sharedQueueList.add ( queue ); + } + return queue; +} + +void timerQueueActiveMgr :: + release ( epicsTimerQueueActiveForC & queue ) +{ + { + epicsGuard < epicsMutex > locker ( this->mutex ); + assert ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ); + queue.timerQueueActiveMgrPrivate::referenceCount--; + if ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ) { + return; + } + else if ( queue.sharingOK () ) { + this->sharedQueueList.remove ( queue ); + } + } + // delete only after we release the guard in case the embedded + // reference is the last one and this object is destroyed + // as a side effect + timerQueueActiveMgrPrivate * pPriv = & queue; + delete pPriv; +} + +timerQueueActiveMgrPrivate::timerQueueActiveMgrPrivate () : + referenceCount ( 0u ) +{ +} + +timerQueueActiveMgrPrivate::~timerQueueActiveMgrPrivate () +{ +} diff --git a/modules/libcom/src/timer/timerQueuePassive.cpp b/modules/libcom/src/timer/timerQueuePassive.cpp new file mode 100644 index 000000000..a352c5672 --- /dev/null +++ b/modules/libcom/src/timer/timerQueuePassive.cpp @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +// +// Note, a free list for this class is not currently used because of +// entanglements between the file scope free list destructor and a +// file scope fdManager destructor which is trying to call a +// destructor for a passive timer queue which is no longer valid +// in pool. +// + +#include + +#define epicsExportSharedSymbols +#include "timerPrivate.h" + +epicsTimerQueuePassive::~epicsTimerQueuePassive () {} + +epicsTimerQueuePassive & epicsTimerQueuePassive::create ( epicsTimerQueueNotify ¬ify ) +{ + return * new timerQueuePassive ( notify ); +} + +timerQueuePassive::timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) : + queue ( notifyIn ) {} + +timerQueuePassive::~timerQueuePassive () {} + +epicsTimer & timerQueuePassive::createTimer () +{ + return this->queue.createTimer (); +} + +epicsTimerForC & timerQueuePassive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) +{ + return this->queue.createTimerForC ( pCallback, pArg ); +} + +double timerQueuePassive::process ( const epicsTime & currentTime ) +{ + return this->queue.process ( currentTime ); +} + +void timerQueuePassive::show ( unsigned int level ) const +{ + printf ( "EPICS non-threaded timer queue at %p\n", + static_cast ( this ) ); + if ( level >=1u ) { + this->queue.show ( level - 1u ); + } +} + +epicsTimerQueue & timerQueuePassive::getEpicsTimerQueue () +{ + return static_cast < epicsTimerQueue &> ( * this ); +} + diff --git a/modules/libcom/src/valgrind/valgrind.h b/modules/libcom/src/valgrind/valgrind.h new file mode 100755 index 000000000..c503172d5 --- /dev/null +++ b/modules/libcom/src/valgrind/valgrind.h @@ -0,0 +1,6587 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2013 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 10 + + +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_arm64_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + + +#if defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ + || defined(__CYGWIN32__) \ + || (defined(_WIN32) && defined(_M_IX86) && defined(__GNUC__)) +# define PLAT_x86_win32 1 +#elif defined(__MINGW64__) \ + || (defined(_WIN64) && defined(_M_X64) && defined(__GNUC__)) +# define PLAT_amd64_win64 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 +/* Big Endian uses ELF version 1 */ +# define PLAT_ppc64be_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 +/* Little Endian uses ELF version 2 */ +# define PLAT_ppc64le_linux 1 +#elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) +# define PLAT_arm_linux 1 +#elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) +# define PLAT_arm64_linux 1 +#elif defined(__linux__) && defined(__s390__) && defined(__s390x__) +# define PLAT_s390x_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==64) +# define PLAT_mips64_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips!=64) +# define PLAT_mips32_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +/* + * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client + * request. Accepts both pointers and integers as arguments. + * + * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind + * client request that does not return a value. + + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. Accepts + * both pointers and integers as arguments. Note that such calls are not + * necessarily pure functions -- they may have side effects. + */ + +#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ + _zzq_request, _zzq_arg1, _zzq_arg2, \ + _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ + _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgl %%edi,%%edi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ + (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ + (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ + (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) + +static __inline uintptr_t +valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, + uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, + uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, + uintptr_t _zzq_arg5) +{ + volatile uintptr_t _zzq_args[6]; + volatile unsigned int _zzq_result; + _zzq_args[0] = (uintptr_t)(_zzq_request); + _zzq_args[1] = (uintptr_t)(_zzq_arg1); + _zzq_args[2] = (uintptr_t)(_zzq_arg2); + _zzq_args[3] = (uintptr_t)(_zzq_arg3); + _zzq_args[4] = (uintptr_t)(_zzq_arg4); + _zzq_args[5] = (uintptr_t)(_zzq_arg5); + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default + __SPECIAL_INSTRUCTION_PREAMBLE + /* %EDX = client_request ( %EAX ) */ + __asm xchg ebx,ebx + __asm mov _zzq_result, edx + } + return _zzq_result; +} + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm xchg edi,edi \ + } \ + } while (0) + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || (defined(PLAT_amd64_win64) && defined(__GNUC__)) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgq %%rdi,%%rdi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------- amd64-Win64 ------------------------- */ + +#if defined(PLAT_amd64_win64) && !defined(__GNUC__) + +#error Unsupported compiler. + +#endif /* PLAT_amd64_win64 */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ + "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +#if defined(PLAT_ppc64le_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long long int _zzq_args[6]; \ + unsigned long long int _zzq_result; \ + unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R12 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr r9, r9, r9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------- */ + +#if defined(PLAT_arm64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ + "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("mov x3, %1\n\t" /*default*/ \ + "mov x4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = client_request ( X4 ) */ \ + "orr x10, x10, x10\n\t" \ + "mov %0, x3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "x3", "x4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = guest_NRADDR */ \ + "orr x11, x11, x11\n\t" \ + "mov %0, x3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "x3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir X8 */ \ + "orr x12, x12, x12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr x9, x9, x9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------ s390x-linux ------------------------ */ + +#if defined(PLAT_s390x_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific + * code. This detection is implemented in platform specific toIR.c + * (e.g. VEX/priv/guest_s390_decoder.c). + */ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "lr 15,15\n\t" \ + "lr 1,1\n\t" \ + "lr 2,2\n\t" \ + "lr 3,3\n\t" + +#define __CLIENT_REQUEST_CODE "lr 2,2\n\t" +#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" +#define __CALL_NO_REDIR_CODE "lr 4,4\n\t" +#define __VEX_INJECT_IR_CODE "lr 5,5\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(/* r2 = args */ \ + "lgr 2,%1\n\t" \ + /* r3 = default */ \ + "lgr 3,%2\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CLIENT_REQUEST_CODE \ + /* results = r3 */ \ + "lgr %0, 3\n\t" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "2", "3", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __GET_NR_CONTEXT_CODE \ + "lgr %0, 3\n\t" \ + : "=a" (__addr) \ + : \ + : "cc", "3", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_R1 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CALL_NO_REDIR_CODE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __VEX_INJECT_IR_CODE); \ + } while (0) + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ---------------- */ + +#if defined(PLAT_mips32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +/* .word 0x342 + * .word 0x742 + * .word 0xC2 + * .word 0x4C2*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "srl $0, $0, 13\n\t" \ + "srl $0, $0, 29\n\t" \ + "srl $0, $0, 3\n\t" \ + "srl $0, $0, 19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* T3 = client_request ( T4 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %t9 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%t9 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ---------------- */ + +#if defined(PLAT_mips64_linux) + +typedef + struct { + unsigned long long nraddr; /* where's the code? */ + } + OrigFn; + +/* dsll $0,$0, 3 + * dsll $0,$0, 13 + * dsll $0,$0, 29 + * dsll $0,$0, 19*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ + "dsll $0,$0,29 ; dsll $0,$0,19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = client_request ( $12 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts + the default behaviour equivalance class tag "0000" into the name. + See pub_tool_redir.h for details -- normally you don't need to + think about this, though. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Also provide end-user facilities for function replacement, rather + than wrapping. A replacement function differs from a wrapper in + that it has no way to get hold of the original function being + called, and hence no way to call onwards to it. In a replacement + function, VALGRIND_GET_ORIG_FN always returns zero. */ + +#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) + +#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movl %%esp,%%edi\n\t" \ + "andl $0xfffffff0,%%esp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movl %%edi,%%esp\n\t" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movq %%rsp,%%r14\n\t" \ + "andq $0xfffffffffffffff0,%%rsp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movq %%r14,%%rsp\n\t" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rlwinm 1,1,0,0,27\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +/* ------------------------- ppc64le-linux ----------------------- */ +#if defined(PLAT_ppc64le_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(12)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +/* This is a bit tricky. We store the original stack pointer in r10 + as it is callee-saves. gcc doesn't allow the use of r11 for some + reason. Also, we can't directly "bic" the stack pointer in thumb + mode since r13 isn't an allowed register number in that context. + So use r4 as a temporary, since that is about to get trashed + anyway, just after each use of this macro. Side effect is we need + to be very careful about any future changes, since + VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ +#define VALGRIND_ALIGN_STACK \ + "mov r10, sp\n\t" \ + "mov r4, sp\n\t" \ + "bic r4, r4, #7\n\t" \ + "mov sp, r4\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, r10\n\t" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------ */ + +#if defined(PLAT_arm64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ + "x18", "x19", "x20", "x30", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ + "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ + "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ + "v26", "v27", "v28", "v29", "v30", "v31" + +/* x21 is callee-saved, so we can use it to save and restore SP around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mov x21, sp\n\t" \ + "bic sp, x21, #15\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, x21\n\t" + +/* These CALL_FN_ macros assume that on arm64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1, #96] \n\t" \ + "str x8, [sp, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------- s390x-linux ------------------------- */ + +#if defined(PLAT_s390x_linux) + +/* Similar workaround as amd64 (see above), but we use r11 as frame + pointer and save the old r11 in r7. r11 might be used for + argvec, therefore we copy argvec in r1 since r1 is clobbered + after the call anyway. */ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"d"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + ".cfi_remember_state\n\t" \ + "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ + "lgr 7,11\n\t" \ + "lgr 11,%2\n\t" \ + ".cfi_def_cfa r11, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "lgr 11, 7\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE \ + "lgr 1,%1\n\t" +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Nb: On s390 the stack pointer is properly aligned *at all times* + according to the s390 GCC maintainer. (The ABI specification is not + precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and + VALGRIND_RESTORE_STACK are not defined here. */ + +/* These regs are trashed by the hidden call. Note that we overwrite + r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the + function a proper return address. All others are ABI defined call + clobbers. */ +#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ + "f0","f1","f2","f3","f4","f5","f6","f7" + +/* Nb: Although r11 is modified in the asm snippets below (inside + VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for + two reasons: + (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not + modified + (2) GCC will complain that r11 cannot appear inside a clobber section, + when compiled with -O -fno-omit-frame-pointer + */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 1, 0(1)\n\t" /* target->r1 */ \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +/* The call abi has the arguments in r2-r6 and stack */ +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1, arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-168\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,168\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-176\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,176\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-184\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,184\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-192\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,192\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-200\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,200\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-208\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,208\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-216\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "mvc 208(8,15), 96(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,216\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ----------------------- */ + +#if defined(PLAT_mips32_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16\n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" /* arg1*/ \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 24\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 24 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "nop\n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 56\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 48(%1) \n\t" \ + "sw $4, 44($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 56 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ------------------------- */ + +#if defined(PLAT_mips64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" /* arg1*/ \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "dsubu $29, $29, 8\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 8\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "dsubu $29, $29, 16\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 16\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "dsubu $29, $29, 24\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 24\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "dsubu $29, $29, 32\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 96(%1)\n\t" \ + "sd $4, 24($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 32\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips64_linux */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* Allows the client program and/or gdbserver to execute a monitor + command. */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, + + /* Disable/enable error reporting level. Takes a single + Word arg which is the delta to this thread's error + disablement indicator. Hence 1 disables or further + disables errors, and -1 moves back towards enablement. + Other values are not allowed. */ + VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, + + /* Initialise IR injection */ + VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0) + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For + Memcheck, it does four things: + + - It records that the size of a block has been changed. This assumes that + the block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - If the block shrunk, it marks the freed memory as being unaddressable. + + - If the block grew, it marks the new area as undefined and defines a red + zone past the end of the new block. + + - The V-bits of the overlap between the old and the new block are preserved. + + VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block + and before deallocation of the old block. + + In many cases, these three client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ + addr, oldSizeB, newSizeB, rzB, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0) + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0) + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0) + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0) + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0) + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0) + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0) + +/* Mark a piece of memory as being a stack. Returns a stack id. + start is the lowest addressable stack byte, end is the highest + addressable stack byte. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0) + +/* Change the start and end address of the stack id. + start is the new lowest addressable stack byte, end is the new highest + addressable stack byte. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0) + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0) + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0) + +/* Disable error reporting for this thread. Behaves in a stack like + way, so you can safely call this multiple times provided that + VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times + to re-enable reporting. The first call of this macro disables + reporting. Subsequent calls have no effect except to increase the + number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable + reporting. Child threads do not inherit this setting from their + parents -- they are always created with reporting enabled. */ +#define VALGRIND_DISABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + 1, 0, 0, 0, 0) + +/* Re-enable error reporting, as per comments on + VALGRIND_DISABLE_ERROR_REPORTING. */ +#define VALGRIND_ENABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + -1, 0, 0, 0, 0) + +/* Execute a monitor command from the client program. + If a connection is opened with GDB, the output will be sent + according to the output mode set for vgdb. + If no connection is opened, output will go to the log output. + Returns 1 if command not recognised, 0 otherwise. */ +#define VALGRIND_MONITOR_COMMAND(command) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ + command, 0, 0, 0, 0) + + +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + +#endif /* __VALGRIND_H */ diff --git a/modules/libcom/src/yacc/ACKNOWLEDGEMENTS b/modules/libcom/src/yacc/ACKNOWLEDGEMENTS new file mode 100644 index 000000000..b66bb2506 --- /dev/null +++ b/modules/libcom/src/yacc/ACKNOWLEDGEMENTS @@ -0,0 +1,25 @@ + Berkeley Yacc owes much to the unflagging efforts of Keith Bostic. +His badgering kept me working on it long after I was ready to quit. + + Berkeley Yacc is based on the excellent algorithm for computing LALR(1) +lookaheads developed by Tom Pennello and Frank DeRemer. The algorithm is +described in their almost impenetrable article in TOPLAS 4,4. + + Finally, much of the credit for the latest version must go to those +who pointed out deficiencies of my earlier releases. Among the most +prolific contributors were + + Benson I. Margulies + Dave Gentzel + Antoine Verheijen + Peter S. Housel + Dale Smith + Ozan Yigit + John Campbell + Bill Sommerfeld + Paul Hilfinger + Gary Bridgewater + Dave Bakken + Dan Lanciani + Richard Sargent + Parag Patel diff --git a/modules/libcom/src/yacc/EPICS_READ_THIS b/modules/libcom/src/yacc/EPICS_READ_THIS new file mode 100644 index 000000000..c6869244a --- /dev/null +++ b/modules/libcom/src/yacc/EPICS_READ_THIS @@ -0,0 +1,54 @@ + +This is the source of the BSD version of yacc that is distributed in the NET2 +release of BSD Unix. We chose this over Bison due to the bison license +agreement. And if you are scratching you head over that comment, read the +addendum in the xxx-info-1 file that comes with bison. + +Anyway, this BSD-yacc has been hacked on to make it generate an all-static +parser C program so that it may be used on an IOC w/o getting things confused. +They way we intend to use it is with the epics-hacked version of flex that +also generates an all-static source program. The general idea is that +your yacc source (xxx.y) file could be formatted like this: + +========================================================= + +%% +YOUR YACC PROGRAM +%% + +int MyYaccParser(char *f1) +{ + static int FirstFlag = 1; + + printf("processing file >%s<\n", f1); + + yyin = fopen(f1, "r"); + if (!FirstFlag) yyrestart(yyin); + FirstFlag = 0; + yyparse(); + fclose(yyin); + + return(0); +} + +static int yyerror(char *str) +{ + printf("Error: %s\n", str); + return(0); +} +========================================================= + +The FirstFlag and yyrestart jazz is the flex-flavored version of how you +restart the scanner if you want to parse another file. Without it, the +scanner can only be used one time. Even if you think your code will only +be used one time, it is desireable to put in the restart stuff anyway, +because some day you (or your boss) will change your mind and without it +the IOC will crash when you make your second call to your parser. + +Note also that there is a yyerror function present. This is mandatory for the +way we are using flex. Without, results are indeterminate... because you will +end up calling some other random yyerror() in the ioc.. probably the one for +the console command interpreter. + + +--John diff --git a/modules/libcom/src/yacc/Makefile b/modules/libcom/src/yacc/Makefile new file mode 100644 index 000000000..4f2083037 --- /dev/null +++ b/modules/libcom/src/yacc/Makefile @@ -0,0 +1,29 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/yacc + +antelope_SRCS += closure.c +antelope_SRCS += error.c +antelope_SRCS += lalr.c +antelope_SRCS += lr0.c +antelope_SRCS += antelope.c +antelope_SRCS += mkpar.c +antelope_SRCS += output.c +antelope_SRCS += reader.c +antelope_SRCS += skeleton.c +antelope_SRCS += symtab.c +antelope_SRCS += verbose.c +antelope_SRCS += warshall.c +antelope_OBJS += epicsTempFile$(OBJ) + +PROD_HOST += antelope + diff --git a/modules/libcom/src/yacc/NEW_FEATURES b/modules/libcom/src/yacc/NEW_FEATURES new file mode 100644 index 000000000..b030c625b --- /dev/null +++ b/modules/libcom/src/yacc/NEW_FEATURES @@ -0,0 +1,46 @@ + The -r option has been implemented. The -r option tells Yacc to +put the read-only tables in y.tab.c and the code and variables in +y.code.c. Keith Bostic asked for this option so that :yyfix could be +eliminated. + + The -l and -t options have been implemented. The -l option tells +Yacc not to include #line directives in the code it produces. The -t +option causes debugging code to be included in the compiled parser. + + The code for error recovery has been changed to implement the same +algorithm as AT&T Yacc. There will still be differences in the way +error recovery works because AT&T Yacc uses more default reductions +than Berkeley Yacc. + + The environment variable TMPDIR determines the directory where +temporary files will be created. If TMPDIR is defined, temporary files +will be created in the directory whose pathname is the value of TMPDIR. +By default, temporary files are created in /tmp. + + The keywords are now case-insensitive. For example, %nonassoc, +%NONASSOC, %NonAssoc, and %nOnAsSoC are all equivalent. + + Commas and semicolons that are not part of C code are treated as +commentary. + + Line-end comments, as in BCPL, are permitted. Line-end comments +begin with // and end at the next end-of-line. Line-end comments are +permitted in C code; they are converted to C comments on output. + + The form of y.output files has been changed to look more like +those produced by AT&T Yacc. + + A new kind of declaration has been added. The form of the declaration +is + + %ident string + +where string is a sequence of characters begining with a double quote +and ending with either a double quote or the next end-of-line, whichever +comes first. The declaration will cause a #ident directive to be written +near the start of the output file. + + If a parser has been compiled with debugging code, that code can be +enabled by setting an environment variable. If the environment variable +YYDEBUG is set to 0, debugging output is suppressed. If it is set to 1, +debugging output is written to standard output. diff --git a/modules/libcom/src/yacc/NOTES b/modules/libcom/src/yacc/NOTES new file mode 100644 index 000000000..9db3c96ce --- /dev/null +++ b/modules/libcom/src/yacc/NOTES @@ -0,0 +1,9 @@ +Berkeley Yacc reflects its origins. The reason so many routines +use exactly six register variables is that Berkeley Yacc was +developed on a VAX using PCC. PCC placed at most six variables +in registers. I went to considerable effort to find which six +variables most belonged in registers. Changes in machines and +compilers make that effort worthless, perhaps even harmful. + +The code contains many instances where address calculations are +performed in particular ways to optimize the code for the VAX. diff --git a/modules/libcom/src/yacc/NO_WARRANTY b/modules/libcom/src/yacc/NO_WARRANTY new file mode 100644 index 000000000..06e8d93a2 --- /dev/null +++ b/modules/libcom/src/yacc/NO_WARRANTY @@ -0,0 +1,3 @@ + Berkeley Yacc is distributed with no warranty whatever. The author +and any other contributors take no responsibility for the consequences of +its use. diff --git a/modules/libcom/src/yacc/README b/modules/libcom/src/yacc/README new file mode 100644 index 000000000..091f23343 --- /dev/null +++ b/modules/libcom/src/yacc/README @@ -0,0 +1,23 @@ + Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made +as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input +specification that conforms to the AT&T Yacc documentation. Specifications +that take advantage of undocumented features of AT&T Yacc will probably be +rejected. + + Berkeley Yacc is distributed with no warranty whatever. The code is certain +to contain errors. Neither the author nor any contributor takes responsibility +for any consequences of its use. + + Berkeley Yacc is in the public domain. The data structures and algorithms +used in Berkeley Yacc are all either taken from documents available to the +general public or are inventions of the author. Anyone may freely distribute +source or binary forms of Berkeley Yacc whether unchanged or modified. +Distributers may charge whatever fees they can obtain for Berkeley Yacc. +Programs generated by Berkeley Yacc may be distributed freely. + + Please report bugs to + + robert.corbett@eng.Sun.COM + +Include a small example if possible. Please include the banner string from +skeleton.c with the bug report. Do not expect rapid responses. diff --git a/modules/libcom/src/yacc/antelope.c b/modules/libcom/src/yacc/antelope.c new file mode 100644 index 000000000..7143b38ff --- /dev/null +++ b/modules/libcom/src/yacc/antelope.c @@ -0,0 +1,347 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include +#include "defs.h" +#define epicsExportSharedSymbols +#include "epicsTempFile.h" +#undef epicsExportSharedSymbols + +char dflag; +char lflag; +char rflag; +char tflag; +char vflag; + +char *symbol_prefix; +char *file_prefix = "y"; +char *myname = "yacc"; +char *temp_form = "yacc.XXXXXXX"; + +int lineno; +int outline; + +char *code_file_name; +char *defines_file_name; +char *input_file_name = ""; +char *output_file_name; +char *verbose_file_name; + +FILE *action_file; /* a temp file, used to save actions associated */ + /* with rules until the parser is written */ +FILE *code_file; /* y.code.c (used when the -r option is specified) */ +FILE *defines_file; /* y.tab.h */ +FILE *input_file; /* the input file */ +FILE *output_file; /* y.tab.c */ +FILE *text_file; /* a temp file, used to save text until all */ + /* symbols have been defined */ +FILE *union_file; /* a temp file, used to save the union */ + /* definition until all symbol have been */ + /* defined */ +FILE *verbose_file; /* y.output */ + +int nitems; +int nrules; +int nsyms; +int ntokens; +int nvars; + +int start_symbol; +char **symbol_name; +short *symbol_value; +short *symbol_prec; +char *symbol_assoc; + +short *ritem; +short *rlhs; +short *rrhs; +short *rprec; +char *rassoc; +short **derives; +char *nullable; + + +void +done(int k) +{ + if (action_file) { fclose(action_file); } + if (text_file) { fclose(text_file); } + if (union_file) { fclose(union_file); } + exit(k); +} + + +static void +onintr(int StupidInconsistantSignalTypes) +{ + done(1); +} + + +static void +set_signals(void) +{ +#ifdef SIGINT + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#endif +#ifdef SIGTERM + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, onintr); +#endif +#ifdef SIGHUP + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, onintr); +#endif +} + + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname); + exit(1); +} + + +static int +getargs(int argc, char *argv[]) +{ + int i; + char *s; + + if (argc > 0) { + myname = strrchr(argv[0], '/'); + if (myname) myname++; + else myname = argv[0]; + } + for (i = 1; i < argc; ++i) + { + s = argv[i]; + if (*s != '-') break; + switch (*++s) + { + case '\0': + input_file = stdin; + if (i + 1 < argc) usage(); + return(0); + + case '-': + ++i; + goto no_more_options; + + case 'b': + if (*++s) + file_prefix = s; + else if (++i < argc) + file_prefix = argv[i]; + else + usage(); + continue; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'p': + if (*++s) + symbol_prefix = s; + else if (++i < argc) + symbol_prefix = argv[i]; + else + usage(); + continue; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + + for (;;) + { + switch (*++s) + { + case '\0': + goto end_of_option; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + } +end_of_option:; + } + +no_more_options:; + if (i + 1 != argc) usage(); + input_file_name = argv[i]; + + return(0); +} + + +char * +allocate(unsigned int n) +{ + char *p; + + p = NULL; + if (n) + { + p = CALLOC(1, n); + if (!p) no_space(); + } + return (p); +} + + +static void +create_file_names(void) +{ + int len; + + len = strlen(file_prefix); + + output_file_name = MALLOC(len + 7); + if (output_file_name == 0) + no_space(); + strcpy(output_file_name, file_prefix); + strcpy(output_file_name + len, OUTPUT_SUFFIX); + + if (rflag) + { + code_file_name = MALLOC(len + 8); + if (code_file_name == 0) + no_space(); + strcpy(code_file_name, file_prefix); + strcpy(code_file_name + len, CODE_SUFFIX); + } + else + code_file_name = output_file_name; + + if (dflag) + { + defines_file_name = MALLOC(len + 7); + if (defines_file_name == 0) + no_space(); + strcpy(defines_file_name, file_prefix); + strcpy(defines_file_name + len, DEFINES_SUFFIX); + } + + if (vflag) + { + verbose_file_name = MALLOC(len + 8); + if (verbose_file_name == 0) + no_space(); + strcpy(verbose_file_name, file_prefix); + strcpy(verbose_file_name + len, VERBOSE_SUFFIX); + } +} + + +static void +open_files(void) +{ + create_file_names(); + + if (input_file == 0) + { + input_file = fopen(input_file_name, "r"); + if (input_file == 0) + open_error(input_file_name); + } + + action_file = epicsTempFile(); + if (action_file == 0) + open_error("temp action file"); + + text_file = epicsTempFile(); + if (text_file == 0) + open_error("temp text file"); + + if (vflag) + { + verbose_file = fopen(verbose_file_name, "w"); + if (verbose_file == 0) + open_error(verbose_file_name); + } + + if (dflag) + { + defines_file = fopen(defines_file_name, "w"); + if (defines_file == 0) + open_error(defines_file_name); + union_file = epicsTempFile(); + if (union_file == 0) + open_error("temp union file"); + } + + output_file = fopen(output_file_name, "w"); + if (output_file == 0) + open_error(output_file_name); + + if (rflag) + { + code_file = fopen(code_file_name, "w"); + if (code_file == 0) + open_error(code_file_name); + } + else + code_file = output_file; +} + + +int +main(int argc, char *argv[]) +{ + set_signals(); + getargs(argc, argv); + open_files(); + reader(); + lr0(); + lalr(); + make_parser(); + verbose(); + output(); + done(0); + /*NOTREACHED*/ +} diff --git a/modules/libcom/src/yacc/closure.c b/modules/libcom/src/yacc/closure.c new file mode 100644 index 000000000..f08c8cd92 --- /dev/null +++ b/modules/libcom/src/yacc/closure.c @@ -0,0 +1,274 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +short *itemset; +short *itemsetend; +unsigned *ruleset; + +static unsigned *first_derives; +static unsigned *EFF; + +#ifdef DEBUG +static void print_closure(int n); +static void print_EFF(void); +static void print_first_derives(void); +#endif + + +static void +set_EFF(void) +{ + unsigned *row; + int symbol; + short *sp; + int rowsize; + int i; + int rule; + + rowsize = WORDSIZE(nvars); + EFF = NEW2(nvars * rowsize, unsigned); + + row = EFF; + for (i = start_symbol; i < nsyms; i++) + { + sp = derives[i]; + for (rule = *sp; rule > 0; rule = *++sp) + { + symbol = ritem[rrhs[rule]]; + if (ISVAR(symbol)) + { + symbol -= start_symbol; + SETBIT(row, symbol); + } + } + row += rowsize; + } + + reflexive_transitive_closure(EFF, nvars); + +#ifdef DEBUG + print_EFF(); +#endif +} + + +void +set_first_derives(void) +{ + unsigned *rrow; + unsigned *vrow; + int j; + unsigned k; + unsigned cword = 0; + short *rp; + + int rule; + int i; + int rulesetsize; + int varsetsize; + + rulesetsize = WORDSIZE(nrules); + varsetsize = WORDSIZE(nvars); + first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; + + set_EFF(); + + rrow = first_derives + ntokens * rulesetsize; + for (i = start_symbol; i < nsyms; i++) + { + vrow = EFF + ((i - ntokens) * varsetsize); + k = BITS_PER_WORD; + for (j = start_symbol; j < nsyms; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *vrow++; + k = 0; + } + + if (cword & (1 << k)) + { + rp = derives[j]; + while ((rule = *rp++) >= 0) + { + SETBIT(rrow, rule); + } + } + } + + vrow += varsetsize; + rrow += rulesetsize; + } + +#ifdef DEBUG + print_first_derives(); +#endif + + FREE(EFF); +} + + +void +closure(short int *nucleus, int n) +{ + int ruleno; + unsigned word; + unsigned i; + short *csp; + unsigned *dsp; + unsigned *rsp; + int rulesetsize; + + short *csend; + unsigned *rsend; + int symbol; + int itemno; + + rulesetsize = WORDSIZE(nrules); + rsp = ruleset; + rsend = ruleset + rulesetsize; + for (rsp = ruleset; rsp < rsend; rsp++) + *rsp = 0; + + csend = nucleus + n; + for (csp = nucleus; csp < csend; ++csp) + { + symbol = ritem[*csp]; + if (ISVAR(symbol)) + { + dsp = first_derives + symbol * rulesetsize; + rsp = ruleset; + while (rsp < rsend) + *rsp++ |= *dsp++; + } + } + + ruleno = 0; + itemsetend = itemset; + csp = nucleus; + for (rsp = ruleset; rsp < rsend; ++rsp) + { + word = *rsp; + if (word) + { + for (i = 0; i < BITS_PER_WORD; ++i) + { + if (word & (1 << i)) + { + itemno = rrhs[ruleno+i]; + while (csp < csend && *csp < itemno) + *itemsetend++ = *csp++; + *itemsetend++ = itemno; + while (csp < csend && *csp == itemno) + ++csp; + } + } + } + ruleno += BITS_PER_WORD; + } + + while (csp < csend) + *itemsetend++ = *csp++; + +#ifdef DEBUG + print_closure(n); +#endif +} + + + +void +finalize_closure(void) +{ + FREE(itemset); + FREE(ruleset); + FREE(first_derives + ntokens * WORDSIZE(nrules)); +} + + +#ifdef DEBUG + +static void +print_closure(int n) +{ + short *isp; + + printf("\n\nn = %d\n\n", n); + for (isp = itemset; isp < itemsetend; isp++) + printf(" %d\n", *isp); +} + + +static void +print_EFF(void) +{ + int i, j; + unsigned *rowp; + unsigned word; + unsigned k; + + printf("\n\nEpsilon Free Firsts\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s", symbol_name[i]); + rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars)); + word = *rowp++; + + k = BITS_PER_WORD; + for (j = 0; j < nvars; k++, j++) + { + if (k >= BITS_PER_WORD) + { + word = *rowp++; + k = 0; + } + + if (word & (1 << k)) + printf(" %s", symbol_name[start_symbol + j]); + } + } +} + + +static void +print_first_derives(void) +{ + int i; + int j; + unsigned *rp; + unsigned cword; + unsigned k; + + printf("\n\n\nFirst Derives\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s derives\n", symbol_name[i]); + rp = first_derives + i * WORDSIZE(nrules); + k = BITS_PER_WORD; + for (j = 0; j <= nrules; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *rp++; + k = 0; + } + + if (cword & (1 << k)) + printf(" %d\n", j); + } + } + + fflush(stdout); +} + +#endif diff --git a/modules/libcom/src/yacc/defs.h b/modules/libcom/src/yacc/defs.h new file mode 100644 index 000000000..36b295fa8 --- /dev/null +++ b/modules/libcom/src/yacc/defs.h @@ -0,0 +1,367 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include +#include +#include + + +/* machine-dependent definitions */ +/* the following definitions are for the Tahoe */ +/* they might have to be changed for other machines */ + +/* MAXCHAR is the largest unsigned character value */ +/* MAXSHORT is the largest value of a C short */ +/* MINSHORT is the most negative value of a C short */ +/* MAXTABLE is the maximum table size */ +/* BITS_PER_WORD is the number of bits in a C unsigned */ +/* WORDSIZE computes the number of words needed to */ +/* store n bits */ +/* BIT returns the value of the n-th bit starting */ +/* from r (0-indexed) */ +/* SETBIT sets the n-th bit starting from r */ + +#define MAXCHAR 255 +#define MAXSHORT 32767 +#define MINSHORT -32768 +#define MAXTABLE 32500 +#define BITS_PER_WORD 32 +#define WORDSIZE(n) (((n)+(BITS_PER_WORD-1))/BITS_PER_WORD) +#define BIT(r, n) ((((r)[(n)>>5])>>((n)&31))&1) +#define SETBIT(r, n) ((r)[(n)>>5]|=((unsigned)1<<((n)&31))) + + +/* character names */ + +#define NUL '\0' /* the null character */ +#define NEWLINE '\n' /* line feed */ +#define SP ' ' /* space */ +#define BS '\b' /* backspace */ +#define HT '\t' /* horizontal tab */ +#define VT '\013' /* vertical tab */ +#define CR '\r' /* carriage return */ +#define FF '\f' /* form feed */ +#define QUOTE '\'' /* single quote */ +#define DOUBLE_QUOTE '\"' /* double quote */ +#define BACKSLASH '\\' /* backslash */ + + +/* defines for constructing filenames */ + +#define CODE_SUFFIX ".code.c" +#define DEFINES_SUFFIX ".tab.h" +#define OUTPUT_SUFFIX ".tab.c" +#define VERBOSE_SUFFIX ".output" + + +/* keyword codes */ + +#define TOKEN 0 +#define LEFT 1 +#define RIGHT 2 +#define NONASSOC 3 +#define MARK 4 +#define TEXT 5 +#define TYPE 6 +#define START 7 +#define UNION 8 +#define IDENT 9 + + +/* symbol classes */ + +#define UNKNOWN 0 +#define TERM 1 +#define NONTERM 2 + + +/* the undefined value */ + +#define UNDEFINED (-1) + + +/* action codes */ + +#define SHIFT 1 +#define REDUCE 2 + + +/* character macros */ + +#define IS_IDENT(c) (isalnum(c) || (c) == '_' || (c) == '.' || (c) == '$') +#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7') +#define NUMERIC_VALUE(c) ((c) - '0') + + +/* symbol macros */ + +#define ISTOKEN(s) ((s) < start_symbol) +#define ISVAR(s) ((s) >= start_symbol) + + +/* storage allocation macros */ + +#define CALLOC(k,n) (calloc((unsigned)(k),(unsigned)(n))) +#define FREE(x) (free((char*)(x))) +#define MALLOC(n) (malloc((unsigned)(n))) +#define NEW(t) ((t*)allocate(sizeof(t))) +#define NEW2(n,t) ((t*)allocate((unsigned)((n)*sizeof(t)))) +#define REALLOC(p,n) (realloc((char*)(p),(unsigned)(n))) + + +/* the structure of a symbol table entry */ + +typedef struct bucket bucket; +struct bucket +{ + struct bucket *link; + struct bucket *next; + char *name; + char *tag; + short value; + short index; + short prec; + char class; + char assoc; +}; + + +/* the structure of the LR(0) state machine */ + +typedef struct core core; +struct core +{ + struct core *next; + struct core *link; + short number; + short accessing_symbol; + short nitems; + short items[1]; +}; + + +/* the structure used to record shifts */ + +typedef struct shifts shifts; +struct shifts +{ + struct shifts *next; + short number; + short nshifts; + short shift[1]; +}; + + +/* the structure used to store reductions */ + +typedef struct reductions reductions; +struct reductions +{ + struct reductions *next; + short number; + short nreds; + short rules[1]; +}; + + +/* the structure used to represent parser actions */ + +typedef struct action action; +struct action +{ + struct action *next; + short symbol; + short number; + short prec; + char action_code; + char assoc; + char suppressed; +}; + + +/* global variables */ + +extern char dflag; +extern char lflag; +extern char rflag; +extern char tflag; +extern char vflag; +extern char *symbol_prefix; + +extern char *myname; +extern char *cptr; +extern char *line; +extern int lineno; +extern int outline; + +extern char *banner[]; +extern char *tables[]; +extern char *header[]; +extern char *body[]; +extern char *trailer[]; + +extern char *code_file_name; +extern char *defines_file_name; +extern char *input_file_name; +extern char *output_file_name; +extern char *verbose_file_name; + +extern FILE *action_file; +extern FILE *code_file; +extern FILE *defines_file; +extern FILE *input_file; +extern FILE *output_file; +extern FILE *text_file; +extern FILE *union_file; +extern FILE *verbose_file; + +extern int nitems; +extern int nrules; +extern int nsyms; +extern int ntokens; +extern int nvars; +extern int ntags; + +extern char unionized; +extern char line_format[]; + +extern int start_symbol; +extern char **symbol_name; +extern short *symbol_value; +extern short *symbol_prec; +extern char *symbol_assoc; + +extern short *ritem; +extern short *rlhs; +extern short *rrhs; +extern short *rprec; +extern char *rassoc; + +extern short **derives; +extern char *nullable; + +extern bucket *first_symbol; +extern bucket *last_symbol; + +extern int nstates; +extern core *first_state; +extern shifts *first_shift; +extern reductions *first_reduction; +extern short *accessing_symbol; +extern core **state_table; +extern shifts **shift_table; +extern reductions **reduction_table; +extern unsigned *LA; +extern short *LAruleno; +extern short *lookaheads; +extern short *goto_map; +extern short *from_state; +extern short *to_state; + +extern action **parser; +extern int SRtotal; +extern int RRtotal; +extern short *SRconflicts; +extern short *RRconflicts; +extern short *defred; +extern short *rules_used; +extern short nunused; +extern short final_state; + +/* + * global functions + */ + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/* main.c */ +extern void done(int k) NORETURN; +extern char *allocate(unsigned int n); + +/* error.c */ +extern void no_space(void) NORETURN; +extern void fatal(char *msg) NORETURN; +extern void open_error(char *filename) NORETURN; +extern void unexpected_EOF(void) NORETURN; +extern void syntax_error(int st_lineno, char *st_line, char *st_cptr) NORETURN; +extern void unterminated_comment(int c_lineno, char *c_line, char *c_cptr) NORETURN; +extern void unterminated_string(int s_lineno, char *s_line, char *s_cptr) NORETURN; +extern void unterminated_text(int t_lineno, char *t_line, char *t_cptr) NORETURN; +extern void unterminated_union(int u_lineno, char *u_line, char *u_cptr) NORETURN; +extern void over_unionized(char *u_cptr) NORETURN; +extern void illegal_tag(int t_lineno, char *t_line, char *t_cptr) NORETURN; +extern void illegal_character(char *c_cptr) NORETURN; +extern void used_reserved(char *s) NORETURN; +extern void tokenized_start(char *s) NORETURN; +extern void retyped_warning(char *s); +extern void reprec_warning(char *s); +extern void revalued_warning(char *s); +extern void terminal_start(char *s); +extern void restarted_warning(void); +extern void no_grammar(void) NORETURN; +extern void terminal_lhs(int s_lineno) NORETURN; +extern void prec_redeclared(void); +extern void unterminated_action(int a_lineno, char *a_line, char *a_cptr) NORETURN; +extern void dollar_warning(int a_lineno, int i); +extern void dollar_error(int a_lineno, char *a_line, char *a_cptr) NORETURN; +extern void untyped_lhs(void) NORETURN; +extern void untyped_rhs(int i, char *s) NORETURN; +extern void unknown_rhs(int i) NORETURN; +extern void default_action_warning(void); +extern void undefined_goal(char *s) NORETURN; +extern void undefined_symbol_warning(char *s); + +/* symtab.c */ +extern bucket *make_bucket(char *name); +extern bucket *lookup(char *name); +extern void create_symbol_table(void); +extern void free_symbol_table(void); +extern void free_symbols(void); + +/* closure.c */ +extern void set_first_derives(void); +extern void closure(short int *nucleus, int n); +extern void finalize_closure(void); + +/* reader.c */ +extern void reader(void); + +/* lr0.c */ +extern void lr0(void); + +/* lalr.c */ +extern void lalr(void); + +/* mkpar.c */ +extern void make_parser(void); +extern void free_parser(void); + +/* warshall.c */ +extern void reflexive_transitive_closure(unsigned int *R, int n); + +/* output.c */ +extern void output(void); +extern int default_goto(int symbol); +extern int matching_vector(int vector); +extern int pack_vector(int vector); + +/* skeleton.c */ +extern void write_section(char *section[]); + +/* verbose.c */ +extern void verbose(void); + +/* system includes */ + +#include +#include +#include diff --git a/modules/libcom/src/yacc/error.c b/modules/libcom/src/yacc/error.c new file mode 100644 index 000000000..d95c92a3a --- /dev/null +++ b/modules/libcom/src/yacc/error.c @@ -0,0 +1,314 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* routines for printing error messages */ + +#include "defs.h" + + +void +fatal(char *msg) +{ + fprintf(stderr, "%s: fatal - %s\n", myname, msg); + done(2); +} + + +void +no_space(void) +{ + fprintf(stderr, "%s: fatal - out of space\n", myname); + done(2); +} + + +void +open_error(char *filename) +{ + fprintf(stderr, "%s: fatal - cannot open \"%s\"\n", myname, filename); + done(2); +} + + +void +unexpected_EOF(void) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unexpected end-of-file\n", + myname, lineno, input_file_name); + done(1); +} + + +static int +print_pos(char *st_line, char *st_cptr) +{ + char *s; + + if (st_line == 0) return(0); + for (s = st_line; *s != '\n'; ++s) + { + if (isprint((int) *s) || *s == '\t') + putc(*s, stderr); + else + putc('?', stderr); + } + putc('\n', stderr); + for (s = st_line; s < st_cptr; ++s) + { + if (*s == '\t') + putc('\t', stderr); + else + putc(' ', stderr); + } + putc('^', stderr); + putc('\n', stderr); + return(0); +} + + +void +syntax_error(int st_lineno, char *st_line, char *st_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", syntax error\n", + myname, st_lineno, input_file_name); + print_pos(st_line, st_cptr); + done(1); +} + + +void +unterminated_comment(int c_lineno, char *c_line, char *c_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unmatched /*\n", + myname, c_lineno, input_file_name); + print_pos(c_line, c_cptr); + done(1); +} + + +void +unterminated_string(int s_lineno, char *s_line, char *s_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unterminated string\n", + myname, s_lineno, input_file_name); + print_pos(s_line, s_cptr); + done(1); +} + + +void +unterminated_text(int t_lineno, char *t_line, char *t_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unmatched %%{\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +void +unterminated_union(int u_lineno, char *u_line, char *u_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unterminated %%union \ +declaration\n", myname, u_lineno, input_file_name); + print_pos(u_line, u_cptr); + done(1); +} + + +void +over_unionized(char *u_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", too many %%union \ +declarations\n", myname, lineno, input_file_name); + print_pos(line, u_cptr); + done(1); +} + + +void +illegal_tag(int t_lineno, char *t_line, char *t_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", illegal tag\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +void +illegal_character(char *c_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", illegal character\n", + myname, lineno, input_file_name); + print_pos(line, c_cptr); + done(1); +} + + +void +used_reserved(char *s) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", illegal use of reserved symbol \ +%s\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +tokenized_start(char *s) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s cannot be \ +declared to be a token\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +retyped_warning(char *s) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", the type of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +reprec_warning(char *s) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", the precedence of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +revalued_warning(char *s) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", the value of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +terminal_start(char *s) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", the start symbol %s is a \ +token\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +restarted_warning(void) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", the start symbol has been \ +redeclared\n", myname, lineno, input_file_name); +} + + +void +no_grammar(void) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", no grammar has been \ +specified\n", myname, lineno, input_file_name); + done(1); +} + + +void +terminal_lhs(int s_lineno) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", a token appears on the lhs \ +of a production\n", myname, s_lineno, input_file_name); + done(1); +} + + +void +prec_redeclared(void) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", conflicting %%prec \ +specifiers\n", myname, lineno, input_file_name); +} + + +void +unterminated_action(int a_lineno, char *a_line, char *a_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", unterminated action\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +void +dollar_warning(int a_lineno, int i) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", $%d references beyond the \ +end of the current rule\n", myname, a_lineno, input_file_name, i); +} + + +void +dollar_error(int a_lineno, char *a_line, char *a_cptr) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", illegal $-name\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +void +untyped_lhs(void) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", $$ is untyped\n", + myname, lineno, input_file_name); + done(1); +} + + +void +untyped_rhs(int i, char *s) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", $%d (%s) is untyped\n", + myname, lineno, input_file_name, i, s); + done(1); +} + + +void +unknown_rhs(int i) +{ + fprintf(stderr, "%s: error - line %d of \"%s\", $%d is untyped\n", + myname, lineno, input_file_name, i); + done(1); +} + + +void +default_action_warning(void) +{ + fprintf(stderr, "%s: warning - line %d of \"%s\", the default action assigns an \ +undefined value to $$\n", myname, lineno, input_file_name); +} + + +void +undefined_goal(char *s) +{ + fprintf(stderr, "%s: error - the start symbol %s is undefined\n", myname, s); + done(1); +} + + +void +undefined_symbol_warning(char *s) +{ + fprintf(stderr, "%s: warning - the symbol %s is undefined\n", myname, s); +} diff --git a/modules/libcom/src/yacc/lalr.c b/modules/libcom/src/yacc/lalr.c new file mode 100644 index 000000000..1c8850f60 --- /dev/null +++ b/modules/libcom/src/yacc/lalr.c @@ -0,0 +1,668 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +typedef + struct shorts + { + struct shorts *next; + short value; + } + shorts; + +int tokensetsize; +short *lookaheads; +short *LAruleno; +unsigned *LA; +short *accessing_symbol; +core **state_table; +shifts **shift_table; +reductions **reduction_table; +short *goto_map; +short *from_state; +short *to_state; + +static void set_state_table(void); +static void set_accessing_symbol(void); +static void set_shift_table(void); +static void set_reduction_table(void); +static void set_maxrhs(void); +static void initialize_LA(void); +static void set_goto_map(void); +static void initialize_F(void); +static void build_relations(void); +static void add_lookback_edge(int stateno, int ruleno, int gotono); +static short **transpose(short int **R, int n); +static void compute_FOLLOWS(void); +static void compute_lookaheads(void); +static void digraph(short **relation); +static void traverse(int i); + +static int infinity; +static int maxrhs; +static int ngotos; +static unsigned *F; +static short **includes; +static shorts **lookback; +static short **R; +static short *INDEX; +static short *VERTICES; +static int top; + + +void +lalr(void) +{ + tokensetsize = WORDSIZE(ntokens); + + set_state_table(); + set_accessing_symbol(); + set_shift_table(); + set_reduction_table(); + set_maxrhs(); + initialize_LA(); + set_goto_map(); + initialize_F(); + build_relations(); + compute_FOLLOWS(); + compute_lookaheads(); +} + + + +static void +set_state_table(void) +{ + core *sp; + + state_table = NEW2(nstates, core *); + for (sp = first_state; sp; sp = sp->next) + state_table[sp->number] = sp; +} + + + +static void +set_accessing_symbol(void) +{ + core *sp; + + accessing_symbol = NEW2(nstates, short); + for (sp = first_state; sp; sp = sp->next) + accessing_symbol[sp->number] = sp->accessing_symbol; +} + + + +static void +set_shift_table(void) +{ + shifts *sp; + + shift_table = NEW2(nstates, shifts *); + for (sp = first_shift; sp; sp = sp->next) + shift_table[sp->number] = sp; +} + + + +static void +set_reduction_table(void) +{ + reductions *rp; + + reduction_table = NEW2(nstates, reductions *); + for (rp = first_reduction; rp; rp = rp->next) + reduction_table[rp->number] = rp; +} + + + +static void +set_maxrhs(void) +{ + short *itemp; + short *item_end; + int length; + int max; + + length = 0; + max = 0; + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + if (*itemp >= 0) + { + length++; + } + else + { + if (length > max) max = length; + length = 0; + } + } + + maxrhs = max; +} + + + +static void +initialize_LA(void) +{ + int i, j, k; + reductions *rp; + + lookaheads = NEW2(nstates + 1, short); + + k = 0; + for (i = 0; i < nstates; i++) + { + lookaheads[i] = k; + rp = reduction_table[i]; + if (rp) + k += rp->nreds; + } + lookaheads[nstates] = k; + + LA = NEW2(k * tokensetsize, unsigned); + LAruleno = NEW2(k, short); + lookback = NEW2(k, shorts *); + + k = 0; + for (i = 0; i < nstates; i++) + { + rp = reduction_table[i]; + if (rp) + { + for (j = 0; j < rp->nreds; j++) + { + LAruleno[k] = rp->rules[j]; + k++; + } + } + } +} + + +static void +set_goto_map(void) +{ + shifts *sp; + int i; + int symbol; + int k; + short *temp_map; + int state2; + int state1; + + goto_map = NEW2(nvars + 1, short) - ntokens; + temp_map = NEW2(nvars + 1, short) - ntokens; + + ngotos = 0; + for (sp = first_shift; sp; sp = sp->next) + { + for (i = sp->nshifts - 1; i >= 0; i--) + { + symbol = accessing_symbol[sp->shift[i]]; + + if (ISTOKEN(symbol)) break; + + if (ngotos == MAXSHORT) + fatal("too many gotos"); + + ngotos++; + goto_map[symbol]++; + } + } + + k = 0; + for (i = ntokens; i < nsyms; i++) + { + temp_map[i] = k; + k += goto_map[i]; + } + + for (i = ntokens; i < nsyms; i++) + goto_map[i] = temp_map[i]; + + goto_map[nsyms] = ngotos; + temp_map[nsyms] = ngotos; + + from_state = NEW2(ngotos, short); + to_state = NEW2(ngotos, short); + + for (sp = first_shift; sp; sp = sp->next) + { + state1 = sp->number; + for (i = sp->nshifts - 1; i >= 0; i--) + { + state2 = sp->shift[i]; + symbol = accessing_symbol[state2]; + + if (ISTOKEN(symbol)) break; + + k = temp_map[symbol]++; + from_state[k] = state1; + to_state[k] = state2; + } + } + + FREE(temp_map + ntokens); +} + + + +/* Map_goto maps a state/symbol pair into its numeric representation. */ + +int +map_goto(int state, int symbol) +{ + int high; + int low; + int middle; + int s; + + low = goto_map[symbol]; + high = goto_map[symbol + 1]; + + for (;;) + { + assert(low <= high); + middle = (low + high) >> 1; + s = from_state[middle]; + if (s == state) + return (middle); + else if (s < state) + low = middle + 1; + else + high = middle - 1; + } +} + + + +static void +initialize_F(void) +{ + int i; + int j; + int k; + shifts *sp; + short *edge; + unsigned *rowp; + short *rp; + short **reads; + int nedges; + int stateno; + int symbol; + int nwords; + + nwords = ngotos * tokensetsize; + F = NEW2(nwords, unsigned); + + reads = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + nedges = 0; + + rowp = F; + for (i = 0; i < ngotos; i++) + { + stateno = to_state[i]; + sp = shift_table[stateno]; + + if (sp) + { + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (ISVAR(symbol)) + break; + SETBIT(rowp, symbol); + } + + for (; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (nullable[symbol]) + edge[nedges++] = map_goto(stateno, symbol); + } + + if (nedges) + { + reads[i] = rp = NEW2(nedges + 1, short); + + for (j = 0; j < nedges; j++) + rp[j] = edge[j]; + + rp[nedges] = -1; + nedges = 0; + } + } + + rowp += tokensetsize; + } + + SETBIT(F, 0); + digraph(reads); + + for (i = 0; i < ngotos; i++) + { + if (reads[i]) + FREE(reads[i]); + } + + FREE(reads); + FREE(edge); +} + + + +static void +build_relations(void) +{ + int i; + int j; + int k; + short *rulep; + short *rp; + shifts *sp; + int length; + int nedges; + int done; + int state1; + int stateno; + int symbol1; + int symbol2; + short *shortp; + short *edge; + short *states; + short **new_includes; + + includes = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + states = NEW2(maxrhs + 1, short); + + for (i = 0; i < ngotos; i++) + { + nedges = 0; + state1 = from_state[i]; + symbol1 = accessing_symbol[to_state[i]]; + + for (rulep = derives[symbol1]; *rulep >= 0; rulep++) + { + length = 1; + states[0] = state1; + stateno = state1; + + for (rp = ritem + rrhs[*rulep]; *rp >= 0; rp++) + { + symbol2 = *rp; + sp = shift_table[stateno]; + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + stateno = sp->shift[j]; + if (accessing_symbol[stateno] == symbol2) break; + } + + states[length++] = stateno; + } + + add_lookback_edge(stateno, *rulep, i); + + length--; + done = 0; + while (!done) + { + done = 1; + rp--; + if (ISVAR(*rp)) + { + stateno = states[--length]; + edge[nedges++] = map_goto(stateno, *rp); + if (nullable[*rp] && length > 0) done = 0; + } + } + } + + if (nedges) + { + includes[i] = shortp = NEW2(nedges + 1, short); + for (j = 0; j < nedges; j++) + shortp[j] = edge[j]; + shortp[nedges] = -1; + } + } + + new_includes = transpose(includes, ngotos); + + for (i = 0; i < ngotos; i++) + if (includes[i]) + FREE(includes[i]); + + FREE(includes); + + includes = new_includes; + + FREE(edge); + FREE(states); +} + + +static void +add_lookback_edge(int stateno, int ruleno, int gotono) +{ + int i, k; + int found; + shorts *sp; + + i = lookaheads[stateno]; + k = lookaheads[stateno + 1]; + found = 0; + while (!found && i < k) + { + if (LAruleno[i] == ruleno) + found = 1; + else + ++i; + } + assert(found); + + sp = NEW(shorts); + sp->next = lookback[i]; + sp->value = gotono; + lookback[i] = sp; +} + + + +short ** +transpose(short int **R, int n) +{ + short **new_R; + short **temp_R; + short *nedges; + short *sp; + int i; + int k; + + nedges = NEW2(n, short); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + nedges[*sp++]++; + } + } + + new_R = NEW2(n, short *); + temp_R = NEW2(n, short *); + + for (i = 0; i < n; i++) + { + k = nedges[i]; + if (k > 0) + { + sp = NEW2(k + 1, short); + new_R[i] = sp; + temp_R[i] = sp; + sp[k] = -1; + } + } + + FREE(nedges); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + *temp_R[*sp++]++ = i; + } + } + + FREE(temp_R); + + return (new_R); +} + + + +static void +compute_FOLLOWS(void) +{ + digraph(includes); +} + + +static void +compute_lookaheads(void) +{ + int i, n; + unsigned *fp1, *fp2, *fp3; + shorts *sp, *next; + unsigned *rowp; + + rowp = LA; + n = lookaheads[nstates]; + for (i = 0; i < n; i++) + { + fp3 = rowp + tokensetsize; + for (sp = lookback[i]; sp; sp = sp->next) + { + fp1 = rowp; + fp2 = F + tokensetsize * sp->value; + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + rowp = fp3; + } + + for (i = 0; i < n; i++) + for (sp = lookback[i]; sp; sp = next) + { + next = sp->next; + FREE(sp); + } + + FREE(lookback); + FREE(F); +} + + +static void +digraph(short **relation) +{ + int i; + + infinity = ngotos + 2; + INDEX = NEW2(ngotos + 1, short); + VERTICES = NEW2(ngotos + 1, short); + top = 0; + + R = relation; + + for (i = 0; i < ngotos; i++) + INDEX[i] = 0; + + for (i = 0; i < ngotos; i++) + { + if (INDEX[i] == 0 && R[i]) + traverse(i); + } + + FREE(INDEX); + FREE(VERTICES); +} + + + +static void +traverse(int i) +{ + unsigned *fp1; + unsigned *fp2; + unsigned *fp3; + int j; + short *rp; + + int height; + unsigned *base; + + VERTICES[++top] = i; + INDEX[i] = height = top; + + base = F + i * tokensetsize; + fp3 = base + tokensetsize; + + rp = R[i]; + if (rp) + { + while ((j = *rp++) >= 0) + { + if (INDEX[j] == 0) + traverse(j); + + if (INDEX[i] > INDEX[j]) + INDEX[i] = INDEX[j]; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + } + + if (INDEX[i] == height) + { + for (;;) + { + j = VERTICES[top--]; + INDEX[j] = infinity; + + if (i == j) + break; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp2++ = *fp1++; + } + } +} diff --git a/modules/libcom/src/yacc/lr0.c b/modules/libcom/src/yacc/lr0.c new file mode 100644 index 000000000..23b950930 --- /dev/null +++ b/modules/libcom/src/yacc/lr0.c @@ -0,0 +1,613 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +extern short *itemset; +extern short *itemsetend; +extern unsigned *ruleset; + +int nstates; +core *first_state; +shifts *first_shift; +reductions *first_reduction; + +static core **state_set; +static core *this_state; +static core *last_state; +static shifts *last_shift; +static reductions *last_reduction; + +static int nshifts; +static short *shift_symbol; + +static short *redset; +static short *shiftset; + +static short **kernel_base; +static short **kernel_end; +static short *kernel_items; + +static int get_state(int symbol); +static void initialize_states(void); +static void new_itemsets(void); +static core *new_state(int symbol); +static void save_shifts(void); +static void save_reductions(void); +#ifdef DEBUG +static void print_derives(void); +#endif + + +static void +allocate_itemsets(void) +{ + short *itemp; + short *item_end; + int symbol; + int i; + int count; + int max; + short *symbol_count; + + count = 0; + symbol_count = NEW2(nsyms, short); + + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + symbol = *itemp; + if (symbol >= 0) + { + count++; + symbol_count[symbol]++; + } + } + + kernel_base = NEW2(nsyms, short *); + kernel_items = NEW2(count, short); + + count = 0; + max = 0; + for (i = 0; i < nsyms; i++) + { + kernel_base[i] = kernel_items + count; + count += symbol_count[i]; + if (max < symbol_count[i]) + max = symbol_count[i]; + } + + shift_symbol = symbol_count; + kernel_end = NEW2(nsyms, short *); +} + +static void +allocate_storage(void) +{ + allocate_itemsets(); + shiftset = NEW2(nsyms, short); + redset = NEW2(nrules + 1, short); + state_set = NEW2(nitems, core *); +} + +static void +append_states(void) +{ + int i; + int j; + int symbol; + +#ifdef TRACE + fprintf(stderr, "Entering append_states()\n"); +#endif + for (i = 1; i < nshifts; i++) + { + symbol = shift_symbol[i]; + j = i; + while (j > 0 && shift_symbol[j - 1] > symbol) + { + shift_symbol[j] = shift_symbol[j - 1]; + j--; + } + shift_symbol[j] = symbol; + } + + for (i = 0; i < nshifts; i++) + { + symbol = shift_symbol[i]; + shiftset[i] = get_state(symbol); + } +} + +static void +free_storage(void) +{ + FREE(shift_symbol); + FREE(redset); + FREE(shiftset); + FREE(kernel_base); + FREE(kernel_end); + FREE(kernel_items); + FREE(state_set); +} + + +static void +generate_states(void) +{ + allocate_storage(); + itemset = NEW2(nitems, short); + ruleset = NEW2(WORDSIZE(nrules), unsigned); + set_first_derives(); + initialize_states(); + + while (this_state) + { + closure(this_state->items, this_state->nitems); + save_reductions(); + new_itemsets(); + append_states(); + + if (nshifts > 0) + save_shifts(); + + this_state = this_state->next; + } + + finalize_closure(); + free_storage(); +} + + + +static int +get_state(int symbol) +{ + int key; + short *isp1; + short *isp2; + short *iend; + core *sp; + int found; + int n; + +#ifdef TRACE + fprintf(stderr, "Entering get_state(%d)\n", symbol); +#endif + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + key = *isp1; + assert(0 <= key && key < nitems); + sp = state_set[key]; + if (sp) + { + found = 0; + while (!found) + { + if (sp->nitems == n) + { + found = 1; + isp1 = kernel_base[symbol]; + isp2 = sp->items; + + while (found && isp1 < iend) + { + if (*isp1++ != *isp2++) + found = 0; + } + } + + if (!found) + { + if (sp->link) + { + sp = sp->link; + } + else + { + sp = sp->link = new_state(symbol); + found = 1; + } + } + } + } + else + { + state_set[key] = sp = new_state(symbol); + } + + return (sp->number); +} + + +static void +initialize_states(void) +{ + int i; + short *start_derives; + core *p; + + start_derives = derives[start_symbol]; + for (i = 0; start_derives[i] >= 0; ++i) + continue; + + p = (core *) MALLOC(sizeof(core) + i*sizeof(short)); + if (p == 0) no_space(); + + p->next = 0; + p->link = 0; + p->number = 0; + p->accessing_symbol = 0; + p->nitems = i; + + for (i = 0; start_derives[i] >= 0; ++i) + p->items[i] = rrhs[start_derives[i]]; + + first_state = last_state = this_state = p; + nstates = 1; +} + +static void +new_itemsets(void) +{ + int i; + int shiftcount; + short *isp; + short *ksp; + int symbol; + + for (i = 0; i < nsyms; i++) + kernel_end[i] = 0; + + shiftcount = 0; + isp = itemset; + while (isp < itemsetend) + { + i = *isp++; + symbol = ritem[i]; + if (symbol > 0) + { + ksp = kernel_end[symbol]; + if (!ksp) + { + shift_symbol[shiftcount++] = symbol; + ksp = kernel_base[symbol]; + } + + *ksp++ = i + 1; + kernel_end[symbol] = ksp; + } + } + + nshifts = shiftcount; +} + + + +static core * +new_state(int symbol) +{ + int n; + core *p; + short *isp1; + short *isp2; + short *iend; + +#ifdef TRACE + fprintf(stderr, "Entering new_state(%d)\n", symbol); +#endif + + if (nstates >= MAXSHORT) + fatal("too many states"); + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + p = (core *) allocate((unsigned) (sizeof(core) + (n - 1) * sizeof(short))); + p->accessing_symbol = symbol; + p->number = nstates; + p->nitems = n; + + isp2 = p->items; + while (isp1 < iend) + *isp2++ = *isp1++; + + last_state->next = p; + last_state = p; + + nstates++; + + return (p); +} + + +#ifdef DEBUG +static void +show_cores(void) +{ + core *p; + int i, j, k, n; + int itemno; + + k = 0; + for (p = first_state; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("state %d, number = %d, accessing symbol = %s\n", + k, p->number, symbol_name[p->accessing_symbol]); + n = p->nitems; + for (i = 0; i < n; ++i) + { + itemno = p->items[i]; + printf("%4d ", itemno); + j = itemno; + while (ritem[j] >= 0) ++j; + printf("%s :", symbol_name[rlhs[-ritem[j]]]); + j = rrhs[-ritem[j]]; + while (j < itemno) + printf(" %s", symbol_name[ritem[j++]]); + printf(" ."); + while (ritem[j] >= 0) + printf(" %s", symbol_name[ritem[j++]]); + printf("\n"); + fflush(stdout); + } + } +} + + +static void +show_ritems(void) +{ + int i; + + for (i = 0; i < nitems; ++i) + printf("ritem[%d] = %d\n", i, ritem[i]); +} + + +static void +show_rrhs(void) +{ + int i; + + for (i = 0; i < nrules; ++i) + printf("rrhs[%d] = %d\n", i, rrhs[i]); +} + + +static void +show_shifts(void) +{ + shifts *p; + int i, j, k; + + k = 0; + for (p = first_shift; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("shift %d, number = %d, nshifts = %d\n", k, p->number, + p->nshifts); + j = p->nshifts; + for (i = 0; i < j; ++i) + printf("\t%d\n", p->shift[i]); + } +} +#endif + +static void +save_shifts(void) +{ + shifts *p; + short *sp1; + short *sp2; + short *send; + + p = (shifts *) allocate((unsigned) (sizeof(shifts) + + (nshifts - 1) * sizeof(short))); + + p->number = this_state->number; + p->nshifts = nshifts; + + sp1 = shiftset; + sp2 = p->shift; + send = shiftset + nshifts; + + while (sp1 < send) + *sp2++ = *sp1++; + + if (last_shift) + { + last_shift->next = p; + last_shift = p; + } + else + { + first_shift = p; + last_shift = p; + } +} + + +static void +save_reductions(void) +{ + short *isp; + short *rp1; + short *rp2; + int item; + int count; + reductions *p; + short *rend; + + count = 0; + for (isp = itemset; isp < itemsetend; isp++) + { + item = ritem[*isp]; + if (item < 0) + { + redset[count++] = -item; + } + } + + if (count) + { + p = (reductions *) allocate((unsigned) (sizeof(reductions) + + (count - 1) * sizeof(short))); + + p->number = this_state->number; + p->nreds = count; + + rp1 = redset; + rp2 = p->rules; + rend = rp1 + count; + + while (rp1 < rend) + *rp2++ = *rp1++; + + if (last_reduction) + { + last_reduction->next = p; + last_reduction = p; + } + else + { + first_reduction = p; + last_reduction = p; + } + } +} + +static void +set_derives(void) +{ + int i, k; + int lhs; + short *rules; + + derives = NEW2(nsyms, short *); + rules = NEW2(nvars + nrules, short); + + k = 0; + for (lhs = start_symbol; lhs < nsyms; lhs++) + { + derives[lhs] = rules + k; + for (i = 0; i < nrules; i++) + { + if (rlhs[i] == lhs) + { + rules[k] = i; + k++; + } + } + rules[k] = -1; + k++; + } + +#ifdef DEBUG + print_derives(); +#endif +} + +void +free_derives(void) +{ + FREE(derives[start_symbol]); + FREE(derives); +} + +#ifdef DEBUG +static void +print_derives(void) +{ + int i; + short *sp; + + printf("\nDERIVES\n\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("%s derives ", symbol_name[i]); + for (sp = derives[i]; *sp >= 0; sp++) + { + printf(" %d", *sp); + } + putchar('\n'); + } + + putchar('\n'); +} +#endif + +static void +set_nullable(void) +{ + int i, j; + int empty; + int done; + + nullable = MALLOC(nsyms); + if (nullable == 0) no_space(); + + for (i = 0; i < nsyms; ++i) + nullable[i] = 0; + + done = 0; + while (!done) + { + done = 1; + for (i = 1; i < nitems; i++) + { + empty = 1; + while ((j = ritem[i]) >= 0) + { + if (!nullable[j]) + empty = 0; + ++i; + } + if (empty) + { + j = rlhs[-j]; + if (!nullable[j]) + { + nullable[j] = 1; + done = 0; + } + } + } + } + +#ifdef DEBUG + for (i = 0; i < nsyms; i++) + { + if (nullable[i]) + printf("%s is nullable\n", symbol_name[i]); + else + printf("%s is not nullable\n", symbol_name[i]); + } +#endif +} + +void +free_nullable(void) +{ + FREE(nullable); +} + +void +lr0(void) +{ + set_derives(); + set_nullable(); + generate_states(); +} diff --git a/modules/libcom/src/yacc/mkpar.c b/modules/libcom/src/yacc/mkpar.c new file mode 100644 index 000000000..5385f0e00 --- /dev/null +++ b/modules/libcom/src/yacc/mkpar.c @@ -0,0 +1,372 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +action **parser; +int SRtotal; +int RRtotal; +short *SRconflicts; +short *RRconflicts; +short *defred; +short *rules_used; +short nunused; +short final_state; + +static int SRcount; +static int RRcount; + + +static action *parse_actions(int stateno); +static action *get_shifts(int stateno); +static action *add_reductions(int stateno, action *actions); +static action *add_reduce(action *actions, int ruleno, int symbol); +static void find_final_state(void); +static void unused_rules(void); +static void remove_conflicts(void); +static void total_conflicts(void); +static void defreds(void); + +void +make_parser(void) +{ + int i; + + parser = NEW2(nstates, action *); + for (i = 0; i < nstates; i++) + parser[i] = parse_actions(i); + + find_final_state(); + remove_conflicts(); + unused_rules(); + if (SRtotal + RRtotal > 0) total_conflicts(); + defreds(); +} + + +static action * +parse_actions(int stateno) +{ + action *actions; + + actions = get_shifts(stateno); + actions = add_reductions(stateno, actions); + return (actions); +} + + +static action * +get_shifts(int stateno) +{ + action *actions, *temp; + shifts *sp; + short *to_state; + int i, k; + int symbol; + + actions = 0; + sp = shift_table[stateno]; + if (sp) + { + to_state = sp->shift; + for (i = sp->nshifts - 1; i >= 0; i--) + { + k = to_state[i]; + symbol = accessing_symbol[k]; + if (ISTOKEN(symbol)) + { + temp = NEW(action); + temp->next = actions; + temp->symbol = symbol; + temp->number = k; + temp->prec = symbol_prec[symbol]; + temp->action_code = SHIFT; + temp->assoc = symbol_assoc[symbol]; + actions = temp; + } + } + } + return (actions); +} + +static action * +add_reductions(int stateno, action *actions) +{ + int i, j, m, n; + int ruleno, tokensetsize; + unsigned *rowp; + + tokensetsize = WORDSIZE(ntokens); + m = lookaheads[stateno]; + n = lookaheads[stateno + 1]; + for (i = m; i < n; i++) + { + ruleno = LAruleno[i]; + rowp = LA + i * tokensetsize; + for (j = ntokens - 1; j >= 0; j--) + { + if (BIT(rowp, j)) + actions = add_reduce(actions, ruleno, j); + } + } + return (actions); +} + + +static action * +add_reduce(action *actions, int ruleno, int symbol) +{ + action *temp, *prev, *next; + + prev = 0; + for (next = actions; next && next->symbol < symbol; next = next->next) + prev = next; + + while (next && next->symbol == symbol && next->action_code == SHIFT) + { + prev = next; + next = next->next; + } + + while (next && next->symbol == symbol && + next->action_code == REDUCE && next->number < ruleno) + { + prev = next; + next = next->next; + } + + temp = NEW(action); + temp->next = next; + temp->symbol = symbol; + temp->number = ruleno; + temp->prec = rprec[ruleno]; + temp->action_code = REDUCE; + temp->assoc = rassoc[ruleno]; + + if (prev) + prev->next = temp; + else + actions = temp; + + return (actions); +} + + +static void +find_final_state(void) +{ + int goal, i; + short *to_state; + shifts *p; + + p = shift_table[0]; + to_state = p->shift; + goal = ritem[1]; + for (i = p->nshifts - 1; i >= 0; --i) + { + final_state = to_state[i]; + if (accessing_symbol[final_state] == goal) break; + } +} + + +static void +unused_rules(void) +{ + int i; + action *p; + + rules_used = (short *) MALLOC(nrules*sizeof(short)); + if (rules_used == 0) no_space(); + + for (i = 0; i < nrules; ++i) + rules_used[i] = 0; + + for (i = 0; i < nstates; ++i) + { + for (p = parser[i]; p; p = p->next) + { + if (p->action_code == REDUCE && p->suppressed == 0) + rules_used[p->number] = 1; + } + } + + nunused = 0; + for (i = 3; i < nrules; ++i) + if (!rules_used[i]) ++nunused; + + if (nunused) + { + if (nunused == 1) + fprintf(stderr, "%s: 1 rule never reduced\n", myname); + else + fprintf(stderr, "%s: %d rules never reduced\n", myname, nunused); + } +} + + +static void +remove_conflicts(void) +{ + int i; + int symbol; + action *p, *pref = NULL; + + SRtotal = 0; + RRtotal = 0; + SRconflicts = NEW2(nstates, short); + RRconflicts = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + { + SRcount = 0; + RRcount = 0; + symbol = -1; + for (p = parser[i]; p; p = p->next) + { + if (p->symbol != symbol) + { + pref = p; + symbol = p->symbol; + } + else if (i == final_state && symbol == 0) + { + SRcount++; + p->suppressed = 1; + } + else if (pref && pref->action_code == SHIFT) + { + if (pref->prec > 0 && p->prec > 0) + { + if (pref->prec < p->prec) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->prec > p->prec) + { + p->suppressed = 2; + } + else if (pref->assoc == LEFT) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->assoc == RIGHT) + { + p->suppressed = 2; + } + else + { + pref->suppressed = 2; + p->suppressed = 2; + } + } + else + { + SRcount++; + p->suppressed = 1; + } + } + else + { + RRcount++; + p->suppressed = 1; + } + } + SRtotal += SRcount; + RRtotal += RRcount; + SRconflicts[i] = SRcount; + RRconflicts[i] = RRcount; + } +} + + +static void +total_conflicts(void) +{ + fprintf(stderr, "%s: ", myname); + if (SRtotal == 1) + fprintf(stderr, "1 shift/reduce conflict"); + else if (SRtotal > 1) + fprintf(stderr, "%d shift/reduce conflicts", SRtotal); + + if (SRtotal && RRtotal) + fprintf(stderr, ", "); + + if (RRtotal == 1) + fprintf(stderr, "1 reduce/reduce conflict"); + else if (RRtotal > 1) + fprintf(stderr, "%d reduce/reduce conflicts", RRtotal); + + fprintf(stderr, ".\n"); +} + + +static int +sole_reduction(int stateno) +{ + int count, ruleno; + action *p; + + count = 0; + ruleno = 0; + for (p = parser[stateno]; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + return 0; + else if (p->action_code == REDUCE && p->suppressed == 0) + { + if (ruleno > 0 && p->number != ruleno) + return 0; + if (p->symbol != 1) + ++count; + ruleno = p->number; + } + } + + if (count == 0) + return 0; + return ruleno; +} + + +static void +defreds(void) +{ + int i; + + defred = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + defred[i] = sole_reduction(i); +} + +static void +free_action_row(action *p) +{ + action *q; + + while (p) + { + q = p->next; + FREE(p); + p = q; + } +} + +void +free_parser(void) +{ + int i; + + for (i = 0; i < nstates; i++) + free_action_row(parser[i]); + + FREE(parser); +} + diff --git a/modules/libcom/src/yacc/output.c b/modules/libcom/src/yacc/output.c new file mode 100644 index 000000000..a1913ed77 --- /dev/null +++ b/modules/libcom/src/yacc/output.c @@ -0,0 +1,1247 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +static int nvectors; +static int nentries; +static short **froms; +static short **tos; +static short *tally; +static short *width; +static short *state_count; +static short *order; +static short *base; +static short *pos; +static int maxtable; +static short *table; +static short *check; +static int lowzero; +static int high; + +static void output_prefix(void); +static void output_rule_data(void); +static void output_yydefred(void); +static void output_actions(void); +static void token_actions(void); +static void goto_actions(void); +static void save_column(int symbol, int default_state); +static void sort_actions(void); +static void pack_table(void); +static void output_base(void); +static void output_table(void); +static void output_check(void); +static void output_defines(void); +static void output_stored_text(void); +static void output_debug(void); +static void output_stype(void); +static void output_trailing_text(void); +static void output_semantic_actions(void); +static void free_itemsets(void); +static void free_shifts(void); +static void free_reductions(void); + + +void +output(void) +{ + free_itemsets(); + free_shifts(); + free_reductions(); + output_prefix(); + output_stored_text(); + output_defines(); + output_rule_data(); + output_yydefred(); + output_actions(); + free_parser(); + output_debug(); + output_stype(); + if (rflag) write_section(tables); + write_section(header); + output_trailing_text(); + write_section(body); + output_semantic_actions(); + write_section(trailer); +} + + +static void +output_prefix(void) +{ + if (symbol_prefix == NULL) + symbol_prefix = "yy"; + else + { + ++outline; + fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylex %slex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerror %serror\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yychar %schar\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyval %sval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylval %slval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyss %sss\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvs %svs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylen %slen\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yytable %stable\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyname %sname\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrule %srule\n", symbol_prefix); + } + ++outline; + fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix); +} + + +static void +output_rule_data(void) +{ + int i; + int j; + + fprintf(output_file, "static short %slhs[] = {%42d,", symbol_prefix, + symbol_value[start_symbol]); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", symbol_value[rlhs[i]]); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + + fprintf(output_file, "static short %slen[] = {%42d,", symbol_prefix, 2); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + j++; + + fprintf(output_file, "%5d,", rrhs[i + 1] - rrhs[i] - 1); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +static void +output_yydefred(void) +{ + int i, j; + + fprintf(output_file, "static short %sdefred[] = {%39d,", symbol_prefix, + (defred[0] ? defred[0] - 2 : 0)); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j < 10) + ++j; + else + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + + fprintf(output_file, "%5d,", (defred[i] ? defred[i] - 2 : 0)); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +static void +output_actions(void) +{ + nvectors = 2*nstates + nvars; + + froms = NEW2(nvectors, short *); + tos = NEW2(nvectors, short *); + tally = NEW2(nvectors, short); + width = NEW2(nvectors, short); + + token_actions(); + FREE(lookaheads); + FREE(LA); + FREE(LAruleno); + FREE(accessing_symbol); + + goto_actions(); + FREE(goto_map + ntokens); + FREE(from_state); + FREE(to_state); + + sort_actions(); + pack_table(); + output_base(); + output_table(); + output_check(); +} + + +static void +token_actions(void) +{ + int i, j; + int shiftcount, reducecount; + int max, min; + short *actionrow, *r, *s; + action *p; + + actionrow = NEW2(2*ntokens, short); + for (i = 0; i < nstates; ++i) + { + if (parser[i]) + { + for (j = 0; j < 2*ntokens; ++j) + actionrow[j] = 0; + + shiftcount = 0; + reducecount = 0; + for (p = parser[i]; p; p = p->next) + { + if (p->suppressed == 0) + { + if (p->action_code == SHIFT) + { + ++shiftcount; + actionrow[p->symbol] = p->number; + } + else if (p->action_code == REDUCE && p->number != defred[i]) + { + ++reducecount; + actionrow[p->symbol + ntokens] = p->number; + } + } + } + + tally[i] = shiftcount; + tally[nstates+i] = reducecount; + width[i] = 0; + width[nstates+i] = 0; + if (shiftcount > 0) + { + froms[i] = r = NEW2(shiftcount, short); + tos[i] = s = NEW2(shiftcount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[j]; + } + } + width[i] = max - min + 1; + } + if (reducecount > 0) + { + froms[nstates+i] = r = NEW2(reducecount, short); + tos[nstates+i] = s = NEW2(reducecount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[ntokens+j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[ntokens+j] - 2; + } + } + width[nstates+i] = max - min + 1; + } + } + } + FREE(actionrow); +} + +static void +goto_actions(void) +{ + int i, j, k; + + state_count = NEW2(nstates, short); + + k = default_goto(start_symbol + 1); + fprintf(output_file, "static short %sdgoto[] = {%40d,", symbol_prefix, k); + save_column(start_symbol + 1, k); + + j = 10; + for (i = start_symbol + 2; i < nsyms; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + k = default_goto(i); + fprintf(output_file, "%5d,", k); + save_column(i, k); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(state_count); +} + +int +default_goto(int symbol) +{ + int i; + int m; + int n; + int default_state; + int max; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + if (m == n) return (0); + + for (i = 0; i < nstates; i++) + state_count[i] = 0; + + for (i = m; i < n; i++) + state_count[to_state[i]]++; + + max = 0; + default_state = 0; + for (i = 0; i < nstates; i++) + { + if (state_count[i] > max) + { + max = state_count[i]; + default_state = i; + } + } + + return (default_state); +} + + + + +static void +save_column(int symbol, int default_state) +{ + int i; + int m; + int n; + short *sp; + short *sp1; + short *sp2; + int count; + int symno; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + count = 0; + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + ++count; + } + if (count == 0) return; + + symno = symbol_value[symbol] + 2*nstates; + + froms[symno] = sp1 = sp = NEW2(count, short); + tos[symno] = sp2 = NEW2(count, short); + + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + { + *sp1++ = from_state[i]; + *sp2++ = to_state[i]; + } + } + + tally[symno] = count; + width[symno] = sp1[-1] - sp[0] + 1; +} + +static void +sort_actions(void) +{ + int i; + int j; + int k; + int t; + int w; + + order = NEW2(nvectors, short); + nentries = 0; + + for (i = 0; i < nvectors; i++) + { + if (tally[i] > 0) + { + t = tally[i]; + w = width[i]; + j = nentries - 1; + + while (j >= 0 && (width[order[j]] < w)) + j--; + + while (j >= 0 && (width[order[j]] == w) && (tally[order[j]] < t)) + j--; + + for (k = nentries - 1; k > j; k--) + order[k + 1] = order[k]; + + order[j + 1] = i; + nentries++; + } + } +} + + +static void +pack_table(void) +{ + int i; + int place; + int state; + + base = NEW2(nvectors, short); + pos = NEW2(nentries, short); + + maxtable = 1000; + table = NEW2(maxtable, short); + check = NEW2(maxtable, short); + + lowzero = 0; + high = 0; + + for (i = 0; i < maxtable; i++) + check[i] = -1; + + for (i = 0; i < nentries; i++) + { + state = matching_vector(i); + + if (state < 0) + place = pack_vector(i); + else + place = base[state]; + + pos[i] = place; + base[order[i]] = place; + } + + for (i = 0; i < nvectors; i++) + { + if (froms[i]) + FREE(froms[i]); + if (tos[i]) + FREE(tos[i]); + } + + FREE(froms); + FREE(tos); + FREE(pos); +} + + +/* The function matching_vector determines if the vector specified by */ +/* the input parameter matches a previously considered vector. The */ +/* test at the start of the function checks if the vector represents */ +/* a row of shifts over terminal symbols or a row of reductions, or a */ +/* column of shifts over a nonterminal symbol. Berkeley Yacc does not */ +/* check if a column of shifts over a nonterminal symbols matches a */ +/* previously considered vector. Because of the nature of LR parsing */ +/* tables, no two columns can match. Therefore, the only possible */ +/* match would be between a row and a column. Such matches are */ +/* unlikely. Therefore, to save time, no attempt is made to see if a */ +/* column matches a previously considered vector. */ +/* */ +/* Matching_vector is poorly designed. The test could easily be made */ +/* faster. Also, it depends on the vectors being in a specific */ +/* order. */ + +int +matching_vector(int vector) +{ + int i; + int j; + int k; + int t; + int w; + int match; + int prev; + + i = order[vector]; + if (i >= 2*nstates) + return (-1); + + t = tally[i]; + w = width[i]; + + for (prev = vector - 1; prev >= 0; prev--) + { + j = order[prev]; + if (width[j] != w || tally[j] != t) + return (-1); + + match = 1; + for (k = 0; match && k < t; k++) + { + if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k]) + match = 0; + } + + if (match) + return (j); + } + + return (-1); +} + + + +int +pack_vector(int vector) +{ + int i, j, k, l; + int t; + int loc; + int ok; + short *from; + short *to; + int newmax; + + i = order[vector]; + t = tally[i]; + assert(t); + + from = froms[i]; + to = tos[i]; + + j = lowzero - from[0]; + for (k = 1; k < t; ++k) + if (lowzero - from[k] > j) + j = lowzero - from[k]; + for (;; ++j) + { + if (j == 0) + continue; + ok = 1; + for (k = 0; ok && k < t; k++) + { + loc = j + from[k]; + if (loc >= maxtable) + { + if (loc >= MAXTABLE) + fatal("maximum table size exceeded"); + + newmax = maxtable; + do { newmax += 200; } while (newmax <= loc); + table = (short *) REALLOC(table, newmax*sizeof(short)); + if (table == 0) no_space(); + check = (short *) REALLOC(check, newmax*sizeof(short)); + if (check == 0) no_space(); + for (l = maxtable; l < newmax; ++l) + { + table[l] = 0; + check[l] = -1; + } + maxtable = newmax; + } + + if (check[loc] != -1) + ok = 0; + } + for (k = 0; ok && k < vector; k++) + { + if (pos[k] == j) + ok = 0; + } + if (ok) + { + for (k = 0; k < t; k++) + { + loc = j + from[k]; + table[loc] = to[k]; + check[loc] = from[k]; + if (loc > high) high = loc; + } + + while (check[lowzero] != -1) + ++lowzero; + + return (j); + } + } +} + + + +static void +output_base(void) +{ + int i, j; + + fprintf(output_file, "static short %ssindex[] = {%39d,", symbol_prefix, base[0]); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nstatic short %srindex[] = {%39d,", symbol_prefix, + base[nstates]); + + j = 10; + for (i = nstates + 1; i < 2*nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nstatic short %sgindex[] = {%39d,", symbol_prefix, + base[2*nstates]); + + j = 10; + for (i = 2*nstates + 1; i < nvectors - 1; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(base); +} + + + +static void +output_table(void) +{ + int i; + int j; + + ++outline; + fprintf(code_file, "#define YYTABLESIZE %d\n", high); + fprintf(output_file, "static short %stable[] = {%40d,", symbol_prefix, + table[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", table[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(table); +} + + + +static void +output_check(void) +{ + int i; + int j; + + fprintf(output_file, "static short %scheck[] = {%40d,", symbol_prefix, + check[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", check[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(check); +} + + +static int +is_C_identifier(char *name) +{ + char *s; + int c; + + s = name; + c = *s; + if (c == '"') + { + c = *++s; + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while ((c = *++s) != '"') + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); + } + + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while ((c = *++s)) + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); +} + + +static void +output_defines(void) +{ + int c, i; + char *s; + + for (i = 2; i < ntokens; ++i) + { + s = symbol_name[i]; + if (is_C_identifier(s)) + { + fprintf(code_file, "#define "); + if (dflag) fprintf(defines_file, "#define "); + c = *s; + if (c == '"') + { + while ((c = *++s) != '"') + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + } + else + { + do + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + while ((c = *++s)); + } + ++outline; + fprintf(code_file, " %d\n", symbol_value[i]); + if (dflag) fprintf(defines_file, " %d\n", symbol_value[i]); + } + } + + ++outline; + fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]); + + if (dflag && unionized) + { + rewind(union_file); + while ((c = getc(union_file)) != EOF) + putc(c, defines_file); + fprintf(defines_file, " YYSTYPE;\nstatic YYSTYPE %slval;\n", + symbol_prefix); + } +} + + +static void +output_stored_text(void) +{ + int c; + FILE *in, *out; + + rewind(text_file); + in = text_file; + if ((c = getc(in)) == EOF) + return; + out = code_file; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +output_debug(void) +{ + int i, j, k, max; + char **symnam, *s; + + ++outline; + fprintf(code_file, "#define YYFINAL %d\n", final_state); + outline += 3; + fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + if (rflag) + fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + + max = 0; + for (i = 2; i < ntokens; ++i) + if (symbol_value[i] > max) + max = symbol_value[i]; + ++outline; + fprintf(code_file, "#define YYMAXTOKEN %d\n", max); + + symnam = (char **) MALLOC((max+1)*sizeof(char *)); + if (symnam == 0) no_space(); + + /* Note that it is not necessary to initialize the element */ + /* symnam[max]. */ + for (i = 0; i < max; ++i) + symnam[i] = 0; + for (i = ntokens - 1; i >= 2; --i) + symnam[symbol_value[i]] = symbol_name[i]; + symnam[0] = "end-of-file"; + + if (!rflag) ++outline; + fprintf(output_file, "#if YYDEBUG\nstatic char *%sname[] = {", symbol_prefix); + j = 80; + for (i = 0; i <= max; ++i) + { + if ((s = symnam[i])) + { + if (s[0] == '"') + { + k = 7; + while (*++s != '"') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"\\\""); + s = symnam[i]; + while (*++s != '"') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\"\","); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + { + j += 7; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 7; + } + fprintf(output_file, "\"'\\\"'\","); + } + else + { + k = 5; + while (*++s != '\'') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"'"); + s = symnam[i]; + while (*++s != '\'') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "'\","); + } + } + else + { + k = strlen(s) + 3; + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + putc('"', output_file); + do { putc(*s, output_file); } while (*++s); + fprintf(output_file, "\","); + } + } + else + { + j += 2; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 2; + } + fprintf(output_file, "0,"); + } + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(symnam); + + if (!rflag) ++outline; + fprintf(output_file, "static char *%srule[] = {\n", symbol_prefix); + for (i = 2; i < nrules; ++i) + { + fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]); + for (j = rrhs[i]; ritem[j] > 0; ++j) + { + s = symbol_name[ritem[j]]; + if (s[0] == '"') + { + fprintf(output_file, " \\\""); + while (*++s != '"') + { + if (*s == '\\') + { + if (s[1] == '\\') + fprintf(output_file, "\\\\\\\\"); + else + fprintf(output_file, "\\\\%c", s[1]); + ++s; + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\""); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + fprintf(output_file, " '\\\"'"); + else if (s[1] == '\\') + { + if (s[2] == '\\') + fprintf(output_file, " '\\\\\\\\"); + else + fprintf(output_file, " '\\\\%c", s[2]); + s += 2; + while (*++s != '\'') + putc(*s, output_file); + putc('\'', output_file); + } + else + fprintf(output_file, " '%c'", s[1]); + } + else + fprintf(output_file, " %s", s); + } + if (!rflag) ++outline; + fprintf(output_file, "\",\n"); + } + + if (!rflag) outline += 2; + fprintf(output_file, "};\n#endif\n"); +} + + +static void +output_stype(void) +{ + if (!unionized && ntags == 0) + { + outline += 3; + fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n"); + } +} + + +static void +output_trailing_text(void) +{ + int c, last; + FILE *in, *out; + + if (line == 0) + return; + + in = input_file; + out = code_file; + c = *cptr; + if (c == '\n') + { + ++lineno; + if ((c = getc(in)) == EOF) + return; + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + else + { + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + do { putc(c, out); } while ((c = *++cptr) != '\n'); + ++outline; + putc('\n', out); + last = '\n'; + } + + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +output_semantic_actions(void) +{ + int c, last; + FILE *out; + + rewind(action_file); + + if ((c = getc(action_file)) == EOF) + return; + + out = code_file; + last = c; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(action_file)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +free_itemsets(void) +{ + core *cp, *next; + + FREE(state_table); + for (cp = first_state; cp; cp = next) + { + next = cp->next; + FREE(cp); + } +} + + +static void +free_shifts(void) +{ + shifts *sp, *next; + + FREE(shift_table); + for (sp = first_shift; sp; sp = next) + { + next = sp->next; + FREE(sp); + } +} + + +static void +free_reductions(void) +{ + reductions *rp, *next; + + FREE(reduction_table); + for (rp = first_reduction; rp; rp = next) + { + next = rp->next; + FREE(rp); + } +} diff --git a/modules/libcom/src/yacc/reader.c b/modules/libcom/src/yacc/reader.c new file mode 100644 index 000000000..93a1a42de --- /dev/null +++ b/modules/libcom/src/yacc/reader.c @@ -0,0 +1,1805 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +/* The line size must be a positive integer. One hundred was chosen */ +/* because few lines in Yacc input grammars exceed 100 characters. */ +/* Note that if a line exceeds LINESIZE characters, the line buffer */ +/* will be expanded to accomodate it. */ + +#define LINESIZE 100 + +char *cache; +int cinc, cache_size; + +int ntags, tagmax; +char **tag_table; + +char saw_eof, unionized; +char *cptr, *line; +int linesize; + +bucket *goal; +int prec; +int gensym; +char last_was_action; + +int maxitems; +bucket **pitem; + +int maxrules; +bucket **plhs; + +int name_pool_size; +char *name_pool; + +char line_format[] = "#line %d \"%s\"\n"; + +#define static + +static void start_rule(bucket *bp, int s_lineno); + + +static void +cachec(int c) +{ + assert(cinc >= 0); + if (cinc >= cache_size) + { + cache_size += 256; + cache = REALLOC(cache, cache_size); + if (cache == 0) no_space(); + } + cache[cinc] = c; + ++cinc; +} + + +static void +get_line(void) +{ + FILE *f = input_file; + int c; + int i; + + if (saw_eof || (c = getc(f)) == EOF) + { + if (line) { FREE(line); line = 0; } + cptr = 0; + saw_eof = 1; + return; + } + + if (line == 0 || linesize != (LINESIZE + 1)) + { + if (line) FREE(line); + linesize = LINESIZE + 1; + line = MALLOC(linesize); + if (line == 0) no_space(); + } + + i = 0; + ++lineno; + for (;;) + { + line[i] = c; + if (c == '\n') { cptr = line; return; } + if (++i >= linesize) + { + linesize += LINESIZE; + line = REALLOC(line, linesize); + if (line == 0) no_space(); + } + c = getc(f); + if (c == EOF) + { + line[i] = '\n'; + saw_eof = 1; + cptr = line; + return; + } + } +} + + +static char * +dup_line(void) +{ + char *p, *s, *t; + + if (line == 0) return (0); + s = line; + while (*s != '\n') ++s; + p = MALLOC(s - line + 1); + if (p == 0) no_space(); + + s = line; + t = p; + while ((*t++ = *s++) != '\n') continue; + return (p); +} + + +static void +skip_comment(void) +{ + char *s; + + int st_lineno = lineno; + char *st_line = dup_line(); + char *st_cptr = st_line + (cptr - line); + + s = cptr + 2; + for (;;) + { + if (*s == '*' && s[1] == '/') + { + cptr = s + 2; + FREE(st_line); + return; + } + if (*s == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(st_lineno, st_line, st_cptr); + s = cptr; + } + else + ++s; + } +} + + +static int +nextc(void) +{ + char *s; + + if (line == 0) + { + get_line(); + if (line == 0) + return (EOF); + } + + s = cptr; + for (;;) + { + switch (*s) + { + case '\n': + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case ',': + case ';': + ++s; + break; + + case '\\': + cptr = s; + return ('%'); + + case '/': + if (s[1] == '*') + { + cptr = s; + skip_comment(); + s = cptr; + break; + } + else if (s[1] == '/') + { + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + } + /* fall through */ + + default: + cptr = s; + return (*s); + } + } +} + + +static int +keyword(void) +{ + int c; + char *t_cptr = cptr; + + c = *++cptr; + if (isalpha(c)) + { + cinc = 0; + for (;;) + { + if (isalpha(c)) + { + if (isupper(c)) c = tolower(c); + cachec(c); + } + else if (isdigit(c) || c == '_' || c == '.' || c == '$') + cachec(c); + else + break; + c = *++cptr; + } + cachec(NUL); + + if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0) + return (TOKEN); + if (strcmp(cache, "type") == 0) + return (TYPE); + if (strcmp(cache, "left") == 0) + return (LEFT); + if (strcmp(cache, "right") == 0) + return (RIGHT); + if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0) + return (NONASSOC); + if (strcmp(cache, "start") == 0) + return (START); + if (strcmp(cache, "union") == 0) + return (UNION); + if (strcmp(cache, "ident") == 0) + return (IDENT); + } + else + { + ++cptr; + if (c == '{') + return (TEXT); + if (c == '%' || c == '\\') + return (MARK); + if (c == '<') + return (LEFT); + if (c == '>') + return (RIGHT); + if (c == '0') + return (TOKEN); + if (c == '2') + return (NONASSOC); + } + syntax_error(lineno, line, t_cptr); + /*NOTREACHED*/ +} + + +static void +copy_ident(void) +{ + int c; + FILE *f = output_file; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '"') syntax_error(lineno, line, cptr); + ++outline; + fprintf(f, "#ident \""); + for (;;) + { + c = *++cptr; + if (c == '\n') + { + fprintf(f, "\"\n"); + return; + } + putc(c, f); + if (c == '"') + { + putc('\n', f); + ++cptr; + return; + } + } +} + + +static void +copy_text(void) +{ + int c; + int quote; + FILE *f = text_file; + int need_newline = 0; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line - 2); + + if (*cptr == '\n') + { + get_line(); + if (line == 0) + unterminated_text(t_lineno, t_line, t_cptr); + } + if (!lflag) fprintf(f, line_format, lineno, input_file_name); + +loop: + c = *cptr++; + switch (c) + { + case '\n': + next_line: + putc('\n', f); + need_newline = 0; + get_line(); + if (line) goto loop; + unterminated_text(t_lineno, t_line, t_cptr); + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + putc(c, f); + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + need_newline = 1; + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + putc(c, f); + need_newline = 1; + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + need_newline = 1; + goto loop; + + case '%': + case '\\': + if (*cptr == '}') + { + if (need_newline) putc('\n', f); + ++cptr; + FREE(t_line); + return; + } + /* fall through */ + + default: + putc(c, f); + need_newline = 1; + goto loop; + } +} + + +static void +copy_union(void) +{ + int c; + int quote; + int depth; + int u_lineno = lineno; + char *u_line = dup_line(); + char *u_cptr = u_line + (cptr - line - 6); + + if (unionized) over_unionized(cptr - 6); + unionized = 1; + + if (!lflag) + fprintf(text_file, line_format, lineno, input_file_name); + + fprintf(text_file, "typedef union"); + if (dflag) fprintf(union_file, "typedef union"); + + depth = 0; +loop: + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + switch (c) + { + case '\n': + next_line: + get_line(); + if (line == 0) unterminated_union(u_lineno, u_line, u_cptr); + goto loop; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth == 0) + { + fprintf(text_file, " YYSTYPE;\n"); + FREE(u_line); + return; + } + goto loop; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', text_file); + if (dflag) putc('*', union_file); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + { + fprintf(text_file, "* "); + if (dflag) fprintf(union_file, "* "); + } + else + { + putc(c, text_file); + if (dflag) putc(c, union_file); + } + } + fprintf(text_file, "*/\n"); + if (dflag) fprintf(union_file, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', text_file); + if (dflag) putc('*', union_file); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '*' && *cptr == '/') + { + putc('/', text_file); + if (dflag) putc('/', union_file); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +static int +hexval(int c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + return (-1); +} + + +static bucket * +get_literal(void) +{ + int c, quote; + int i; + int n; + char *s; + bucket *bp; + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line); + + quote = *cptr++; + cinc = 0; + for (;;) + { + c = *cptr++; + if (c == quote) break; + if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + char *c_cptr = cptr - 1; + + c = *cptr++; + switch (c) + { + case '\n': + get_line(); + if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); + continue; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = c - '0'; + c = *cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + c = *++cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + ++cptr; + } + } + if (n > MAXCHAR) illegal_character(c_cptr); + c = n; + break; + + case 'x': + c = *cptr++; + n = hexval(c); + if (n < 0 || n >= 16) + illegal_character(c_cptr); + for (;;) + { + c = *cptr; + i = hexval(c); + if (i < 0 || i >= 16) break; + ++cptr; + n = (n << 4) + i; + if (n > MAXCHAR) illegal_character(c_cptr); + } + c = n; + break; + + case 'a': c = 7; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + } + } + cachec(c); + } + FREE(s_line); + + n = cinc; + s = MALLOC(n); + if (s == 0) no_space(); + + for (i = 0; i < n; ++i) + s[i] = cache[i]; + + cinc = 0; + if (n == 1) + cachec('\''); + else + cachec('"'); + + for (i = 0; i < n; ++i) + { + c = ((unsigned char *)s)[i]; + if (c == '\\' || c == cache[0]) + { + cachec('\\'); + cachec(c); + } + else if (isprint(c)) + cachec(c); + else + { + cachec('\\'); + switch (c) + { + case 7: cachec('a'); break; + case '\b': cachec('b'); break; + case '\f': cachec('f'); break; + case '\n': cachec('n'); break; + case '\r': cachec('r'); break; + case '\t': cachec('t'); break; + case '\v': cachec('v'); break; + default: + cachec(((c >> 6) & 7) + '0'); + cachec(((c >> 3) & 7) + '0'); + cachec((c & 7) + '0'); + break; + } + } + } + + if (n == 1) + cachec('\''); + else + cachec('"'); + + cachec(NUL); + bp = lookup(cache); + bp->class = TERM; + if (n == 1 && bp->value == UNDEFINED) + bp->value = *(unsigned char *)s; + FREE(s); + + return (bp); +} + + +static int +is_reserved(char *name) +{ + char *s; + + if (strcmp(name, ".") == 0 || + strcmp(name, "$accept") == 0 || + strcmp(name, "$end") == 0) + return (1); + + if (name[0] == '$' && name[1] == '$' && isdigit((int) name[2])) + { + s = name + 3; + while (isdigit((int) *s)) ++s; + if (*s == NUL) return (1); + } + + return (0); +} + + +static bucket * +get_name(void) +{ + int c; + + cinc = 0; + for (c = *cptr; IS_IDENT(c); c = *++cptr) + cachec(c); + cachec(NUL); + + if (is_reserved(cache)) used_reserved(cache); + + return (lookup(cache)); +} + + +static int +get_number(void) +{ + int c; + int n; + + n = 0; + for (c = *cptr; isdigit(c); c = *++cptr) + n = 10*n + (c - '0'); + + return (n); +} + + +static char * +get_tag(void) +{ + int c; + int i; + char *s; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line); + + ++cptr; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '$') + illegal_tag(t_lineno, t_line, t_cptr); + + cinc = 0; + do { cachec(c); c = *++cptr; } while (IS_IDENT(c)); + cachec(NUL); + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '>') + illegal_tag(t_lineno, t_line, t_cptr); + ++cptr; + + for (i = 0; i < ntags; ++i) + { + if (strcmp(cache, tag_table[i]) == 0) + return (tag_table[i]); + } + + if (ntags >= tagmax) + { + tagmax += 16; + tag_table = (char **) + (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *)) + : MALLOC(tagmax*sizeof(char *))); + if (tag_table == 0) no_space(); + } + + s = MALLOC(cinc); + if (s == 0) no_space(); + strcpy(s, cache); + tag_table[ntags] = s; + ++ntags; + FREE(t_line); + return (s); +} + + +static void +declare_tokens(int assoc) +{ + int c; + bucket *bp; + int value; + char *tag = 0; + + if (assoc != TOKEN) ++prec; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c == '<') + { + tag = get_tag(); + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + + for (;;) + { + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp == goal) tokenized_start(bp->name); + bp->class = TERM; + + if (tag) + { + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } + + if (assoc != TOKEN) + { + if (bp->prec && prec != bp->prec) + reprec_warning(bp->name); + bp->assoc = assoc; + bp->prec = prec; + } + + c = nextc(); + if (c == EOF) unexpected_EOF(); + value = UNDEFINED; + if (isdigit(c)) + { + value = get_number(); + if (bp->value != UNDEFINED && value != bp->value) + revalued_warning(bp->name); + bp->value = value; + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + } +} + + +static void +declare_types(void) +{ + int c; + bucket *bp; + char *tag; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '<') syntax_error(lineno, line, cptr); + tag = get_tag(); + + for (;;) + { + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } +} + + +static void +declare_start(void) +{ + int c; + bucket *bp; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '.' && c != '$') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (bp->class == TERM) + terminal_start(bp->name); + if (goal && goal != bp) + restarted_warning(); + goal = bp; +} + + +static void +read_declarations(void) +{ + int c, k; + + cache_size = 256; + cache = MALLOC(cache_size); + if (cache == 0) no_space(); + + for (;;) + { + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '%') syntax_error(lineno, line, cptr); + switch (k = keyword()) + { + case MARK: + return; + + case IDENT: + copy_ident(); + break; + + case TEXT: + copy_text(); + break; + + case UNION: + copy_union(); + break; + + case TOKEN: + case LEFT: + case RIGHT: + case NONASSOC: + declare_tokens(k); + break; + + case TYPE: + declare_types(); + break; + + case START: + declare_start(); + break; + } + } +} + + +static void +initialize_grammar(void) +{ + nitems = 4; + maxitems = 300; + pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); + pitem[0] = 0; + pitem[1] = 0; + pitem[2] = 0; + pitem[3] = 0; + + nrules = 3; + maxrules = 100; + plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + plhs[0] = 0; + plhs[1] = 0; + plhs[2] = 0; + rprec = (short *) MALLOC(maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rprec[0] = 0; + rprec[1] = 0; + rprec[2] = 0; + rassoc = (char *) MALLOC(maxrules*sizeof(char)); + if (rassoc == 0) no_space(); + rassoc[0] = TOKEN; + rassoc[1] = TOKEN; + rassoc[2] = TOKEN; +} + + +static void +expand_items(void) +{ + maxitems += 300; + pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); +} + + +static void +expand_rules(void) +{ + maxrules += 100; + plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = (char *) REALLOC(rassoc, maxrules*sizeof(char)); + if (rassoc == 0) no_space(); +} + + +static void +advance_to_start(void) +{ + int c; + bucket *bp; + char *s_cptr; + int s_lineno; + + for (;;) + { + c = nextc(); + if (c != '%') break; + s_cptr = cptr; + switch (keyword()) + { + case MARK: + no_grammar(); + + case TEXT: + copy_text(); + break; + + case START: + declare_start(); + break; + + default: + syntax_error(lineno, line, s_cptr); + } + } + + c = nextc(); + if (!isalpha(c) && c != '_' && c != '.' && c != '_') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (goal == 0) + { + if (bp->class == TERM) + terminal_start(bp->name); + goal = bp; + } + + s_lineno = lineno; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != ':') syntax_error(lineno, line, cptr); + start_rule(bp, s_lineno); + ++cptr; +} + + +static void +start_rule(bucket *bp, int s_lineno) +{ + if (bp->class == TERM) + terminal_lhs(s_lineno); + bp->class = NONTERM; + if (nrules >= maxrules) + expand_rules(); + plhs[nrules] = bp; + rprec[nrules] = UNDEFINED; + rassoc[nrules] = TOKEN; +} + + +static void +end_rule(void) +{ + int i; + + if (!last_was_action && plhs[nrules]->tag) + { + for (i = nitems - 1; pitem[i]; --i) continue; + if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag) + default_action_warning(); + } + + last_was_action = 0; + if (nitems >= maxitems) expand_items(); + pitem[nitems] = 0; + ++nitems; + ++nrules; +} + + +static void +insert_empty_rule(void) +{ + bucket *bp, **bpp; + + assert(cache); + sprintf(cache, "$$%d", ++gensym); + bp = make_bucket(cache); + last_symbol->next = bp; + last_symbol = bp; + bp->tag = plhs[nrules]->tag; + bp->class = NONTERM; + + if ((nitems += 2) > maxitems) + expand_items(); + bpp = pitem + nitems - 1; + *bpp-- = bp; + while ((bpp[0] = bpp[-1])) --bpp; + + if (++nrules >= maxrules) + expand_rules(); + plhs[nrules] = plhs[nrules-1]; + plhs[nrules-1] = bp; + rprec[nrules] = rprec[nrules-1]; + rprec[nrules-1] = 0; + rassoc[nrules] = rassoc[nrules-1]; + rassoc[nrules-1] = TOKEN; +} + + +static void +add_symbol(void) +{ + int c; + bucket *bp; + int s_lineno = lineno; + + c = *cptr; + if (c == '\'' || c == '"') + bp = get_literal(); + else + bp = get_name(); + + c = nextc(); + if (c == ':') + { + end_rule(); + start_rule(bp, s_lineno); + ++cptr; + return; + } + + if (last_was_action) + insert_empty_rule(); + last_was_action = 0; + + if (++nitems > maxitems) + expand_items(); + pitem[nitems-1] = bp; +} + + +static void +copy_action(void) +{ + int c; + int i, n; + int depth; + int quote; + char *tag; + FILE *f = action_file; + int a_lineno = lineno; + char *a_line = dup_line(); + char *a_cptr = a_line + (cptr - line); + + if (last_was_action) + insert_empty_rule(); + last_was_action = 1; + + fprintf(f, "case %d:\n", nrules - 2); + if (!lflag) + fprintf(f, line_format, lineno, input_file_name); + if (*cptr == '=') ++cptr; + + n = 0; + for (i = nitems - 1; pitem[i]; --i) ++n; + + depth = 0; +loop: + c = *cptr; + if (c == '$') + { + if (cptr[1] == '<') + { + int d_lineno = lineno; + char *d_line = dup_line(); + char *d_cptr = d_line + (cptr - line); + + ++cptr; + tag = get_tag(); + c = *cptr; + if (c == '$') + { + fprintf(f, "yyval.%s", tag); + ++cptr; + FREE(d_line); + goto loop; + } + else if (isdigit(c)) + { + i = get_number(); + if (i > n) dollar_warning(d_lineno, i); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + FREE(d_line); + goto loop; + } + else if (c == '-' && isdigit((int) cptr[1])) + { + ++cptr; + i = -get_number() - n; + fprintf(f, "yyvsp[%d].%s", i, tag); + FREE(d_line); + goto loop; + } + else + dollar_error(d_lineno, d_line, d_cptr); + } + else if (cptr[1] == '$') + { + if (ntags) + { + tag = plhs[nrules]->tag; + if (tag == 0) untyped_lhs(); + fprintf(f, "yyval.%s", tag); + } + else + fprintf(f, "yyval"); + cptr += 2; + goto loop; + } + else if (isdigit((int) cptr[1])) + { + ++cptr; + i = get_number(); + if (ntags) + { + if (i <= 0 || i > n) + unknown_rhs(i); + tag = pitem[nitems + i - n - 1]->tag; + if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]->name); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + } + else + { + if (i > n) + dollar_warning(lineno, i); + fprintf(f, "yyvsp[%d]", i - n); + } + goto loop; + } + else if (cptr[1] == '-') + { + cptr += 2; + i = get_number(); + if (ntags) + unknown_rhs(-i); + fprintf(f, "yyvsp[%d]", -i - n); + goto loop; + } + } + if (isalpha(c) || c == '_' || c == '$') + { + do + { + putc(c, f); + c = *++cptr; + } while (isalnum(c) || c == '_' || c == '$'); + goto loop; + } + putc(c, f); + ++cptr; + switch (c) + { + case '\n': + next_line: + get_line(); + if (line) goto loop; + unterminated_action(a_lineno, a_line, a_cptr); + + case ';': + if (depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + FREE(a_line); + return; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + FREE(a_line); + return; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +static int +mark_symbol(void) +{ + int c; + bucket *bp; + + c = cptr[1]; + if (c == '%' || c == '\\') + { + cptr += 2; + return (1); + } + + if (c == '=') + cptr += 2; + else if ((c == 'p' || c == 'P') && + ((c = cptr[2]) == 'r' || c == 'R') && + ((c = cptr[3]) == 'e' || c == 'E') && + ((c = cptr[4]) == 'c' || c == 'C') && + ((c = cptr[5], !IS_IDENT(c)))) + cptr += 5; + else + syntax_error(lineno, line, cptr); + + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + { + syntax_error(lineno, line, cptr); + /*NOTREACHED*/ + } + + if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules]) + prec_redeclared(); + + rprec[nrules] = bp->prec; + rassoc[nrules] = bp->assoc; + return (0); +} + + +static void +read_grammar(void) +{ + int c; + + initialize_grammar(); + advance_to_start(); + + for (;;) + { + c = nextc(); + if (c == EOF) break; + if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\'' || + c == '"') + add_symbol(); + else if (c == '{' || c == '=') + copy_action(); + else if (c == '|') + { + end_rule(); + start_rule(plhs[nrules-1], 0); + ++cptr; + } + else if (c == '%') + { + if (mark_symbol()) break; + } + else + syntax_error(lineno, line, cptr); + } + end_rule(); +} + + +static void +free_tags(void) +{ + int i; + + if (tag_table == 0) return; + + for (i = 0; i < ntags; ++i) + { + assert(tag_table[i]); + FREE(tag_table[i]); + } + FREE(tag_table); +} + + +static void +pack_names(void) +{ + bucket *bp; + char *p, *s, *t; + + name_pool_size = 13; /* 13 == sizeof("$end") + sizeof("$accept") */ + for (bp = first_symbol; bp; bp = bp->next) + name_pool_size += strlen(bp->name) + 1; + name_pool = MALLOC(name_pool_size); + if (name_pool == 0) no_space(); + + strcpy(name_pool, "$accept"); + strcpy(name_pool+8, "$end"); + t = name_pool + 13; + for (bp = first_symbol; bp; bp = bp->next) + { + p = t; + s = bp->name; + while ((*t++ = *s++)) continue; + FREE(bp->name); + bp->name = p; + } +} + + +static void +check_symbols(void) +{ + bucket *bp; + + if (goal->class == UNKNOWN) + undefined_goal(goal->name); + + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == UNKNOWN) + { + undefined_symbol_warning(bp->name); + bp->class = TERM; + } + } +} + + +static void +pack_symbols(void) +{ + bucket *bp; + bucket **v; + int i, j, k, n; + + nsyms = 2; + ntokens = 1; + for (bp = first_symbol; bp; bp = bp->next) + { + ++nsyms; + if (bp->class == TERM) ++ntokens; + } + start_symbol = ntokens; + nvars = nsyms - ntokens; + + symbol_name = (char **) MALLOC(nsyms*sizeof(char *)); + if (symbol_name == 0) no_space(); + symbol_value = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_value == 0) no_space(); + symbol_prec = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_prec == 0) no_space(); + symbol_assoc = MALLOC(nsyms); + if (symbol_assoc == 0) no_space(); + + v = (bucket **) MALLOC(nsyms*sizeof(bucket *)); + if (v == 0) no_space(); + + v[0] = 0; + v[start_symbol] = 0; + + i = 1; + j = start_symbol + 1; + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == TERM) + v[i++] = bp; + else + v[j++] = bp; + } + assert(i == ntokens && j == nsyms); + + for (i = 1; i < ntokens; ++i) + v[i]->index = i; + + goal->index = start_symbol + 1; + k = start_symbol + 2; + while (++i < nsyms) + if (v[i] != goal) + { + v[i]->index = k; + ++k; + } + + goal->value = 0; + k = 1; + for (i = start_symbol + 1; i < nsyms; ++i) + { + if (v[i] != goal) + { + v[i]->value = k; + ++k; + } + } + + k = 0; + for (i = 1; i < ntokens; ++i) + { + n = v[i]->value; + if (n > 256) + { + for (j = k++; j > 0 && symbol_value[j-1] > n; --j) + symbol_value[j] = symbol_value[j-1]; + symbol_value[j] = n; + } + } + + if (v[1]->value == UNDEFINED) + v[1]->value = 256; + + j = 0; + n = 257; + for (i = 2; i < ntokens; ++i) + { + if (v[i]->value == UNDEFINED) + { + while (j < k && n == symbol_value[j]) + { + while (++j < k && n == symbol_value[j]) continue; + ++n; + } + v[i]->value = n; + ++n; + } + } + + symbol_name[0] = name_pool + 8; + symbol_value[0] = 0; + symbol_prec[0] = 0; + symbol_assoc[0] = TOKEN; + for (i = 1; i < ntokens; ++i) + { + symbol_name[i] = v[i]->name; + symbol_value[i] = v[i]->value; + symbol_prec[i] = v[i]->prec; + symbol_assoc[i] = v[i]->assoc; + } + symbol_name[start_symbol] = name_pool; + symbol_value[start_symbol] = -1; + symbol_prec[start_symbol] = 0; + symbol_assoc[start_symbol] = TOKEN; + for (++i; i < nsyms; ++i) + { + k = v[i]->index; + symbol_name[k] = v[i]->name; + symbol_value[k] = v[i]->value; + symbol_prec[k] = v[i]->prec; + symbol_assoc[k] = v[i]->assoc; + } + + FREE(v); +} + + +static void +pack_grammar(void) +{ + int i, j; + int assoc, prec; + + ritem = (short *) MALLOC(nitems*sizeof(short)); + if (ritem == 0) no_space(); + rlhs = (short *) MALLOC(nrules*sizeof(short)); + if (rlhs == 0) no_space(); + rrhs = (short *) MALLOC((nrules+1)*sizeof(short)); + if (rrhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, nrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = REALLOC(rassoc, nrules); + if (rassoc == 0) no_space(); + + ritem[0] = -1; + ritem[1] = goal->index; + ritem[2] = 0; + ritem[3] = -2; + rlhs[0] = 0; + rlhs[1] = 0; + rlhs[2] = start_symbol; + rrhs[0] = 0; + rrhs[1] = 0; + rrhs[2] = 1; + + j = 4; + for (i = 3; i < nrules; ++i) + { + rlhs[i] = plhs[i]->index; + rrhs[i] = j; + assoc = TOKEN; + prec = 0; + while (pitem[j]) + { + ritem[j] = pitem[j]->index; + if (pitem[j]->class == TERM) + { + prec = pitem[j]->prec; + assoc = pitem[j]->assoc; + } + ++j; + } + ritem[j] = -i; + ++j; + if (rprec[i] == UNDEFINED) + { + rprec[i] = prec; + rassoc[i] = assoc; + } + } + rrhs[i] = j; + + FREE(plhs); + FREE(pitem); +} + + +static void +print_grammar(void) +{ + int i, j, k; + int spacing = 0; + FILE *f = verbose_file; + + if (!vflag) return; + + k = 1; + for (i = 2; i < nrules; ++i) + { + if (rlhs[i] != rlhs[i-1]) + { + if (i != 2) fprintf(f, "\n"); + fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]); + spacing = strlen(symbol_name[rlhs[i]]) + 1; + } + else + { + fprintf(f, "%4d ", i - 2); + j = spacing; + while (--j >= 0) putc(' ', f); + putc('|', f); + } + + while (ritem[k] >= 0) + { + fprintf(f, " %s", symbol_name[ritem[k]]); + ++k; + } + ++k; + putc('\n', f); + } +} + + +void +reader(void) +{ + write_section(banner); + create_symbol_table(); + read_declarations(); + read_grammar(); + free_symbol_table(); + free_tags(); + pack_names(); + check_symbols(); + pack_symbols(); + pack_grammar(); + free_symbols(); + print_grammar(); +} diff --git a/modules/libcom/src/yacc/skeleton.c b/modules/libcom/src/yacc/skeleton.c new file mode 100644 index 000000000..ab683c666 --- /dev/null +++ b/modules/libcom/src/yacc/skeleton.c @@ -0,0 +1,302 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +/* If the skeleton is changed, the banner should be changed so that */ +/* the altered version can be easily distinguished from the original. */ +/* */ +/* The #defines included with the banner are there because they are */ +/* useful in subsequent code. The macros #defined in the header or */ +/* the body either are not useful outside of semantic actions or */ +/* are conditional. */ + +char *banner[] = +{ + "#define YYBYACC 1", + "#define YYMAJOR 1", + "#define YYMINOR 9", + "#define yyclearin (yychar=(-1))", + "#define yyerrok (yyerrflag=0)", + "#define YYRECOVERING (yyerrflag!=0)", + "static int yyparse(void);",/* JRW */ + 0 +}; + + +char *tables[] = +{ + "static short yylhs[];", /* JRW */ + "static short yylen[];", /* JRW */ + "static short yydefred[];", /* JRW */ + "static short yydgoto[];", /* JRW */ + "static short yysindex[];", /* JRW */ + "static short yyrindex[];", /* JRW */ + "static short yygindex[];", /* JRW */ + "static short yytable[];", /* JRW */ + "static short yycheck[];", /* JRW */ + "#if YYDEBUG", + "static char *yyname[];", /* JRW */ + "static char *yyrule[];", /* JRW */ + "#endif", + 0 +}; + + +char *header[] = +{ + "#ifdef YYSTACKSIZE", + "#undef YYMAXDEPTH", + "#define YYMAXDEPTH YYSTACKSIZE", + "#else", + "#ifdef YYMAXDEPTH", + "#define YYSTACKSIZE YYMAXDEPTH", + "#else", + "#define YYSTACKSIZE 500", + "#define YYMAXDEPTH 500", + "#endif", + "#endif", + "#if YYDEBUG", /* MRK */ + "static int yydebug;", /* JRW */ + "#endif", /* MRK */ + "static int yynerrs;", /* JRW */ + "static int yyerrflag;", /* JRW */ + "static int yychar;", /* JRW */ + "static short *yyssp;", /* JRW */ + "static YYSTYPE *yyvsp;", /* JRW */ + "static YYSTYPE yyval;", /* JRW */ + "static YYSTYPE yylval;", /* JRW */ + "static short yyss[YYSTACKSIZE];", /* JRW */ + "static YYSTYPE yyvs[YYSTACKSIZE];", /* JRW */ + "#define yystacksize YYSTACKSIZE", + 0 +}; + + +char *body[] = +{ + "#define YYABORT goto yyabort", + "#define YYREJECT goto yyabort", + "#define YYACCEPT goto yyaccept", + "#define YYERROR goto yyerrlab", + "static int", /* JRW */ + "yyparse(void)", /* JRW */ + "{", + " int yym, yyn, yystate;", + "#if YYDEBUG", + " char *yys;", + " extern char *getenv();", + "", + " if ((yys = getenv(\"YYDEBUG\")))", + " {", + " yyn = *yys;", + " if (yyn >= '0' && yyn <= '9')", + " yydebug = yyn - '0';", + " }", + "#endif", + "", + " yynerrs = 0;", + " yyerrflag = 0;", + " yychar = (-1);", + "", + " yyssp = yyss;", + " yyvsp = yyvs;", + " *yyssp = yystate = 0;", + "", + "yyloop:", + " if ((yyn = yydefred[yystate])) goto yyreduce;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " }", + " if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, shifting to state %d\\n\",", + " YYPREFIX, yystate, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " yychar = (-1);", + " if (yyerrflag > 0) --yyerrflag;", + " goto yyloop;", + " }", + " if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + " yyn = yytable[yyn];", + " goto yyreduce;", + " }", + " if (yyerrflag) goto yyinrecovery;", + " yyerror(\"syntax error\");", + " ++yynerrs;", + "yyinrecovery:", + " if (yyerrflag < 3)", + " {", + " yyerrflag = 3;", + " for (;;)", + " {", + " if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, error recovery shifting\\", + " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " goto yyloop;", + " }", + " else", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: error recovery discarding state %d\ +\\n\",", + " YYPREFIX, *yyssp);", + "#endif", + " if (yyssp <= yyss) goto yyabort;", + " --yyssp;", + " --yyvsp;", + " }", + " }", + " }", + " else", + " {", + " if (yychar == 0) goto yyabort;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, error recovery discards token %d\ + (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " yychar = (-1);", + " goto yyloop;", + " }", + "yyreduce:", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",", + " YYPREFIX, yystate, yyn, yyrule[yyn]);", + "#endif", + " yym = yylen[yyn];", + " yyval = yyvsp[1-yym];", + " switch (yyn)", + " {", + 0 +}; + + +char *trailer[] = +{ + " }", + " yyssp -= yym;", + " yystate = *yyssp;", + " yyvsp -= yym;", + " yym = yylhs[yyn];", + " if (yystate == 0 && yym == 0)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state 0 to\\", + " state %d\\n\", YYPREFIX, YYFINAL);", + "#endif", + " yystate = YYFINAL;", + " *++yyssp = YYFINAL;", + " *++yyvsp = yyval;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, YYFINAL, yychar, yys);", + " }", + "#endif", + " }", + " if (yychar == 0) goto yyaccept;", + " goto yyloop;", + " }", + " if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yystate)", + " yystate = yytable[yyn];", + " else", + " yystate = yydgoto[yym];", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state %d \\", + "to state %d\\n\", YYPREFIX, *yyssp, yystate);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate;", + " *++yyvsp = yyval;", + " goto yyloop;", + "yyoverflow:", + " yyerror(\"yacc stack overflow\");", + "yyabort:", + " return (1);", + "yyaccept:", + " return (0);", + "}", + 0 +}; + + +void +write_section(char *section[]) +{ + int c; + int i; + char *s; + FILE *f; + + f = code_file; + for (i = 0; (s = section[i]); ++i) + { + ++outline; + while ((c = *s)) + { + putc(c, f); + ++s; + } + putc('\n', f); + } +} diff --git a/modules/libcom/src/yacc/symtab.c b/modules/libcom/src/yacc/symtab.c new file mode 100644 index 000000000..486835a21 --- /dev/null +++ b/modules/libcom/src/yacc/symtab.c @@ -0,0 +1,126 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + + +/* TABLE_SIZE is the number of entries in the symbol table. */ +/* TABLE_SIZE must be a power of two. */ + +#define TABLE_SIZE 1024 + + +bucket **symbol_table; +bucket *first_symbol; +bucket *last_symbol; + + +static int +hash(char *name) +{ + char *s; + int c, k; + + assert(name && *name); + s = name; + k = *s; + while ((c = *++s)) + k = (31*k + c) & (TABLE_SIZE - 1); + + return (k); +} + + +bucket * +make_bucket(char *name) +{ + bucket *bp; + + assert(name); + bp = (bucket *) MALLOC(sizeof(bucket)); + if (bp == 0) no_space(); + bp->link = 0; + bp->next = 0; + bp->name = MALLOC(strlen(name) + 1); + if (bp->name == 0) no_space(); + bp->tag = 0; + bp->value = UNDEFINED; + bp->index = 0; + bp->prec = 0; + bp-> class = UNKNOWN; + bp->assoc = TOKEN; + + if (bp->name == 0) no_space(); + strcpy(bp->name, name); + + return (bp); +} + + +bucket * +lookup(char *name) +{ + bucket *bp, **bpp; + + bpp = symbol_table + hash(name); + bp = *bpp; + + while (bp) + { + if (strcmp(name, bp->name) == 0) return (bp); + bpp = &bp->link; + bp = *bpp; + } + + *bpp = bp = make_bucket(name); + last_symbol->next = bp; + last_symbol = bp; + + return (bp); +} + +void +create_symbol_table(void) +{ + int i; + bucket *bp; + + symbol_table = (bucket **) MALLOC(TABLE_SIZE*sizeof(bucket *)); + if (symbol_table == 0) no_space(); + for (i = 0; i < TABLE_SIZE; i++) + symbol_table[i] = 0; + + bp = make_bucket("error"); + bp->index = 1; + bp->class = TERM; + + first_symbol = bp; + last_symbol = bp; + symbol_table[hash("error")] = bp; +} + + +void +free_symbol_table(void) +{ + FREE(symbol_table); + symbol_table = 0; +} + + +void +free_symbols(void) +{ + bucket *p, *q; + + for (p = first_symbol; p; p = q) + { + q = p->next; + FREE(p); + } +} diff --git a/modules/libcom/src/yacc/verbose.c b/modules/libcom/src/yacc/verbose.c new file mode 100644 index 000000000..eb03cf386 --- /dev/null +++ b/modules/libcom/src/yacc/verbose.c @@ -0,0 +1,349 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +static short *null_rules; + +static void log_unused(void); +static void log_conflicts(void); +static void print_state(int state); +static void print_conflicts(int state); +static void print_core(int state); +static void print_nulls(int state); +static void print_actions(int state); +static void print_shifts(action *p); +static void print_reductions(action *p, int defred); +static void print_gotos(int stateno); + + +void +verbose(void) +{ + int i; + + if (!vflag) return; + + null_rules = (short *) MALLOC(nrules*sizeof(short)); + if (null_rules == 0) no_space(); + fprintf(verbose_file, "\f\n"); + for (i = 0; i < nstates; i++) + print_state(i); + FREE(null_rules); + + if (nunused) + log_unused(); + if (SRtotal || RRtotal) + log_conflicts(); + + fprintf(verbose_file, "\n\n%d terminals, %d nonterminals\n", ntokens, nvars); + fprintf(verbose_file, "%d grammar rules, %d states\n", nrules - 2, nstates); +} + + +static void +log_unused(void) +{ + int i; + short *p; + + fprintf(verbose_file, "\n\nRules never reduced:\n"); + for (i = 3; i < nrules; ++i) + { + if (!rules_used[i]) + { + fprintf(verbose_file, "\t%s :", symbol_name[rlhs[i]]); + for (p = ritem + rrhs[i]; *p >= 0; ++p) + fprintf(verbose_file, " %s", symbol_name[*p]); + fprintf(verbose_file, " (%d)\n", i - 2); + } + } +} + + +static void +log_conflicts(void) +{ + int i; + + fprintf(verbose_file, "\n\n"); + for (i = 0; i < nstates; i++) + { + if (SRconflicts[i] || RRconflicts[i]) + { + fprintf(verbose_file, "State %d contains ", i); + if (SRconflicts[i] == 1) + fprintf(verbose_file, "1 shift/reduce conflict"); + else if (SRconflicts[i] > 1) + fprintf(verbose_file, "%d shift/reduce conflicts", + SRconflicts[i]); + if (SRconflicts[i] && RRconflicts[i]) + fprintf(verbose_file, ", "); + if (RRconflicts[i] == 1) + fprintf(verbose_file, "1 reduce/reduce conflict"); + else if (RRconflicts[i] > 1) + fprintf(verbose_file, "%d reduce/reduce conflicts", + RRconflicts[i]); + fprintf(verbose_file, ".\n"); + } + } +} + + +static void +print_state(int state) +{ + if (state) + fprintf(verbose_file, "\n\n"); + if (SRconflicts[state] || RRconflicts[state]) + print_conflicts(state); + fprintf(verbose_file, "state %d\n", state); + print_core(state); + print_nulls(state); + print_actions(state); +} + + +static void +print_conflicts(int state) +{ + int symbol, act = 0, number = 0; + action *p; + + symbol = -1; + for (p = parser[state]; p; p = p->next) + { + if (p->suppressed == 2) + continue; + + if (p->symbol != symbol) + { + symbol = p->symbol; + number = p->number; + if (p->action_code == SHIFT) + act = SHIFT; + else + act = REDUCE; + } + else if (p->suppressed == 1) + { + if (state == final_state && symbol == 0) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(accept, reduce %d) on $end\n", state, p->number - 2); + } + else + { + if (act == SHIFT) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(shift %d, reduce %d) on %s\n", state, number, p->number - 2, + symbol_name[symbol]); + } + else + { + fprintf(verbose_file, "%d: reduce/reduce conflict \ +(reduce %d, reduce %d) on %s\n", state, number - 2, p->number - 2, + symbol_name[symbol]); + } + } + } + } +} + + +static void +print_core(int state) +{ + int i; + int k; + int rule; + core *statep; + short *sp; + short *sp1; + + statep = state_table[state]; + k = statep->nitems; + + for (i = 0; i < k; i++) + { + sp1 = sp = ritem + statep->items[i]; + + while (*sp >= 0) ++sp; + rule = -(*sp); + fprintf(verbose_file, "\t%s : ", symbol_name[rlhs[rule]]); + + for (sp = ritem + rrhs[rule]; sp < sp1; sp++) + fprintf(verbose_file, "%s ", symbol_name[*sp]); + + putc('.', verbose_file); + + while (*sp >= 0) + { + fprintf(verbose_file, " %s", symbol_name[*sp]); + sp++; + } + fprintf(verbose_file, " (%d)\n", -2 - *sp); + } +} + + +static void +print_nulls(int state) +{ + action *p; + int i, j, k, nnulls; + + nnulls = 0; + for (p = parser[state]; p; p = p->next) + { + if (p->action_code == REDUCE && + (p->suppressed == 0 || p->suppressed == 1)) + { + i = p->number; + if (rrhs[i] + 1 == rrhs[i+1]) + { + for (j = 0; j < nnulls && i > null_rules[j]; ++j) + continue; + + if (j == nnulls) + { + ++nnulls; + null_rules[j] = i; + } + else if (i != null_rules[j]) + { + ++nnulls; + for (k = nnulls - 1; k > j; --k) + null_rules[k] = null_rules[k-1]; + null_rules[j] = i; + } + } + } + } + + for (i = 0; i < nnulls; ++i) + { + j = null_rules[i]; + fprintf(verbose_file, "\t%s : . (%d)\n", symbol_name[rlhs[j]], + j - 2); + } + fprintf(verbose_file, "\n"); +} + + +static void +print_actions(int stateno) +{ + action *p; + shifts *sp; + int as; + + if (stateno == final_state) + fprintf(verbose_file, "\t$end accept\n"); + + p = parser[stateno]; + if (p) + { + print_shifts(p); + print_reductions(p, defred[stateno]); + } + + sp = shift_table[stateno]; + if (sp && sp->nshifts > 0) + { + as = accessing_symbol[sp->shift[sp->nshifts - 1]]; + if (ISVAR(as)) + print_gotos(stateno); + } +} + + +static void +print_shifts(action *p) +{ + int count; + action *q; + + count = 0; + for (q = p; q; q = q->next) + { + if (q->suppressed < 2 && q->action_code == SHIFT) + ++count; + } + + if (count > 0) + { + for (; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + fprintf(verbose_file, "\t%s shift %d\n", + symbol_name[p->symbol], p->number); + } + } +} + + +static void +print_reductions(action *p, int defred) +{ + int k, anyreds; + action *q; + + anyreds = 0; + for (q = p; q ; q = q->next) + { + if (q->action_code == REDUCE && q->suppressed < 2) + { + anyreds = 1; + break; + } + } + + if (anyreds == 0) + fprintf(verbose_file, "\t. error\n"); + else + { + for (; p; p = p->next) + { + if (p->action_code == REDUCE && p->number != defred) + { + k = p->number - 2; + if (p->suppressed == 0) + fprintf(verbose_file, "\t%s reduce %d\n", + symbol_name[p->symbol], k); + } + } + + if (defred > 0) + fprintf(verbose_file, "\t. reduce %d\n", defred - 2); + } +} + + +static void +print_gotos(int stateno) +{ + int i, k; + int as; + short *to_state; + shifts *sp; + + putc('\n', verbose_file); + sp = shift_table[stateno]; + to_state = sp->shift; + for (i = 0; i < sp->nshifts; ++i) + { + k = to_state[i]; + as = accessing_symbol[k]; + if (ISVAR(as)) + fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k); + } +} + diff --git a/modules/libcom/src/yacc/warshall.c b/modules/libcom/src/yacc/warshall.c new file mode 100644 index 000000000..4477074c5 --- /dev/null +++ b/modules/libcom/src/yacc/warshall.c @@ -0,0 +1,88 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +static void +transitive_closure(unsigned int *R, int n) +{ + int rowsize; + unsigned i; + unsigned *rowj; + unsigned *rp; + unsigned *rend; + unsigned *ccol; + unsigned *relend; + unsigned *cword; + unsigned *rowi; + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + cword = R; + i = 0; + rowi = R; + while (rowi < relend) + { + ccol = cword; + rowj = R; + + while (rowj < relend) + { + if (*ccol & (1 << i)) + { + rp = rowi; + rend = rowj + rowsize; + while (rowj < rend) + *rowj++ |= *rp++; + } + else + { + rowj += rowsize; + } + + ccol += rowsize; + } + + if (++i >= BITS_PER_WORD) + { + i = 0; + cword++; + } + + rowi += rowsize; + } +} + +void +reflexive_transitive_closure(unsigned int *R, int n) +{ + int rowsize; + unsigned i; + unsigned *rp; + unsigned *relend; + + transitive_closure(R, n); + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + i = 0; + rp = R; + while (rp < relend) + { + *rp |= (1 << i); + if (++i >= BITS_PER_WORD) + { + i = 0; + rp++; + } + + rp += rowsize; + } +} diff --git a/modules/libcom/src/yacc/yacc.html b/modules/libcom/src/yacc/yacc.html new file mode 100644 index 000000000..1258b439f --- /dev/null +++ b/modules/libcom/src/yacc/yacc.html @@ -0,0 +1,135 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + + +
+
+
+
+

NAME

+     Yacc - an LALR(1) parser generator
+
+
+
+

SYNOPSIS

+     yacc [ -dlrtv ] [ -b file_prefix  ]  [  -p  symbol_prefix  ]
+     filename
+
+
+
+

DESCRIPTION

+     Yacc reads the grammar specification in  the  file  filename
+     and  generates  an LR(1) parser for it.  The parsers consist
+     of a set of LALR(1) parsing  tables  and  a  driver  routine
+     written in the C programming language.  Yacc normally writes
+     the parse tables and the driver routine to the file y.tab.c.
+
+     The following options are available:
+
+          -b file_prefix
+               The -b option changes the prefix prepended to  the
+               output   file  names  to  the  string  denoted  by
+               file_prefix.  The default prefix is the  character
+               y.
+
+          -d   The -d option causes the header file y.tab.h to be
+               written.
+
+          -l   If the -l  option  is  not  specified,  yacc  will
+               insert  #line  directives  in  the generated code.
+               The #line directives let  the  C  compiler  relate
+               errors  in the generated code to the user's origi-
+               nal code.  If the -l  option  is  specified,  yacc
+               will  not  insert  the  #line  directives.   #line
+               directives specified by the user will be retained.
+
+          -p symbol_prefix
+               The -p option  changes  the  prefix  prepended  to
+               yacc-generated  symbols  to  the string denoted by
+               symbol_prefix.  The default prefix is  the  string
+               yy.
+
+          -r   The -r option  causes  yacc  to  produce  separate
+               files for code and tables.  The code file is named
+               y.code.c, and the tables file is named y.tab.c.
+
+          -t   The -t option changes the preprocessor  directives
+               generated  by  yacc  so  that debugging statements
+               will be incorporated in the compiled code.
+
+          -v   The -v option causes a human-readable  description
+               of  the generated parser to be written to the file
+               y.output.
+
+
+     If the  environment  variable  TMPDIR  is  set,  the  string
+     denoted  by TMPDIR will be used as the name of the directory
+     where the temporary files are created.
+
+
+
+

FILES

+     y.code.c
+     y.tab.c
+     y.tab.h
+     y.output
+     /tmp/yacc.aXXXXXX
+     /tmp/yacc.tXXXXXX
+     /tmp/yacc.uXXXXXX
+
+
+
+

DIAGNOSTICS

+     If there are rules that are never  reduced,  the  number  of
+     such  rules is reported on standard error.  If there are any
+     LALR(1) conflicts, the number of conflicts  is  reported  on
+     standard error.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Man(1) output converted with +man2html +
+ + diff --git a/modules/libcom/src/yajl/Makefile b/modules/libcom/src/yajl/Makefile new file mode 100644 index 000000000..39146ca79 --- /dev/null +++ b/modules/libcom/src/yajl/Makefile @@ -0,0 +1,28 @@ +#************************************************************************* +# Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/yacc + +# Yet Another JSON Library +SRC_DIRS += $(LIBCOM)/yajl +INC += yajl_alloc.h +INC += yajl_common.h +INC += yajl_gen.h +INC += yajl_parse.h +# The other yajl_*.h files are for internal use only + +Com_SRCS += yajl.c +Com_SRCS += yajl_alloc.c +Com_SRCS += yajl_buf.c +Com_SRCS += yajl_encode.c +Com_SRCS += yajl_gen.c +Com_SRCS += yajl_lex.c +Com_SRCS += yajl_parser.c diff --git a/modules/libcom/src/yajl/RULES b/modules/libcom/src/yajl/RULES new file mode 100644 index 000000000..9cd228cbe --- /dev/null +++ b/modules/libcom/src/yajl/RULES @@ -0,0 +1,17 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +# Ensure epicsVersion.h gets built first +yajl$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_alloc$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_buf$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_encode$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_gen$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_lex$(DEP): $(COMMON_DIR)/epicsVersion.h +yajl_parser$(DEP): $(COMMON_DIR)/epicsVersion.h diff --git a/modules/libcom/src/yajl/yajl.c b/modules/libcom/src/yajl/yajl.c new file mode 100644 index 000000000..6c4977598 --- /dev/null +++ b/modules/libcom/src/yajl/yajl.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_alloc.h" + +const char * +yajl_status_to_string(yajl_status stat) +{ + const char * statStr = "unknown"; + switch (stat) { + case yajl_status_ok: + statStr = "ok, no error"; + break; + case yajl_status_client_canceled: + statStr = "client canceled parse"; + break; + case yajl_status_error: + statStr = "parse error"; + break; + } + return statStr; +} + +yajl_handle +yajl_alloc(const yajl_callbacks * callbacks, + yajl_alloc_funcs * afs, + void * ctx) +{ + yajl_handle hand = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); + if (hand == NULL) { + return NULL; + } + + /* copy in pointers to allocation routines */ + memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + hand->callbacks = callbacks; + hand->ctx = ctx; + hand->lexer = NULL; + hand->bytesConsumed = 0; + hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); + hand->flags = 0; + yajl_bs_init(hand->stateStack, &(hand->alloc)); + yajl_bs_push(hand->stateStack, yajl_state_start); + + return hand; +} + +int +yajl_config(yajl_handle h, yajl_option opt, ...) +{ + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_allow_comments: + case yajl_dont_validate_strings: + case yajl_allow_trailing_garbage: + case yajl_allow_multiple_values: + case yajl_allow_partial_values: + if (va_arg(ap, int)) h->flags |= opt; + else h->flags &= ~opt; + break; + default: + rv = 0; + } + va_end(ap); + + return rv; +} + +void +yajl_free(yajl_handle handle) +{ + yajl_bs_free(handle->stateStack); + yajl_buf_free(handle->decodeBuf); + if (handle->lexer) { + yajl_lex_free(handle->lexer); + handle->lexer = NULL; + } + YA_FREE(&(handle->alloc), handle); +} + +yajl_status +yajl_parse(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen) +{ + yajl_status status; + + /* lazy allocation of the lexer */ + if (hand->lexer == NULL) { + hand->lexer = yajl_lex_alloc(&(hand->alloc), + hand->flags & yajl_allow_comments, + !(hand->flags & yajl_dont_validate_strings)); + } + if (hand->lexer == NULL) { + return yajl_status_error; + } + + status = yajl_do_parse(hand, jsonText, jsonTextLen); + return status; +} + + +yajl_status +yajl_complete_parse(yajl_handle hand) +{ + /* The lexer is lazy allocated in the first call to parse. if parse is + * never called, then no data was provided to parse at all. This is a + * "premature EOF" error unless yajl_allow_partial_values is specified. + * allocating the lexer now is the simplest possible way to handle this + * case while preserving all the other semantics of the parser + * (multiple values, partial values, etc). */ + if (hand->lexer == NULL) { + hand->lexer = yajl_lex_alloc(&(hand->alloc), + hand->flags & yajl_allow_comments, + !(hand->flags & yajl_dont_validate_strings)); + } + + return yajl_do_finish(hand); +} + +unsigned char * +yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, size_t jsonTextLen) +{ + return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); +} + +size_t +yajl_get_bytes_consumed(yajl_handle hand) +{ + if (!hand) return 0; + else return hand->bytesConsumed; +} + + +void +yajl_free_error(yajl_handle hand, unsigned char * str) +{ + /* use memory allocation functions if set */ + YA_FREE(&(hand->alloc), str); +} + +/* XXX: add utility routines to parse from file */ diff --git a/modules/libcom/src/yajl/yajl_alloc.c b/modules/libcom/src/yajl/yajl_alloc.c new file mode 100644 index 000000000..5b2601685 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_alloc.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#include + +#define epicsExportSharedSymbols +#include "yajl_alloc.h" + +static void * yajl_internal_malloc(void *ctx, size_t sz) +{ + return malloc(sz); +} + +static void * yajl_internal_realloc(void *ctx, void * previous, + size_t sz) +{ + return realloc(previous, sz); +} + +static void yajl_internal_free(void *ctx, void * ptr) +{ + free(ptr); +} + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) +{ + yaf->malloc = yajl_internal_malloc; + yaf->free = yajl_internal_free; + yaf->realloc = yajl_internal_realloc; + yaf->ctx = NULL; +} + diff --git a/modules/libcom/src/yajl/yajl_alloc.h b/modules/libcom/src/yajl/yajl_alloc.h new file mode 100644 index 000000000..471f76f66 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_alloc.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#ifndef __YAJL_ALLOC_H__ +#define __YAJL_ALLOC_H__ + +#include "yajl_common.h" + +#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) +#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) +#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) + +YAJL_API void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); + +#endif diff --git a/modules/libcom/src/yajl/yajl_buf.c b/modules/libcom/src/yajl/yajl_buf.c new file mode 100644 index 000000000..0f9c28046 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_buf.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_buf.h" + +#define YAJL_BUF_INIT_SIZE 2048 + +struct yajl_buf_t { + size_t len; + size_t used; + unsigned char * data; + yajl_alloc_funcs * alloc; +}; + +static +void yajl_buf_ensure_available(yajl_buf buf, size_t want) +{ + size_t need; + + assert(buf != NULL); + + /* first call */ + if (buf->data == NULL) { + buf->len = YAJL_BUF_INIT_SIZE; + buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); + buf->data[0] = 0; + } + + need = buf->len; + + while (want >= (need - buf->used)) need <<= 1; + + if (need != buf->len) { + buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); + buf->len = need; + } +} + +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) +{ + yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); + if (b == NULL) { + return NULL; + } + + memset((void *) b, 0, sizeof(struct yajl_buf_t)); + b->alloc = alloc; + return b; +} + +void yajl_buf_free(yajl_buf buf) +{ + assert(buf != NULL); + if (buf->data) YA_FREE(buf->alloc, buf->data); + YA_FREE(buf->alloc, buf); +} + +void yajl_buf_append(yajl_buf buf, const void * data, size_t len) +{ + yajl_buf_ensure_available(buf, len); + if (len > 0) { + assert(data != NULL); + memcpy(buf->data + buf->used, data, len); + buf->used += len; + buf->data[buf->used] = 0; + } +} + +void yajl_buf_clear(yajl_buf buf) +{ + buf->used = 0; + if (buf->data) buf->data[buf->used] = 0; +} + +const unsigned char * yajl_buf_data(yajl_buf buf) +{ + return buf->data; +} + +size_t yajl_buf_len(yajl_buf buf) +{ + return buf->used; +} + +void +yajl_buf_truncate(yajl_buf buf, size_t len) +{ + assert(len <= buf->used); + buf->used = len; +} diff --git a/modules/libcom/src/yajl/yajl_buf.h b/modules/libcom/src/yajl/yajl_buf.h new file mode 100644 index 000000000..0ae73aa35 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_buf.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_BUF_H__ +#define __YAJL_BUF_H__ + +#include "yajl_common.h" +#include "yajl_alloc.h" + +/* + * Implementation/performance notes. If this were moved to a header + * only implementation using #define's where possible we might be + * able to sqeeze a little performance out of the guy by killing function + * call overhead. YMMV. + */ + +/** + * yajl_buf is a buffer with exponential growth. the buffer ensures that + * you are always null padded. + */ +typedef struct yajl_buf_t * yajl_buf; + +/* allocate a new buffer */ +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); + +/* free the buffer */ +void yajl_buf_free(yajl_buf buf); + +/* append a number of bytes to the buffer */ +void yajl_buf_append(yajl_buf buf, const void * data, size_t len); + +/* empty the buffer */ +void yajl_buf_clear(yajl_buf buf); + +/* get a pointer to the beginning of the buffer */ +const unsigned char * yajl_buf_data(yajl_buf buf); + +/* get the length of the buffer */ +size_t yajl_buf_len(yajl_buf buf); + +/* truncate the buffer */ +void yajl_buf_truncate(yajl_buf buf, size_t len); + +#endif diff --git a/modules/libcom/src/yajl/yajl_bytestack.h b/modules/libcom/src/yajl/yajl_bytestack.h new file mode 100644 index 000000000..d75e456b4 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_bytestack.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * A header only implementation of a simple stack of bytes, used in YAJL + * to maintain parse state. + */ + +#ifndef __YAJL_BYTESTACK_H__ +#define __YAJL_BYTESTACK_H__ + +#include "yajl_common.h" + +#define YAJL_BS_INC 128 + +typedef struct yajl_bytestack_t +{ + unsigned char * stack; + size_t size; + size_t used; + yajl_alloc_funcs * yaf; +} yajl_bytestack; + +/* initialize a bytestack */ +#define yajl_bs_init(obs, _yaf) { \ + (obs).stack = NULL; \ + (obs).size = 0; \ + (obs).used = 0; \ + (obs).yaf = (_yaf); \ + } \ + + +/* initialize a bytestack */ +#define yajl_bs_free(obs) \ + if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); + +#define yajl_bs_current(obs) \ + (assert((obs).used > 0), (obs).stack[(obs).used - 1]) + +#define yajl_bs_push(obs, byte) { \ + if (((obs).size - (obs).used) == 0) { \ + (obs).size += YAJL_BS_INC; \ + (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ + (void *) (obs).stack, (obs).size);\ + } \ + (obs).stack[((obs).used)++] = (byte); \ +} + +/* removes the top item of the stack, returns nothing */ +#define yajl_bs_pop(obs) { ((obs).used)--; } + +#define yajl_bs_set(obs, byte) \ + (obs).stack[((obs).used) - 1] = (byte); + + +#endif diff --git a/modules/libcom/src/yajl/yajl_common.h b/modules/libcom/src/yajl/yajl_common.h new file mode 100644 index 000000000..4bc63eead --- /dev/null +++ b/modules/libcom/src/yajl/yajl_common.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_COMMON_H__ +#define __YAJL_COMMON_H__ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** YAJL API history in brief + * + * Originally macro not defined + * YAJL 1.0.12 + * Bundled with EPICS Base 3.15.0.1 + * + * YAJL 2.1.0 + * Changes argument type for yajl_integer() from 'int' to 'long long' + * Changes argument type for yajl_string() and yajl_map_key() from 'unsigned' to 'size_t' + * Replacement of struct yajl_parser_config with yajl_config() + * Replacement of yajl_parse_complete() with yajl_complete_parse() + */ +#define EPICS_YAJL_VERSION VERSION_INT(2,1,0,0) + +#define YAJL_MAX_DEPTH 128 + +#define YAJL_API epicsShareFunc + +/** pointer to a malloc function, supporting client overriding memory + * allocation routines */ +typedef void * (*yajl_malloc_func)(void *ctx, size_t sz); + +/** pointer to a free function, supporting client overriding memory + * allocation routines */ +typedef void (*yajl_free_func)(void *ctx, void * ptr); + +/** pointer to a realloc function which can resize an allocation. */ +typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz); + +/** A structure which can be passed to yajl_*_alloc routines to allow the + * client to specify memory allocation functions to be used. */ +typedef struct +{ + /** pointer to a function that can allocate uninitialized memory */ + yajl_malloc_func malloc; + /** pointer to a function that can resize memory allocations */ + yajl_realloc_func realloc; + /** pointer to a function that can free memory allocated using + * reallocFunction or mallocFunction */ + yajl_free_func free; + /** a context pointer that will be passed to above allocation routines */ + void * ctx; +} yajl_alloc_funcs; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/yajl/yajl_encode.c b/modules/libcom/src/yajl/yajl_encode.c new file mode 100644 index 000000000..980021ee5 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_encode.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_encode.h" + +static void CharToHex(unsigned char c, char * hexBuf) +{ + const char * hexchar = "0123456789ABCDEF"; + hexBuf[0] = hexchar[c >> 4]; + hexBuf[1] = hexchar[c & 0x0F]; +} + +void +yajl_string_encode(const yajl_print_t print, + void * ctx, + const unsigned char * str, + size_t len, + int escape_solidus) +{ + size_t beg = 0; + size_t end = 0; + char hexBuf[7]; + hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; + hexBuf[6] = 0; + + while (end < len) { + const char * escaped = NULL; + switch (str[end]) { + case '\r': escaped = "\\r"; break; + case '\n': escaped = "\\n"; break; + case '\\': escaped = "\\\\"; break; + /* it is not required to escape a solidus in JSON: + * read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt + * specifically, this production from the grammar: + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + */ + case '/': if (escape_solidus) escaped = "\\/"; break; + case '"': escaped = "\\\""; break; + case '\f': escaped = "\\f"; break; + case '\b': escaped = "\\b"; break; + case '\t': escaped = "\\t"; break; + default: + if ((unsigned char) str[end] < 32) { + CharToHex(str[end], hexBuf + 4); + escaped = hexBuf; + } + break; + } + if (escaped != NULL) { + print(ctx, (const char *) (str + beg), end - beg); + print(ctx, escaped, (unsigned int)strlen(escaped)); + beg = ++end; + } else { + ++end; + } + } + print(ctx, (const char *) (str + beg), end - beg); +} + +static void hexToDigit(unsigned int * val, const unsigned char * hex) +{ + unsigned int i; + for (i=0;i<4;i++) { + unsigned char c = hex[i]; + if (c >= 'A') c = (c & ~0x20) - 7; + c -= '0'; + assert(!(c & 0xF0)); + *val = (*val << 4) | c; + } +} + +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) +{ + if (codepoint < 0x80) { + utf8Buf[0] = (char) codepoint; + utf8Buf[1] = 0; + } else if (codepoint < 0x0800) { + utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); + utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[2] = 0; + } else if (codepoint < 0x10000) { + utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); + utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[3] = 0; + } else if (codepoint < 0x200000) { + utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); + utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); + utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); + utf8Buf[4] = 0; + } else { + utf8Buf[0] = '?'; + utf8Buf[1] = 0; + } +} + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + size_t len) +{ + size_t beg = 0; + size_t end = 0; + + while (end < len) { + if (str[end] == '\\') { + char utf8Buf[5]; + const char * unescaped = "?"; + yajl_buf_append(buf, str + beg, end - beg); + switch (str[++end]) { + case 'r': unescaped = "\r"; break; + case 'n': unescaped = "\n"; break; + case '\\': unescaped = "\\"; break; + case '/': unescaped = "/"; break; + case '"': unescaped = "\""; break; + case 'f': unescaped = "\f"; break; + case 'b': unescaped = "\b"; break; + case 't': unescaped = "\t"; break; + case 'u': { + unsigned int codepoint = 0; + hexToDigit(&codepoint, str + ++end); + end+=3; + /* check if this is a surrogate */ + if ((codepoint & 0xFC00) == 0xD800) { + end++; + if (str[end] == '\\' && str[end + 1] == 'u') { + unsigned int surrogate = 0; + hexToDigit(&surrogate, str + end + 2); + codepoint = + (((codepoint & 0x3F) << 10) | + ((((codepoint >> 6) & 0xF) + 1) << 16) | + (surrogate & 0x3FF)); + end += 5; + } else { + unescaped = "?"; + break; + } + } + + Utf32toUtf8(codepoint, utf8Buf); + unescaped = utf8Buf; + + if (codepoint == 0) { + yajl_buf_append(buf, unescaped, 1); + beg = ++end; + continue; + } + + break; + } + default: + assert("this should never happen" == NULL); + } + yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped)); + beg = ++end; + } else { + end++; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} + +#define ADV_PTR s++; if (!(len--)) return 0; + +int yajl_string_validate_utf8(const unsigned char * s, size_t len) +{ + if (!len) return 1; + if (!s) return 0; + + while (len--) { + /* single byte */ + if (*s <= 0x7f) { + /* noop */ + } + /* two byte */ + else if ((*s >> 5) == 0x6) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } + /* three byte */ + else if ((*s >> 4) == 0x0e) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } + /* four byte */ + else if ((*s >> 3) == 0x1e) { + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + ADV_PTR; + if (!((*s >> 6) == 0x2)) return 0; + } else { + return 0; + } + + s++; + } + + return 1; +} diff --git a/modules/libcom/src/yajl/yajl_encode.h b/modules/libcom/src/yajl/yajl_encode.h new file mode 100644 index 000000000..28d63958c --- /dev/null +++ b/modules/libcom/src/yajl/yajl_encode.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_ENCODE_H__ +#define __YAJL_ENCODE_H__ + +#include "yajl_buf.h" +#include "yajl_gen.h" + +void yajl_string_encode(const yajl_print_t printer, + void * ctx, + const unsigned char * str, + size_t length, + int escape_solidus); + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + size_t length); + +int yajl_string_validate_utf8(const unsigned char * s, size_t len); + +#endif diff --git a/modules/libcom/src/yajl/yajl_gen.c b/modules/libcom/src/yajl/yajl_gen.c new file mode 100644 index 000000000..7f669247d --- /dev/null +++ b/modules/libcom/src/yajl/yajl_gen.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "yajl_gen.h" +#include "yajl_buf.h" +#include "yajl_encode.h" + +typedef enum { + yajl_gen_start, + yajl_gen_map_start, + yajl_gen_map_key, + yajl_gen_map_val, + yajl_gen_array_start, + yajl_gen_in_array, + yajl_gen_complete, + yajl_gen_error +} yajl_gen_state; + +struct yajl_gen_t +{ + unsigned int flags; + unsigned int depth; + const char * indentString; + yajl_gen_state state[YAJL_MAX_DEPTH]; + yajl_print_t print; + void * ctx; /* yajl_buf */ + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +int +yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...) +{ + int rv = 1; + va_list ap; + va_start(ap, opt); + + switch(opt) { + case yajl_gen_beautify: + case yajl_gen_validate_utf8: + if (va_arg(ap, int)) g->flags |= opt; + else g->flags &= ~opt; + break; + case yajl_gen_indent_string: { + const char *indent = va_arg(ap, const char *); + g->indentString = indent; + for (; *indent; indent++) { + if (*indent != '\n' + && *indent != '\v' + && *indent != '\f' + && *indent != '\t' + && *indent != '\r' + && *indent != ' ') + { + g->indentString = NULL; + rv = 0; + } + } + break; + } + case yajl_gen_print_callback: + yajl_buf_free(g->ctx); + g->print = va_arg(ap, const yajl_print_t); + g->ctx = va_arg(ap, void *); + break; + default: + rv = 0; + } + + va_end(ap); + + return rv; +} + + + +yajl_gen +yajl_gen_alloc(const yajl_alloc_funcs * afs) +{ + yajl_gen g = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); + if (!g) return NULL; + + memset((void *) g, 0, sizeof(struct yajl_gen_t)); + /* copy in pointers to allocation routines */ + memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + g->print = (yajl_print_t)&yajl_buf_append; + g->ctx = yajl_buf_alloc(&(g->alloc)); + g->indentString = " "; + + return g; +} + +void +yajl_gen_free(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); + YA_FREE(&(g->alloc), g); +} + +#define INSERT_SEP \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_in_array) { \ + g->print(g->ctx, ",", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); \ + } else if (g->state[g->depth] == yajl_gen_map_val) { \ + g->print(g->ctx, ":", 1); \ + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, " ", 1); \ + } + +#define INSERT_WHITESPACE \ + if ((g->flags & yajl_gen_beautify)) { \ + if (g->state[g->depth] != yajl_gen_map_val) { \ + unsigned int _i; \ + for (_i=0;_idepth;_i++) \ + g->print(g->ctx, \ + g->indentString, \ + (unsigned int)strlen(g->indentString)); \ + } \ + } + +#define ENSURE_NOT_KEY \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_map_start) { \ + return yajl_gen_keys_must_be_strings; \ + } \ + +/* check that we're not complete, or in error state. in a valid state + * to be generating */ +#define ENSURE_VALID_STATE \ + if (g->state[g->depth] == yajl_gen_error) { \ + return yajl_gen_in_error_state;\ + } else if (g->state[g->depth] == yajl_gen_complete) { \ + return yajl_gen_generation_complete; \ + } + +#define INCREMENT_DEPTH \ + if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define DECREMENT_DEPTH \ + if (--(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define APPENDED_ATOM \ + switch (g->state[g->depth]) { \ + case yajl_gen_start: \ + g->state[g->depth] = yajl_gen_complete; \ + break; \ + case yajl_gen_map_start: \ + case yajl_gen_map_key: \ + g->state[g->depth] = yajl_gen_map_val; \ + break; \ + case yajl_gen_array_start: \ + g->state[g->depth] = yajl_gen_in_array; \ + break; \ + case yajl_gen_map_val: \ + g->state[g->depth] = yajl_gen_map_key; \ + break; \ + default: \ + break; \ + } \ + +#define FINAL_NEWLINE \ + if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) \ + g->print(g->ctx, "\n", 1); + +yajl_gen_status +yajl_gen_integer(yajl_gen g, long long int number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%lld", number); + g->print(g->ctx, i, (unsigned int)strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_double(yajl_gen g, double number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; + if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; + INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%.20g", number); + g->print(g->ctx, i, (unsigned int)strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_number(yajl_gen g, const char * s, size_t l) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, s, l); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_string(yajl_gen g, const unsigned char * str, + size_t len) +{ + // if validation is enabled, check that the string is valid utf8 + // XXX: This checking could be done a little faster, in the same pass as + // the string encoding + if (g->flags & yajl_gen_validate_utf8) { + if (!yajl_string_validate_utf8(str, len)) { + return yajl_gen_invalid_string; + } + } + ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "\"", 1); + yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus); + g->print(g->ctx, "\"", 1); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_null(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "null", strlen("null")); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_bool(yajl_gen g, int boolean) +{ + const char * val = boolean ? "true" : "false"; + + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, val, (unsigned int)strlen(val)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + + g->state[g->depth] = yajl_gen_map_start; + g->print(g->ctx, "{", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + DECREMENT_DEPTH; + + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "}", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + g->state[g->depth] = yajl_gen_array_start; + g->print(g->ctx, "[", 1); + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + DECREMENT_DEPTH; + if ((g->flags & yajl_gen_beautify)) g->print(g->ctx, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "]", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, + size_t * len) +{ + if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; + *buf = yajl_buf_data((yajl_buf)g->ctx); + *len = yajl_buf_len((yajl_buf)g->ctx); + return yajl_gen_status_ok; +} + +void +yajl_gen_clear(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); +} diff --git a/modules/libcom/src/yajl/yajl_gen.h b/modules/libcom/src/yajl/yajl_gen.h new file mode 100644 index 000000000..3ea05e555 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_gen.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_gen.h + * Interface to YAJL's JSON generation facilities. + */ + +#ifndef __YAJL_GEN_H__ +#define __YAJL_GEN_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** generator status codes */ + typedef enum { + /** no error */ + yajl_gen_status_ok = 0, + /** at a point where a map key is generated, a function other than + * yajl_gen_string was called */ + yajl_gen_keys_must_be_strings, + /** YAJL's maximum generation depth was exceeded. see + * YAJL_MAX_DEPTH */ + yajl_max_depth_exceeded, + /** A generator function (yajl_gen_XXX) was called while in an error + * state */ + yajl_gen_in_error_state, + /** A complete JSON document has been generated */ + yajl_gen_generation_complete, + /** yajl_gen_double was passed an invalid floating point value + * (infinity or NaN). */ + yajl_gen_invalid_number, + /** A print callback was passed in, so there is no internal + * buffer to get from */ + yajl_gen_no_buf, + /** returned from yajl_gen_string() when the yajl_gen_validate_utf8 + * option is enabled and an invalid was passed by client code. + */ + yajl_gen_invalid_string + } yajl_gen_status; + + /** an opaque handle to a generator */ + typedef struct yajl_gen_t * yajl_gen; + + /** a callback used for "printing" the results. */ + typedef void (*yajl_print_t)(void * ctx, + const char * str, + size_t len); + + /** configuration parameters for the parser, these may be passed to + * yajl_gen_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { + /** generate indented (beautiful) output */ + yajl_gen_beautify = 0x01, + /** + * Set an indent string which is used when yajl_gen_beautify + * is enabled. Maybe something like \\t or some number of + * spaces. The default is four spaces ' '. + */ + yajl_gen_indent_string = 0x02, + /** + * Set a function and context argument that should be used to + * output generated json. the function should conform to the + * yajl_print_t prototype while the context argument is a + * void * of your choosing. + * + * example: + * yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr); + */ + yajl_gen_print_callback = 0x04, + /** + * Normally the generator does not validate that strings you + * pass to it via yajl_gen_string() are valid UTF8. Enabling + * this option will cause it to do so. + */ + yajl_gen_validate_utf8 = 0x08, + /** + * the forward solidus (slash or '/' in human) is not required to be + * escaped in json text. By default, YAJL will not escape it in the + * iterest of saving bytes. Setting this flag will cause YAJL to + * always escape '/' in generated JSON strings. + */ + yajl_gen_escape_solidus = 0x10 + } yajl_gen_option; + + /** allow the modification of generator options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise + */ + YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...); + + /** allocate a generator handle + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs); + + /** free a generator handle */ + YAJL_API void yajl_gen_free(yajl_gen handle); + + YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number); + /** generate a floating point number. number may not be infinity or + * NaN, as these have no representation in JSON. In these cases the + * generator will return 'yajl_gen_invalid_number' */ + YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); + YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, + const char * num, + size_t len); + YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, + const unsigned char * str, + size_t len); + YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); + YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); + + /** access the null terminated generator buffer. If incrementally + * outputing JSON, one should call yajl_gen_clear to clear the + * buffer. This allows stream generation. */ + YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, + const unsigned char ** buf, + size_t * len); + + /** clear yajl's output buffer, but maintain all internal generation + * state. This function will not "reset" the generator state, and is + * intended to enable incremental JSON outputing. */ + YAJL_API void yajl_gen_clear(yajl_gen hand); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/yajl/yajl_lex.c b/modules/libcom/src/yajl/yajl_lex.c new file mode 100644 index 000000000..0159cfa55 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_lex.c @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_lex.h" +#include "yajl_buf.h" + +#ifdef YAJL_LEXER_DEBUG +static const char * +tokToStr(yajl_tok tok) +{ + switch (tok) { + case yajl_tok_bool: return "bool"; + case yajl_tok_colon: return "colon"; + case yajl_tok_comma: return "comma"; + case yajl_tok_eof: return "eof"; + case yajl_tok_error: return "error"; + case yajl_tok_left_brace: return "brace"; + case yajl_tok_left_bracket: return "bracket"; + case yajl_tok_null: return "null"; + case yajl_tok_integer: return "integer"; + case yajl_tok_double: return "double"; + case yajl_tok_right_brace: return "brace"; + case yajl_tok_right_bracket: return "bracket"; + case yajl_tok_string: return "string"; + case yajl_tok_string_with_escapes: return "string_with_escapes"; + } + return "unknown"; +} +#endif + +/* Impact of the stream parsing feature on the lexer: + * + * YAJL support stream parsing. That is, the ability to parse the first + * bits of a chunk of JSON before the last bits are available (still on + * the network or disk). This makes the lexer more complex. The + * responsibility of the lexer is to handle transparently the case where + * a chunk boundary falls in the middle of a token. This is + * accomplished is via a buffer and a character reading abstraction. + * + * Overview of implementation + * + * When we lex to end of input string before end of token is hit, we + * copy all of the input text composing the token into our lexBuf. + * + * Every time we read a character, we do so through the readChar function. + * readChar's responsibility is to handle pulling all chars from the buffer + * before pulling chars from input text + */ + +struct yajl_lexer_t { + /* the overal line and char offset into the data */ + size_t lineOff; + size_t charOff; + + /* error */ + yajl_lex_error error; + + /* a input buffer to handle the case where a token is spread over + * multiple chunks */ + yajl_buf buf; + + /* in the case where we have data in the lexBuf, bufOff holds + * the current offset into the lexBuf. */ + size_t bufOff; + + /* are we using the lex buf? */ + unsigned int bufInUse; + + /* shall we allow comments? */ + unsigned int allowComments; + + /* shall we validate utf8 inside strings? */ + unsigned int validateUTF8; + + yajl_alloc_funcs * alloc; +}; + +#define readChar(lxr, txt, off) \ + (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \ + (*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \ + ((txt)[(*(off))++])) + +#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--)) + +yajl_lexer +yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, unsigned int validateUTF8) +{ + yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t)); + if (lxr == NULL) { + return NULL; + } + + memset((void *) lxr, 0, sizeof(struct yajl_lexer_t)); + lxr->buf = yajl_buf_alloc(alloc); + lxr->allowComments = allowComments; + lxr->validateUTF8 = validateUTF8; + lxr->alloc = alloc; + return lxr; +} + +void +yajl_lex_free(yajl_lexer lxr) +{ + yajl_buf_free(lxr->buf); + YA_FREE(lxr->alloc, lxr); + return; +} + +/* a lookup table which lets us quickly determine three things: + * VEC - valid escaped control char + * note. the solidus '/' may be escaped or not. + * IJC - invalid json char + * VHC - valid hex char + * NFP - needs further processing (from a string scanning perspective) + * NUC - needs utf8 checking when enabled (from a string scanning perspective) + */ +#define VEC 0x01 +#define IJC 0x02 +#define VHC 0x04 +#define NFP 0x08 +#define NUC 0x10 + +static const char charLookupTable[256] = +{ +/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , + +/*20*/ 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , 0 , 0 , +/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , +/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , +/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , + +/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , +/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*58*/ 0 , 0 , 0 , 0 , NFP|VEC|IJC, 0 , 0 , 0 , + +/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , +/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , +/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , +/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC , + NUC , NUC , NUC , NUC , NUC , NUC , NUC , NUC +}; + +/** process a variable length utf8 encoded codepoint. + * + * returns: + * yajl_tok_string - if valid utf8 char was parsed and offset was + * advanced + * yajl_tok_eof - if end of input was hit before validation could + * complete + * yajl_tok_error - if invalid utf8 was encountered + * + * NOTE: on error the offset will point to the first char of the + * invalid utf8 */ +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } + +static yajl_tok +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + unsigned char curChar) +{ + if (curChar <= 0x7f) { + /* single byte */ + return yajl_tok_string; + } else if ((curChar >> 5) == 0x6) { + /* two byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } else if ((curChar >> 4) == 0x0e) { + /* three byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } else if ((curChar >> 3) == 0x1e) { + /* four byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } + } + + return yajl_tok_error; +} + +/* lex a string. input is the lexer, pointer to beginning of + * json text, and start of string (offset). + * a token is returned which has the following meanings: + * yajl_tok_string: lex of string was successful. offset points to + * terminating '"'. + * yajl_tok_eof: end of text was encountered before we could complete + * the lex. + * yajl_tok_error: embedded in the string were unallowable chars. offset + * points to the offending char + */ +#define STR_CHECK_EOF \ +if (*offset >= jsonTextLen) { \ + tok = yajl_tok_eof; \ + goto finish_string_lex; \ +} + +/** scan a string for interesting characters that might need further + * review. return the number of chars that are uninteresting and can + * be skipped. + * (lth) hi world, any thoughts on how to make this routine faster? */ +static size_t +yajl_string_scan(const unsigned char * buf, size_t len, int utf8check) +{ + unsigned char mask = IJC|NFP|(utf8check ? NUC : 0); + size_t skip = 0; + while (skip < len && !(charLookupTable[*buf] & mask)) + { + skip++; + buf++; + } + return skip; +} + +static yajl_tok +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + yajl_tok tok = yajl_tok_error; + int hasEscapes = 0; + + for (;;) { + unsigned char curChar; + + /* now jump into a faster scanning routine to skip as much + * of the buffers as possible */ + { + const unsigned char * p; + size_t len; + + if ((lexer->bufInUse && yajl_buf_len(lexer->buf) && + lexer->bufOff < yajl_buf_len(lexer->buf))) + { + p = ((const unsigned char *) yajl_buf_data(lexer->buf) + + (lexer->bufOff)); + len = yajl_buf_len(lexer->buf) - lexer->bufOff; + lexer->bufOff += yajl_string_scan(p, len, lexer->validateUTF8); + } + else if (*offset < jsonTextLen) + { + p = jsonText + *offset; + len = jsonTextLen - *offset; + *offset += yajl_string_scan(p, len, lexer->validateUTF8); + } + } + + STR_CHECK_EOF; + + curChar = readChar(lexer, jsonText, offset); + + /* quote terminates */ + if (curChar == '"') { + tok = yajl_tok_string; + break; + } + /* backslash escapes a set of control chars, */ + else if (curChar == '\\') { + hasEscapes = 1; + STR_CHECK_EOF; + + /* special case \u */ + curChar = readChar(lexer, jsonText, offset); + if (curChar == 'u') { + unsigned int i = 0; + + for (i=0;i<4;i++) { + STR_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if (!(charLookupTable[curChar] & VHC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_hex_char; + goto finish_string_lex; + } + } + } else if (!(charLookupTable[curChar] & VEC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_escaped_char; + goto finish_string_lex; + } + } + /* when not validating UTF8 it's a simple table lookup to determine + * if the present character is invalid */ + else if(charLookupTable[curChar] & IJC) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_json_char; + goto finish_string_lex; + } + /* when in validate UTF8 mode we need to do some extra work */ + else if (lexer->validateUTF8) { + yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, + offset, curChar); + + if (t == yajl_tok_eof) { + tok = yajl_tok_eof; + goto finish_string_lex; + } else if (t == yajl_tok_error) { + lexer->error = yajl_lex_string_invalid_utf8; + goto finish_string_lex; + } + } + /* accept it, and move on */ + } + finish_string_lex: + /* tell our buddy, the parser, wether he needs to process this string + * again */ + if (hasEscapes && tok == yajl_tok_string) { + tok = yajl_tok_string_with_escapes; + } + + return tok; +} + +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; + +static yajl_tok +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + /** XXX: numbers are the only entities in json that we must lex + * _beyond_ in order to know that they are complete. There + * is an ambiguous case for integers at EOF. */ + + unsigned char c; + + yajl_tok tok = yajl_tok_integer; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional leading minus */ + if (c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + /* a single zero, or a series of integers */ + if (c == '0') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } else if (c >= '1' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_minus; + return yajl_tok_error; + } + + /* optional fraction (indicates this is floating point) */ + if (c == '.') { + int numRd = 0; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + while (c >= '0' && c <= '9') { + numRd++; + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (!numRd) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_decimal; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* optional exponent (indicates this is floating point) */ + if (c == 'e' || c == 'E') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional sign */ + if (c == '+' || c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (c >= '0' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_exponent; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* we always go "one too far" */ + unreadChar(lexer, offset); + + return tok; +} + +static yajl_tok +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset) +{ + unsigned char c; + + yajl_tok tok = yajl_tok_comment; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* either slash or star expected */ + if (c == '/') { + /* now we throw away until end of line */ + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c != '\n'); + } else if (c == '*') { + /* now we throw away until end of comment */ + for (;;) { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '*') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '/') { + break; + } else { + unreadChar(lexer, offset); + } + } + } + } else { + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + } + + return tok; +} + +yajl_tok +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + const unsigned char ** outBuf, size_t * outLen) +{ + yajl_tok tok = yajl_tok_error; + unsigned char c; + size_t startOffset = *offset; + + *outBuf = NULL; + *outLen = 0; + + for (;;) { + assert(*offset <= jsonTextLen); + + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + + c = readChar(lexer, jsonText, offset); + + switch (c) { + case '{': + tok = yajl_tok_left_brace; + goto lexed; + case '}': + tok = yajl_tok_right_brace; + goto lexed; + case '[': + tok = yajl_tok_left_bracket; + goto lexed; + case ']': + tok = yajl_tok_right_bracket; + goto lexed; + case ',': + tok = yajl_tok_comma; + goto lexed; + case ':': + tok = yajl_tok_colon; + goto lexed; + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + startOffset++; + break; + case 't': { + const char * want = "rue"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'f': { + const char * want = "alse"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'n': { + const char * want = "ull"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_null; + goto lexed; + } + case '"': { + tok = yajl_lex_string(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + /* integer parsing wants to start from the beginning */ + unreadChar(lexer, offset); + tok = yajl_lex_number(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '/': + /* hey, look, a probable comment! If comments are disabled + * it's an error. */ + if (!lexer->allowComments) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_unallowed_comment; + tok = yajl_tok_error; + goto lexed; + } + /* if comments are enabled, then we should try to lex + * the thing. possible outcomes are + * - successful lex (tok_comment, which means continue), + * - malformed comment opening (slash not followed by + * '*' or '/') (tok_error) + * - eof hit. (tok_eof) */ + tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + if (tok == yajl_tok_comment) { + /* "error" is silly, but that's the initial + * state of tok. guilty until proven innocent. */ + tok = yajl_tok_error; + yajl_buf_clear(lexer->buf); + lexer->bufInUse = 0; + startOffset = *offset; + break; + } + /* hit error or eof, bail */ + goto lexed; + default: + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + goto lexed; + } + } + + + lexed: + /* need to append to buffer if the buffer is in use or + * if it's an EOF token */ + if (tok == yajl_tok_eof || lexer->bufInUse) { + if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); + lexer->bufInUse = 1; + yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset); + lexer->bufOff = 0; + + if (tok != yajl_tok_eof) { + *outBuf = yajl_buf_data(lexer->buf); + *outLen = yajl_buf_len(lexer->buf); + lexer->bufInUse = 0; + } + } else if (tok != yajl_tok_error) { + *outBuf = jsonText + startOffset; + *outLen = *offset - startOffset; + } + + /* special case for strings. skip the quotes. */ + if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) + { + assert(*outLen >= 2); + (*outBuf)++; + *outLen -= 2; + } + + +#ifdef YAJL_LEXER_DEBUG + if (tok == yajl_tok_error) { + printf("lexical error: %s\n", + yajl_lex_error_to_string(yajl_lex_get_error(lexer))); + } else if (tok == yajl_tok_eof) { + printf("EOF hit\n"); + } else { + printf("lexed %s: '", tokToStr(tok)); + fwrite(*outBuf, 1, *outLen, stdout); + printf("'\n"); + } +#endif + + return tok; +} + +const char * +yajl_lex_error_to_string(yajl_lex_error error) +{ + switch (error) { + case yajl_lex_e_ok: + return "ok, no error"; + case yajl_lex_string_invalid_utf8: + return "invalid bytes in UTF8 string."; + case yajl_lex_string_invalid_escaped_char: + return "inside a string, '\\' occurs before a character " + "which it may not."; + case yajl_lex_string_invalid_json_char: + return "invalid character inside string."; + case yajl_lex_string_invalid_hex_char: + return "invalid (non-hex) character occurs after '\\u' inside " + "string."; + case yajl_lex_invalid_char: + return "invalid char in json text."; + case yajl_lex_invalid_string: + return "invalid string in json text."; + case yajl_lex_missing_integer_after_exponent: + return "malformed number, a digit is required after the exponent."; + case yajl_lex_missing_integer_after_decimal: + return "malformed number, a digit is required after the " + "decimal point."; + case yajl_lex_missing_integer_after_minus: + return "malformed number, a digit is required after the " + "minus sign."; + case yajl_lex_unallowed_comment: + return "probable comment found in input text, comments are " + "not enabled."; + } + return "unknown error code"; +} + + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error +yajl_lex_get_error(yajl_lexer lexer) +{ + if (lexer == NULL) return (yajl_lex_error) -1; + return lexer->error; +} + +size_t yajl_lex_current_line(yajl_lexer lexer) +{ + return lexer->lineOff; +} + +size_t yajl_lex_current_char(yajl_lexer lexer) +{ + return lexer->charOff; +} + +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t offset) +{ + const unsigned char * outBuf; + size_t outLen; + size_t bufLen = yajl_buf_len(lexer->buf); + size_t bufOff = lexer->bufOff; + unsigned int bufInUse = lexer->bufInUse; + yajl_tok tok; + + tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, + &outBuf, &outLen); + + lexer->bufOff = bufOff; + lexer->bufInUse = bufInUse; + yajl_buf_truncate(lexer->buf, bufLen); + + return tok; +} diff --git a/modules/libcom/src/yajl/yajl_lex.h b/modules/libcom/src/yajl/yajl_lex.h new file mode 100644 index 000000000..40c434427 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_lex.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_LEX_H__ +#define __YAJL_LEX_H__ + +#include "yajl_common.h" + +typedef enum { + yajl_tok_bool, + yajl_tok_colon, + yajl_tok_comma, + yajl_tok_eof, + yajl_tok_error, + yajl_tok_left_brace, + yajl_tok_left_bracket, + yajl_tok_null, + yajl_tok_right_brace, + yajl_tok_right_bracket, + + /* we differentiate between integers and doubles to allow the + * parser to interpret the number without re-scanning */ + yajl_tok_integer, + yajl_tok_double, + + /* we differentiate between strings which require further processing, + * and strings that do not */ + yajl_tok_string, + yajl_tok_string_with_escapes, + + /* comment tokens are not currently returned to the parser, ever */ + yajl_tok_comment +} yajl_tok; + +typedef struct yajl_lexer_t * yajl_lexer; + +yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, + unsigned int validateUTF8); + +void yajl_lex_free(yajl_lexer lexer); + +/** + * run/continue a lex. "offset" is an input/output parameter. + * It should be initialized to zero for a + * new chunk of target text, and upon subsetquent calls with the same + * target text should passed with the value of the previous invocation. + * + * the client may be interested in the value of offset when an error is + * returned from the lexer. This allows the client to render useful +n * error messages. + * + * When you pass the next chunk of data, context should be reinitialized + * to zero. + * + * Finally, the output buffer is usually just a pointer into the jsonText, + * however in cases where the entity being lexed spans multiple chunks, + * the lexer will buffer the entity and the data returned will be + * a pointer into that buffer. + * + * This behavior is abstracted from client code except for the performance + * implications which require that the client choose a reasonable chunk + * size to get adequate performance. + */ +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t * offset, + const unsigned char ** outBuf, size_t * outLen); + +/** have a peek at the next token, but don't move the lexer forward */ +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + size_t jsonTextLen, size_t offset); + + +typedef enum { + yajl_lex_e_ok = 0, + yajl_lex_string_invalid_utf8, + yajl_lex_string_invalid_escaped_char, + yajl_lex_string_invalid_json_char, + yajl_lex_string_invalid_hex_char, + yajl_lex_invalid_char, + yajl_lex_invalid_string, + yajl_lex_missing_integer_after_decimal, + yajl_lex_missing_integer_after_exponent, + yajl_lex_missing_integer_after_minus, + yajl_lex_unallowed_comment +} yajl_lex_error; + +const char * yajl_lex_error_to_string(yajl_lex_error error); + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); + +/** get the current offset into the most recently lexed json string. */ +size_t yajl_lex_current_offset(yajl_lexer lexer); + +/** get the number of lines lexed by this lexer instance */ +size_t yajl_lex_current_line(yajl_lexer lexer); + +/** get the number of chars lexed by this lexer instance since the last + * \n or \r */ +size_t yajl_lex_current_char(yajl_lexer lexer); + +#endif diff --git a/modules/libcom/src/yajl/yajl_parse.h b/modules/libcom/src/yajl/yajl_parse.h new file mode 100644 index 000000000..f113c7cf4 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_parse.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * \file yajl_parse.h + * Interface to YAJL's JSON stream parsing facilities. + */ + +#ifndef __YAJL_PARSE_H__ +#define __YAJL_PARSE_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** error codes returned from this interface */ + typedef enum { + /** no error was encountered */ + yajl_status_ok, + /** a client callback returned zero, stopping the parse */ + yajl_status_client_canceled, + /** An error occured during the parse. Call yajl_get_error for + * more information about the encountered error */ + yajl_status_error + } yajl_status; + + /** attain a human readable, english, string for an error */ + YAJL_API const char * yajl_status_to_string(yajl_status code); + + /** an opaque handle to a parser */ + typedef struct yajl_handle_t * yajl_handle; + + /** yajl is an event driven parser. this means as json elements are + * parsed, you are called back to do something with the data. The + * functions in this table indicate the various events for which + * you will be called back. Each callback accepts a "context" + * pointer, this is a void * that is passed into the yajl_parse + * function which the client code may use to pass around context. + * + * All callbacks return an integer. If non-zero, the parse will + * continue. If zero, the parse will be canceled and + * yajl_status_client_canceled will be returned from the parse. + * + * \attention { + * A note about the handling of numbers: + * + * yajl will only convert numbers that can be represented in a + * double or a 64 bit (long long) int. All other numbers will + * be passed to the client in string form using the yajl_number + * callback. Furthermore, if yajl_number is not NULL, it will + * always be used to return numbers, that is yajl_integer and + * yajl_double will be ignored. If yajl_number is NULL but one + * of yajl_integer or yajl_double are defined, parsing of a + * number larger than is representable in a double or 64 bit + * integer will result in a parse error. + * } + */ + typedef struct { + int (* yajl_null)(void * ctx); + int (* yajl_boolean)(void * ctx, int boolVal); + int (* yajl_integer)(void * ctx, long long integerVal); + int (* yajl_double)(void * ctx, double doubleVal); + /** A callback which passes the string representation of the number + * back to the client. Will be used for all numbers when present */ + int (* yajl_number)(void * ctx, const char * numberVal, + size_t numberLen); + + /** strings are returned as pointers into the JSON text when, + * possible, as a result, they are _not_ null padded */ + int (* yajl_string)(void * ctx, const unsigned char * stringVal, + size_t stringLen); + + int (* yajl_start_map)(void * ctx); + int (* yajl_map_key)(void * ctx, const unsigned char * key, + size_t stringLen); + int (* yajl_end_map)(void * ctx); + + int (* yajl_start_array)(void * ctx); + int (* yajl_end_array)(void * ctx); + } yajl_callbacks; + + /** allocate a parser handle + * \param callbacks a yajl callbacks structure specifying the + * functions to call when different JSON entities + * are encountered in the input text. May be NULL, + * which is only useful for validation. + * \param afs memory allocation functions, may be NULL for to use + * C runtime library routines (malloc and friends) + * \param ctx a context pointer that will be passed to callbacks. + */ + YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, + yajl_alloc_funcs * afs, + void * ctx); + + + /** configuration parameters for the parser, these may be passed to + * yajl_config() along with option specific argument(s). In general, + * all configuration parameters default to *off*. */ + typedef enum { + /** Ignore javascript style comments present in + * JSON input. Non-standard, but rather fun + * arguments: toggled off with integer zero, on otherwise. + * + * example: + * yajl_config(h, yajl_allow_comments, 1); // turn comment support on + */ + yajl_allow_comments = 0x01, + /** + * When set the parser will verify that all strings in JSON input are + * valid UTF8 and will emit a parse error if this is not so. When set, + * this option makes parsing slightly more expensive (~7% depending + * on processor and compiler in use) + * + * example: + * yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking + */ + yajl_dont_validate_strings = 0x02, + /** + * By default, upon calls to yajl_complete_parse(), yajl will + * ensure the entire input text was consumed and will raise an error + * otherwise. Enabling this flag will cause yajl to disable this + * check. This can be useful when parsing json out of a that contains more + * than a single JSON document. + */ + yajl_allow_trailing_garbage = 0x04, + /** + * Allow multiple values to be parsed by a single handle. The + * entire text must be valid JSON, and values can be seperated + * by any kind of whitespace. This flag will change the + * behavior of the parser, and cause it continue parsing after + * a value is parsed, rather than transitioning into a + * complete state. This option can be useful when parsing multiple + * values from an input stream. + */ + yajl_allow_multiple_values = 0x08, + /** + * When yajl_complete_parse() is called the parser will + * check that the top level value was completely consumed. I.E., + * if called whilst in the middle of parsing a value + * yajl will enter an error state (premature EOF). Setting this + * flag suppresses that check and the corresponding error. + */ + yajl_allow_partial_values = 0x10 + } yajl_option; + + /** allow the modification of parser options subsequent to handle + * allocation (via yajl_alloc) + * \returns zero in case of errors, non-zero otherwise + */ + YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...); + + /** free a parser handle */ + YAJL_API void yajl_free(yajl_handle handle); + + /** Parse some json! + * \param hand - a handle to the json parser allocated with yajl_alloc + * \param jsonText - a pointer to the UTF8 json text to be parsed + * \param jsonTextLength - the length, in bytes, of input text + */ + YAJL_API yajl_status yajl_parse(yajl_handle hand, + const unsigned char * jsonText, + size_t jsonTextLength); + + /** Parse any remaining buffered json. + * Since yajl is a stream-based parser, without an explicit end of + * input, yajl sometimes can't decide if content at the end of the + * stream is valid or not. For example, if "1" has been fed in, + * yajl can't know whether another digit is next or some character + * that would terminate the integer token. + * + * \param hand - a handle to the json parser allocated with yajl_alloc + */ + YAJL_API yajl_status yajl_complete_parse(yajl_handle hand); + + /** get an error string describing the state of the + * parse. + * + * If verbose is non-zero, the message will include the JSON + * text where the error occured, along with an arrow pointing to + * the specific char. + * + * \returns A dynamically allocated string will be returned which should + * be freed with yajl_free_error + */ + YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, + size_t jsonTextLength); + + /** + * get the amount of data consumed from the last chunk passed to YAJL. + * + * In the case of a successful parse this can help you understand if + * the entire buffer was consumed (which will allow you to handle + * "junk at end of input"). + * + * In the event an error is encountered during parsing, this function + * affords the client a way to get the offset into the most recent + * chunk where the error occured. 0 will be returned if no error + * was encountered. + */ + YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand); + + /** free an error returned from yajl_get_error */ + YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/libcom/src/yajl/yajl_parser.c b/modules/libcom/src/yajl/yajl_parser.c new file mode 100644 index 000000000..7e4da6924 --- /dev/null +++ b/modules/libcom/src/yajl/yajl_parser.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_encode.h" +#include "yajl_bytestack.h" + +#ifndef LLONG_MAX +#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL +#define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1) +#endif + +#define MAX_VALUE_TO_MULTIPLY ((LLONG_MAX / 10) + (LLONG_MAX % 10)) + + /* same semantics as strtol */ +long long +yajl_parse_integer(const unsigned char *number, size_t length) +{ + long long ret = 0; + long sign = 1; + const unsigned char *pos = number; + if (*pos == '-') { pos++; sign = -1; } + if (*pos == '+') { pos++; } + + while (pos < number + length) { + if ( ret > MAX_VALUE_TO_MULTIPLY ) { + errno = ERANGE; + return sign == 1 ? LLONG_MAX : LLONG_MIN; + } + ret *= 10; + if (LLONG_MAX - ret < (*pos - '0')) { + errno = ERANGE; + return sign == 1 ? LLONG_MAX : LLONG_MIN; + } + ret += (*pos++ - '0'); + } + + return sign * ret; +} + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen, int verbose) +{ + size_t offset = hand->bytesConsumed; + unsigned char * str; + const char * errorType = NULL; + const char * errorText = NULL; + char text[72]; + const char * arrow = " (right here) ------^\n"; + + if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) { + errorType = "parse"; + errorText = hand->parseError; + } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) { + errorType = "lexical"; + errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); + } else { + errorType = "unknown"; + } + + { + size_t memneeded = 0; + memneeded += strlen(errorType); + memneeded += strlen(" error"); + if (errorText != NULL) { + memneeded += strlen(": "); + memneeded += strlen(errorText); + } + str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2); + if (!str) return NULL; + str[0] = 0; + strcat((char *) str, errorType); + strcat((char *) str, " error"); + if (errorText != NULL) { + strcat((char *) str, ": "); + strcat((char *) str, errorText); + } + strcat((char *) str, "\n"); + } + + /* now we append as many spaces as needed to make sure the error + * falls at char 41, if verbose was specified */ + if (verbose) { + size_t start, end, i; + size_t spacesNeeded; + + spacesNeeded = (offset < 30 ? 40 - offset : 10); + start = (offset >= 30 ? offset - 30 : 0); + end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); + + for (i=0;ialloc), (unsigned int)(strlen((char *) str) + + strlen((char *) text) + + strlen(arrow) + 1)); + if (newStr) { + newStr[0] = 0; + strcat((char *) newStr, (char *) str); + strcat((char *) newStr, text); + strcat((char *) newStr, arrow); + } + YA_FREE(&(hand->alloc), str); + str = (unsigned char *) newStr; + } + } + return str; +} + +/* check for client cancelation */ +#define _CC_CHK(x) \ + if (!(x)) { \ + yajl_bs_set(hand->stateStack, yajl_state_parse_error); \ + hand->parseError = \ + "client cancelled parse via callback return value"; \ + return yajl_status_client_canceled; \ + } + + +yajl_status +yajl_do_finish(yajl_handle hand) +{ + yajl_status stat; + stat = yajl_do_parse(hand,(const unsigned char *) " ",1); + + if (stat != yajl_status_ok) return stat; + + switch(yajl_bs_current(hand->stateStack)) + { + case yajl_state_parse_error: + case yajl_state_lexical_error: + return yajl_status_error; + case yajl_state_got_value: + case yajl_state_parse_complete: + return yajl_status_ok; + default: + if (!(hand->flags & yajl_allow_partial_values)) + { + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "premature EOF"; + return yajl_status_error; + } + return yajl_status_ok; + } +} + +yajl_status +yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen) +{ + yajl_tok tok; + const unsigned char * buf; + size_t bufLen; + size_t * offset = &(hand->bytesConsumed); + + *offset = 0; + + around_again: + switch (yajl_bs_current(hand->stateStack)) { + case yajl_state_parse_complete: + if (hand->flags & yajl_allow_multiple_values) { + yajl_bs_set(hand->stateStack, yajl_state_got_value); + goto around_again; + } + if (!(hand->flags & yajl_allow_trailing_garbage)) { + if (*offset != jsonTextLen) { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + if (tok != yajl_tok_eof) { + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "trailing garbage"; + } + goto around_again; + } + } + return yajl_status_ok; + case yajl_state_lexical_error: + case yajl_state_parse_error: + return yajl_status_error; + case yajl_state_start: + case yajl_state_got_value: + case yajl_state_map_need_val: + case yajl_state_array_need_val: + case yajl_state_array_start: { + /* for arrays and maps, we advance the state for this + * depth, then push the state of the next depth. + * If an error occurs during the parsing of the nesting + * enitity, the state at this level will not matter. + * a state that needs pushing will be anything other + * than state_start */ + + yajl_state stateToPush = yajl_state_start; + + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + + switch (tok) { + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_string) { + _CC_CHK(hand->callbacks->yajl_string(hand->ctx, + buf, bufLen)); + } + break; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_string) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + _CC_CHK(hand->callbacks->yajl_string( + hand->ctx, yajl_buf_data(hand->decodeBuf), + yajl_buf_len(hand->decodeBuf))); + } + break; + case yajl_tok_bool: + if (hand->callbacks && hand->callbacks->yajl_boolean) { + _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, + *buf == 't')); + } + break; + case yajl_tok_null: + if (hand->callbacks && hand->callbacks->yajl_null) { + _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); + } + break; + case yajl_tok_left_brace: + if (hand->callbacks && hand->callbacks->yajl_start_map) { + _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); + } + stateToPush = yajl_state_map_start; + break; + case yajl_tok_left_bracket: + if (hand->callbacks && hand->callbacks->yajl_start_array) { + _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); + } + stateToPush = yajl_state_array_start; + break; + case yajl_tok_integer: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx,(const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_integer) { + long long int i = 0; + i = yajl_parse_integer(buf, bufLen); + if ((i == LLONG_MIN || i == LLONG_MAX) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "integer overflow" ; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, + i)); + } + } + break; + case yajl_tok_double: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx, (const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_double) { + double d = 0.0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + d = strtod((char *) buf, NULL); + if ((d == HUGE_VAL || d == -HUGE_VAL) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "numeric (floating point) " + "overflow"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_double(hand->ctx, + d)); + } + } + break; + case yajl_tok_right_bracket: { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_array_start || + s == yajl_state_array_need_val) + { + if (hand->callbacks && + hand->callbacks->yajl_end_array) + { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + /* intentional fall-through */ + } + case yajl_tok_colon: + case yajl_tok_comma: + case yajl_tok_right_brace: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "unallowed token at this point in JSON text"; + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "invalid token, internal error"; + goto around_again; + } + /* got a value. transition depends on the state we're in. */ + { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_start || s == yajl_state_got_value) { + yajl_bs_set(hand->stateStack, yajl_state_parse_complete); + } else if (s == yajl_state_map_need_val) { + yajl_bs_set(hand->stateStack, yajl_state_map_got_val); + } else { + yajl_bs_set(hand->stateStack, yajl_state_array_got_val); + } + } + if (stateToPush != yajl_state_start) { + yajl_bs_push(hand->stateStack, stateToPush); + } + + goto around_again; + } + case yajl_state_map_start: + case yajl_state_map_need_key: { + /* only difference between these two states is that in + * start '}' is valid, whereas in need_key, we've parsed + * a comma, and a string key _must_ follow */ + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + bufLen = yajl_buf_len(hand->decodeBuf); + } + /* intentional fall-through */ + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, + bufLen)); + } + yajl_bs_set(hand->stateStack, yajl_state_map_sep); + goto around_again; + case yajl_tok_right_brace: { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_map_start || + s == yajl_state_map_need_key) { + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + } + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "invalid object key (must be a string)"; + goto around_again; + } + } + case yajl_state_map_sep: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_colon: + yajl_bs_set(hand->stateStack, yajl_state_map_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "object key and value must " + "be separated by a colon (':')"; + goto around_again; + } + } + case yajl_state_map_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_brace: + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_map_need_key); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "after key and value, inside map, " + "I expect ',' or '}'"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + } + case yajl_state_array_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_bracket: + if (hand->callbacks && hand->callbacks->yajl_end_array) { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_array_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_ok; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "after array element, I expect ',' or ']'"; + goto around_again; + } + } + } + + abort(); + return yajl_status_error; +} diff --git a/modules/libcom/src/yajl/yajl_parser.h b/modules/libcom/src/yajl/yajl_parser.h new file mode 100644 index 000000000..bd5a74dfd --- /dev/null +++ b/modules/libcom/src/yajl/yajl_parser.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2007-2011, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __YAJL_PARSER_H__ +#define __YAJL_PARSER_H__ + +#include "yajl_parse.h" +#include "yajl_bytestack.h" +#include "yajl_buf.h" +#include "yajl_lex.h" + + +typedef enum { + yajl_state_start = 0, + yajl_state_parse_complete, + yajl_state_parse_error, + yajl_state_lexical_error, + yajl_state_map_start, + yajl_state_map_sep, + yajl_state_map_need_val, + yajl_state_map_got_val, + yajl_state_map_need_key, + yajl_state_array_start, + yajl_state_array_got_val, + yajl_state_array_need_val, + yajl_state_got_value, +} yajl_state; + +struct yajl_handle_t { + const yajl_callbacks * callbacks; + void * ctx; + yajl_lexer lexer; + const char * parseError; + /* the number of bytes consumed from the last client buffer, + * in the case of an error this will be an error offset, in the + * case of an error this can be used as the error offset */ + size_t bytesConsumed; + /* temporary storage for decoded strings */ + yajl_buf decodeBuf; + /* a stack of states. access with yajl_state_XXX routines */ + yajl_bytestack stateStack; + /* memory allocation routines */ + yajl_alloc_funcs alloc; + /* bitfield */ + unsigned int flags; +}; + +yajl_status +yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, + size_t jsonTextLen); + +yajl_status +yajl_do_finish(yajl_handle handle); + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + size_t jsonTextLen, int verbose); + +/* A little built in integer parsing routine with the same semantics as strtol + * that's unaffected by LOCALE. */ +long long +yajl_parse_integer(const unsigned char *number, size_t length); + + +#endif diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile new file mode 100755 index 000000000..e72c46c61 --- /dev/null +++ b/modules/libcom/test/Makefile @@ -0,0 +1,268 @@ +#************************************************************************* +# Copyright (c) 2006 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP = .. +include $(TOP)/configure/CONFIG + +PROD_LIBS += Com +PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32 +PROD_SYS_LIBS_solaris += socket nsl + +TESTPROD_HOST += epicsUnitTestTest +epicsUnitTestTest_SRCS += epicsUnitTestTest.c +# Not much point running this on vxWorks or RTEMS... +TESTS += epicsUnitTestTest + +TESTPROD_HOST += epicsTypesTest +epicsTypesTest_SRCS += epicsTypesTest.cpp +testHarness_SRCS += epicsTypesTest.cpp +TESTS += epicsTypesTest + +TESTPROD_HOST += epicsInlineTest +epicsInlineTest_SRCS += epicsInlineTest1.c +epicsInlineTest_SRCS += epicsInlineTest2.c +epicsInlineTest_SRCS += epicsInlineTest3.cpp +epicsInlineTest_SRCS += epicsInlineTest4.cpp +testHarness_SRCS += $(epicsInlineTest_SRCS) +TESTS += epicsInlineTest + +TESTPROD_HOST += epicsCalcTest +epicsCalcTest_SRCS += epicsCalcTest.cpp +testHarness_SRCS += epicsCalcTest.cpp +TESTS += epicsCalcTest + +TESTPROD_HOST += epicsAlgorithmTest +epicsAlgorithmTest_SRCS += epicsAlgorithmTest.cpp +testHarness_SRCS += epicsAlgorithmTest.cpp +TESTS += epicsAlgorithmTest + +TESTPROD_HOST += epicsMathTest +epicsMathTest_SRCS += epicsMathTest.c +testHarness_SRCS += epicsMathTest.c +TESTS += epicsMathTest + +TESTPROD_HOST += epicsMMIOTest +epicsMMIOTest_SRCS += epicsMMIOTest.c +testHarness_SRCS += epicsMMIOTest.c +TESTS += epicsMMIOTest + +TESTPROD_HOST += epicsEllTest +epicsEllTest_SRCS += epicsEllTest.c +testHarness_SRCS += epicsEllTest.c +TESTS += epicsEllTest + +TESTPROD_HOST += epicsEnvTest +epicsEnvTest_SRCS += epicsEnvTest.c +testHarness_SRCS += epicsEnvTest.c +TESTS += epicsEnvTest + +TESTPROD_HOST += epicsErrlogTest +epicsErrlogTest_SRCS += epicsErrlogTest.c +testHarness_SRCS += epicsErrlogTest.c +TESTS += epicsErrlogTest + +TESTPROD_HOST += epicsStdioTest +epicsStdioTest_SRCS += epicsStdioTest.c +testHarness_SRCS += epicsStdioTest.c +TESTS += epicsStdioTest + +TESTPROD_HOST += epicsStdlibTest +epicsStdlibTest_SRCS += epicsStdlibTest.c +testHarness_SRCS += epicsStdlibTest.c +TESTS += epicsStdlibTest + +TESTPROD_HOST += epicsSockResolveTest +epicsSockResolveTest_SRCS += epicsSockResolveTest.c +testHarness_SRCS += epicsSockResolveTest.c +TESTS += epicsSockResolveTest + +TESTPROD_HOST += epicsStringTest +epicsStringTest_SRCS += epicsStringTest.c +testHarness_SRCS += epicsStringTest.c +TESTS += epicsStringTest + +TESTPROD_HOST += epicsTimeTest +epicsTimeTest_SRCS += epicsTimeTest.cpp +testHarness_SRCS += epicsTimeTest.cpp +TESTS += epicsTimeTest + +TESTPROD_HOST += epicsTimeZoneTest +epicsTimeZoneTest_SRCS += epicsTimeZoneTest.c +libComTestHarness_SRCS_RTEMS += epicsTimeZoneTest.c +TESTS += epicsTimeZoneTest + +TESTPROD_HOST += epicsThreadTest +epicsThreadTest_SRCS += epicsThreadTest.cpp +testHarness_SRCS += epicsThreadTest.cpp +TESTS += epicsThreadTest + +TESTPROD_HOST += epicsThreadOnceTest +epicsThreadOnceTest_SRCS += epicsThreadOnceTest.c +testHarness_SRCS += epicsThreadOnceTest.c +TESTS += epicsThreadOnceTest + +TESTPROD_HOST += epicsThreadPriorityTest +epicsThreadPriorityTest_SRCS += epicsThreadPriorityTest.cpp +testHarness_SRCS += epicsThreadPriorityTest.cpp +TESTS += epicsThreadPriorityTest + +TESTPROD_HOST += epicsThreadPrivateTest +epicsThreadPrivateTest_SRCS += epicsThreadPrivateTest.cpp +testHarness_SRCS += epicsThreadPrivateTest.cpp +TESTS += epicsThreadPrivateTest + +TESTPROD_HOST += epicsThreadHooksTest +epicsThreadHooksTest_SRCS += epicsThreadHooksTest.c +testHarness_SRCS += epicsThreadHooksTest.c +TESTS += epicsThreadHooksTest + +TESTPROD_HOST += epicsThreadPoolTest +epicsThreadPoolTest_SRCS += epicsThreadPoolTest.c +testHarness_SRCS += epicsThreadPoolTest.c +TESTS += epicsThreadPoolTest + +TESTPROD_HOST += epicsExitTest +epicsExitTest_SRCS += epicsExitTest.c +testHarness_SRCS += epicsExitTest.c +TESTS += epicsExitTest + +TESTPROD_HOST += epicsTimerTest +epicsTimerTest_SRCS += epicsTimerTest.cpp +testHarness_SRCS += epicsTimerTest.cpp +TESTS += epicsTimerTest + +TESTPROD_HOST += ringPointerTest +ringPointerTest_SRCS += ringPointerTest.c +testHarness_SRCS += ringPointerTest.c +TESTS += ringPointerTest + +TESTPROD_HOST += ringBytesTest +ringBytesTest_SRCS += ringBytesTest.c +testHarness_SRCS += ringBytesTest.c +TESTS += ringBytesTest + +TESTPROD_HOST += epicsEventTest +epicsEventTest_SRCS += epicsEventTest.cpp +testHarness_SRCS += epicsEventTest.cpp +TESTS += epicsEventTest + +TESTPROD_HOST += epicsMutexTest +epicsMutexTest_SRCS += epicsMutexTest.cpp +testHarness_SRCS += epicsMutexTest.cpp +TESTS += epicsMutexTest + +TESTPROD_HOST += epicsSpinTest +epicsSpinTest_SRCS += epicsSpinTest.c +testHarness_SRCS += epicsSpinTest.c +TESTS += epicsSpinTest + +TESTPROD_HOST += epicsAtomicTest +epicsAtomicTest_SRCS += epicsAtomicTest.cpp +testHarness_SRCS += epicsAtomicTest.cpp +TESTS += epicsAtomicTest + +TESTPROD_HOST += macDefExpandTest +macDefExpandTest_SRCS += macDefExpandTest.c +testHarness_SRCS += macDefExpandTest.c +TESTS += macDefExpandTest + +TESTPROD_HOST += cvtFastTest +cvtFastTest_SRCS += cvtFastTest.cpp +testHarness_SRCS += cvtFastTest.cpp +TESTS += cvtFastTest + +TESTPROD_HOST += macLibTest +macLibTest_SRCS += macLibTest.c +testHarness_SRCS += macLibTest.c +TESTS += macLibTest + +# Perl module tests: +TESTS += macLib + +TESTPROD_HOST += taskwdTest +taskwdTest_SRCS += taskwdTest.c +testHarness_SRCS += taskwdTest.c +TESTS += taskwdTest + +TESTPROD_HOST += blockingSockTest +blockingSockTest_SRCS += blockingSockTest.cpp +testHarness_SRCS += blockingSockTest.cpp +TESTS += blockingSockTest + +TESTPROD_HOST += epicsMessageQueueTest +epicsMessageQueueTest_SRCS += epicsMessageQueueTest.cpp +testHarness_SRCS += epicsMessageQueueTest.cpp +TESTS += epicsMessageQueueTest + +TESTPROD_HOST += epicsStackTraceTest +epicsStackTraceTest_SRCS += epicsStackTraceTest.c +testHarness_SRCS += epicsStackTraceTest.c +TESTS += epicsStackTraceTest + +TESTPROD_HOST += ipAddrToAsciiTest +ipAddrToAsciiTest_SRCS += ipAddrToAsciiTest.cpp +testHarness_SRCS += ipAddrToAsciiTest.cpp +TESTS += ipAddrToAsciiTest + +TESTPROD_HOST += osiSockTest +osiSockTest_SRCS += osiSockTest.c +testHarness_SRCS += osiSockTest.c +TESTS += osiSockTest + +ifneq ($(OS_CLASS),WIN32) +# This test can only be run on a build host, and is broken on Windows +TESTPROD_HOST += yajl_test +yajl_test_SRCS += yajl_test.c +TESTS += yajlTest +endif + +# The testHarness runs all the test programs in a known working order. +testHarness_SRCS += epicsRunLibComTests.c + +libComTestHarness_SRCS += $(testHarness_SRCS) +libComTestHarness_SRCS_RTEMS += rtemsTestHarness.c + +PROD_vxWorks = libComTestHarness +PROD_RTEMS += libComTestHarness + +TESTSPEC_vxWorks = libComTestHarness.munch; epicsRunLibComTests +TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + + +# The following are not test programs, they measure performance. +# They should not be added to TESTS or to epicsRunLibComTests.c + +TESTPROD_HOST += epicsThreadPerform +epicsThreadPerform_SRCS += epicsThreadPerform.cpp +testHarness_SRCS += epicsThreadPerform.cpp + +TESTPROD_HOST += epicsMaxThreads +epicsMaxThreads_SRCS += epicsMaxThreads.c +testHarness_SRCS += epicsMaxThreads.c + +TESTPROD_HOST += buckTest +buckTest_SRCS += buckTest.c +testHarness_SRCS += buckTest.c + +#TESTPROD_HOST += fdmgrTest +fdmgrTest_SRCS += fdmgrTest.c +fdmgrTest_LIBS += ca +# FIXME: program never exits. + +TESTPROD_HOST += epicsAtomicPerform +epicsAtomicPerform_SRCS += epicsAtomicPerform.cpp +testHarness_SRCS += epicsAtomicPerform.cpp + +TESTPROD_HOST += cvtFastPerform +cvtFastPerform_SRCS += cvtFastPerform.cpp +testHarness_SRCS += cvtFastPerform.cpp + +include $(TOP)/configure/RULES diff --git a/modules/libcom/test/blockingSockTest.cpp b/modules/libcom/test/blockingSockTest.cpp new file mode 100644 index 000000000..1a16bb9e6 --- /dev/null +++ b/modules/libcom/test/blockingSockTest.cpp @@ -0,0 +1,300 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "osiSock.h" +#include "osiWireFormat.h" +#include "epicsThread.h" +#include "epicsSignal.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +union address { + struct sockaddr_in ia; + struct sockaddr sa; +}; + +class circuit { +public: + circuit ( SOCKET ); + void recvTest (); + void shutdown (); + void close (); + bool recvWakeupDetected () const; + bool sendWakeupDetected () const; + virtual const char * pName () = 0; +protected: + SOCKET sock; + epicsThreadId id; + bool recvWakeup; + bool sendWakeup; +protected: + virtual ~circuit() {} +}; + +class serverCircuit : public circuit { +public: + serverCircuit ( SOCKET ); +private: + const char * pName (); +}; + +class clientCircuit : public circuit { +public: + clientCircuit ( const address & ); +private: + const char * pName (); +}; + +class server { +public: + server ( const address & ); + void start (); + void daemon (); + void stop (); + address addr () const; +protected: + address srvaddr; + SOCKET sock; + epicsThreadId id; + bool exit; +}; + +circuit::circuit ( SOCKET sockIn ) : + sock ( sockIn ), + id ( 0 ), + recvWakeup ( false ), + sendWakeup ( false ) +{ + testOk ( this->sock != INVALID_SOCKET, "Socket valid" ); +} + +bool circuit::recvWakeupDetected () const +{ + return this->recvWakeup; +} + +bool circuit::sendWakeupDetected () const +{ + return this->sendWakeup; +} + +void circuit::shutdown () +{ + int status = ::shutdown ( this->sock, SHUT_RDWR ); + testOk ( status == 0, "Shutdown() returned Ok" ); +} + +void circuit::close () +{ + epicsSocketDestroy ( this->sock ); +} + +void circuit::recvTest () +{ + char buf [1]; + while ( true ) { + int status = recv ( this->sock, + buf, (int) sizeof ( buf ), 0 ); + if ( status == 0 ) { + testDiag ( "%s was disconnected", this->pName () ); + this->recvWakeup = true; + break; + } + else if ( status > 0 ) { + testDiag ( "%s received %i characters", this->pName (), status ); + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + testDiag ( "%s socket recv() error was \"%s\"", + this->pName (), sockErrBuf ); + this->recvWakeup = true; + break; + } + } +} + +extern "C" void socketRecvTest ( void * pParm ) +{ + circuit * pCir = reinterpret_cast < circuit * > ( pParm ); + pCir->recvTest (); +} + +clientCircuit::clientCircuit ( const address & addrIn ) : + circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) +{ + address tmpAddr = addrIn; + int status = ::connect ( + this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) ); + testOk ( status == 0, "Client end connected" ); + + circuit * pCir = this; + this->id = epicsThreadCreate ( + "client circuit", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + socketRecvTest, pCir ); + testOk ( this->id != 0, "Client thread created" ); +} + + +const char * clientCircuit::pName () +{ + return "client circuit"; +} + +extern "C" void serverDaemon ( void * pParam ) { + server * pSrv = reinterpret_cast < server * > ( pParam ); + pSrv->daemon (); +} + +server::server ( const address & addrIn ) : + srvaddr ( addrIn ), + sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ), + id ( 0 ), exit ( false ) +{ + testOk ( this->sock != INVALID_SOCKET, "Server socket valid" ); + + // setup server side + osiSocklen_t slen = sizeof ( this->srvaddr ); + int status = bind ( this->sock, & this->srvaddr.sa, slen ); + if ( status ) { + testDiag ( "bind to server socket failed, status = %d", status ); + } + if ( getsockname(this->sock, & this->srvaddr.sa, & slen) != 0 ) { + testAbort ( "Failed to read socket address" ); + } + status = listen ( this->sock, 10 ); + testOk ( status == 0, "Server socket listening" ); +} + +void server::start () +{ + this->id = epicsThreadCreate ( + "server daemon", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + serverDaemon, this ); + testOk ( this->id != 0, "Server thread created" ); +} + +void server::daemon () +{ + while ( ! this->exit ) { + // accept client side + address addr; + osiSocklen_t addressSize = sizeof ( addr ); + SOCKET ns = accept ( this->sock, + & addr.sa, & addressSize ); + if ( this->exit ) + break; + testOk ( ns != INVALID_SOCKET, "Accepted socket valid" ); + circuit * pCir = new serverCircuit ( ns ); + testOk ( pCir != 0, "Server circuit created" ); + } +} + +void server::stop () +{ + this->exit = true; + epicsSocketDestroy ( this->sock ); +} + +address server::addr () const +{ + return this->srvaddr; +} + +serverCircuit::serverCircuit ( SOCKET sockIn ) : + circuit ( sockIn ) +{ + circuit * pCir = this; + epicsThreadId threadId = epicsThreadCreate ( + "server circuit", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + socketRecvTest, pCir ); + testOk ( threadId != 0, "Server circuit thread created" ); +} + +const char * serverCircuit::pName () +{ + return "server circuit"; +} + +static const char *mechName(int mech) +{ + static const struct { + int mech; + const char *name; + } mechs[] = { + {-1, "Unknown shutdown mechanism" }, + {esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" }, + {esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" }, + {esscimqi_socketSigAlarmRequired, "esscimqi_socketSigAlarmRequired" } + }; + + for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) { + if (mech == mechs[i].mech) + return mechs[i].name; + } + return "Unknown shutdown mechanism value"; +} + +MAIN(blockingSockTest) +{ + testPlan(13); + osiSockAttach(); + + address addr; + memset ( (char *) & addr, 0, sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + addr.ia.sin_port = 0; + + server srv ( addr ); + srv.start (); + addr = srv.addr (); + clientCircuit client ( addr ); + + epicsThreadSleep ( 1.0 ); + testOk ( ! client.recvWakeupDetected (), "Client is asleep" ); + + testDiag("Trying Shutdown mechanism"); + client.shutdown (); + epicsThreadSleep ( 1.0 ); + int mech = -1; + if ( client.recvWakeupDetected () ) { + mech = esscimqi_socketBothShutdownRequired; + testDiag("Shutdown succeeded"); + } + else { + testDiag("Trying Close mechanism"); + client.close (); + epicsThreadSleep ( 1.0 ); + if ( client.recvWakeupDetected () ) { + mech = esscimqi_socketCloseRequired; + testDiag("Close succeeded"); + } + } + testDiag("This OS behaves like \"%s\".", mechName(mech)); + + int query = epicsSocketSystemCallInterruptMechanismQuery (); + if (! testOk(mech == query, "Declared mechanism works") ) + testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"", + mechName(query)); + + srv.stop (); + epicsThreadSleep ( 1.0 ); + + osiSockRelease(); + return testDone(); +} + diff --git a/modules/libcom/test/buckTest.c b/modules/libcom/test/buckTest.c new file mode 100644 index 000000000..6fc34954e --- /dev/null +++ b/modules/libcom/test/buckTest.c @@ -0,0 +1,82 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "epicsTime.h" +#include "epicsAssert.h" +#include "bucketLib.h" +#include "testMain.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +MAIN(buckTest) +{ + unsigned id1; + unsigned id2; + char * pValSave1; + char * pValSave2; + int s; + BUCKET * pb; + char * pVal; + unsigned i; + epicsTimeStamp start, finish; + double duration; + const int LOOPS = 500000; + + pb = bucketCreate(8); + if (!pb) { + return -1; + } + + id1 = 0x1000a432; + pValSave1 = "fred"; + s = bucketAddItemUnsignedId(pb, &id1, pValSave1); + verify (s == S_bucket_success); + + pValSave2 = "jane"; + id2 = 0x0000a432; + s = bucketAddItemUnsignedId(pb, &id2, pValSave2); + verify (s == S_bucket_success); + + epicsTimeGetCurrent(&start); + for (i=0; i +#include +#include +#include +#include +#include +#include + +#include "epicsStdio.h" +#include "cvtFast.h" +#include "epicsTime.h" +#include "testMain.h" + +using namespace std; + +class PerfConverter { +public: + virtual const char * name(void) const = 0; + virtual int maxPrecision(void) const = 0; + virtual void target (double srcD, float srcF, char *dst, size_t len, int prec) const = 0; + virtual void add(int prec, double elapsed) = 0; + virtual double total(int prec) = 0; + virtual ~PerfConverter () {}; +}; + +class Perf { +public: + Perf ( int maxConverters ); + virtual ~Perf (); + void addConverter( PerfConverter * c ); + void execute (int count, bool verbose); + void report (const char *title, int count); +protected: + static unsigned const nUnrolled = 10; + static const unsigned uSecPerSec = 1000000; + static unsigned const nIterations = 10000; + + const int maxConverters; + PerfConverter **converters; + int nConverters; + int maxPrecision; + bool verbose; + + void measure ( double srcD, float srcF, int prec ); + +private: + Perf ( const Perf & ); + Perf & operator = ( Perf & ); +}; + +Perf :: Perf ( int maxConverters_ ) : + maxConverters ( maxConverters_ ), + converters ( new PerfConverter * [ maxConverters_ ] ), + nConverters ( 0 ), + maxPrecision ( 0 ) +{ +} + +Perf :: ~Perf () +{ + for ( int j = 0; j < nConverters; j++ ) + delete converters[ j ]; + + delete [] converters; +} + +void Perf :: addConverter(PerfConverter *c) +{ + if ( nConverters >= maxConverters ) + throw std :: runtime_error ( "Too many converters" ); + + converters[ nConverters++ ] = c; + + int prec = c->maxPrecision(); + if ( prec > maxPrecision ) + maxPrecision = prec; +} + +void Perf :: execute (const int count, bool verbose_) +{ + verbose = verbose_; + + for ( int i = 0; i < count; i++ ) { + double srcDbl = rand (); + srcDbl /= (RAND_MAX + 1.0); + srcDbl *= 20.0; + srcDbl -= 10.0; + float srcFlt = (float) srcDbl; + + for ( int prec = 0; prec <= maxPrecision; prec++ ) { + measure (srcFlt, srcDbl, prec); + } + } + report ( "Small numbers, -10..+10", count ); + + for ( int i = 0; i < count; i++ ) { + double mVal = rand (); + mVal /= (RAND_MAX + 1.0); + double eVal = rand (); + eVal /= (RAND_MAX + 1.0); + + double dVal = eVal; + dVal *= FLT_MAX_EXP - FLT_MIN_EXP; + dVal += FLT_MIN_EXP; + int dEVal = static_cast < int > ( dVal + 0.5 ); + double srcDbl = ldexp ( mVal, dEVal ); + float srcFlt = (float) srcDbl; + + for ( int prec = 0; prec <= maxPrecision; prec++ ) { + measure (srcFlt, srcDbl, prec); + } + } + report ( "Random mantissa+exponent", count ); +} + +void Perf :: report (const char *title, const int count) +{ + printf( "\n%s\n\nprec\t", title ); + for ( int j = 0; j < nConverters; j++ ) + printf( "%-16s ", converters[j]->name() ); + + for (int prec = 0; prec <= maxPrecision; prec++ ) { + printf( "\n %2d\t", prec ); + for (int j = 0; j < nConverters; j++ ) { + PerfConverter *c = converters[j]; + if (prec > c->maxPrecision()) + printf( "%11s ", "-" ); + else { + printf( "%11.9f sec ", c->total(prec) / count ); + } + } + } + printf( "\n\n" ); +} + +void Perf :: measure (double srcD, float srcF, int prec) +{ + char buf[40]; + + for ( int j = 0; j < nConverters; j++ ) { + PerfConverter *c = converters[j]; + + if (prec > c->maxPrecision()) + continue; + + std::memset(buf, 0, sizeof(buf)); + + epicsTime beg = epicsTime :: getCurrent (); + for ( unsigned i = 0; i < nIterations; i++ ) { + c->target (srcD, srcF, buf, sizeof(buf) - 1, prec); + } + epicsTime end = epicsTime :: getCurrent (); + + double elapsed = end - beg; + elapsed /= nIterations * nUnrolled; + c->add( prec, elapsed ); + + if (verbose) + printf ( "%17s: %11.9f sec, prec=%2i '%s'\n", + c->name (), elapsed, prec, buf ); + } +} + + +// Conversions to be measured + +class PerfCvtFastFloat : public PerfConverter { + static const int digits = 12; +public: + PerfCvtFastFloat () + { + for (int i = 0; i <= digits; i++) + measured[i] = 0; // Some targets seem to need this + } + int maxPrecision (void) const { return digits; } + const char *name (void) const { return "cvtFloatToString"; } + void target (double srcD, float srcF, char *dst, size_t len, int prec) const + { + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + cvtFloatToString ( srcF, dst, prec ); + } + void add (int prec, double elapsed) { measured[prec] += elapsed; } + double total (int prec) { + double total = measured[prec]; + measured[prec] = 0; + return total; + } +private: + double measured[digits+1]; +}; + + +class PerfCvtFastDouble : public PerfConverter { + static const int digits = 17; +public: + PerfCvtFastDouble () + { + for (int i = 0; i <= digits; i++) + measured[i] = 0; // Some targets seem to need this + } + int maxPrecision (void) const { return digits; } + const char *name (void) const { return "cvtDoubleToString"; } + void target (double srcD, float srcF, char *dst, size_t len, int prec) const + { + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + cvtDoubleToString ( srcD, dst, prec ); + } + void add(int prec, double elapsed) { measured[prec] += elapsed; } + double total (int prec) { + double total = measured[prec]; + measured[prec] = 0; + return total; + } +private: + double measured[digits+1]; +}; + + +class PerfSNPrintf : public PerfConverter { + static const int digits = 17; +public: + PerfSNPrintf () + { + for (int i = 0; i <= digits; i++) + measured[i] = 0; // Some targets seem to need this + } + int maxPrecision (void) const { return digits; } + const char *name (void) const { return "epicsSnprintf"; } + void target (double srcD, float srcF, char *dst, size_t len, int prec) const + { + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + epicsSnprintf ( dst, len, "%.*g", prec, srcD ); + } + void add(int prec, double elapsed) { measured[prec] += elapsed; } + double total (int prec) { + double total = measured[prec]; + measured[prec] = 0; + return total; + } +private: + double measured[digits+1]; +}; + + +// This is a quick-and-dirty std::streambuf converter that writes directly +// into the output buffer. Performance is slower than epicsSnprintf(). + +struct membuf: public std::streambuf { + membuf(char *array, size_t size) { + this->setp(array, array + size - 1); + } +}; + +struct omemstream: virtual membuf, std::ostream { + omemstream(char *array, size_t size): + membuf(array, size), + std::ostream(this) { + } +}; + +static void ossConvertD(char *dst, size_t len, int prec, double src) { + omemstream oss(dst, len); + oss.precision(prec); + oss << src << ends; +} + +class PerfStreamBuf : public PerfConverter { + static const int digits = 17; +public: + PerfStreamBuf () + { + for (int i = 0; i <= digits; i++) + measured[i] = 0; // Some targets seem to need this + } + int maxPrecision (void) const { return digits; } + const char *name (void) const { return "std::streambuf"; } + void target (double srcD, float srcF, char *dst, size_t len, int prec) const + { + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + ossConvertD ( dst, len, prec, srcD ); + } + void add(int prec, double elapsed) { measured[prec] += elapsed; } + double total (int prec) { + double total = measured[prec]; + measured[prec] = 0; + return total; + } +private: + double measured[digits+1]; +}; + + +MAIN(cvtFastPerform) +{ + Perf t(4); + + t.addConverter( new PerfCvtFastFloat ); + t.addConverter( new PerfCvtFastDouble ); + t.addConverter( new PerfSNPrintf ); + t.addConverter( new PerfStreamBuf ); + + // The parameter to execute() below are: + // count = number of different random numbers to measure + // verbose = whether to display individual measurements + +#ifdef vxWorks + t.execute (3, true); // Slow... +#else + t.execute (5, false); +#endif + + return 0; +} diff --git a/modules/libcom/test/cvtFastTest.c b/modules/libcom/test/cvtFastTest.c new file mode 100644 index 000000000..ac853ecc3 --- /dev/null +++ b/modules/libcom/test/cvtFastTest.c @@ -0,0 +1,459 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* cvtFastTest.c + * + * Test the string converters in libCom/cvtFast/cvtFast.c + * + * To Do: Test Char/Uchar/Short/Ushort/Long/Ulong versions + */ + +#include +#include +#include + +#include "epicsUnitTest.h" +#include "cvtFast.h" +#include "epicsStdlib.h" +#include "testMain.h" + +#define tryIString(typ, lit, siz) \ + len = cvt##typ##ToString(lit, buf); \ + testOk(len == siz, "cvt"#typ"ToString(" #lit ") == " #siz " (%u) -> \"%s\"", (unsigned)len, buf); \ + status = epicsParse##typ(buf, &val_##typ, 10, NULL); \ + testOk(!status, "epicsParse"#typ"('%s') OK", buf); \ + testOk(val_##typ == lit, #lit " => '%s'", buf); + +#define tryFString(typ, lit, prec, siz) \ + len = cvt##typ##ToString(lit, buf, prec); \ + testOk(len == siz, "cvt"#typ"ToString(" #lit ", %d) == " #siz " (%u) -> \"%s\"", prec, (unsigned)len, buf); \ + status = epicsParse##typ(buf, &val_##typ, NULL); \ + testOk(!status, "epicsParse"#typ"('%s') OK", buf); \ + testOk(fabs(val_##typ - lit) < 0.5 * pow(10, -prec), #lit " => '%s'", buf); + + +MAIN(cvtFastTest) +{ + char buf[80]; + size_t len; + long status; + epicsUInt32 val_UInt32; + epicsInt32 val_Int32; + epicsUInt64 val_UInt64; + epicsInt64 val_Int64; + epicsFloat32 val_Float; + epicsFloat64 val_Double; + +#ifdef _WIN32 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || \ + (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) + _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +#endif + + testPlan(1062); + + /* Arguments: type, value, num chars */ + testDiag("------------------------------------------------------"); + testDiag("** Positive Int32 **"); + tryIString(Int32, 0, 1); + tryIString(Int32, 1, 1); + tryIString(Int32, 10, 2); + tryIString(Int32, 100, 3); + tryIString(Int32, 254, 3); + tryIString(Int32, 255, 3); + tryIString(Int32, 256, 3); + tryIString(Int32, 257, 3); + tryIString(Int32, 1000, 4); + tryIString(Int32, 10000, 5); + tryIString(Int32, 32766, 5); + tryIString(Int32, 32767, 5); + tryIString(Int32, 32768, 5); + tryIString(Int32, 32769, 5); + tryIString(Int32, 65534, 5); + tryIString(Int32, 65535, 5); + tryIString(Int32, 65536, 5); + tryIString(Int32, 65537, 5); + tryIString(Int32, 2147483646, 10); + tryIString(Int32, 2147483647, 10); + + testDiag("------------------------------------------------------"); + testDiag("** Negative Int32 **"); + tryIString(Int32, -1, 2); + tryIString(Int32, -10, 3); + tryIString(Int32, -100, 4); + tryIString(Int32, -254, 4); + tryIString(Int32, -255, 4); + tryIString(Int32, -256, 4); + tryIString(Int32, -257, 4); + tryIString(Int32, -1000, 5); + tryIString(Int32, -10000, 6); + tryIString(Int32, -32766, 6); + tryIString(Int32, -32767, 6); + tryIString(Int32, -32768, 6); + tryIString(Int32, -32769, 6); + tryIString(Int32, -65534, 6); + tryIString(Int32, -65535, 6); + tryIString(Int32, -65536, 6); + tryIString(Int32, -65537, 6); + tryIString(Int32, -2147483647, 11); + tryIString(Int32, -2147483648LL, 11); + + testDiag("------------------------------------------------------"); + testDiag("** UInt32 **"); + tryIString(UInt32, 0, 1); + tryIString(UInt32, 1, 1); + tryIString(UInt32, 10, 2); + tryIString(UInt32, 100, 3); + tryIString(UInt32, 254, 3); + tryIString(UInt32, 255, 3); + tryIString(UInt32, 256, 3); + tryIString(UInt32, 257, 3); + tryIString(UInt32, 1000, 4); + tryIString(UInt32, 10000, 5); + tryIString(UInt32, 32766, 5); + tryIString(UInt32, 32767, 5); + tryIString(UInt32, 32768, 5); + tryIString(UInt32, 32769, 5); + tryIString(UInt32, 65534, 5); + tryIString(UInt32, 65535, 5); + tryIString(UInt32, 65536, 5); + tryIString(UInt32, 65537, 5); + tryIString(UInt32, 2147483646ULL, 10); + tryIString(UInt32, 2147483647ULL, 10); + tryIString(UInt32, 2147483648ULL, 10); + tryIString(UInt32, 4294967294ULL, 10); + tryIString(UInt32, 4294967295ULL, 10); + + testDiag("------------------------------------------------------"); + testDiag("** Positive Int64 **"); + tryIString(Int64, 0, 1); + tryIString(Int64, 1, 1); + tryIString(Int64, 10, 2); + tryIString(Int64, 100, 3); + tryIString(Int64, 254, 3); + tryIString(Int64, 255, 3); + tryIString(Int64, 256, 3); + tryIString(Int64, 257, 3); + tryIString(Int64, 1000, 4); + tryIString(Int64, 10000, 5); + tryIString(Int64, 32766, 5); + tryIString(Int64, 32767, 5); + tryIString(Int64, 32768, 5); + tryIString(Int64, 32769, 5); + tryIString(Int64, 65534, 5); + tryIString(Int64, 65535, 5); + tryIString(Int64, 65536, 5); + tryIString(Int64, 65537, 5); + tryIString(Int64, 2147483646, 10); + tryIString(Int64, 2147483647, 10); + tryIString(Int64, 2147483648LL, 10); + tryIString(Int64, 9223372036854775806LL, 19); + tryIString(Int64, 9223372036854775807LL, 19); + + testDiag("------------------------------------------------------"); + testDiag("** Negative Int64 **"); + tryIString(Int64, -1, 2); + tryIString(Int64, -10, 3); + tryIString(Int64, -100, 4); + tryIString(Int64, -254, 4); + tryIString(Int64, -255, 4); + tryIString(Int64, -256, 4); + tryIString(Int64, -257, 4); + tryIString(Int64, -1000, 5); + tryIString(Int64, -10000, 6); + tryIString(Int64, -32766, 6); + tryIString(Int64, -32767, 6); + tryIString(Int64, -32768, 6); + tryIString(Int64, -32769, 6); + tryIString(Int64, -65534, 6); + tryIString(Int64, -65535, 6); + tryIString(Int64, -65536, 6); + tryIString(Int64, -65537, 6); + tryIString(Int64, -2147483647, 11); + tryIString(Int64, -2147483648LL, 11); + tryIString(Int64, -2147483649LL, 11); + tryIString(Int64, -9223372036854775806LL, 20); + tryIString(Int64, -9223372036854775807LL, 20); + tryIString(Int64, -9223372036854775807LL-1, 20); + + testDiag("------------------------------------------------------"); + testDiag("** UInt64 **"); + tryIString(UInt64, 0, 1); + tryIString(UInt64, 1, 1); + tryIString(UInt64, 10, 2); + tryIString(UInt64, 100, 3); + tryIString(UInt64, 254, 3); + tryIString(UInt64, 255, 3); + tryIString(UInt64, 256, 3); + tryIString(UInt64, 257, 3); + tryIString(UInt64, 1000, 4); + tryIString(UInt64, 10000, 5); + tryIString(UInt64, 32766, 5); + tryIString(UInt64, 32767, 5); + tryIString(UInt64, 32768, 5); + tryIString(UInt64, 32769, 5); + tryIString(UInt64, 65534, 5); + tryIString(UInt64, 65535, 5); + tryIString(UInt64, 65536, 5); + tryIString(UInt64, 65537, 5); + tryIString(UInt64, 2147483646, 10); + tryIString(UInt64, 2147483647, 10); + tryIString(UInt64, 2147483648U, 10); + tryIString(UInt64, 2147483649U, 10); + tryIString(UInt64, 4294967294U, 10); + tryIString(UInt64, 4294967295U, 10); + tryIString(UInt64, 4294967296ULL, 10); + tryIString(UInt64, 4294967297ULL, 10); + tryIString(UInt64, 9223372036854775806ULL, 19); + tryIString(UInt64, 9223372036854775807ULL, 19); + tryIString(UInt64, 9223372036854775808ULL, 19); + tryIString(UInt64, 18446744073709551614ULL, 20); + tryIString(UInt64, 18446744073709551615ULL, 20); + + /* Arguments: type, value, precision, num chars */ + testDiag("------------------------------------------------------"); + testDiag("** Positive Float fixed-point **"); + tryFString(Float, 0, 0, 1); + tryFString(Float, 0, 1, 3); + tryFString(Float, 0, 2, 4); + tryFString(Float, 0, 3, 5); + tryFString(Float, 0, 4, 6); + tryFString(Float, 0, 5, 7); + tryFString(Float, 0, 6, 8); + tryFString(Float, 0, 7, 9); + tryFString(Float, 0, 8, 10); + tryFString(Float, FLT_MIN, 0, 1); + tryFString(Float, FLT_MIN, 1, 3); + tryFString(Float, FLT_MIN, 2, 4); + tryFString(Float, FLT_MIN, 3, 5); + tryFString(Float, FLT_MIN, 4, 6); + tryFString(Float, FLT_MIN, 5, 7); + tryFString(Float, FLT_MIN, 6, 8); + tryFString(Float, FLT_MIN, 7, 9); + tryFString(Float, FLT_MIN, 8, 10); + tryFString(Float, 0.000000004999999, 8, 10); + tryFString(Float, 0.000000005000001, 8, 10); + tryFString(Float, 0.00000004999999, 7, 9); + tryFString(Float, 0.00000005000001, 7, 9); + tryFString(Float, 0.0000004999999, 6, 8); + tryFString(Float, 0.0000005000001, 6, 8); + tryFString(Float, 0.000004999999, 5, 7); + tryFString(Float, 0.000005000001, 5, 7); + tryFString(Float, 0.00004999999, 4, 6); + tryFString(Float, 0.00005000001, 4, 6); + tryFString(Float, 0.0004999999, 3, 5); + tryFString(Float, 0.0005000001, 3, 5); + tryFString(Float, 0.004999999, 2, 4); + tryFString(Float, 0.005000001, 2, 4); + tryFString(Float, 0.04999999, 1, 3); + tryFString(Float, 0.05000001, 1, 3); + tryFString(Float, 0.4999999, 0, 1); + tryFString(Float, 0.5000001, 0, 1); + tryFString(Float, 1, 0, 1); + tryFString(Float, 1, 1, 3); + tryFString(Float, 1, 2, 4); + tryFString(Float, 1, 3, 5); + tryFString(Float, 1, 4, 6); + tryFString(Float, 1, 5, 7); + tryFString(Float, 1, 6, 8); + tryFString(Float, 1, 7, 9); + tryFString(Float, 1, 8, 10); + tryFString(Float, 1.0500001, 1, 3); + tryFString(Float, 1.1, 1, 3); + tryFString(Float, 1.1499999, 1, 3); + tryFString(Float, 9.5000001, 0, 2); + tryFString(Float, 10, 0, 2); + tryFString(Float, 10, 1, 4); + tryFString(Float, 10, 8, 11); + tryFString(Float, 100, 0, 3); + tryFString(Float, 100, 1, 5); + tryFString(Float, 100, 8, 12); + tryFString(Float, 1000, 0, 4); + tryFString(Float, 1000, 1, 6); + tryFString(Float, 1000, 8, 13); + tryFString(Float, 10000, 0, 5); + tryFString(Float, 10000, 1, 7); + tryFString(Float, 10000, 8, 14); + tryFString(Float, 100000, 0, 6); + tryFString(Float, 100000, 1, 8); + tryFString(Float, 100000, 8, 15); + tryFString(Float, 1000000, 0, 7); + tryFString(Float, 1000000, 1, 9); + tryFString(Float, 1000000, 8, 16); + tryFString(Float, 10000000, 0, 8); + tryFString(Float, 10000000, 1, 10); + tryFString(Float, 10000000, 8, 17); + + testDiag("------------------------------------------------------"); + testDiag("** Negative Float fixed-point **"); + tryFString(Float, -FLT_MIN, 0, 2); + tryFString(Float, -FLT_MIN, 1, 4); + tryFString(Float, -FLT_MIN, 8, 11); + tryFString(Float, -1, 0, 2); + tryFString(Float, -1, 1, 4); + tryFString(Float, -1, 8, 11); + tryFString(Float, -1.0500001, 1, 4); + tryFString(Float, -1.1, 1, 4); + tryFString(Float, -1.1499999, 1, 4); + tryFString(Float, -9.5000001, 0, 3); + tryFString(Float, -10, 0, 3); + tryFString(Float, -10, 1, 5); + tryFString(Float, -10, 8, 12); + tryFString(Float, -100, 0, 4); + tryFString(Float, -100, 1, 6); + tryFString(Float, -100, 8, 13); + tryFString(Float, -1000, 0, 5); + tryFString(Float, -1000, 1, 7); + tryFString(Float, -1000, 8, 14); + tryFString(Float, -10000, 0, 6); + tryFString(Float, -10000, 1, 8); + tryFString(Float, -10000, 8, 15); + tryFString(Float, -100000, 0, 7); + tryFString(Float, -100000, 1, 9); + tryFString(Float, -100000, 8, 16); + tryFString(Float, -1000000, 0, 8); + tryFString(Float, -1000000, 1, 10); + tryFString(Float, -1000000, 8, 17); + tryFString(Float, -10000000, 0, 9); + tryFString(Float, -10000000, 1, 11); + tryFString(Float, -10000000, 8, 18); + + /* + * Values > 1e7 trigger the %e format. + */ + testDiag("------------------------------------------------------"); + testDiag("** Positive Float scientific **"); + tryFString(Float, 1e+08, 0, 6); + tryFString(Float, 1e+08, 1, 7); + tryFString(Float, 1e+08, 2, 8); + tryFString(Float, 1e+08, 3, 9); + tryFString(Float, 1e+08, 4, 10); + tryFString(Float, 1e+08, 5, 11); + tryFString(Float, 1e+08, 6, 12); + + testDiag("------------------------------------------------------"); + testDiag("** Positive Double fixed-point **"); + tryFString(Double, 0, 0, 1); + tryFString(Double, 0, 1, 3); + tryFString(Double, 0, 2, 4); + tryFString(Double, 0, 3, 5); + tryFString(Double, 0, 4, 6); + tryFString(Double, 0, 5, 7); + tryFString(Double, 0, 6, 8); + tryFString(Double, 0, 7, 9); + tryFString(Double, 0, 8, 10); + tryFString(Double, DBL_MIN, 0, 1); + tryFString(Double, DBL_MIN, 1, 3); + tryFString(Double, DBL_MIN, 2, 4); + tryFString(Double, DBL_MIN, 3, 5); + tryFString(Double, DBL_MIN, 4, 6); + tryFString(Double, DBL_MIN, 5, 7); + tryFString(Double, DBL_MIN, 6, 8); + tryFString(Double, DBL_MIN, 7, 9); + tryFString(Double, DBL_MIN, 8, 10); + tryFString(Double, 0.000000004999999, 8, 10); + tryFString(Double, 0.000000005000001, 8, 10); + tryFString(Double, 0.00000004999999, 7, 9); + tryFString(Double, 0.00000005000001, 7, 9); + tryFString(Double, 0.0000004999999, 6, 8); + tryFString(Double, 0.0000005000001, 6, 8); + tryFString(Double, 0.000004999999, 5, 7); + tryFString(Double, 0.000005000001, 5, 7); + tryFString(Double, 0.00004999999, 4, 6); + tryFString(Double, 0.00005000001, 4, 6); + tryFString(Double, 0.0004999999, 3, 5); + tryFString(Double, 0.0005000001, 3, 5); + tryFString(Double, 0.004999999, 2, 4); + tryFString(Double, 0.005000001, 2, 4); + tryFString(Double, 0.04999999, 1, 3); + tryFString(Double, 0.05000001, 1, 3); + tryFString(Double, 0.4999999, 0, 1); + tryFString(Double, 0.5000001, 0, 1); + tryFString(Double, 1, 0, 1); + tryFString(Double, 1, 1, 3); + tryFString(Double, 1, 2, 4); + tryFString(Double, 1, 3, 5); + tryFString(Double, 1, 4, 6); + tryFString(Double, 1, 5, 7); + tryFString(Double, 1, 6, 8); + tryFString(Double, 1, 7, 9); + tryFString(Double, 1, 8, 10); + tryFString(Double, 1.0500001, 1, 3); + tryFString(Double, 1.1, 1, 3); + tryFString(Double, 1.1499999, 1, 3); + tryFString(Double, 9.5000001, 0, 2); + tryFString(Double, 10, 0, 2); + tryFString(Double, 10, 1, 4); + tryFString(Double, 10, 8, 11); + tryFString(Double, 100, 0, 3); + tryFString(Double, 100, 1, 5); + tryFString(Double, 100, 8, 12); + tryFString(Double, 1000, 0, 4); + tryFString(Double, 1000, 1, 6); + tryFString(Double, 1000, 8, 13); + tryFString(Double, 10000, 0, 5); + tryFString(Double, 10000, 1, 7); + tryFString(Double, 10000, 8, 14); + tryFString(Double, 100000, 0, 6); + tryFString(Double, 100000, 1, 8); + tryFString(Double, 100000, 8, 15); + tryFString(Double, 1000000, 0, 7); + tryFString(Double, 1000000, 1, 9); + tryFString(Double, 1000000, 8, 16); + tryFString(Double, 10000000, 0, 8); + tryFString(Double, 10000000, 1, 10); + tryFString(Double, 10000000, 8, 17); + + testDiag("------------------------------------------------------"); + testDiag("** Negative Double fixed-point **"); + tryFString(Double, -DBL_MIN, 0, 2); + tryFString(Double, -DBL_MIN, 1, 4); + tryFString(Double, -DBL_MIN, 8, 11); + tryFString(Double, -1, 0, 2); + tryFString(Double, -1, 1, 4); + tryFString(Double, -1, 8, 11); + tryFString(Double, -1.0500001, 1, 4); + tryFString(Double, -1.1, 1, 4); + tryFString(Double, -1.1499999, 1, 4); + tryFString(Double, -9.5000001, 0, 3); + tryFString(Double, -10, 0, 3); + tryFString(Double, -10, 1, 5); + tryFString(Double, -10, 8, 12); + tryFString(Double, -100, 0, 4); + tryFString(Double, -100, 1, 6); + tryFString(Double, -100, 8, 13); + tryFString(Double, -1000, 0, 5); + tryFString(Double, -1000, 1, 7); + tryFString(Double, -1000, 8, 14); + tryFString(Double, -10000, 0, 6); + tryFString(Double, -10000, 1, 8); + tryFString(Double, -10000, 8, 15); + tryFString(Double, -100000, 0, 7); + tryFString(Double, -100000, 1, 9); + tryFString(Double, -100000, 8, 16); + tryFString(Double, -1000000, 0, 8); + tryFString(Double, -1000000, 1, 10); + tryFString(Double, -1000000, 8, 17); + tryFString(Double, -10000000, 0, 9); + tryFString(Double, -10000000, 1, 11); + tryFString(Double, -10000000, 8, 18); + + /* + * Values > 1e7 trigger the %e format. + * Windows may print 3 digit exponents... + */ + testDiag("------------------------------------------------------"); + testDiag("** Positive Double scientific **"); + tryFString(Double, 1e+17, 0, 7); + tryFString(Double, 1e+17, 1, 8); + tryFString(Double, 1e+17, 2, 9); + tryFString(Double, 1e+17, 3, 10); + tryFString(Double, 1e+17, 4, 11); + tryFString(Double, 1e+17, 5, 12); + + return testDone(); +} diff --git a/modules/libcom/test/epicsAlgorithmTest.cpp b/modules/libcom/test/epicsAlgorithmTest.cpp new file mode 100644 index 000000000..fa8c0c03f --- /dev/null +++ b/modules/libcom/test/epicsAlgorithmTest.cpp @@ -0,0 +1,63 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// epicsAlgorithmTest.cpp +// Authors: Jeff Hill & Andrew Johnson + +#include "epicsUnitTest.h" +#include "epicsAlgorithm.h" +#include "epicsMath.h" +#include "testMain.h" + +MAIN(epicsAlgorithm) +{ + testPlan(22); + + float f1 = 3.3f; + float f2 = 3.4f; + float Inf = epicsINF; + float NaN = epicsNAN; + + testOk(epicsMin(f1, f2) == f1, "epicsMin(f1, f2)"); + testOk(epicsMin(f1, -Inf) == -Inf, "epicsMin(f1, -Inf)"); + testOk(isnan(epicsMin(f1, NaN)), "epicsMin(f1, NaN)"); + testOk(epicsMin(f1, Inf) == f1, "epicsMin(f1, Inf)"); + + testOk(epicsMin(f2, f1) == f1, "epicsMin(f2, f1)"); + testOk(epicsMin(-Inf, f1) == -Inf, "epicsMin(-Inf, f1)"); + testOk(isnan(epicsMin(NaN, f1)), "epicsMin(NaN, f1)"); + testOk(epicsMin(Inf, f1) == f1, "epicsMin(Inf, f1)"); + + testOk(epicsMax(f2, f1) == f2, "epicsMax(f2, f1)"); + testOk(epicsMax(-Inf, f1) == f1, "epicsMax(-Inf, f1)"); + testOk(isnan(epicsMax(NaN, f1)), "epicsMax(NaN, f1)"); + testOk(epicsMax(Inf, f1) == Inf, "epicsMax(Inf, f1)"); + + testOk(epicsMax(f1, f2) == f2, "epicsMax(f1, f2)"); + testOk(epicsMax(f1, -Inf) == f1, "epicsMax(f1, -Inf)"); + testOk(isnan(epicsMax(f1, NaN)), "epicsMax(f1, NaN)"); + testOk(epicsMax(f1, Inf) == Inf, "epicsMax(f1, Inf)"); + + epicsSwap(f1, f2); + testOk(f1==3.4f && f2==3.3f, "epicsSwap(f1, f2)"); + + int i1 = 3; + int i2 = 4; + + testOk(epicsMin(i1, i2) == i1, "epicsMin(i1,i2)"); + testOk(epicsMin(i2, i1) == i1, "epicsMin(i2,i1)"); + + testOk(epicsMax(i1, i2) == i2, "epicsMax(i1,i2)"); + testOk(epicsMax(i2, i1) == i2, "epicsMax(i2,i1)"); + + epicsSwap(i1, i2); + testOk(i1 == 4 && i2 == 3, "epicsSwap(i1, i2)"); + + return testDone(); +} + diff --git a/modules/libcom/test/epicsAtomicPerform.cpp b/modules/libcom/test/epicsAtomicPerform.cpp new file mode 100644 index 000000000..2ef005efe --- /dev/null +++ b/modules/libcom/test/epicsAtomicPerform.cpp @@ -0,0 +1,574 @@ + +#include +#include +#include +#include + +#include "epicsInterrupt.h" +#include "epicsAtomic.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +using std :: size_t; +using namespace epics; +using namespace atomic; + +class RefCtr { +public: + RefCtr (); + ~RefCtr (); + void reference (); + void unreference (); +private: + size_t m_cnt; +}; + +class Ownership { +public: + Ownership (); + Ownership ( RefCtr & refCtr ); + Ownership ( const Ownership & ); + ~Ownership (); + Ownership & operator = ( const Ownership & ); +private: + RefCtr * _pRefCtr; + static RefCtr m_noOwnership; +}; + +inline RefCtr :: RefCtr () +{ + epicsAtomicSetSizeT ( & m_cnt, 0 ); +} + +inline RefCtr :: ~RefCtr () +{ + unsigned cnt = epicsAtomicGetSizeT ( & m_cnt ); + assert ( cnt == 0u ); +} + +inline void RefCtr :: reference () +{ + epicsAtomicIncrSizeT ( & m_cnt ); +} + +inline void RefCtr :: unreference () +{ + epicsAtomicDecrSizeT ( & m_cnt ); +} + +RefCtr Ownership :: m_noOwnership; + +inline Ownership :: Ownership () : + _pRefCtr ( & m_noOwnership ) +{ + m_noOwnership.reference (); +} + +inline Ownership :: Ownership ( RefCtr & refCtr ) : + _pRefCtr ( & refCtr ) +{ + refCtr.reference (); +} + +inline Ownership :: Ownership ( const Ownership & ownership ) : + _pRefCtr ( ownership._pRefCtr ) +{ + _pRefCtr->reference (); +} + +inline Ownership :: ~Ownership () +{ + _pRefCtr->unreference (); +} + +inline Ownership & Ownership :: + operator = ( const Ownership & ownership ) +{ + RefCtr * const pOldRefCtr = _pRefCtr; + _pRefCtr = ownership._pRefCtr; + _pRefCtr->reference (); + pOldRefCtr->unreference (); + return *this; +} + +inline Ownership retOwnership ( const Ownership & ownership ) +{ + return Ownership ( ownership ); +} + +inline Ownership recurRetOwner10 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( ownershipIn ) ) ) ) ); + return retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( + retOwnership ( ownership ) ) ) ) ); +} + +inline Ownership recurRetOwner100 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( ownershipIn ) ) ) ) ); + return recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( + recurRetOwner10 ( ownership ) ) ) ) ); +} + +inline Ownership recurRetOwner1000 ( const Ownership & ownershipIn ) +{ + Ownership ownership = + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( ownershipIn ) ) ) ) ); + return recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( + recurRetOwner100 ( ownership ) ) ) ) ); +} + +inline void passRefOwnership ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + ownershipOut = ownershipIn; +} + +inline void passRefOwnership10 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership ( ownershipTmp8, ownershipOut ); +} + +inline void passRefOwnership100 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership10 ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership10 ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership10 ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership10 ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership10 ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership10 ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership10 ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership10 ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership10 ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership10 ( ownershipTmp8, ownershipOut ); +} + +inline void passRefOwnership1000 ( const Ownership & ownershipIn, Ownership & ownershipOut ) +{ + Ownership ownershipTmp0; + passRefOwnership100 ( ownershipIn, ownershipTmp0 ); + Ownership ownershipTmp1; + passRefOwnership100 ( ownershipTmp0, ownershipTmp1 ); + Ownership ownershipTmp2; + passRefOwnership100 ( ownershipTmp1, ownershipTmp2 ); + Ownership ownershipTmp3; + passRefOwnership100 ( ownershipTmp2, ownershipTmp3 ); + Ownership ownershipTmp4; + passRefOwnership100 ( ownershipTmp3, ownershipTmp4 ); + Ownership ownershipTmp5; + passRefOwnership100 ( ownershipTmp4, ownershipTmp5 ); + Ownership ownershipTmp6; + passRefOwnership100 ( ownershipTmp5, ownershipTmp6 ); + Ownership ownershipTmp7; + passRefOwnership100 ( ownershipTmp6, ownershipTmp7 ); + Ownership ownershipTmp8; + passRefOwnership100 ( ownershipTmp7, ownershipTmp8 ); + passRefOwnership100 ( ownershipTmp8, ownershipOut ); +} + +time_t extTime = 0; + +template < class T > +class OrdinaryIncr { +public: + OrdinaryIncr () : m_target ( 0 ) {} + void run (); + void diagnostic ( double delay ); +private: + T m_target; +}; + +// tests the time it takes to perform a call to an external +// function and also increment an integer word. The +// epicsInterruptIsInterruptContext function is an +// out-of-line function implemented in a sharable library +// so hopefully it wont be optimized away. +template < class T > +inline void OrdinaryIncr < T > :: run () +{ + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); + m_target += epicsInterruptIsInterruptContext (); +} + +template < class T > +void OrdinaryIncr < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "raw incr of \"%s\" and a NOOP function call takes %f microseconds", + pName, delay ); +} + +template < class T > +class AtomicIncr { +public: + AtomicIncr () : m_target ( 0 ) {} + void run (); + void diagnostic ( double delay ); +private: + T m_target; +}; + +template < class T > +inline void AtomicIncr < T > :: run () +{ + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); + increment ( m_target ); +} + +template < class T > +void AtomicIncr < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "epicsAtomicIncr \"%s\" takes %f microseconds", + pName, delay ); +} + +template < class T > T trueValue (); +template < class T > T falseValue (); + +// int +template <> +inline int trueValue < int > () { return 1; } + +template <> +inline int falseValue < int > () { return 0; } + +// size_t +template <> +inline size_t trueValue < size_t > () { return 1u; } + +template <> +inline size_t falseValue < size_t > () { return 0u; } + +// EpicsAtomicPtrT +template <> +inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > () +{ static char c; return & c; } + +template <> +inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () +{ return 0u; } + +template < class T > +class AtomicCmpAndSwap { +public: + AtomicCmpAndSwap () : m_target ( 0 ) {} + void run (); + void diagnostic ( double delay ); +private: + T m_target; +}; + +template < class T > +inline void AtomicCmpAndSwap < T > :: run () +{ + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); + compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); +} + +template < class T > +void AtomicCmpAndSwap < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "epicsAtomicCmpAndSwap of \"%s\" takes %f microseconds", + pName, delay ); +} + +template < class T > +class AtomicSet { +public: + AtomicSet () : m_target ( 0 ) {} + void run (); + void diagnostic ( double delay ); +private: + T m_target; +}; + +template < class T > +inline void AtomicSet < T > :: run () +{ + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); + set ( m_target, 0 ); +} + +template < class T > +void AtomicSet < T > :: diagnostic ( double delay ) +{ + delay /= 10.0; + delay *= 1e6; + const char * const pName = typeid ( T ) . name (); + testDiag ( "epicsAtomicSet of \"%s\" takes %f microseconds", + pName, delay ); +} + +static const unsigned N = 10000; + +void recursiveOwnershipRetPerformance () +{ + RefCtr refCtr; + epicsTime begin = epicsTime::getCurrent (); + for ( size_t i = 0; i < N; i++ ) { + Ownership ownership ( refCtr ); + recurRetOwner1000 ( ownership ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "retOwnership() takes %f microseconds", delay ); +} + +void ownershipPassRefPerformance () +{ + RefCtr refCtr; + epicsTime begin = epicsTime::getCurrent (); + for ( size_t i = 0; i < N; i++ ) { + Ownership ownershipSrc ( refCtr ); + Ownership ownershipDest; + passRefOwnership1000 ( ownershipSrc, ownershipDest ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 1000u; // convert to delay per call + delay *= 1e6; // convert to micro seconds + testDiag ( "passRefOwnership() takes %f microseconds", delay ); +} + +template < class T > +class Ten +{ +public: + void run (); + void diagnostic ( double delay ); + typedef Ten < Ten < T > > Hundred; + typedef Ten < Hundred > Thousand; +private: + T m_target; +}; + +template < class T > +inline void Ten < T > :: run () +{ + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); + m_target.run (); +} + +template < class T > +void Ten < T > :: diagnostic ( double delay ) +{ + m_target.diagnostic ( delay / 10.0 ); +} + +template < class T > +void measurePerformance () +{ + epicsTime begin = epicsTime::getCurrent (); + T target; + for ( size_t i = 0; i < N; i++ ) { + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + target.run (); + } + double delay = epicsTime::getCurrent () - begin; + delay /= ( N * 10u ); // convert to delay per call + target.diagnostic ( delay ); +} + +template < class T > +void measure () +{ + measurePerformance < typename Ten < T > :: Hundred > (); +} + + +// template instances, needed for vxWorks 5.5.2 + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class AtomicSet < int >; +template class AtomicSet < size_t >; +template class AtomicSet < EpicsAtomicPtrT >; +template class OrdinaryIncr < int >; +template class OrdinaryIncr < size_t >; +template class AtomicIncr < int >; +template class AtomicIncr < size_t >; +template class AtomicCmpAndSwap < int >; +template class AtomicCmpAndSwap < size_t >; +template class AtomicCmpAndSwap < EpicsAtomicPtrT >; + +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; +template class Ten >; + +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; +template class Ten > >; + +template void measurePerformance < Ten < Ten < AtomicSet < int > > > >(void); +template void measurePerformance < Ten < Ten < AtomicSet < size_t > > > >(void); +template void measurePerformance < Ten < Ten < AtomicSet < EpicsAtomicPtrT > > > >(void); +template void measurePerformance < Ten < Ten < OrdinaryIncr < int > > > >(void); +template void measurePerformance < Ten < Ten < OrdinaryIncr < size_t > > > >(void); +template void measurePerformance < Ten < Ten < AtomicIncr < int > > > >(void); +template void measurePerformance < Ten < Ten < AtomicIncr < size_t > > > >(void); +template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < int > > > >(void); +template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < size_t > > > >(void); +template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < EpicsAtomicPtrT > > > >(void); + +template void measure < AtomicSet < int > > (void); +template void measure < AtomicSet < size_t > > (void); +template void measure < AtomicSet < EpicsAtomicPtrT > > (void); +template void measure < OrdinaryIncr < int > > (void); +template void measure < OrdinaryIncr < size_t > > (void); +template void measure < AtomicIncr < int > > (void); +template void measure < AtomicIncr < size_t > > (void); +template void measure < AtomicCmpAndSwap < int > > (void); +template void measure < AtomicCmpAndSwap < size_t > > (void); +template void measure < AtomicCmpAndSwap < EpicsAtomicPtrT > > (void); + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + + +MAIN ( epicsAtomicPerform ) +{ + testPlan ( 0 ); + // + // The tests running here are measuring fast + // functions so they tend to be impacted + // by where the cache lines are wrt to the + // virtual pages perhap + // + measure < AtomicSet < int > > (); + measure < AtomicSet < size_t > > (); + measure < AtomicSet < void * > > (); + measure < OrdinaryIncr < int > > (); + measure < OrdinaryIncr < size_t > > (); + measure < AtomicIncr < int > > (); + measure < AtomicIncr < size_t > > (); + measure < AtomicCmpAndSwap < int > > (); + measure < AtomicCmpAndSwap < size_t > > (); + measure < AtomicCmpAndSwap < void * > > (); + recursiveOwnershipRetPerformance (); + ownershipPassRefPerformance (); + return testDone(); +} diff --git a/modules/libcom/test/epicsAtomicTest.cpp b/modules/libcom/test/epicsAtomicTest.cpp new file mode 100644 index 000000000..fd9fc2e6b --- /dev/null +++ b/modules/libcom/test/epicsAtomicTest.cpp @@ -0,0 +1,426 @@ + +#include +#include + +#include "epicsAtomic.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +namespace { + +template < class T > +struct TestDataIncrDecr { + T m_testValue; + size_t m_testIterations; +}; + +template < class T > +struct TestDataAddSub { + T m_testValue; + size_t m_testIterations; + static const T delta = 17; +}; + +template < class T > +static void incr ( void *arg ) +{ + using epics::atomic::increment; + TestDataIncrDecr < T > * const pTestData = + reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); + increment ( pTestData->m_testValue ); + increment ( pTestData->m_testIterations ); +} + +template < class T > +static void decr ( void *arg ) +{ + using epics::atomic::decrement; + using epics::atomic::increment; + TestDataIncrDecr < T > * const pTestData = + reinterpret_cast < TestDataIncrDecr < T > * > ( arg ); + decrement ( pTestData->m_testValue ); + increment ( pTestData->m_testIterations ); +} + + +template < class T > +static void add ( void *arg ) +{ + using epics::atomic::add; + using epics::atomic::increment; + TestDataAddSub < T > * const pTestData = + reinterpret_cast < TestDataAddSub < T > * > ( arg ); + add ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); + increment ( pTestData->m_testIterations ); +} + +template < class T > +static void sub ( void *arg ) +{ + using epics::atomic::subtract; + using epics::atomic::increment; + TestDataAddSub < T > * const pTestData = + reinterpret_cast < TestDataAddSub < T > * > ( arg ); + subtract ( pTestData->m_testValue, TestDataAddSub < T > :: delta ); + increment ( pTestData->m_testIterations ); +} + +template < class T > +struct TestDataCAS { + T m_testValue; + size_t m_testIterationsSet; + size_t m_testIterationsNotSet; +}; + +template < class T > +static T trueValue (); +template < class T > +static T falseValue (); + +// int +template <> +inline int trueValue < int > () { return 1; } + +template <> +inline int falseValue < int > () { return 0; } + +// size_t +template <> +inline size_t trueValue < size_t > () { return 1u; } + +template <> +inline size_t falseValue < size_t > () { return 0u; } + +// EpicsAtomicPtrT +template <> +inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > () +{ static char c; return & c; } + +template <> +inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () +{ return 0u; } + +template < class T > +static void cas ( void *arg ) +{ + using epics::atomic::set; + using epics::atomic::increment; + using epics::atomic::decrement; + using epics::atomic::compareAndSwap; + + TestDataCAS < T > * const pTestData = + reinterpret_cast < TestDataCAS < T > * > ( arg ); + /* + * intentionally waste cpu and maximize + * contention for the shared data + */ + increment ( pTestData->m_testIterationsNotSet ); + while ( ! compareAndSwap ( pTestData->m_testValue, + falseValue < T > (), + trueValue < T > () ) ) { + } + decrement ( pTestData->m_testIterationsNotSet ); + set ( pTestData->m_testValue, falseValue < T > () ); + increment ( pTestData->m_testIterationsSet ); +} + +template < class T > +void testIncrDecr () +{ + using epics::atomic::set; + using epics::atomic::get; + + static const size_t N = 90; + static const T NT = static_cast < T > ( N ); + + const unsigned int stackSize = + epicsThreadGetStackSize ( epicsThreadStackSmall ); + + TestDataIncrDecr < T > testData = { 0, N }; + set ( testData.m_testValue, NT ); + testOk ( get ( testData.m_testValue ) == NT, + "get returns initial incr/decr test data value that was set" ); + set ( testData.m_testIterations, 0u ); + testOk ( get ( testData.m_testIterations ) == 0u, + "get returns initial incr/decr test thread iterations value that was set" ); + for ( size_t i = 0u; i < N; i++ ) { + epicsThreadMustCreate ( "incr", + 50, stackSize, incr < T >, & testData ); + epicsThreadMustCreate ( "decr", + 50, stackSize, decr < T >, & testData ); + if(i%10==0) + testDiag("iteration %u", (unsigned)i); + } + while ( testData.m_testIterations < 2 * N ) { + epicsThreadSleep ( 0.01 ); + } + testOk ( get ( testData.m_testIterations ) == 2 * N, + "proper number of incr/decr test thread iterations" ); + testOk ( get ( testData.m_testValue ) == NT, + "proper final incr/decr test value" ); +} + +template < class T > +void testAddSub () +{ + using epics::atomic::set; + using epics::atomic::get; + + static const size_t N = 90; + static const T NDT = TestDataAddSub < T > :: delta * + static_cast < T > ( N ); + + const unsigned int stackSize = + epicsThreadGetStackSize ( epicsThreadStackSmall ); + + TestDataIncrDecr < T > testData = { 0, N }; + set ( testData.m_testValue, NDT ); + testOk ( get ( testData.m_testValue ) == NDT, + "get returns initial add/sub test data value that was set" ); + set ( testData.m_testIterations, 0u ); + testOk ( get ( testData.m_testIterations ) == 0u, + "get returns initial incr/decr test thread iterations value that was set" ); + for ( size_t i = 0u; i < N; i++ ) { + epicsThreadMustCreate ( "add", + 50, stackSize, add < T >, & testData ); + epicsThreadMustCreate ( "sub", + 50, stackSize, sub < T >, & testData ); + } + while ( testData.m_testIterations < 2 * N ) { + epicsThreadSleep ( 0.01 ); + } + testOk ( get ( testData.m_testIterations ) == 2 * N, + "proper number of add/sub test thread iterations" ); + testOk ( get ( testData.m_testValue ) == NDT, + "proper final add/sub test value" ); +} + +template < class T > +void testCAS () +{ + using epics::atomic::set; + using epics::atomic::get; + + static const size_t N = 10; + + const unsigned int stackSize = + epicsThreadGetStackSize ( epicsThreadStackSmall ); + + TestDataCAS < T > testData = { 0, N, N }; + set ( testData.m_testIterationsSet, 0 ); + testOk ( get ( testData.m_testIterationsSet ) == 0u, + "get returns initial CAS test thread " + "iterations set value" ); + set ( testData.m_testIterationsNotSet, 0 ); + testOk ( get ( testData.m_testIterationsNotSet ) == 0u, + "get returns initial CAS test thread " + "iterations not set value" ); + set ( testData.m_testValue, trueValue < T > () ); + testOk ( get ( testData.m_testValue ) == trueValue < T > (), + "set/get a true value" ); + for ( size_t i = 0u; i < N; i++ ) { + epicsThreadMustCreate ( "tns", + 50, stackSize, cas < T >, & testData ); + } + set ( testData.m_testValue, falseValue < T > () ); + while ( testData.m_testIterationsSet < N ) { + epicsThreadSleep ( 0.01 ); + } + testOk ( get ( testData.m_testIterationsSet ) == N, + "proper number of CAS test thread set iterations" ); + testOk ( get ( testData.m_testIterationsNotSet ) == 0u, + "proper number of CAS test thread not set iterations" ); +} + +// template instances, needed for vxWorks 5.5.2 + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template void incr < int > (void *); +template void decr < int > (void *); +template void incr < size_t > (void *); +template void decr < size_t > (void *); +template void add < int > (void *); +template void sub < int > (void *); +template void add < size_t > (void *); +template void sub < size_t > (void *); +template void cas < int > (void *); +template void cas < size_t > (void *); +template void cas < EpicsAtomicPtrT > (void *); + +template void testIncrDecr < int > (void); +template void testIncrDecr < size_t > (void); +template void testAddSub < int > (void); +template void testAddSub < size_t > (void); +template void testCAS < int > (void); +template void testCAS < size_t > (void); +template void testCAS < EpicsAtomicPtrT > (void); + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +static void testClassify() +{ + testDiag("Classify Build conditions"); +#ifdef EPICS_ATOMIC_CMPLR_NAME + testDiag("Compiler dependent impl name %s", EPICS_ATOMIC_CMPLR_NAME); +#else + testDiag("Compiler dependent impl name undefined"); +#endif +#ifdef EPICS_ATOMIC_OS_NAME + testDiag("OS dependent impl name %s", EPICS_ATOMIC_OS_NAME); +#else + testDiag("OS dependent impl name undefined"); +#endif + +#ifdef __GNUC__ +#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER + testDiag("GCC using atomic builtin memory barrier"); +#else + testDiag("GCC using asm memory barrier"); +#endif +#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER + testDiag("GCC use builtin for int"); +#endif +#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER + testDiag("GCC use builtin for size_t"); +#endif + +#ifndef EPICS_ATOMIC_INCR_INTT + testDiag("Use default epicsAtomicIncrIntT()"); +#endif +#ifndef EPICS_ATOMIC_INCR_SIZET + testDiag("Use default epicsAtomicIncrSizeT()"); +#endif +#ifndef EPICS_ATOMIC_DECR_INTT + testDiag("Use default epicsAtomicDecrIntT()"); +#endif +#ifndef EPICS_ATOMIC_DECR_SIZET + testDiag("Use default epicsAtomicDecrSizeT()"); +#endif +#ifndef EPICS_ATOMIC_ADD_INTT + testDiag("Use default epicsAtomicAddIntT()"); +#endif +#ifndef EPICS_ATOMIC_ADD_SIZET + testDiag("Use default epicsAtomicAddSizeT()"); +#endif +#ifndef EPICS_ATOMIC_SUB_SIZET + testDiag("Use default epicsAtomicSubSizeT()"); +#endif +#ifndef EPICS_ATOMIC_SET_INTT + testDiag("Use default epicsAtomicSetIntT()"); +#endif +#ifndef EPICS_ATOMIC_SET_SIZET + testDiag("Use default epicsAtomicSetSizeT()"); +#endif +#ifndef EPICS_ATOMIC_SET_PTRT + testDiag("Use default epicsAtomicSetPtrT()"); +#endif +#ifndef EPICS_ATOMIC_GET_INTT + testDiag("Use default epicsAtomicGetIntT()"); +#endif +#ifndef EPICS_ATOMIC_GET_SIZET + testDiag("Use default epicsAtomicGetSizeT()"); +#endif +#ifndef EPICS_ATOMIC_GET_PTRT + testDiag("Use default epicsAtomicGetPtrT()"); +#endif +#ifndef EPICS_ATOMIC_CAS_INTT + testDiag("Use default epicsAtomicCmpAndSwapIntT()"); +#endif +#ifndef EPICS_ATOMIC_CAS_SIZET + testDiag("Use default epicsAtomicCmpAndSwapSizeT()"); +#endif +#ifndef EPICS_ATOMIC_CAS_PTRT + testDiag("Use default epicsAtomicCmpAndSwapPtrT()"); +#endif +#endif /* __GNUC__ */ +} + +static +void testBasic() +{ + using epics::atomic::set; + using epics::atomic::get; + using epics::atomic::decrement; + using epics::atomic::increment; + using epics::atomic::add; + using epics::atomic::subtract; + using epics::atomic::compareAndSwap; + + testDiag("Test basic operation symantics"); + + int Int = 0; + size_t Sizet = 0; + void *voidp = NULL; + + set(Int, -42); + set(Sizet, 42); + set(voidp, (void*)&voidp); + + increment(Int); + increment(Sizet); + + testOk1(get(Int)==-41); + testOk1(get(Sizet)==43); + testOk1(get(voidp)==(void*)&voidp); + + decrement(Int); + decrement(Sizet); + + testOk1(get(Int)==-42); + testOk1(get(Sizet)==42); + + add(Int, -2); + subtract(Sizet, 2); + + testOk1(get(Int)==-44); + testOk1(get(Sizet)==40); + + testOk1(compareAndSwap(Int, -34, -10)==-44); + testOk1(compareAndSwap(Sizet, 34, 10)==40); + testOk1(compareAndSwap(voidp, NULL, (void*)&Sizet)==(void*)&voidp); + + testOk1(get(Int)==-44); + testOk1(get(Sizet)==40); + testOk1(get(voidp)==(void*)&voidp); + + testOk1(compareAndSwap(Int, -44, -10)==-44); + testOk1(compareAndSwap(Sizet, 40, 10)==40); + testOk1(compareAndSwap(voidp, (void*)&voidp, (void*)&Sizet)==(void*)&voidp); + + testOk1(get(Int)==-10); + testOk1(get(Sizet)==10); + testOk1(get(voidp)==(void*)&Sizet); +} + +} // namespace + +MAIN ( epicsAtomicTest ) +{ + + testPlan ( 50 ); + testDiag("In %s", EPICS_FUNCTION); + testClassify (); + testBasic(); +#if defined(__rtems__) + testSkip(31, "Tests assume time sliced thread scheduling"); +#else + testIncrDecr < int > (); + testIncrDecr < size_t > (); + testAddSub < int > (); + testAddSub < size_t > (); + testCAS < int > (); + testCAS < size_t > (); + testCAS < EpicsAtomicPtrT > (); +#endif + + return testDone (); +} + diff --git a/modules/libcom/test/epicsCalcTest.cpp b/modules/libcom/test/epicsCalcTest.cpp new file mode 100644 index 000000000..1ebc0c578 --- /dev/null +++ b/modules/libcom/test/epicsCalcTest.cpp @@ -0,0 +1,992 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// Author: Andrew Johnson + +#include +#include + +#include "epicsUnitTest.h" +#include "epicsTypes.h" +#include "epicsMath.h" +#include "epicsAlgorithm.h" +#include "postfix.h" +#include "testMain.h" + +/* Infrastructure for running tests */ + +double doCalc(const char *expr) { + /* Evaluate expression, return result */ + double args[CALCPERFORM_NARGS] = { + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + }; + char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); + short err; + double result = 0.0; + result /= result; /* Start as NaN */ + + if(!rpn) { + testAbort("postfix: %s no memory", expr); + return epicsNAN; + } + + if (postfix(expr, rpn, &err)) { + testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); + } else + if (calcPerform(args, &result, rpn) && finite(result)) { + testDiag("calcPerform: error evaluating '%s'", expr); + } + free(rpn); + return result; +} + +void testCalc(const char *expr, double expected) { + /* Evaluate expression, test against expected result */ + bool pass = false; + double args[CALCPERFORM_NARGS] = { + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + }; + char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); + short err; + double result = 0.0; + result /= result; /* Start as NaN */ + + if(!rpn) { + testFail("postfix: %s no memory", expr); + return; + } + + if (postfix(expr, rpn, &err)) { + testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); + } else + if (calcPerform(args, &result, rpn) && finite(result)) { + testDiag("calcPerform: error evaluating '%s'", expr); + } + + if (finite(expected) && finite(result)) { + pass = fabs(expected - result) < 1e-8; + } else if (isnan(expected)) { + pass = (bool) isnan(result); + } else { + pass = (result == expected); + } + if (!testOk(pass, "%s", expr)) { + testDiag("Expected result is %g, actually got %g", expected, result); + calcExprDump(rpn); + } + free(rpn); +} + +void testUInt32Calc(const char *expr, epicsUInt32 expected) { + /* Evaluate expression, test against expected result */ + bool pass = false; + double args[CALCPERFORM_NARGS] = { + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + }; + char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); + short err; + epicsUInt32 uresult; + double result = 0.0; + result /= result; /* Start as NaN */ + + if(!rpn) { + testFail("postfix: %s no memory", expr); + return; + } + + if (postfix(expr, rpn, &err)) { + testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); + } else + if (calcPerform(args, &result, rpn) && finite(result)) { + testDiag("calcPerform: error evaluating '%s'", expr); + } + + uresult = (epicsUInt32) result; + pass = (uresult == expected); + if (!testOk(pass, "%s", expr)) { + testDiag("Expected result is 0x%x (%u), actually got 0x%x (%u)", + expected, expected, uresult, uresult); + calcExprDump(rpn); + } + free(rpn); +} + +void testArgs(const char *expr, unsigned long einp, unsigned long eout) { + char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); + short err = 0; + unsigned long vinp, vout; + + if(!rpn) { + testFail("postfix: %s no memory", expr); + return; + } + + if (postfix(expr, rpn, &err)) { + testFail("postfix: %s in expression '%s'", calcErrorStr(err), expr); + return; + } + if (calcArgUsage(rpn, &vinp, &vout)) { + testFail("calcArgUsage returned error for '%s'", expr); + return; + } + if (!testOk(vinp == einp && vout == eout, "Args for '%s'", expr)) { + testDiag("Expected (%lx, %lx) got (%lx, %lx)", einp, eout, vinp, vout); + } + free(rpn); +} + +void testBadExpr(const char *expr, short expected_err) { + /* Parse an invalid expression, test against expected error code */ + char *rpn = (char*)malloc(INFIX_TO_POSTFIX_SIZE(strlen(expr)+1)); + short err = 0; + + if(!rpn) { + testFail("postfix: %s no memory", expr); + return; + } + + postfix(expr, rpn, &err); + if (!testOk(err == expected_err, "Bad expression '%s'", expr)) { + testDiag("Expected '%s', actually got '%s'", + calcErrorStr(expected_err), calcErrorStr(err)); + calcExprDump(rpn); + } + free(rpn); +} + +/* Test an expression that is also valid C code */ +#define testExpr(expr) testCalc(#expr, expr); + +/* These are the argument bits for testArgs */ +#define A_A 0x001 +#define A_B 0x002 +#define A_C 0x004 +#define A_D 0x008 +#define A_E 0x010 +#define A_F 0x020 +#define A_G 0x040 +#define A_H 0x080 +#define A_I 0x100 +#define A_J 0x200 +#define A_K 0x400 +#define A_L 0x800 + + +/* Macros and functions to make some expressions into valid C code */ + +#ifndef PI +#define PI 3.14159265358979 +#endif +#define Inf epicsINF +#define NaN epicsNAN +#define D2R (PI/180.) +#define R2D (180./PI) +#define ABS(x) fabs(x) +#define AND & +#define ATAN2(x,y) atan2(y,x) +#define LN(x) log(x) +#define LOG(x) log10(x) +#define LOGE(x) log(x) +#define NINT(x) (double)(long)((x) >= 0 ? (x)+0.5 : (x)-0.5) +#define OR | +#define SQR(x) sqrt(x) +#define XOR ^ + +static inline double MAX(double a) { + return a; +} +static inline double MAX(double a, double b) { + return epicsMax(a,b); +} +static inline double MAX(double a, double b, double c) { + return MAX(MAX(a,b),c); +} +static inline double MAX(double a, double b, double c, double d) { + return MAX(MAX(a,b,c),d); +} +static inline double MAX(double a, double b, double c, double d, double e) { + return MAX(MAX(a,b,c,d),e); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f) { + return MAX(MAX(a,b,c,d,e),f); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g) { + return MAX(MAX(a,b,c,d,e,f),g); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h) { + return MAX(MAX(a,b,c,d,e,f,g),h); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i) { + return MAX(MAX(a,b,c,d,e,f,g,h),i); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return MAX(MAX(a,b,c,d,e,f,g,h,i),j); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k) { + return MAX(MAX(a,b,c,d,e,f,g,h,i,j),k); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k, double l) { + return MAX(MAX(a,b,c,d,e,f,g,h,i,j,k),l); +} + +static inline double MIN(double a) { + return a; +} +static inline double MIN(double a, double b) { + return epicsMin(a,b); +} +static inline double MIN(double a, double b, double c) { + return MIN(MIN(a,b),c); +} +static inline double MIN(double a, double b, double c, double d) { + return MIN(MIN(a,b,c),d); +} +static inline double MIN(double a, double b, double c, double d, double e) { + return MIN(MIN(a,b,c,d),e); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f) { + return MIN(MIN(a,b,c,d,e),f); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g) { + return MIN(MIN(a,b,c,d,e,f),g); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h) { + return MIN(MIN(a,b,c,d,e,f,g),h); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i) { + return MIN(MIN(a,b,c,d,e,f,g,h),i); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return MIN(MIN(a,b,c,d,e,f,g,h,i),j); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k) { + return MIN(MIN(a,b,c,d,e,f,g,h,i,j),k); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k, double l) { + return MIN(MIN(a,b,c,d,e,f,g,h,i,j,k),l); +} + +/* The test code below generates lots of spurious warnings because + * it's making sure that our operator priorities match those of C. + * Disable them to quieten the compilation process where possible. + */ +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +# pragma GCC diagnostic ignored "-Wparentheses" +#endif + +MAIN(epicsCalcTest) +{ + int repeat; + const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, + g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; + + testPlan(613); + + /* LITERAL_OPERAND elements */ + testExpr(0); + testExpr(1); + testExpr(2); + testExpr(3); + testExpr(4); + testExpr(5); + testExpr(6); + testExpr(7); + testExpr(8); + testExpr(9); + testExpr(.1); + testExpr(0.1); + testExpr(0X0); + testExpr(0x10); + testExpr(0x7fffffff); + testCalc("0x80000000", -2147483648.0); + testCalc("0xffffffff", -1); + testExpr(Inf); + testCalc("Infinity", Inf); + testExpr(NaN); + + /* OPERAND elements */ + testExpr(a); + testExpr(b); + testExpr(c); + testExpr(d); + testExpr(e); + testExpr(f); + testExpr(g); + testExpr(h); + testExpr(i); + testExpr(j); + testExpr(k); + testExpr(l); + testExpr(PI); + testExpr(D2R); + testExpr(R2D); + + for (repeat=0; repeat<100; repeat++) { + double res = doCalc("rndm"); + if (res<0 || res >1) { + testDiag("rndm returned %g", res); + break; + } + } + testOk(repeat == 100, "rndm"); + + /* UNARY_MINUS element */ + testExpr(-1); + testExpr(-Inf); + testExpr(- -1); + testCalc("-0x80000000", 2147483648.0); + + /* UNARY_OPERATOR elements */ + testExpr((1)); + testExpr(!0); + testExpr(!1); + testExpr(!!0); + testExpr(ABS(1.0)); + testExpr(ABS(-1.)); + testExpr(acos(1.)); + testExpr(asin(0.5)); + testExpr(atan(0.5)); + testExpr(ATAN2(1., 2.)); + testExpr(ceil(0.5)); + testExpr(cos(0.5)); + testExpr(cosh(0.5)); + testExpr(exp(1.)); + testExpr(floor(1.5)); + + testExpr(finite(0.)); + testExpr(finite(Inf)); + testExpr(finite(-Inf)); + testExpr(finite(NaN)); + testCalc("finite(0,1,2)", 1); + testCalc("finite(0,1,NaN)", 0); + testCalc("finite(0,NaN,2)", 0); + testCalc("finite(NaN,1,2)", 0); + testCalc("finite(0,1,Inf)", 0); + testCalc("finite(0,Inf,2)", 0); + testCalc("finite(Inf,1,2)", 0); + testCalc("finite(0,1,-Inf)", 0); + testCalc("finite(0,-Inf,2)", 0); + testCalc("finite(-Inf,1,2)", 0); + testExpr(isinf(0.)); + testExpr(isinf(Inf)); + testExpr(!!isinf(-Inf)); // Some GCCs return -1/0/+1 rather than 0/+1 + testExpr(isinf(NaN)); + testExpr(isnan(0.)); + testExpr(isnan(Inf)); + testExpr(isnan(-Inf)); + testExpr(isnan(NaN)); + testCalc("isnan(0,1,2)", 0); + testCalc("isnan(0,1,NaN)", 1); + testCalc("isnan(0,NaN,2)", 1); + testCalc("isnan(NaN,1,2)", 1); + testCalc("isnan(0,1,Inf)", 0); + testCalc("isnan(0,Inf,2)", 0); + testCalc("isnan(Inf,1,2)", 0); + testCalc("isnan(0,1,-Inf)", 0); + testCalc("isnan(0,-Inf,2)", 0); + testCalc("isnan(-Inf,1,2)", 0); + + testExpr(LN(5.)); + testExpr(LOG(5.)); + testExpr(LOGE(2.)); + + testExpr(MAX(-99)); + testExpr(MAX( 1., 2.)); + testExpr(MAX( 1., Inf)); + testExpr(MAX( 1.,-Inf)); + testExpr(MAX( 1., NaN)); + testExpr(MAX( Inf, 1.)); + testExpr(MAX(-Inf, 1.)); + testExpr(MAX( NaN, 1.)); + testExpr(MAX( 1., 2.,3.)); + testExpr(MAX( 1., 3.,2.)); + testExpr(MAX( 2., 1.,3.)); + testExpr(MAX( 2., 3.,1.)); + testExpr(MAX( 3., 1.,2.)); + testExpr(MAX( 3., 2.,1.)); + testExpr(MAX( 1., 2., Inf)); + testExpr(MAX( 1., 2.,-Inf)); + testExpr(MAX( 1., 2., NaN)); + testExpr(MAX( 1., Inf,2.)); + testExpr(MAX( 1.,-Inf,2.)); + testExpr(MAX( 1., NaN,2.)); + testExpr(MAX( Inf, 1.,2.)); + testExpr(MAX(-Inf, 1.,2.)); + testExpr(MAX( NaN, 1.,2.)); + testExpr(MAX( 1., 2., 3., 4.)); + testExpr(MAX( 1., 2., 4., 3.)); + testExpr(MAX( 1., 4., 3., 2.)); + testExpr(MAX( 4., 2., 3., 1.)); + testExpr(MAX( 1., 2., 3.,NaN)); + testExpr(MAX( 1., 2.,NaN, 3.)); + testExpr(MAX( 1.,NaN, 3., 2.)); + testExpr(MAX(NaN, 2., 3., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.)); + testExpr(MAX( 1., 2., 3., 5., 4.)); + testExpr(MAX( 1., 2., 5., 4., 3.)); + testExpr(MAX( 1., 5., 3., 4., 2.)); + testExpr(MAX( 5., 2., 3., 4., 1.)); + testExpr(MAX( 1., 2., 3., 4.,NaN)); + testExpr(MAX( 1., 2., 3.,NaN, 4.)); + testExpr(MAX( 1., 2.,NaN, 4., 3.)); + testExpr(MAX( 1.,NaN, 3., 4., 2.)); + testExpr(MAX(NaN, 2., 3., 4., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5., 6.)); + testExpr(MAX( 1., 2., 3., 4., 6., 5.)); + testExpr(MAX( 1., 2., 3., 6., 5., 4.)); + testExpr(MAX( 1., 2., 6., 4., 5., 3.)); + testExpr(MAX( 1., 6., 3., 4., 5., 2.)); + testExpr(MAX( 6., 2., 3., 4., 5., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.,NaN)); + testExpr(MAX( 1., 2., 3., 4.,NaN, 5.)); + testExpr(MAX( 1., 2., 3.,NaN, 5., 4.)); + testExpr(MAX( 1., 2.,NaN, 4., 5., 3.)); + testExpr(MAX( 1.,NaN, 3., 4., 5., 2.)); + testExpr(MAX(NaN, 2., 3., 4., 5., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.,Inf)); + testExpr(MAX( 1., 2., 3., 4.,Inf, 5.)); + testExpr(MAX( 1., 2., 3.,Inf, 5., 4.)); + testExpr(MAX( 1., 2.,Inf, 4., 5., 3.)); + testExpr(MAX( 1.,Inf, 3., 4., 5., 2.)); + testExpr(MAX(Inf, 2., 3., 4., 5., 1.)); + testExpr(MAX(1,2,3,4,5,6,7,8,9,10,11,12)); + testExpr(MAX(5,4,3,2,1,0,-1,-2,-3,-4,-5,-6)); + testExpr(MAX(-1,1,0)); + + testExpr(MIN(99)); + testExpr(MIN(1.,2.)); + testExpr(MIN(1.,Inf)); + testExpr(MIN(1.,-Inf)); + testExpr(MIN(1.,NaN)); + testExpr(MIN(NaN,1.)); + testExpr(MIN( 1., 2.,3.)); + testExpr(MIN( 1., 3.,2.)); + testExpr(MIN( 2., 1.,3.)); + testExpr(MIN( 2., 3.,1.)); + testExpr(MIN( 3., 1.,2.)); + testExpr(MIN( 3., 2.,1.)); + testExpr(MIN( 1., 2., Inf)); + testExpr(MIN( 1., 2.,-Inf)); + testExpr(MIN( 1., 2., NaN)); + testExpr(MIN( 1., Inf,2.)); + testExpr(MIN( 1.,-Inf,2.)); + testExpr(MIN( 1., NaN,2.)); + testExpr(MIN( Inf, 1.,2.)); + testExpr(MIN(-Inf, 1.,2.)); + testExpr(MIN( NaN, 1.,2.)); + testExpr(MIN( 1., 2., 3., 4.)); + testExpr(MIN( 1., 2., 4., 3.)); + testExpr(MIN( 1., 4., 3., 2.)); + testExpr(MIN( 4., 2., 3., 1.)); + testExpr(MIN( 1., 2., 3.,NaN)); + testExpr(MIN( 1., 2.,NaN, 3.)); + testExpr(MIN( 1.,NaN, 3., 2.)); + testExpr(MIN(NaN, 2., 3., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.)); + testExpr(MIN( 1., 2., 3., 5., 4.)); + testExpr(MIN( 1., 2., 5., 4., 3.)); + testExpr(MIN( 1., 5., 3., 4., 2.)); + testExpr(MIN( 5., 2., 3., 4., 1.)); + testExpr(MIN( 1., 2., 3., 4.,NaN)); + testExpr(MIN( 1., 2., 3.,NaN, 4.)); + testExpr(MIN( 1., 2.,NaN, 4., 3.)); + testExpr(MIN( 1.,NaN, 3., 4., 2.)); + testExpr(MIN(NaN, 2., 3., 4., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5., 6.)); + testExpr(MIN( 2., 1., 3., 4., 5., 6.)); + testExpr(MIN( 3., 2., 1., 4., 5., 6.)); + testExpr(MIN( 4., 2., 3., 1., 5., 6.)); + testExpr(MIN( 5., 2., 3., 4., 1., 6.)); + testExpr(MIN( 6., 2., 3., 4., 5., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.,NaN)); + testExpr(MIN( 1., 2., 3., 4.,NaN, 5.)); + testExpr(MIN( 1., 2., 3.,NaN, 5., 4.)); + testExpr(MIN( 1., 2.,NaN, 4., 5., 3.)); + testExpr(MIN( 1.,NaN, 3., 4., 5., 2.)); + testExpr(MIN(NaN, 2., 3., 4., 5., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.,-Inf)); + testExpr(MIN( 1., 2., 3., 4.,-Inf, 5.)); + testExpr(MIN( 1., 2., 3.,-Inf, 5., 4.)); + testExpr(MIN( 1., 2.,-Inf, 4., 5., 3.)); + testExpr(MIN( 1.,-Inf, 3., 4., 5., 2.)); + testExpr(MIN(-Inf, 2., 3., 4., 5., 1.)); + testExpr(MIN(1,2,3,4,5,6,7,8,9,10,11,12)); + testExpr(MIN(5,4,3,2,1,0,-1,-2,-3,-4,-5,-6)); + testExpr(MIN(1,-1,0)); + testExpr(MAX(MIN(0,2),MAX(0),MIN(3,2,1))); + + testExpr(NINT(0.4)); + testExpr(NINT(0.6)); + testExpr(NINT(-0.4)); + testExpr(NINT(-0.6)); + testExpr(sin(0.5)); + testExpr(sinh(0.5)); + testExpr(SQR(10.)); + testExpr(sqrt(16.)); + testExpr(tan(0.5)); + testExpr(tanh(0.5)); + testExpr(~5); + testExpr(~~5); + + /* BINARY_OPERATOR elements */ + testExpr(0 != 1); + testExpr(0 != 0); + testExpr(1 != 0); + testExpr(1 != 0 != 2); + testExpr(0.0 != Inf); + testExpr(0.0 != -Inf); + testExpr(0.0 != NaN); + testExpr(Inf != 0.0); + testExpr(Inf != Inf); + testExpr(Inf != -Inf); + testExpr(Inf != NaN); + testExpr(-Inf != 0.0); + testExpr(-Inf != Inf); + testExpr(-Inf != -Inf); + testExpr(-Inf != NaN); + testExpr(NaN != 0.0); + testExpr(NaN != Inf); + testExpr(NaN != -Inf); + testExpr(NaN != NaN); + + testCalc("0 # 1", 0 != 1); + testCalc("0 # 0", 0 != 0); + testCalc("1 # 0", 1 != 0); + testCalc("1 # 0 # 2", 1 != 0 != 2); + + testExpr(7 % 4); + testExpr(-7 % 4); + testExpr(63 % 16 % 6) + testCalc("1 % 0", NaN); + + testExpr(7 & 4); + + testExpr(0 && 0); + testExpr(0 && 1); + testExpr(1 && 0); + testExpr(1 && 1); + + testExpr(2 * 2); + testExpr(0.0 * Inf); + testExpr(0.0 * -Inf); + testExpr(0.0 * NaN); + testExpr(Inf * 0.0); + testExpr(Inf * Inf); + testExpr(Inf * -Inf); + testExpr(Inf * NaN); + testExpr(-Inf * 0.0); + testExpr(-Inf * Inf); + testExpr(-Inf * -Inf); + testExpr(-Inf * NaN); + testExpr(NaN * 0.0); + testExpr(NaN * Inf); + testExpr(NaN * -Inf); + testExpr(NaN * NaN); + + testCalc("2 ** 0.2", pow(2., 0.2)); + testCalc("2 ** -0.2", pow(2., -0.2)); + testCalc("-0.2 ** 2", pow(-0.2, 2.)); + testCalc("-0.2 ** -2", pow(-0.2, -2)); + testCalc("2 ** 2 ** 3", pow(pow(2., 2.), 3.)); + + testExpr(0 + 1); + testExpr(0.0 + Inf); + testExpr(0.0 + -Inf); + testExpr(0.0 + NaN); + testExpr(Inf + 0.0); + testExpr(Inf + Inf); +#if defined(_WIN64) && defined(_MSC_VER) + testCalc("Inf + -Inf", NaN); +#else + testExpr(Inf + -Inf); +#endif + testExpr(Inf + NaN); + testExpr(-Inf + 0.0); +#if defined(_WIN64) && defined(_MSC_VER) + testCalc("-Inf + Inf", NaN); +#else + testExpr(-Inf + Inf); +#endif + testExpr(-Inf + -Inf); + testExpr(-Inf + NaN); + testExpr(NaN + 0.0); + testExpr(NaN + Inf); + testExpr(NaN + -Inf); + testExpr(NaN + NaN); + + testExpr(0 - 1); + testExpr(0 - 1 - 2); + testExpr(0.0 - Inf); + testExpr(0.0 - -Inf); + testExpr(0.0 - NaN); + testExpr(Inf - 0.0); + testExpr(Inf - Inf); + testExpr(Inf - -Inf); + testExpr(Inf - NaN); + testExpr(-Inf - 0.0); + testExpr(-Inf - Inf); + testExpr(-Inf - -Inf); + testExpr(-Inf - NaN); + testExpr(NaN - 0.0); + testExpr(NaN - Inf); + testExpr(NaN - -Inf); + testExpr(NaN - NaN); + + testExpr(2.0 / 3.0); + testExpr(1.0 / 2.0 / 3.0); + testExpr(0.0 / Inf); + testExpr(0.0 / -Inf); + testExpr(0.0 / NaN); + testExpr(Inf / 1.0); + testExpr(Inf / Inf); + testExpr(Inf / -Inf); + testExpr(Inf / NaN); + testExpr(-Inf / 1.0); + testExpr(-Inf / Inf); + testExpr(-Inf / -Inf); + testExpr(-Inf / NaN); + testExpr(NaN / 1.0); + testExpr(NaN / Inf); + testExpr(NaN / -Inf); + testExpr(NaN / NaN); + + testExpr(0 < 1); + testExpr(0 < 0); + testExpr(1 < 0); + testExpr(2 < 0 < 2) + testExpr(0.0 < Inf); + testExpr(0.0 < -Inf); + testExpr(0.0 < NaN); + testExpr(Inf < 0.0); + testExpr(Inf < Inf); + testExpr(Inf < -Inf); + testExpr(Inf < NaN); + testExpr(-Inf < 0.0); + testExpr(-Inf < Inf); + testExpr(-Inf < -Inf); + testExpr(-Inf < NaN); + testExpr(NaN < 0.0); + testExpr(NaN < Inf); + testExpr(NaN < -Inf); + testExpr(NaN < NaN); + + testExpr(1 << 2); + testExpr(1 << 3 << 2) + + testExpr(0 <= 1); + testExpr(0 <= 0); + testExpr(1 <= 0); + testExpr(3 <= 2 <= 3) + testExpr(0.0 <= Inf); + testExpr(0.0 <= -Inf); + testExpr(0.0 <= NaN); + testExpr(Inf <= 0.0); + testExpr(Inf <= Inf); + testExpr(Inf <= -Inf); + testExpr(Inf <= NaN); + testExpr(-Inf <= 0.0); + testExpr(-Inf <= Inf); + testExpr(-Inf <= -Inf); + testExpr(-Inf <= NaN); + testExpr(NaN <= 0.0); + testExpr(NaN <= Inf); + testExpr(NaN <= -Inf); + testExpr(NaN <= NaN); + + testCalc("0 = 1", 0 == 1); + testCalc("0 = 0", 0 == 0); + testCalc("1 = 0", 1 == 0); + testCalc("2 = 2 = 1", 2 == 2 == 1); + + testExpr(0 == 1); + testExpr(0 == 0); + testExpr(1 == 0); + testExpr(2 == 2 == 1); + testExpr(0.0 == Inf); + testExpr(0.0 == -Inf); + testExpr(0.0 == NaN); + testExpr(Inf == 0.0); + testExpr(Inf == Inf); + testExpr(Inf == -Inf); + testExpr(Inf == NaN); + testExpr(-Inf == 0.0); + testExpr(-Inf == Inf); + testExpr(-Inf == -Inf); + testExpr(-Inf == NaN); + testExpr(NaN == 0.0); + testExpr(NaN == Inf); + testExpr(NaN == -Inf); + testExpr(NaN == NaN); + + testExpr(0 > 1); + testExpr(0 > 0); + testExpr(1 > 0); + testExpr(2 > 0 > 2); + testExpr(0.0 > Inf); + testExpr(0.0 > -Inf); + testExpr(0.0 > NaN); + testExpr(Inf > 0.0); + testExpr(Inf > Inf); + testExpr(Inf > -Inf); + testExpr(Inf > NaN); + testExpr(-Inf > 0.0); + testExpr(-Inf > Inf); + testExpr(-Inf > -Inf); + testExpr(-Inf > NaN); + testExpr(NaN > 0.0); + testExpr(NaN > Inf); + testExpr(NaN > -Inf); + testExpr(NaN > NaN); + + testExpr(0 >= 1); + testExpr(0 >= 0); + testExpr(1 >= 0); + testExpr(3 >= 2 >= 3); + testExpr(0.0 >= Inf); + testExpr(0.0 >= -Inf); + testExpr(0.0 >= NaN); + testExpr(Inf >= 0.0); + testExpr(Inf >= Inf); + testExpr(Inf >= -Inf); + testExpr(Inf >= NaN); + testExpr(-Inf >= 0.0); + testExpr(-Inf >= Inf); + testExpr(-Inf >= -Inf); + testExpr(-Inf >= NaN); + testExpr(NaN >= 0.0); + testExpr(NaN >= Inf); + testExpr(NaN >= -Inf); + testExpr(NaN >= NaN); + + testExpr(8 >> 1); + testExpr(64 >> 2 >> 1); + + testExpr(7 AND 4); + + testExpr(1 OR 8); + + testExpr(3 XOR 9); + + testCalc("2 ^ 0.2", pow(2., 0.2)); + testCalc("2 ^ -0.2", pow(2., -0.2)); + testCalc("(-0.2) ^ 2", pow(-0.2, 2.)); + testCalc("(-0.2) ^ -2", pow(-0.2, -2.)); + testCalc("2 ^ 2 ^ 3", pow(pow(2., 2.), 3.)); + + testExpr(1 | 8); + + testExpr(0 || 0); + testExpr(0 || 1); + testExpr(1 || 0); + testExpr(1 || 1); + + /* CONDITIONAL elements */ + testExpr(0 ? 1 : 2); + testExpr(1 ? 1 : 2); + testExpr(Inf ? 1 : 2); + testExpr(NaN ? 1 : 2); + testExpr(0 ? 0 ? 2 : 3 : 4); + testExpr(0 ? 1 ? 2 : 3 : 4); + testExpr(1 ? 0 ? 2 : 3 : 4); + testExpr(1 ? 1 ? 2 : 3 : 4); + testExpr(0 ? 2 : 0 ? 3 : 4); + testExpr(0 ? 2 : 1 ? 3 : 4); + testExpr(1 ? 2 : 0 ? 3 : 4); + testExpr(1 ? 2 : 1 ? 3 : 4); + + /* STORE_OPERATOR and EXPR_TERM elements*/ + testCalc("a := 0; a", 0); + testCalc("b := 0; b", 0); + testCalc("c := 0; c", 0); + testCalc("d := 0; d", 0); + testCalc("e := 0; e", 0); + testCalc("f := 0; f", 0); + testCalc("g := 0; g", 0); + testCalc("h := 0; h", 0); + testCalc("i := 0; i", 0); + testCalc("j := 0; j", 0); + testCalc("k := 0; k", 0); + testCalc("l := 0; l", 0); + + testCalc("a; a := 0", a); + testCalc("b; b := 0", b); + testCalc("c; c := 0", c); + testCalc("d; d := 0", d); + testCalc("e; e := 0", e); + testCalc("f; f := 0", f); + testCalc("g; g := 0", g); + testCalc("h; h := 0", h); + testCalc("i; i := 0", i); + testCalc("j; j := 0", j); + testCalc("k; k := 0", k); + testCalc("l; l := 0", l); + + // Check relative precedences. + testExpr(0 ? 1 : 2 | 4); // 0 1 + testExpr(1 ? 1 : 2 | 4); // 0 1 + testExpr(0 ? 2 | 4 : 1); // 0 1 + testExpr(1 ? 2 | 4 : 1); // 0 1 + testExpr(0 ? 1 : 2 & 3); // 0 2 + testExpr(1 ? 1 : 2 & 3); // 0 2 + testExpr(0 ? 2 & 3 : 1); // 0 2 + testExpr(1 ? 2 & 3 : 1); // 0 2 + testExpr(0 ? 2 : 3 >= 1); // 0 3 + testExpr(0 ? 3 >= 1 : 2); // 0 3 + testExpr(1 ? 0 == 1 : 2); // 0 3 + testExpr(1 ? 2 : 0 == 1); // 0 3 + testExpr(0 ? 1 : 2 + 4); // 0 4 + testExpr(1 ? 1 : 2 + 4); // 0 4 + testExpr(0 ? 2 + 4 : 1); // 0 4 + testExpr(1 ? 2 + 4 : 1); // 0 4 + testExpr(0 ? 1 : 2 * 4); // 0 5 + testExpr(1 ? 1 : 2 * 4); // 0 5 + testExpr(0 ? 2 * 4 : 1); // 0 5 + testExpr(1 ? 2 * 4 : 1); // 0 5 + testCalc("0 ? 1 : 2 ** 3", 8); // 0 6 + testCalc("1 ? 1 : 2 ** 3", 1); // 0 6 + testCalc("0 ? 2 ** 3 : 1", 1); // 0 6 + testCalc("1 ? 2 ** 3 : 1", 8); // 0 6 + testCalc("1 | 3 XOR 1", (1 | 3) ^ 1); // 1 1 + testExpr(1 XOR 3 | 1); // 1 1 + testExpr(3 | 1 & 2); // 1 2 + testExpr(2 | 4 > 3); // 1 3 + testExpr(2 OR 4 > 3); // 1 3 + testExpr(2 XOR 3 >= 0); // 1 3 + testExpr(2 | 1 - 3); // 1 4 + testExpr(2 | 4 / 2); // 1 5 + testCalc("1 | 2 ** 3", 1 | (int) pow(2., 3.));// 1 6 + testExpr(3 << 2 & 10); // 2 2 + testCalc("18 & 6 << 2", (18 & 6) << 2); // 2 2 + testExpr(36 >> 2 & 10); // 2 2 + testCalc("18 & 20 >> 2", (18 & 20) >> 2); // 2 2 + testExpr(3 & 4 == 4); // 2 3 + testExpr(3 AND 4 == 4); // 2 3 + testCalc("1 << 2 != 4", 1 << (2 != 4)); // 2 3 + testCalc("16 >> 2 != 4", 16 >> (2 != 4)); // 2 3 + testExpr(3 AND -2); // 2 8 + testExpr(0 < 1 ? 2 : 3); // 3 0 + testExpr(1 <= 0 ? 2 : 3); // 3 0 + testExpr(0 + -1); // 4 8 + testExpr(0 - -1); // 4 8 + testExpr(10 + 10 * 2); // 4 5 + testExpr(20 + 20 / 2); // 4 5 + testExpr(-1 + 1); // 7 4 + testExpr(-1 - 2); // 7 4 + testCalc("-2 ** 2", pow(-2., 2.)); // 7 6 + testCalc("-2 ^ 2", pow(-2., 2.)); // 7 6 + + // Check parentheses + testCalc("(1 | 2) ** 3", pow((double) (1 | 2), 3.));// 8 6 + testCalc("1+(1|2)**3", 1+pow((double) (1 | 2), 3.));// 8 6 + testExpr(1+(1?(1<2):(1>2))*2); + + testArgs("a", A_A, 0); + testArgs("A", A_A, 0); + testArgs("B", A_B, 0); + testArgs("C", A_C, 0); + testArgs("D", A_D, 0); + testArgs("E", A_E, 0); + testArgs("F", A_F, 0); + testArgs("G", A_G, 0); + testArgs("H", A_H, 0); + testArgs("I", A_I, 0); + testArgs("J", A_J, 0); + testArgs("K", A_K, 0); + testArgs("L", A_L, 0); + testArgs("A+B+C+D+E+F+G+H+I+J+K+L", + A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L, 0); + testArgs("0.1;A:=0", 0, A_A); + testArgs("1.1;B:=0", 0, A_B); + testArgs("2.1;C:=0", 0, A_C); + testArgs("3.1;D:=0", 0, A_D); + testArgs("4.1;E:=0", 0, A_E); + testArgs("5.1;F:=0", 0, A_F); + testArgs("6.1;G:=0", 0, A_G); + testArgs("7.1;H:=0", 0, A_H); + testArgs("8.1;I:=0", 0, A_I); + testArgs("9.1;J:=0", 0, A_J); + testArgs("10.1;K:=0", 0, A_K); + testArgs("11.1;L:=0", 0, A_L); + testArgs("12.1;A:=0;B:=A;C:=B;D:=C", 0, A_A|A_B|A_C|A_D); + testArgs("13.1;B:=A;A:=B;C:=D;D:=C", A_A|A_D, A_A|A_B|A_C|A_D); + + // Malformed expressions + testBadExpr("0x0.1", CALC_ERR_SYNTAX); + testBadExpr("1*", CALC_ERR_INCOMPLETE); + testBadExpr("*1", CALC_ERR_SYNTAX); + testBadExpr("MIN", CALC_ERR_INCOMPLETE); + testBadExpr("MIN()", CALC_ERR_SYNTAX); + testBadExpr("MIN(A,)", CALC_ERR_SYNTAX); + testBadExpr("MIN(A,B,)", CALC_ERR_SYNTAX); + testBadExpr("MAX", CALC_ERR_INCOMPLETE); + testBadExpr("MAX()", CALC_ERR_SYNTAX); + testBadExpr("MAX(A,)", CALC_ERR_SYNTAX); + testBadExpr("MAX(A,B,)", CALC_ERR_SYNTAX); + testBadExpr("1?", CALC_ERR_CONDITIONAL); + testBadExpr("1?1", CALC_ERR_CONDITIONAL); + testBadExpr(":1", CALC_ERR_SYNTAX); + + // Bit manipulations wrt bit 31 (bug lp:1514520) + // using integer literals + testUInt32Calc("0xaaaaaaaa AND 0xffff0000", 0xaaaa0000u); + testUInt32Calc("0xaaaaaaaa OR 0xffff0000", 0xffffaaaau); + testUInt32Calc("0xaaaaaaaa XOR 0xffff0000", 0x5555aaaau); + testUInt32Calc("~0xaaaaaaaa", 0x55555555u); + testUInt32Calc("~~0xaaaaaaaa", 0xaaaaaaaau); + testUInt32Calc("0xaaaaaaaa >> 8", 0xffaaaaaau); + testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u); + // using integer literals assigned to variables + testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u); + testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau); + testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a XOR b", 0x5555aaaau); + testUInt32Calc("a:=0xaaaaaaaa; ~a", 0x55555555u); + testUInt32Calc("a:=0xaaaaaaaa; ~~a", 0xaaaaaaaau); + testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau); + testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u); + + // Test proper conversion of double values (+ 0.1 enforces double literal) + // when used as inputs to the bitwise operations. + // 0xaaaaaaaa = -1431655766 or 2863311530u + testUInt32Calc("-1431655766.1 OR 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 OR 0", 0xaaaaaaaau); + testUInt32Calc("0 OR -1431655766.1", 0xaaaaaaaau); + testUInt32Calc("0 OR 2863311530.1", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 XOR 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 XOR 0", 0xaaaaaaaau); + testUInt32Calc("0 XOR -1431655766.1", 0xaaaaaaaau); + testUInt32Calc("0 XOR 2863311530.1", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 AND 0xffffffff", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 AND 0xffffffff", 0xaaaaaaaau); + testUInt32Calc("0xffffffff AND -1431655766.1", 0xaaaaaaaau); + testUInt32Calc("0xffffffff AND 2863311530.1", 0xaaaaaaaau); + testUInt32Calc("~ -1431655766.1", 0x55555555u); + testUInt32Calc("~ 2863311530.1", 0x55555555u); + testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau); + + return testDone(); +} + diff --git a/modules/libcom/test/epicsEllTest.c b/modules/libcom/test/epicsEllTest.c new file mode 100644 index 000000000..62ee14e39 --- /dev/null +++ b/modules/libcom/test/epicsEllTest.c @@ -0,0 +1,268 @@ +/*************************************************************************\ +* Copyright (c) 2016 Michael Davidsaver +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "ellLib.h" +#include "dbDefs.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +struct myItem { + ELLNODE node; + int list; + int num; +}; + +static void testList(void) +{ + ELLLIST list1; + ELLLIST list2 = ELLLIST_INIT; + int i1 = 1; + struct myItem *pitem, *pfirst, *pick; + + list1.count = 27; + list1.node.next = (ELLNODE *) 0x01010101; + list1.node.previous = (ELLNODE *) 0x10101010; + + ellInit(&list1); + testOk1(list1.count == 0); + testOk1(list1.node.next == NULL); + testOk1(list1.node.previous == NULL); + + testOk1(list2.count == 0); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + /* First build a couple lists and fill them with nodes. */ + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + pitem->node.next = (ELLNODE *) 0x10101010; + pitem->node.previous = (ELLNODE *) 0x10101010; + pitem->list = 1; + pitem->num = i1++; + + ellAdd(&list1, &pitem->node); + + testOk1(list1.count == 1); + testOk1(list1.node.next == &pitem->node); + testOk1(list1.node.previous == &pitem->node); + testOk1(pitem->node.next == NULL); + testOk1(pitem->node.previous == NULL); + + pfirst = pitem; + while (i1 <= 21) { /* put 21 nodes into LIST1 */ + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + pitem->list = 1; + pitem->num = i1++; + ellAdd(&list1, &pitem->node); + } + + testOk1(list1.count == 21); + testOk1(list1.node.next == &pfirst->node); + testOk1(list1.node.previous == &pitem->node); + testOk1(pitem->node.next == NULL); + + testOk1(ellFirst(&list1) == &pfirst->node); + testOk1(ellLast(&list1) == &pitem->node); + testOk1(ellNext(&pitem->node) == NULL); + testOk1(ellNext(&pfirst->node) == pfirst->node.next); + + testOk1(ellNth(&list1, 0) == NULL); + + pick = (struct myItem *)ellNth(&list1, 21); + testOk1(pick == pitem); + + testOk1(ellNth(&list1, 22) == NULL); + testOk1(ellNth(&list1, 1) == &pfirst->node); + testOk1(ellNth(&list1, 2) == pfirst->node.next); + testOk1(ellNth(&list1, 20) == pitem->node.previous); + + testOk1(ellPrevious(&pitem->node) == pitem->node.previous); + testOk1(ellPrevious(&pfirst->node) == NULL); + testOk1(ellPrevious(pfirst->node.next) == &pfirst->node); + + pick = (struct myItem *)ellGet(&list1); + testOk1(pick == pfirst); + testOk1(list1.node.next == pfirst->node.next); + + ellAdd(&list2, &pick->node); + + pick = (struct myItem *)ellGet(&list2); + testOk1(pick == pfirst); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + testOk1(ellCount(&list1) == 20); + testOk1(ellCount(&list2) == 0); + + ellConcat(&list1, &list2); + + testOk1(ellCount(&list1) == 20); + testOk1(ellCount(&list2) == 0); + testOk1(list1.node.previous == &pitem->node); + + ellAdd(&list2, &pick->node); + ellConcat(&list1, &list2); + + testOk1(ellCount(&list1) == 21); + testOk1(ellCount(&list2) == 0); + testOk1(list1.node.previous == &pick->node); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + ellDelete(&list1, &pick->node); + testOk1(ellCount(&list1) == 20); + + ellAdd(&list2, &pick->node); + ellConcat(&list2, &list1); + testOk1(ellFind(&list1, &pick->node) == -1); /* empty list */ + testOk1(ellFind(&list2, &pick->node) == pick->num); /* first node */ + + pick = (struct myItem *)ellNth(&list2, 18); + testOk1(ellFind(&list2, &pick->node) == 18); /* 18th node */ + + ellDelete(&list2, &pick->node); + ellInsert(&list2, NULL, &pick->node); /* move #18 to top */ + testOk1(ellCount(&list2) == 21); + testOk1(((struct myItem *)list2.node.next)->num == 18); + + testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); + + pick = (struct myItem *)ellGet(&list2); + pitem = (struct myItem *)ellNth(&list2, 17); + ellInsert(&list2, &pitem->node, &pick->node); + testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); + + testOk1(((struct myItem *)ellFirst(&list2))->num == 1); + testOk1(((struct myItem *)ellNth(&list2,17))->num == 17); + testOk1(((struct myItem *)ellNth(&list2,18))->num == 18); + + pick = (struct myItem *)ellLast(&list2); + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + ellInsert(&list2, &pick->node, &pitem->node); + testOk1(ellCount(&list2) == 22); + testOk1(ellFind(&list2, ellNth(&list2, 22)) == 22); + + ellDelete(&list2, &pitem->node); + free(pitem); + + ellExtract(&list2, ellNth(&list2,9), ellNth(&list2, 19), &list1); + testOk1(ellCount(&list2) == 10); + testOk1(ellCount(&list1) == 11); + + testOk1(ellFind(&list2, ellNth(&list2, 10)) == 10); + testOk1(ellFind(&list1, ellNth(&list1, 11)) == 11); + + ellFree(&list2); + testOk1(ellCount(&list2) == 0); + + pick = (struct myItem *)ellFirst(&list1); + i1 = 1; + while(pick != NULL) { + pick->num = i1++; + pick = (struct myItem *)ellNext(&pick->node); + } + pick = (struct myItem *)ellFirst(&list1); + testOk1(pick != NULL); + + pitem = (struct myItem *)ellNStep(&pick->node, 3); + testOk1(pitem != NULL); + testOk1(pitem->num == 4); + + pitem = (struct myItem *)ellNStep(&pick->node, 30); + testOk1(pitem == NULL); + + pitem = (struct myItem *)ellNStep(&pick->node, 10); + testOk1(pitem != NULL); + testOk1(pitem->num == 11); + + pitem = (struct myItem *)ellNStep(&pitem->node, 0); + testOk1(pitem->num == 11); + + pitem = (struct myItem *)ellNStep(&pitem->node, -4); + testOk1(pitem->num == 7); + + ellFree2(&list1, free); + testOk1(ellCount(&list1) == 0); +} + +typedef struct { int A, B; } input_t; + +static int myItemCmp(const ELLNODE *a, const ELLNODE *b) +{ + struct myItem *A = CONTAINER(a, struct myItem, node), + *B = CONTAINER(b, struct myItem, node); + + if (A->num < B->num) return -1; + else if(A->num > B->num) return 1; + else if(A->list < B->list) return -1; + else if(A->list > B->list) return 1; + else return 0; +} + +static const input_t input[] = { + {-4, 0}, + {-5, 0}, + {0,0}, + {50,0}, + {0,1}, + {5,0}, + {5,1} +}; + +static +void testSort(const input_t *inp, size_t ninp) +{ + unsigned i; + ELLLIST list = ELLLIST_INIT; + struct myItem *alloc = calloc(ninp, sizeof(*alloc)); + + if(!alloc) testAbort("testSort allocation fails"); + + for(i=0; inum = inp[i].A; + it->list= inp[i].B; + + ellAdd(&list, &it->node); + } + + ellSortStable(&list, &myItemCmp); + + testOk(ellCount(&list)==ninp, "output length %u == %u", (unsigned)ellCount(&list), (unsigned)ninp); + if(ellCount(&list)==0) { + testSkip(ninp-1, "all items lost"); + } + + { + struct myItem *prev = CONTAINER(ellFirst(&list), struct myItem, node), + *next; + + for(next = CONTAINER(ellNext(&prev->node), struct myItem, node); + next; + prev = next, next = CONTAINER(ellNext(&next->node), struct myItem, node)) + { + int cond = (prev->numnum) || (prev->num==next->num && prev->listlist); + testOk(cond, "%d:%d < %d:%d", prev->num, prev->list, next->num, next->list); + } + } +} + +MAIN(epicsEllTest) +{ + testPlan(77); + + testList(); + testSort(input, NELEMENTS(input)); + + return testDone(); +} diff --git a/modules/libcom/test/epicsEnvTest.c b/modules/libcom/test/epicsEnvTest.c new file mode 100644 index 000000000..bbce4b749 --- /dev/null +++ b/modules/libcom/test/epicsEnvTest.c @@ -0,0 +1,78 @@ +/*************************************************************************\ +* Copyright (c) 2013 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsEnvTest.c */ + +/* Author: Andrew Johnson + * Date: 2013-12-13 + */ + +/* Check environment variable APIs. + * TODO: Add tests for envDefs.h routines. + * + * The thread test is needed on VxWorks 6.x, where the OS can be + * configured to maintain separate, totally independent sets + * of environment variables for each thread. This configuration + * is not supported by EPICS which expects child threads to at + * least inherit the partent thread's environment variables. + */ + +#include +#include + +#include "envDefs.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define PARENT "Parent" +#define CHILD "Child" + +static void child(void *arg) +{ + const char *value = getenv(PARENT); + + if (!testOk(value && (strcmp(value, PARENT) == 0), + "Child thread sees parent environment values")) { +#ifdef vxWorks + testDiag("VxWorks image needs ENV_VAR_USE_HOOKS configured as FALSE"); +#else + testDiag("Check OS configuration, environment inheritance needed"); +#endif + } + + epicsEnvSet(CHILD, CHILD); +} + +MAIN(epicsEnvTest) +{ + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + const char *value; + + testPlan(3); + + epicsEnvSet(PARENT, PARENT); + + value = getenv(PARENT); + if (!testOk(value && (strcmp(value, PARENT) == 0), + "epicsEnvSet correctly modifies environment")) + testAbort("environment variables not working"); + + epicsThreadCreate("child", 50, stackSize, child, NULL); + epicsThreadSleep(0.1); + + value = getenv(CHILD); + if (value && (strcmp(value, CHILD) == 0)) + testDiag("Child and parent threads share a common environment"); + + value = getenv(PARENT); + testOk(value && (strcmp(value, PARENT) == 0), + "PARENT environment variable not modified"); + + testDone(); + return 0; +} + diff --git a/modules/libcom/test/epicsErrlogTest.c b/modules/libcom/test/epicsErrlogTest.c new file mode 100644 index 000000000..28d88f6af --- /dev/null +++ b/modules/libcom/test/epicsErrlogTest.c @@ -0,0 +1,489 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Michael Davidsaver + * Date: 2010-09-24 + */ + +#include +#include +#include + +#include "epicsAssert.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "dbDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" +#include "iocLog.h" +#include "logClient.h" +#include "envDefs.h" +#include "osiSock.h" +#include "fdmgr.h" + +#define LOGBUFSIZE 2048 + +static +const char longmsg[]="A0123456789abcdef" + "B0123456789abcdef" + "C0123456789abcdef" + "D0123456789abcdef" + "E0123456789abcdef" + "F0123456789abcdef" + "G0123456789abcdef" + "H0123456789abcdef" + "I0123456789abcdef" + "J0123456789abcdef" + "K0123456789abcdef" + "L0123456789abcdef" + "M0123456789abcdef" + "N0123456789abcdef" + "O0123456789abcdef" + "P0123456789abcdef" + "Q0123456789abcdef" + "R0123456789abcdef" + "S0123456789abcdef" + ; +STATIC_ASSERT(NELEMENTS(longmsg)==324); + +static +const char truncmsg[]="A0123456789abcdef" + "B0123456789abcdef" + "C0123456789abcdef" + "D0123456789abcdef" + "E0123456789abcdef" + "F0123456789abcdef" + "G0123456789abcdef" + "H0123456789abcdef" + "I0123456789abcdef" + "J0123456789abcdef" + "K0123456789abcdef" + "L0123456789abcdef" + "M0123456789abcdef" + "N0123456789abcdef" + "O01<>\n" + ; +STATIC_ASSERT(NELEMENTS(truncmsg)==256); + +typedef struct { + unsigned int count; + const char* expect; + size_t checkLen; + epicsEventId jammer; + int jam; +} clientPvt; + +static void testLogPrefix(void); +static void acceptNewClient( void *pParam ); +static void readFromClient( void *pParam ); +static void testPrefixLogandCompare( const char* logmessage); + +static void *pfdctx; +static SOCKET sock; +static SOCKET insock; + +static const char* prefixactualmsg[]= { + "A message without prefix", + "A message with prefix", + "DONE" + }; +static const char *prefixstring = "fac=LI21 "; +static const char prefixexpectedmsg[] = "A message without prefix" + "fac=LI21 A message with prefix" + "fac=LI21 DONE" + ; +static char prefixmsgbuffer[1024]; + + +static +void testEqInt_(int lhs, int rhs, const char *LHS, const char *RHS) +{ + testOk(lhs==rhs, "%s (%d) == %s (%d)", LHS, lhs, RHS, rhs); +} +#define testEqInt(L, R) testEqInt_(L, R, #L, #R); +static +void logClient(void* raw, const char* msg) +{ + clientPvt *pvt = raw; + size_t len; + char show[46]; + + /* Simulate thread priority on non-realtime + * OSs like Linux. This will cause the logging + * thread to sleep with the buffer lock held. + */ + if (pvt->jam > 0) { + pvt->jam = 0; + epicsEventMustWait(pvt->jammer); + } else if (pvt->jam < 0) { + pvt->jam++; + if (pvt->jam == 0) + epicsEventMustWait(pvt->jammer); + } + + len = strlen(msg); + if (len > 45) { + /* Only show start and end of long messages */ + strncpy(show, msg, 20); + show[20] = 0; + strcat(show + 20, " ... "); + strcat(show + 25, msg + len - 20); + } + else { + strcpy(show, msg); + } + + if (pvt->checkLen) + if (!testOk(pvt->checkLen == len, "Received %d chars", (int) len)) { + testDiag("Expected %d", (int) pvt->checkLen); + if (!pvt->expect) + testDiag("Message is \"%s\"", show); + } + + if (pvt->expect) { + if (!testOk(strcmp(pvt->expect, msg) == 0, "Message is \"%s\"", show)) { + len = strlen(pvt->expect); + if (len > 45) { + testDiag("Expected \"%.20s ... %s\"", + pvt->expect, pvt->expect + len - 20); + } + else { + testDiag("Expected \"%s\"", pvt->expect); + } + } + } + + pvt->count++; +} + +MAIN(epicsErrlogTest) +{ + size_t mlen, i, N; + char msg[256]; + clientPvt pvt, pvt2; + + testPlan(32); + + strcpy(msg, truncmsg); + + errlogInit2(LOGBUFSIZE, 256); + + pvt.count = 0; + pvt2.count = 0; + + pvt.expect = NULL; + pvt2.expect = NULL; + + pvt.checkLen = 0; + pvt2.checkLen = 0; + + pvt.jam = 0; + pvt2.jam = 0; + + pvt.jammer = epicsEventMustCreate(epicsEventEmpty); + pvt2.jammer = epicsEventMustCreate(epicsEventEmpty); + + testDiag("Check listener registration"); + + errlogAddListener(&logClient, &pvt); + + pvt.expect = "Testing"; + pvt.checkLen = strlen(pvt.expect); + + errlogPrintfNoConsole("%s", pvt.expect); + errlogFlush(); + + testEqInt(pvt.count, 1); + + errlogAddListener(&logClient, &pvt2); + + pvt2.expect = pvt.expect = "Testing2"; + pvt2.checkLen = pvt.checkLen = strlen(pvt.expect); + + errlogPrintfNoConsole("%s", pvt.expect); + errlogFlush(); + + testEqInt(pvt.count, 2); + testEqInt(pvt2.count, 1); + + /* Removes the first listener */ + testOk(1 == errlogRemoveListeners(&logClient, &pvt), + "Removed 1 listener"); + + pvt2.expect = "Testing3"; + pvt2.checkLen = strlen(pvt2.expect); + + errlogPrintfNoConsole("%s", pvt2.expect); + errlogFlush(); + + testEqInt(pvt.count, 2); + testEqInt(pvt2.count, 2); + + /* Add the second listener again, then remove both instances */ + errlogAddListener(&logClient, &pvt2); + testOk(2 == errlogRemoveListeners(&logClient, &pvt2), + "Removed 2 listeners"); + + errlogPrintfNoConsole("Something different"); + errlogFlush(); + + testEqInt(pvt.count, 2); + testEqInt(pvt2.count, 2); + + /* Re-add one listener */ + errlogAddListener(&logClient, &pvt); + + testDiag("Check truncation"); + + pvt.expect = truncmsg; + pvt.checkLen = 255; + + errlogPrintfNoConsole("%s", longmsg); + errlogFlush(); + + testEqInt(pvt.count, 3); + + pvt.expect = NULL; + + testDiag("Check priority"); + /* For the following tests it is important that + * the buffer should not flush until we request it + */ + pvt.jam = 1; + + errlogPrintfNoConsole("%s", longmsg); + epicsThreadSleep(0.1); + + testEqInt(pvt.count, 3); + + epicsEventSignal(pvt.jammer); + errlogFlush(); + + testEqInt(pvt.count, 4); + + testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE); + + pvt.checkLen = 0; + + for (mlen = 8; mlen <= 255; mlen *= 2) { + double eff; + char save = msg[mlen - 1]; + + N = LOGBUFSIZE / mlen; /* # of of messages to send */ + msg[mlen - 1] = '\0'; + pvt.count = 0; + /* pvt.checkLen = mlen - 1; */ + + pvt.jam = 1; + + for (i = 0; i < N; i++) { + errlogPrintfNoConsole("%s", msg); + } + + epicsEventSignal(pvt.jammer); + errlogFlush(); + + eff = (double) (pvt.count * mlen) / LOGBUFSIZE * 100.0; + testDiag(" For %d messages of length %d got %u (%.1f%% efficient)", + (int) N, (int) mlen, pvt.count, eff); + + msg[mlen - 1] = save; + N = pvt.count; /* Save final count for the test below */ + + /* Clear "errlog: messages were discarded" status */ + pvt.checkLen = 0; + errlogPrintfNoConsole("."); + errlogFlush(); + } + + testDiag("Checking buffer use after partial flush"); + + /* Use the numbers from the largest block size above */ + mlen /= 2; + msg[mlen - 1] = '\0'; + + pvt.jam = 1; + pvt.count = 0; + + testDiag("Filling with %d messages of size %d", (int) N, (int) mlen); + + for (i = 0; i < N; i++) { + errlogPrintfNoConsole("%s", msg); + } + epicsThreadSleep(0.1); /* should really be a second Event */ + + testEqInt(pvt.count, 0); + + /* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */ + pvt.jam = -2; + epicsEventSignal(pvt.jammer); + epicsThreadSleep(0.1); + + testDiag("Drained %u messages", pvt.count); + testEqInt(pvt.count, 2); + + /* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */ + errlogPrintfNoConsole("%s", msg); /* Use up that space */ + + testDiag("Overflow the buffer"); + errlogPrintfNoConsole("%s", msg); + + testEqInt(pvt.count, 2); + + epicsEventSignal(pvt.jammer); /* Empty */ + errlogFlush(); + + testDiag("Logged %u messages", pvt.count); + testEqInt(pvt.count, N+1); + + /* Clean up */ + testOk(1 == errlogRemoveListeners(&logClient, &pvt), + "Removed 1 listener"); + + testLogPrefix(); + + return testDone(); +} +/* + * Tests the log prefix code + * The prefix is only applied to log messages as they go out to the socket, + * so we need to create a server listening on a port, accept connections etc. + * This code is a reduced version of the code in iocLogServer. + */ +static void testLogPrefix(void) { + struct sockaddr_in serverAddr; + int status; + struct timeval timeout; + struct sockaddr_in actualServerAddr; + osiSocklen_t actualServerAddrSize; + char portstring[16]; + + + testDiag("Testing iocLogPrefix"); + + timeout.tv_sec = 5; /* in seconds */ + timeout.tv_usec = 0; + + memset((void*)prefixmsgbuffer, 0, sizeof prefixmsgbuffer); + + /* Clear "errlog: messages were discarded" status */ + errlogPrintfNoConsole("."); + errlogFlush(); + + sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + testAbort("epicsSocketCreate failed."); + } + + /* We listen on a an available port. */ + memset((void *)&serverAddr, 0, sizeof serverAddr); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(0); + + status = bind (sock, (struct sockaddr *)&serverAddr, + sizeof (serverAddr) ); + if (status < 0) { + testAbort("bind failed; all ports in use?"); + } + + status = listen(sock, 10); + if (status < 0) { + testAbort("listen failed!"); + } + + /* Determine the port that the OS chose */ + actualServerAddrSize = sizeof actualServerAddr; + memset((void *)&actualServerAddr, 0, sizeof serverAddr); + status = getsockname(sock, (struct sockaddr *) &actualServerAddr, + &actualServerAddrSize); + if (status < 0) { + testAbort("Can't find port number!"); + } + + sprintf(portstring, "%d", ntohs(actualServerAddr.sin_port)); + testDiag("Listening on port %s", portstring); + + /* Set the EPICS environment variables for logging. */ + epicsEnvSet ( "EPICS_IOC_LOG_INET", "localhost" ); + epicsEnvSet ( "EPICS_IOC_LOG_PORT", portstring ); + + pfdctx = (void *) fdmgr_init(); + if (status < 0) { + testAbort("fdmgr_init failed!"); + } + + status = fdmgr_add_callback(pfdctx, sock, fdi_read, + acceptNewClient, &serverAddr); + + if (status < 0) { + testAbort("fdmgr_add_callback failed!"); + } + + testOk1(iocLogInit() == 0); + fdmgr_pend_event(pfdctx, &timeout); + + testPrefixLogandCompare(prefixactualmsg[0]); + + iocLogPrefix(prefixstring); + testPrefixLogandCompare(prefixactualmsg[1]); + testPrefixLogandCompare(prefixactualmsg[2]); + epicsSocketDestroy(sock); +} + +static void testPrefixLogandCompare( const char* logmessage ) { + struct timeval timeout; + timeout.tv_sec = 5; /* in seconds */ + timeout.tv_usec = 0; + + errlogPrintfNoConsole("%s", logmessage); + errlogFlush(); + iocLogFlush(); + fdmgr_pend_event(pfdctx, &timeout); +} + +static void acceptNewClient ( void *pParam ) +{ + osiSocklen_t addrSize; + struct sockaddr_in addr; + int status; + + addrSize = sizeof ( addr ); + insock = epicsSocketAccept ( sock, (struct sockaddr *)&addr, &addrSize ); + testOk(insock != INVALID_SOCKET && addrSize >= sizeof (addr), + "Accepted new client"); + + status = fdmgr_add_callback(pfdctx, insock, fdi_read, + readFromClient, NULL); + + testOk(status >= 0, "Client read configured"); +} + +static void readFromClient(void *pParam) +{ + char recvbuf[1024]; + int recvLength; + + memset(recvbuf, 0, 1024); + recvLength = recv(insock, recvbuf, 1024, 0); + if (recvLength > 0) { + strcat(prefixmsgbuffer, recvbuf); + + /* If we have received all of the messages. */ + if (strstr(prefixmsgbuffer, "DONE") != NULL) { + size_t msglen = strlen(prefixexpectedmsg); + int prefixcmp = strncmp(prefixexpectedmsg, prefixmsgbuffer, msglen); + + if (!testOk(prefixcmp == 0, "prefix matches")) { + testDiag("Expected '%s'\n", prefixexpectedmsg); + testDiag("Obtained '%s'\n", prefixmsgbuffer); + } + } + } +} diff --git a/modules/libcom/test/epicsEventTest.cpp b/modules/libcom/test/epicsEventTest.cpp new file mode 100644 index 000000000..7c1a24141 --- /dev/null +++ b/modules/libcom/test/epicsEventTest.cpp @@ -0,0 +1,261 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsEventTest.cpp */ + +/* Author: Marty Kraimer Date: 26JAN2000 */ +/* timeout accuracy tests by Jeff Hill */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsRingPointer.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + epicsEventId event; + epicsMutexId lockRing; + int quit; + epicsRingPointerId ring; +} info; + +extern "C" { + +static void consumer(void *arg) +{ + info *pinfo = (info *)arg; + int errors = 0; + + testDiag("consumer: starting"); + while (!pinfo->quit) { + epicsEventStatus status = epicsEventWait(pinfo->event); + if (status != epicsEventOK) { + testDiag("consumer: epicsEventWait returned %d", status); + errors++; + } + while (epicsRingPointerGetUsed(pinfo->ring) >= 2) { + epicsRingPointerId message[2]; + for (int i = 0; i < 2; i++) { + message[i] = epicsRingPointerPop(pinfo->ring); + if (message[i] == 0) { + testDiag("consumer: epicsRingPointerPop returned 0"); + errors++; + } + } + if (message[0] != message[1]) { + testDiag("consumer: message %p %p\n", message[0], message[1]); + errors++; + } + } + } + testOk(errors == 0, "consumer: errors = %d", errors); +} + +static void producer(void *arg) +{ + info *pinfo = (info *)arg; + const char *name = epicsThreadGetNameSelf(); + epicsThreadId myId = epicsThreadGetIdSelf(); + int errors = 0; + int ntimes = 0; + + testDiag("%s: starting", name); + while(!pinfo->quit) { + ++ntimes; + epicsMutexLockStatus status = epicsMutexLock(pinfo->lockRing); + if (status != epicsMutexLockOK) { + testDiag("%s: epicsMutexLock returned %d", name, status); + errors++; + } + if (epicsRingPointerGetFree(pinfo->ring) >= 2) { + for (int i = 0; i < 2; i++) { + if (!epicsRingPointerPush(pinfo->ring, myId)) { + testDiag("%s: epicsRingPointerPush fail", name); + errors++; + } + if (i == 0 && (ntimes % 4 == 0)) + epicsThreadSleep(0.1); + } + } else { + testFail("%s: ring buffer full", name); + errors++; + } + epicsMutexUnlock(pinfo->lockRing); + epicsThreadSleep(1.0); + epicsEventMustTrigger(pinfo->event); + } + testOk(errors == 0, "%s: errors = %d", name, errors); +} + +#define SLEEPERCOUNT 3 +struct wakeInfo { + epicsEventId event; + epicsMutexId countMutex; + int count; +}; +static void sleeper(void *arg) +{ + struct wakeInfo *wp = (struct wakeInfo *)arg; + epicsEventMustWait(wp->event); + epicsMutexLock(wp->countMutex); + wp->count++; + epicsMutexUnlock(wp->countMutex); +} +static void eventWakeupTest(void) +{ + struct wakeInfo wakeInfo, *wp = &wakeInfo; + int i, c; + + wp->event = epicsEventMustCreate(epicsEventEmpty); + wp->countMutex = epicsMutexMustCreate(); + wp->count = 0; + for (i = 0 ; i < SLEEPERCOUNT ; i++) + epicsThreadCreate("Sleeper", + epicsThreadPriorityScanHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + sleeper, + wp); + epicsThreadSleep(0.5); + epicsMutexLock(wp->countMutex); + c = wp->count; + epicsMutexUnlock(wp->countMutex); + testOk(c == 0, "all threads still sleeping"); + for (i = 1 ; i <= SLEEPERCOUNT ; i++) { + epicsEventMustTrigger(wp->event); + epicsThreadSleep(0.5); + epicsMutexLock(wp->countMutex); + c = wp->count; + epicsMutexUnlock(wp->countMutex); + testOk(c == i, "%d thread%s awakened, expected %d", c, c == 1 ? "" : "s", i); + } + epicsEventDestroy(wp->event); + epicsMutexDestroy(wp->countMutex); +} + + +} // extern "C" + +static double eventWaitMeasureDelayError( const epicsEventId &id, const double & delay ) +{ + epicsTime beg = epicsTime::getCurrent(); + epicsEventWaitWithTimeout ( id, delay ); + epicsTime end = epicsTime::getCurrent(); + double meas = end - beg; + double error = fabs ( delay - meas ); + testDiag("epicsEventWaitWithTimeout(%.6f) delay error %.6f sec", + delay, error ); + return error; +} + +static void eventWaitTest() +{ + double errorSum = 0.0; + epicsEventId event = epicsEventMustCreate ( epicsEventEmpty ); + int i; + for ( i = 0u; i < 20; i++ ) { + double delay = ldexp ( 1.0 , -i ); + errorSum += eventWaitMeasureDelayError ( event, delay ); + } + errorSum += eventWaitMeasureDelayError ( event, 0.0 ); + epicsEventDestroy ( event ); + double meanError = errorSum / ( i + 1 ); + testOk(meanError < 0.05, "Average error %.6f sec", meanError); +} + + +MAIN(epicsEventTest) +{ + const int nthreads = 3; + epicsThreadId *id; + char **name; + epicsEventId event; + int status; + + testPlan(13+SLEEPERCOUNT); + + event = epicsEventMustCreate(epicsEventEmpty); + + status = epicsEventWaitWithTimeout(event, 0.0); + testOk(status == epicsEventWaitTimeout, + "epicsEventWaitWithTimeout(event, 0.0) = %d", status); + + status = epicsEventWaitWithTimeout(event, 1.0); + testOk(status == epicsEventWaitTimeout, + "epicsEventWaitWithTimeout(event, 1.0) = %d", status); + + status = epicsEventTryWait(event); + testOk(status == epicsEventWaitTimeout, + "epicsEventTryWait(event) = %d", status); + + status = epicsEventTrigger(event); + testOk(status == epicsEventOK, + "epicsEventTrigger(event) = %d", status); + status = epicsEventWaitWithTimeout(event, 1.0); + testOk(status == epicsEventOK, + "epicsEventWaitWithTimeout(event, 1.0) = %d", status); + + epicsEventMustTrigger(event); + status = epicsEventWaitWithTimeout(event,DBL_MAX); + testOk(status == epicsEventOK, + "epicsEventWaitWithTimeout(event, DBL_MAX) = %d", status); + + epicsEventMustTrigger(event); + status = epicsEventTryWait(event); + testOk(status == epicsEventOK, + "epicsEventTryWait(event) = %d", status); + + info *pinfo = (info *)calloc(1,sizeof(info)); + pinfo->event = event; + pinfo->lockRing = epicsMutexCreate(); + pinfo->ring = epicsRingPointerCreate(1024*2); + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + epicsThreadCreate("consumer", 50, stackSize, consumer, pinfo); + id = (epicsThreadId *)calloc(nthreads, sizeof(epicsThreadId)); + name = (char **)calloc(nthreads, sizeof(char *)); + for(int i = 0; i < nthreads; i++) { + name[i] = (char *)calloc(16, sizeof(char)); + sprintf(name[i],"producer %d",i); + id[i] = epicsThreadCreate(name[i], 40, stackSize, producer, pinfo); + epicsThreadSleep(0.1); + } + epicsThreadSleep(5.0); + + testDiag("setting quit"); + pinfo->quit = 1; + epicsThreadSleep(2.0); + + epicsEventMustTrigger(pinfo->event); + epicsThreadSleep(1.0); + + eventWaitTest(); + eventWakeupTest(); + + free(name); + free(id); + epicsRingPointerDelete(pinfo->ring); + epicsMutexDestroy(pinfo->lockRing); + epicsEventDestroy(event); + free(pinfo); + + return testDone(); +} diff --git a/modules/libcom/test/epicsExitTest.c b/modules/libcom/test/epicsExitTest.c new file mode 100644 index 000000000..dc901b5ac --- /dev/null +++ b/modules/libcom/test/epicsExitTest.c @@ -0,0 +1,107 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsExitTest.cpp */ + +/* Author: Marty Kraimer Date: 09JUL2004*/ + +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsAssert.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + char name[64]; + epicsEventId terminate; + epicsEventId terminated; +}info; + +static void atExit(void *pvt) +{ + info *pinfo = (info *)pvt; + testPass("%s reached atExit", pinfo->name); + epicsEventSignal(pinfo->terminate); + /*Now wait for thread to terminate*/ + epicsEventMustWait(pinfo->terminated); + testPass("%s destroying pinfo", pinfo->name); + epicsEventDestroy(pinfo->terminate); + epicsEventDestroy(pinfo->terminated); + free(pinfo); +} + +static void atThreadExit(void *pvt) +{ + info *pinfo = (info *)pvt; + testPass("%s terminating", pinfo->name); + epicsEventSignal(pinfo->terminated); +} + +static void thread(void *arg) +{ + info *pinfo = (info *)arg; + + strcpy(pinfo->name, epicsThreadGetNameSelf()); + testDiag("%s starting", pinfo->name); + pinfo->terminate = epicsEventMustCreate(epicsEventEmpty); + pinfo->terminated = epicsEventMustCreate(epicsEventEmpty); + testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo); + testOk(!epicsAtThreadExit(atThreadExit, pinfo), + "Registered atThreadExit(%p)", pinfo); + testDiag("%s waiting for atExit", pinfo->name); + epicsEventMustWait(pinfo->terminate); +} + +int count; + +static void counter(void *pvt) +{ + count++; +} + +static void mainExit(void *pvt) +{ + testPass("Reached mainExit"); + testDone(); +} + +MAIN(epicsExitTest) +{ + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + info *pinfoA = (info *)calloc(1, sizeof(info)); + info *pinfoB = (info *)calloc(1, sizeof(info)); + + testPlan(15); + + testOk(!epicsAtExit(counter, NULL), "Registered counter()"); + count = 0; + epicsExitCallAtExits(); + testOk(count == 1, "counter() called once"); + epicsExitCallAtExits(); + testOk(count == 1, "unregistered counter() not called"); + + testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()"); + + epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA); + epicsThreadSleep(0.1); + epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB); + epicsThreadSleep(1.0); + + testDiag("Calling epicsExit"); + epicsExit(0); + return 0; +} diff --git a/modules/libcom/test/epicsInlineTest1.c b/modules/libcom/test/epicsInlineTest1.c new file mode 100644 index 000000000..e377e5f1d --- /dev/null +++ b/modules/libcom/test/epicsInlineTest1.c @@ -0,0 +1,64 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* This test checks the variations on inline function defintions. + * + * "static inline int func(void) {...}" + * + * Consistent meaning in C89, C99, and C++ (98 and 11). + * If not inline'd results in a private symbol in each compilation unit. + * Thus the non-inline'd version is duplicated in each compilation unit. + * However, definitions in different compilation units may be different. + * + * "inline int func(void) {...}" + * Warning: Not consistent, avoid use in headers meant for C or C++ + * + * In C++ this may be safely defined in more than one compilation unit. + * Where not inlined it will result in a weak public symbol. + * Thus non-inline'd version isn't duplicated, but must be the same + * in all compilation units. + * + */ + +#include "compilerSpecific.h" +#include "epicsUnitTest.h" + +#include "testMain.h" + +static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) +{ + return 1; +} + +/* Fails to link in C99 +inline int epicsInlineTestFn2(void) +{ + return 42; +} +*/ + +void epicsInlineTest1(void) +{ + testDiag("epicsInlineTest1()"); + testOk1(epicsInlineTestFn1()==1); + /*testOk1(epicsInlineTestFn2()==42);*/ +} + +void epicsInlineTest2(void); +void epicsInlineTest3(void); +void epicsInlineTest4(void); + +MAIN(epicsInlineTest) +{ + testPlan(6); + testDiag("Test variation on inline int func()"); + epicsInlineTest1(); + epicsInlineTest2(); + epicsInlineTest3(); + epicsInlineTest4(); + return testDone(); +} diff --git a/modules/libcom/test/epicsInlineTest2.c b/modules/libcom/test/epicsInlineTest2.c new file mode 100644 index 000000000..b787276fb --- /dev/null +++ b/modules/libcom/test/epicsInlineTest2.c @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "compilerSpecific.h" +#include "epicsUnitTest.h" + +static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) +{ + return 2; +} + +/* Fails to link in C99 +inline int epicsInlineTestFn2(void) +{ + return 42; +} +*/ + +void epicsInlineTest2(void) +{ + testDiag("epicsInlineTest2()"); + testOk1(epicsInlineTestFn1()==2); + /*testOk1(epicsInlineTestFn2()==42);*/ +} diff --git a/modules/libcom/test/epicsInlineTest3.cpp b/modules/libcom/test/epicsInlineTest3.cpp new file mode 100644 index 000000000..9647be6dc --- /dev/null +++ b/modules/libcom/test/epicsInlineTest3.cpp @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "compilerSpecific.h" +#include "epicsUnitTest.h" + +static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) +{ + return 3; +} + +inline int epicsInlineTestFn2(void) +{ + return 42; +} + +extern "C" +void epicsInlineTest3(void) +{ + testDiag("epicsInlineTest3()"); + testOk1(epicsInlineTestFn1()==3); + testOk1(epicsInlineTestFn2()==42); +} diff --git a/modules/libcom/test/epicsInlineTest4.cpp b/modules/libcom/test/epicsInlineTest4.cpp new file mode 100644 index 000000000..545b45ffd --- /dev/null +++ b/modules/libcom/test/epicsInlineTest4.cpp @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2015 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "compilerSpecific.h" +#include "epicsUnitTest.h" + +static EPICS_ALWAYS_INLINE int epicsInlineTestFn1(void) +{ + return 4; +} + +inline int epicsInlineTestFn2(void) +{ + return 42; +} + +extern "C" +void epicsInlineTest4(void) +{ + testDiag("epicsInlineTest4()"); + testOk1(epicsInlineTestFn1()==4); + testOk1(epicsInlineTestFn2()==42); +} diff --git a/modules/libcom/test/epicsMMIOTest.c b/modules/libcom/test/epicsMMIOTest.c new file mode 100644 index 000000000..939627348 --- /dev/null +++ b/modules/libcom/test/epicsMMIOTest.c @@ -0,0 +1,87 @@ +/*************************************************************************\ +* Copyright (c) 2013 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Michael Davidsaver + */ + +#include "epicsAssert.h" +#include "epicsEndian.h" +#include "epicsTypes.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#include "epicsMMIO.h" + +#if EPICS_BYTE_ORDER==EPICS_ENDIAN_BIG +#define BE16 0x1234 +#define BE32 0x12345678 +#define LE16 0x3412 +#define LE32 0x78563412 +#else +#define LE16 0x1234 +#define LE32 0x12345678 +#define BE16 0x3412 +#define BE32 0x78563412 +#endif + +union hydra16 { + epicsUInt16 u16; + epicsUInt8 bytes[2]; +}; + +union hydra32 { + epicsUInt32 u32; + epicsUInt8 bytes[4]; +}; + +MAIN(epicsMMIOTest) +{ + epicsUInt8 B; + union hydra16 H16; + union hydra32 H32; + + STATIC_ASSERT(sizeof(H16)==2); + STATIC_ASSERT(sizeof(H32)==4); + + testPlan(14); + + testDiag("8-bit ops"); + + iowrite8(&B, 5); + testOk1(B==5); + testOk1(ioread8(&B)==5); + + testDiag("16-bit ops"); + + nat_iowrite16(&H16.bytes, 0x1234); + testOk1(H16.u16==0x1234); + testOk1(nat_ioread16(&H16.bytes)==0x1234); + + be_iowrite16(&H16.bytes, 0x1234); + testOk1(H16.u16==BE16); + testOk1(be_ioread16(&H16.bytes)==0x1234); + + le_iowrite16(&H16.bytes, 0x1234); + testOk1(H16.u16==LE16); + testOk1(le_ioread16(&H16.bytes)==0x1234); + + testDiag("32-bit ops"); + + nat_iowrite32(&H32.bytes, 0x12345678); + testOk1(H32.u32==0x12345678); + testOk1(nat_ioread32(&H32.bytes)==0x12345678); + + be_iowrite32(&H32.bytes, 0x12345678); + testOk1(H32.u32==BE32); + testOk1(be_ioread32(&H32.bytes)==0x12345678); + + le_iowrite32(&H32.bytes, 0x12345678); + testOk1(H32.u32==LE32); + testOk1(le_ioread32(&H32.bytes)==0x12345678); + + return testDone(); +} diff --git a/modules/libcom/test/epicsMathTest.c b/modules/libcom/test/epicsMathTest.c new file mode 100644 index 000000000..8ea763cf0 --- /dev/null +++ b/modules/libcom/test/epicsMathTest.c @@ -0,0 +1,95 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMathTest.c + * + * Author Marty Kraimer + */ + +#include "epicsUnitTest.h" +#include "epicsMath.h" +#include "testMain.h" + +MAIN(epicsMathTest) +{ + double huge = 1e300; + double tiny = 1e-300; + double c; + + testPlan(35); + + testOk1(!isnan(0.0)); + testOk1(!isinf(0.0)); + + testOk1(!isnan(epicsINF)); + testOk1(isinf(epicsINF)); + testOk1(epicsINF == epicsINF); + testOk1(epicsINF > 0.0); + testOk1(epicsINF - epicsINF != 0.0); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif + testOk1(epicsINF + -epicsINF != 0.0); + testOk1(-epicsINF + epicsINF != 0.0); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif + + testOk1(isnan(epicsINF - epicsINF)); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif + testOk1(isnan(epicsINF + -epicsINF)); + testOk1(isnan(-epicsINF + epicsINF)); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif + + testOk1(isnan(epicsNAN)); + testOk1(!isinf(epicsNAN)); + testOk1(epicsNAN != epicsNAN); + testOk1(!(epicsNAN < epicsNAN)); + testOk1(!(epicsNAN <= epicsNAN)); + testOk1(!(epicsNAN == epicsNAN)); + testOk1(!(epicsNAN >= epicsNAN)); + testOk1(!(epicsNAN > epicsNAN)); + testOk1(isnan(epicsNAN - epicsNAN)); + +#if defined(_WIN64) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64"); +#endif + testOk1(isnan(epicsNAN + -epicsNAN)); + testOk1(isnan(-epicsNAN + epicsNAN)); +#if defined(_WIN64) && defined(_MSC_VER) + testTodoEnd(); +#endif + + c = huge / tiny; + testOk(!isnan(c), "!isnan(1e300 / 1e-300)"); + testOk(isinf(c), "isinf(1e300 / 1e-300)"); + testOk(c > 0.0, "1e300 / 1e-300 > 0.0"); + + c = (-huge) / tiny; + testOk(!isnan(c), "!isnan(-1e300 / 1e-300)"); + testOk(isinf(c), "isinf(-1e300 / 1e-300)"); + testOk(c < 0.0, "-1e300 / 1e-300 < 0.0"); + + c = huge / huge; + testOk(!isnan(c), "!isnan(1e300 / 1e300)"); + testOk(!isinf(c), "!isinf(1e300 / 1e300)"); + testOk(c == 1.0, "1e300 / 1e300 == 1.0"); + + c = tiny / tiny; + testOk(!isnan(c), "!isnan(1e-300 / 1e-300)"); + testOk(!isinf(c), "!isinf(1e-300 / 1e-300)"); + testOk(c == 1.0, "1e300 / 1e-300 == 1.0"); + + return testDone(); +} diff --git a/modules/libcom/test/epicsMaxThreads.c b/modules/libcom/test/epicsMaxThreads.c new file mode 100644 index 000000000..8d0d06da5 --- /dev/null +++ b/modules/libcom/test/epicsMaxThreads.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMaxThreads.cpp */ + +/* Author: Marty Kraimer Date: 09JUL2004*/ + +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "errlog.h" +#include "testMain.h" + +static epicsEventId started; + +static void thread(void *arg) +{ + epicsEventSignal(started); + epicsThreadSuspendSelf(); +} + +MAIN(epicsMaxThreads) +{ + unsigned int stackSize; + epicsThreadId id; + int i = 0; + + stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + printf("stackSize %d\n",stackSize); + + started = epicsEventMustCreate(epicsEventEmpty); + + while(1) { + id = epicsThreadCreate("thread",50,stackSize,thread,0); + if(!id) break; + i++; + if ((i % 100) == 0) + printf ("Reached %d...\n", i); + epicsEventMustWait(started); + } + + printf("Max number of \"Small\" threads on this OS is %d\n", i); + return 0; +} diff --git a/modules/libcom/test/epicsMessageQueueTest.cpp b/modules/libcom/test/epicsMessageQueueTest.cpp new file mode 100644 index 000000000..ae090fc47 --- /dev/null +++ b/modules/libcom/test/epicsMessageQueueTest.cpp @@ -0,0 +1,328 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author W. Eric Norum + */ +#include +#include +#include +#include + +#include "epicsMessageQueue.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsEvent.h" +#include "epicsAssert.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static const char *msg1 = "1234567890This is a very long message."; +static volatile int sendExit = 0; +static volatile int recvExit = 0; +static epicsEventId finished; +static unsigned int mediumStack; + +/* + * In Numerical Recipes in C: The Art of Scientific Computing (William H. + * Press, Brian P. Flannery, Saul A. Teukolsky, William T. Vetterling; New + * York: Cambridge University Press, 1992 (2nd ed., p. 277)), the follow- + * ing comments are made: + * "If you want to generate a random integer between 1 and 10, you + * should always do it by using high-order bits, as in + * j=1+(int) (10.0*rand()/(RAND_MAX+1.0)); + * and never by anything resembling + * j=1+(rand() % 10); + */ +static int +randBelow(int n) +{ + return (int)((double)n*rand()/(RAND_MAX+1.0)); +} + +extern "C" void +badReceiver(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + char cbuf[80]; + int len; + + cbuf[0] = '\0'; + len = q->receive(cbuf, 1); + if (len < 0 && cbuf[0] == '\0') + testPass("receive into undersized buffer returned error (%d)", len); + else if (len == 1 && cbuf[0] == *msg1) + testPass("receive into undersized buffer truncated message"); + else + testFail("receive into undersized buffer returned %d", len); + + epicsThreadSleep(3.0); + + cbuf[0] = '\0'; + len = q->receive(cbuf, 1); + if (len < 0 && cbuf[0] == '\0') + testPass("receive into undersized buffer returned error (%d)", len); + else if (len == 1 && cbuf[0] == *msg1) + testPass("receive into undersized buffer truncated message"); + else + testFail("receive into undersized buffer returned %d", len); +} + +extern "C" void +receiver(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + const char *myName = epicsThreadGetNameSelf(); + char cbuf[80]; + int expectmsg[4]; + int len; + int sender, msgNum; + int errors = 0; + + for (sender = 1 ; sender <= 4 ; sender++) + expectmsg[sender-1] = 1; + while (!recvExit) { + cbuf[0] = '\0'; + len = q->receive(cbuf, sizeof cbuf, 5.0); + if (len < 0 && !recvExit) { + testDiag("receiver() received unexpected timeout"); + ++errors; + } + else if (sscanf(cbuf, "Sender %d -- %d", &sender, &msgNum) == 2 && + sender >= 1 && sender <= 4) { + if (expectmsg[sender-1] != msgNum) { + ++errors; + testDiag("%s received %d '%.*s' -- expected %d", + myName, len, len, cbuf, expectmsg[sender-1]); + } + expectmsg[sender-1] = msgNum + 1; + epicsThreadSleep(0.001 * (randBelow(20))); + } + } + for (sender = 1 ; sender <= 4 ; sender++) { + if (expectmsg[sender-1] > 1) + testDiag("Received %d messages from Sender %d", + expectmsg[sender-1]-1, sender); + } + if (!testOk1(errors == 0)) + testDiag("Error count was %d", errors); + testDiag("%s exiting", myName); + epicsEventSignal(finished); +} + +extern "C" void +sender(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + char cbuf[80]; + int len; + int i = 0; + + while (!sendExit) { + len = sprintf(cbuf, "%s -- %d.", epicsThreadGetNameSelf(), ++i); + while (q->trySend((void *)cbuf, len) < 0) + epicsThreadSleep(0.005 * (randBelow(5))); + epicsThreadSleep(0.005 * (randBelow(20))); + } + testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i); +} + +extern "C" void messageQueueTest(void *parm) +{ + epicsThreadId myThreadId = epicsThreadGetIdSelf(); + unsigned int i; + char cbuf[80]; + int len; + int pass; + int want; + + epicsMessageQueue *q1 = new epicsMessageQueue(4, 20); + + testDiag("Simple single-thread tests:"); + i = 0; + testOk1(q1->pending() == 0); + while (q1->trySend((void *)msg1, i ) == 0) { + i++; + testOk(q1->pending() == i, "q1->pending() == %d", i); + } + testOk1(q1->pending() == 4); + + want = 0; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 3); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + q1->trySend((void *)msg1, i++); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + q1->trySend((void *)msg1, i++); + testOk1(q1->pending() == 3); + + i = 3; + while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + --i; + testOk(q1->pending() == i, "q1->pending() == %d", i); + want++; + if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + } + testOk1(q1->pending() == 0); + + testDiag("Test sender timeout:"); + i = 0; + testOk1(q1->pending() == 0); + while (q1->send((void *)msg1, i, 1.0 ) == 0) { + i++; + testOk(q1->pending() == i, "q1->pending() == %d", i); + } + testOk1(q1->pending() == 4); + + want = 0; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 3); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + q1->send((void *)msg1, i++, 1.0); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + q1->send((void *)msg1, i++, 1.0); + testOk1(q1->pending() == 3); + + i = 3; + while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + --i; + testOk(q1->pending() == i, "q1->pending() == %d", i); + want++; + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", + want, want, msg1, len, len, cbuf); + } + testOk1(q1->pending() == 0); + + testDiag("Test receiver with timeout:"); + for (i = 0 ; i < 4 ; i++) + testOk1 (q1->send((void *)msg1, i, 1.0) == 0); + testOk1(q1->pending() == 4); + for (i = 0 ; i < 4 ; i++) + testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i, + "q1->receive(...) == %d", i); + testOk1(q1->pending() == 0); + testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0); + testOk1(q1->pending() == 0); + + testDiag("Single receiver with invalid size, single sender tests:"); + epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium, + mediumStack, badReceiver, q1); + epicsThreadSleep(1.0); + testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver"); + epicsThreadSleep(2.0); + testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver"); + epicsThreadSleep(2.0); + + testDiag("Single receiver, single sender tests:"); + epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh); + epicsThreadCreate("Receiver one", epicsThreadPriorityMedium, + mediumStack, receiver, q1); + for (pass = 1 ; pass <= 3 ; pass++) { + for (i = 0 ; i < 10 ; i++) { + if (q1->trySend((void *)msg1, i) < 0) + break; + if (pass >= 3) + epicsThreadSleep(0.5); + } + switch (pass) { + case 1: + if (i<6) + testDiag(" priority-based scheduler, sent %d messages", i); + epicsThreadSetPriority(myThreadId, epicsThreadPriorityLow); + break; + case 2: + if (i<10) + testDiag(" scheduler not strict priority, sent %d messages", i); + else + testDiag(" strict priority scheduler, sent 10 messages"); + break; + case 3: + testOk(i == 10, "%d of 10 messages sent with sender pauses", i); + break; + } + epicsThreadSleep(1.0); + } + + /* + * Single receiver, multiple sender tests + */ + testDiag("Single receiver, multiple sender tests:"); + testDiag("This test lasts 60 seconds..."); + testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow, + mediumStack, sender, q1), + "Created Sender 1"); + testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium, + mediumStack, sender, q1), + "Created Sender 2"); + testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh, + mediumStack, sender, q1), + "Created Sender 3"); + testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh, + mediumStack, sender, q1), + "Created Sender 4"); + + for (i = 0; i < 10; i++) { + testDiag("... %2d", 10 - i); + epicsThreadSleep(6.0); + } + + sendExit = 1; + epicsThreadSleep(1.0); + recvExit = 1; + testDiag("Scheduler exiting"); +} + +MAIN(epicsMessageQueueTest) +{ + testPlan(62); + + finished = epicsEventMustCreate(epicsEventEmpty); + mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium); + + epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium, + mediumStack, messageQueueTest, NULL); + + epicsEventMustWait(finished); + testDiag("Main thread signalled"); + epicsThreadSleep(1.0); + + return testDone(); +} diff --git a/modules/libcom/test/epicsMutexTest.cpp b/modules/libcom/test/epicsMutexTest.cpp new file mode 100644 index 000000000..d44e5c0f1 --- /dev/null +++ b/modules/libcom/test/epicsMutexTest.cpp @@ -0,0 +1,289 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMutexTest.c */ + +/* + * Author: Marty Kraimer Date: 26JAN2000 + * Jeff Hill (added mutex performance test ) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct info { + int threadnum; + epicsMutexId mutex; + int quit; +}info; + +extern "C" void mutexThread(void * arg) +{ + info *pinfo = (info *)arg; + testDiag("mutexThread %d starting", pinfo->threadnum); + while (pinfo->quit--) { + epicsMutexLockStatus status = epicsMutexLock(pinfo->mutex); + testOk(status == epicsMutexLockOK, + "mutexThread %d epicsMutexLock returned %d", + pinfo->threadnum, (int)status); + epicsThreadSleep(.1); + epicsMutexUnlock(pinfo->mutex); + epicsThreadSleep(.9); + } + testDiag("mutexThread %d exiting", pinfo->threadnum); + return; +} + +inline void lockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.unlock (); +} + +inline void tenLockPairs ( epicsMutex & mutex ) +{ + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); +} + +inline void tenLockPairsSquared ( epicsMutex & mutex ) +{ + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); +} + +inline void doubleRecursiveLockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.lock (); + mutex.unlock (); + mutex.unlock (); +} + +inline void tenDoubleRecursiveLockPairs ( epicsMutex & mutex ) +{ + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); +} + +inline void tenDoubleRecursiveLockPairsSquared ( epicsMutex & mutex ) +{ + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); +} + +inline void quadRecursiveLockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.lock (); + mutex.lock (); + mutex.lock (); + mutex.unlock (); + mutex.unlock (); + mutex.unlock (); + mutex.unlock (); +} + +inline void tenQuadRecursiveLockPairs ( epicsMutex & mutex ) +{ + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); +} + +inline void tenQuadRecursiveLockPairsSquared ( epicsMutex & mutex ) +{ + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); +} + +void epicsMutexPerformance () +{ + epicsMutex mutex; + unsigned i; + + // test a single lock pair + epicsTime begin = epicsTime::getCurrent (); + static const unsigned N = 1000; + for ( i = 0; i < N; i++ ) { + tenLockPairsSquared ( mutex ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*1/unlock()*1 takes %f microseconds", delay); + + // test a two times recursive lock pair + begin = epicsTime::getCurrent (); + for ( i = 0; i < N; i++ ) { + tenDoubleRecursiveLockPairsSquared ( mutex ); + } + delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*2/unlock()*2 takes %f microseconds", delay); + + // test a four times recursive lock pair + begin = epicsTime::getCurrent (); + for ( i = 0; i < N; i++ ) { + tenQuadRecursiveLockPairsSquared ( mutex ); + } + delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*4/unlock()*4 takes %f microseconds", delay); +} + +struct verifyTryLock { + epicsMutexId mutex; + epicsEventId done; +}; + +extern "C" void verifyTryLockThread ( void *pArg ) +{ + struct verifyTryLock *pVerify = + ( struct verifyTryLock * ) pArg; + + testOk1(epicsMutexTryLock(pVerify->mutex) == epicsMutexLockTimeout); + epicsEventSignal ( pVerify->done ); +} + +void verifyTryLock () +{ + struct verifyTryLock verify; + + verify.mutex = epicsMutexMustCreate (); + verify.done = epicsEventMustCreate ( epicsEventEmpty ); + + testOk1(epicsMutexTryLock(verify.mutex) == epicsMutexLockOK); + + epicsThreadCreate ( "verifyTryLockThread", 40, + epicsThreadGetStackSize(epicsThreadStackSmall), + verifyTryLockThread, &verify ); + + testOk1(epicsEventWait ( verify.done ) == epicsEventWaitOK); + + epicsMutexUnlock ( verify.mutex ); + epicsMutexDestroy ( verify.mutex ); + epicsEventDestroy ( verify.done ); +} + +MAIN(epicsMutexTest) +{ + const int nthreads = 3; + const int nrounds = 5; + unsigned int stackSize; + epicsThreadId *id; + int i; + char **name; + void **arg; + info **pinfo; + epicsMutexId mutex; + int status; + + testPlan(5 + nthreads * nrounds); + + verifyTryLock (); + + mutex = epicsMutexMustCreate(); + status = epicsMutexLock(mutex); + testOk(status == 0, "epicsMutexLock returned %d", status); + status = epicsMutexTryLock(mutex); + testOk(status == 0, "epicsMutexTryLock returned %d", status); + epicsMutexUnlock(mutex); + epicsMutexUnlock(mutex); + + id = (epicsThreadId *)calloc(nthreads,sizeof(epicsThreadId)); + name = (char **)calloc(nthreads,sizeof(char *)); + arg = (void **)calloc(nthreads,sizeof(void *)); + pinfo = (info **)calloc(nthreads,sizeof(info *)); + stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + for(i=0; ithreadnum = i; + pinfo[i]->mutex = mutex; + pinfo[i]->quit = nrounds; + arg[i] = pinfo[i]; + id[i] = epicsThreadCreate(name[i],40,stackSize, + mutexThread, + arg[i]); + } + epicsThreadSleep(2.0 + nrounds); + + epicsMutexPerformance (); + + free(pinfo); + free(arg); + free(name); + free(id); + epicsMutexDestroy(mutex); + + return testDone(); +} diff --git a/modules/libcom/test/epicsRunLibComTests.c b/modules/libcom/test/epicsRunLibComTests.c new file mode 100644 index 000000000..29c5eda3f --- /dev/null +++ b/modules/libcom/test/epicsRunLibComTests.c @@ -0,0 +1,122 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Run libCom tests as a batch. + * + * Do *not* include performance measurements here, they don't help to + * prove functionality (which is the point of this convenience routine). + */ + +#include +#include +#include + +int blockingSockTest(void); +int epicsAlgorithm(void); +int epicsAtomicTest(void); +int epicsCalcTest(void); +int epicsEllTest(void); +int epicsEnvTest(void); +int epicsErrlogTest(void); +int epicsEventTest(void); +int epicsExitTest(void); +int epicsMathTest(void); +int epicsMessageQueueTest(void); +int epicsMMIOTest(void); +int epicsMutexTest(void); +int epicsSockResolveTest(void); +int epicsSpinTest(void); +int epicsStackTraceTest(void); +int epicsStdioTest(void); +int epicsStdlibTest(void); +int epicsStringTest(void); +int epicsThreadHooksTest(void); +int epicsThreadOnceTest(void); +int epicsThreadPoolTest(void); +int epicsThreadPriorityTest(void); +int epicsThreadPrivateTest(void); +int epicsThreadTest(void); +int epicsTimerTest(void); +int epicsTimeTest(void); +#ifdef __rtems__ +int epicsTimeZoneTest(void); +#endif +int epicsTypesTest(void); +int epicsInlineTest(void); +int ipAddrToAsciiTest(void); +int macDefExpandTest(void); +int macLibTest(void); +int osiSockTest(void); +int ringBytesTest(void); +int ringPointerTest(void); +int taskwdTest(void); + +void epicsRunLibComTests(void) +{ + testHarness(); + + /* + * Thread startup sets some internal variables so do it first + */ + runTest(epicsThreadTest); + + /* + * Timer tests get confused if run after some of the other tests + */ + runTest(epicsTimerTest); + + /* + * Run the regular tests in alphabetical order + */ + runTest(blockingSockTest); + runTest(epicsAlgorithm); + runTest(epicsAtomicTest); + runTest(epicsCalcTest); + runTest(epicsEllTest); + runTest(epicsEnvTest); + runTest(epicsErrlogTest); + runTest(epicsEventTest); + runTest(epicsInlineTest); + runTest(epicsMathTest); + runTest(epicsMessageQueueTest); + runTest(epicsMMIOTest); + runTest(epicsMutexTest); + runTest(epicsSockResolveTest); + runTest(epicsSpinTest); + runTest(epicsStackTraceTest); + runTest(epicsStdioTest); + runTest(epicsStdlibTest); + runTest(epicsStringTest); + runTest(epicsThreadHooksTest); + runTest(epicsThreadOnceTest); + runTest(epicsThreadPoolTest); + runTest(epicsThreadPriorityTest); + runTest(epicsThreadPrivateTest); + runTest(epicsTimeTest); +#ifdef __rtems__ + runTest(epicsTimeZoneTest); +#endif + runTest(epicsTypesTest); + runTest(ipAddrToAsciiTest); + runTest(macDefExpandTest); + runTest(macLibTest); + runTest(osiSockTest); + runTest(ringBytesTest); + runTest(ringPointerTest); + runTest(taskwdTest); + + /* + * Report now in case epicsExitTest dies + */ + testHarnessDone(); + + /* + * epicsExitTest must come last as it never returns + */ + runTest(epicsExitTest); +} diff --git a/modules/libcom/test/epicsSockResolveTest.c b/modules/libcom/test/epicsSockResolveTest.c new file mode 100644 index 000000000..8131d9dbf --- /dev/null +++ b/modules/libcom/test/epicsSockResolveTest.c @@ -0,0 +1,99 @@ +/*************************************************************************\ +* Copyright (c) 2012 Brookhaven Science Associates as Operator of +* Brookhaven National Lab. +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +\*************************************************************************/ + +#include "dbDefs.h" +#include "osiSock.h" + +#include "epicsUnitTest.h" +#include "testMain.h" + +#define DEFAULT_PORT 4000 + +typedef struct { + const char *input; + unsigned long IP; + unsigned short port; +} testData; + +static testData okdata[] = { + {"127.0.0.1", 0x7f000001, DEFAULT_PORT}, + {"127.0.0.1:42", 0x7f000001, 42}, + {"localhost", 0x7f000001, DEFAULT_PORT}, + {"localhost:42", 0x7f000001, 42}, + {"2424", 2424, DEFAULT_PORT}, + {"2424:42", 2424, 42}, + {"255.255.255.255", 0xffffffff, DEFAULT_PORT}, + {"255.255.255.255:65535", 0xffffffff, 65535}, +}; + +static const char * baddata[] = { + "127.0.0.1:NaN", + "127.0.0.test", + "127.0.0.test:42", + "16name.invalid", + "16name.invalid:42", + "1.2.3.4.5", + "1.2.3.4.5:6", + "1.2.3.4:5.6", + "256.255.255.255", + "255.256.255.255", + "255.255.256.255", + "255.255.255.256", + "255.255.255.255:65536", +}; + +MAIN(epicsSockResolveTest) +{ + int i; + + testPlan(3*NELEMENTS(okdata) + NELEMENTS(baddata)); + osiSockAttach(); + + { + struct in_addr addr; + + /* See RFCs 2606 and 6761 */ + if (hostToIPAddr("guaranteed.invalid.", &addr) == 0) { + testAbort("hostToIPAddr() is broken, testing not possible"); + } + } + + testDiag("Tests of aToIPAddr"); + + for (i=0; i %d", + okdata[i].input, DEFAULT_PORT, ret); + if (ret) { + testSkip(2, " aToIPAddr() failed"); + } + else { + testOk(addr.sin_addr.s_addr == htonl(okdata[i].IP), " IP correct"); + testOk(addr.sin_port == htons(okdata[i].port), " Port correct"); + } + } + + for (i=0; i %d", + baddata[i], DEFAULT_PORT, ret); + if (ret==0) { + testDiag(" IP=0x%lx, port=%d", + (unsigned long) ntohl(addr.sin_addr.s_addr), + ntohs(addr.sin_port)); + } + } + + osiSockRelease(); + return testDone(); +} diff --git a/modules/libcom/test/epicsSpinTest.c b/modules/libcom/test/epicsSpinTest.c new file mode 100644 index 000000000..c45348beb --- /dev/null +++ b/modules/libcom/test/epicsSpinTest.c @@ -0,0 +1,247 @@ +/*************************************************************************\ +* Copyright (c) 2012 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2012 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + * + * based on epicsMutexTest by Marty Kraimer and Jeff Hill + * + */ + +#include +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsAtomic.h" +#include "epicsSpin.h" +#include "epicsEvent.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct info { + int threadnum; + epicsSpinId spin; + int *counter; + int rounds; + epicsEventId done; +} info; + +#define spinDelay 0.016667 + +void spinThread(void *arg) +{ + info *pinfo = (info *) arg; + testDiag("spinThread %d starting", pinfo->threadnum); + epicsThreadSleep(0.1); /* Try to align threads */ + while (pinfo->rounds--) { + epicsThreadSleep(spinDelay); + epicsSpinLock(pinfo->spin); + pinfo->counter[0]++; + epicsSpinUnlock(pinfo->spin); + } + testDiag("spinThread %d exiting", pinfo->threadnum); + epicsEventSignal(pinfo->done); +} + +static void lockPair(struct epicsSpin *spin) +{ + epicsSpinLock(spin); + epicsSpinUnlock(spin); +} + +static void tenLockPairs(struct epicsSpin *spin) +{ + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); + lockPair(spin); +} + +static void tenLockPairsSquared(struct epicsSpin *spin) +{ + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); + tenLockPairs(spin); +} + +void epicsSpinPerformance () +{ + static const unsigned N = 10000; + unsigned i; + epicsSpinId spin; + epicsTimeStamp begin; + epicsTimeStamp end; + double delay; + + /* Initialize spinlock */ + spin = epicsSpinCreate(); + if (!spin) + testAbort("epicsSpinCreate() returned NULL"); + + /* test a single lock pair */ + epicsTimeGetCurrent(&begin); + for ( i = 0; i < N; i++ ) { + tenLockPairsSquared(spin); + } + epicsTimeGetCurrent(&end); + + delay = epicsTimeDiffInSeconds(&end, &begin); + delay /= N * 100u; /* convert to delay per lock pair */ + delay *= 1e6; /* convert to micro seconds */ + testDiag("lock()*1/unlock()*1 takes %f microseconds", delay); + epicsSpinDestroy(spin); +} + +struct verifyTryLock; + +struct verifyTryLockEnt { + epicsEventId done; + struct verifyTryLock *main; +}; + +struct verifyTryLock { + epicsSpinId spin; + int flag; + struct verifyTryLockEnt *ents; +}; + +static void verifyTryLockThread(void *pArg) +{ + struct verifyTryLockEnt *pVerify = + (struct verifyTryLockEnt *) pArg; + + while(epicsAtomicGetIntT(&pVerify->main->flag)==0) { + int ret = epicsSpinTryLock(pVerify->main->spin); + if(ret!=0) { + epicsAtomicCmpAndSwapIntT(&pVerify->main->flag, 0, ret); + break; + } else + epicsSpinUnlock(pVerify->main->spin); + } + + epicsEventSignal(pVerify->done); +} + +/* Start one thread per CPU which will all try lock + * the same spinlock. They break as soon as one + * fails to take the lock. + */ +static void verifyTryLock() +{ + int N, i; + struct verifyTryLock verify; + + N = epicsThreadGetCPUs(); + if(N==1) { + testSkip(1, "verifyTryLock() only for SMP systems"); + return; + } + + verify.flag = 0; + verify.spin = epicsSpinMustCreate(); + + testDiag("Starting %d spinners", N); + + verify.ents = calloc(N, sizeof(*verify.ents)); + for(i=0; ithreadnum = i; + pinfo[i]->spin = spin; + pinfo[i]->counter = &counter; + pinfo[i]->rounds = nrounds; + pinfo[i]->done = epicsEventMustCreate(epicsEventEmpty); + id[i] = epicsThreadCreate(name[i], 40, stackSize, + spinThread, + pinfo[i]); + } + for (i = 0; i < nthreads; i++) { + epicsEventMustWait(pinfo[i]->done); + epicsEventDestroy(pinfo[i]->done); + free(name[i]); + free(pinfo[i]); + } + testOk(counter == nthreads * nrounds, "Loops run = %d (expecting %d)", + counter, nthreads * nrounds); + + free(pinfo); + free(name); + free(id); + epicsSpinDestroy(spin); + + epicsSpinPerformance(); + + return testDone(); +} diff --git a/modules/libcom/test/epicsStackTraceTest.c b/modules/libcom/test/epicsStackTraceTest.c new file mode 100644 index 000000000..b89fc7836 --- /dev/null +++ b/modules/libcom/test/epicsStackTraceTest.c @@ -0,0 +1,230 @@ +/* + * Copyright: Stanford University / SLAC National Laboratory. + * + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + * + * Author: Till Straumann , 2014 + */ + +/* + * Check stack trace functionality + */ + +#include "epicsStackTrace.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#include +#include +#include + +#define TST_BUFSZ 10000 + +#define MAXP 10 + +/* estimated size of (compiled) epicsStackTraceRecurseGbl */ +#define WINDOW_SZ 400 + +typedef struct TestDataRec_ { + char buf[TST_BUFSZ]; + int pos; +} TestDataRec, *TestData; + +typedef void (*RecFn)(int); + +/* We want a stack trace and need a few nested routines. + * The whole magic here is intended to prevent a compiler + * from optimizing the call stack away: + * - call via a volatile pointer + * - add a call to a no-op function at the end so that + * tail-call optimization doesn't eliminate the call + * stack. + * + * We use a local (static) and a global routine to test + * if the stacktrace supports either flavor. + */ + +void epicsStackTraceRecurseGbl(int lvl); +static void epicsStackTraceRecurseLcl(int lvl); + +void nopFn(int lvl) +{ +} + +RecFn volatile lfp = epicsStackTraceRecurseLcl; +RecFn volatile gfp = epicsStackTraceRecurseGbl; +RecFn volatile nfp = nopFn; + +static void +epicsStackTraceRecurseLcl(int lvl) +{ + if ( lvl ) + gfp(lvl-1); + else + epicsStackTrace(); + + /* call something so that the call through gfp() doesn't + * get optimized into a jump (tail-call optimization) + */ + nfp(0); +} + +void epicsStackTraceRecurseGbl(int lvl) +{ + if ( lvl ) + lfp(lvl-1); + else + epicsStackTrace(); + + /* call something so that the call through gfp() doesn't + * get optimized into a jump (tail-call optimization) + */ + nfp(0); +} + +static void logClient(void *ptr, const char *msg) +{ + TestData td = ptr; + size_t sz = strlen(msg); + size_t mx = sizeof(td->buf) - td->pos - 1; + + if ( sz > mx ) + sz = mx; + strncpy( td->buf+td->pos, msg, sz ); + td->pos += sz; +} + +static int +findStringOcc(const char *buf, const char *what) +{ + int rval = 0; + size_t l = strlen(what); + int ch; + + while ( (buf = strstr(buf, what)) ) { + /* Is it just a prefix? */ + ch = buf[l]; + if ( ! isalnum(ch) && '_' != ch ) { + rval++; + } + buf += l; + } + + testDiag("found %i x %s\n", rval, what); + + return rval; +} + +static int +findNumOcc(const char *buf) +{ + void *ptrs[MAXP]; + int n_ptrs = 0; + int i,j; + int rval = 0; + + while ( n_ptrs < sizeof(ptrs)/sizeof(ptrs[0]) && (buf=strchr(buf,'[')) ) { + if ( 1 == sscanf(buf+1,"%p", &ptrs[n_ptrs]) ) + n_ptrs++; + buf++; + } + /* We should find an address close to epicsStackTraceRecurseGbl twice */ + for (i=0; i= (char*)epicsStackTraceRecurseGbl && (char*)ptrs[i] < (char*)epicsStackTraceRecurseGbl + WINDOW_SZ ) { + rval ++; + testDiag("found address %p again\n", ptrs[i]); + } + } + j++; + } + } + return rval; +} + +MAIN(epicsStackTraceTest) +{ + int features, all_features; + TestDataRec testData; + int gblFound, lclFound, numFound, dynFound; + char *nl, *p; + + testData.pos = 0; + + testPlan(5); + + features = epicsStackTraceGetFeatures(); + + all_features = EPICS_STACKTRACE_LCL_SYMBOLS + | EPICS_STACKTRACE_GBL_SYMBOLS + | EPICS_STACKTRACE_DYN_SYMBOLS + | EPICS_STACKTRACE_ADDRESSES; + + if ( ! testOk( (features & ~all_features) == 0, + "epicsStackTraceGetFeatures() obtains features") ) + testAbort("epicsStackTraceGetFeatures() not working as expected"); + + testData.pos = 0; + + testDiag("calling a few nested routines and eventually dump a stack trace"); + + eltc(0); + errlogAddListener( logClient, &testData ); + gfp(3); + errlogRemoveListeners( logClient, &testData ); + eltc(1); + + /* ensure there's a terminating NUL -- we have reserved space for it */ + testData.buf[testData.pos] = 0; + + testDiag("now scan the result for what we expect"); + + dynFound = findStringOcc( testData.buf, "epicsStackTrace" ); + gblFound = findStringOcc( testData.buf, "epicsStackTraceRecurseGbl" ); + lclFound = findStringOcc( testData.buf, "epicsStackTraceRecurseLcl" ); + numFound = findNumOcc ( testData.buf ); + + if ( (features & EPICS_STACKTRACE_DYN_SYMBOLS) ) { + testOk( dynFound == 1, "dumping symbol from library" ); + } else { + testSkip(1, "no support for dumping library symbols on this platform"); + } + + if ( (features & EPICS_STACKTRACE_GBL_SYMBOLS) ) { + testOk( gblFound == 2, "dumping global symbols" ); + } else { + testSkip(1, "no support for dumping global symbols on this platform"); + } + + if ( (features & EPICS_STACKTRACE_LCL_SYMBOLS) ) { + testOk( lclFound == 2, "dumping local symbols" ); + } else { + testSkip(1, "no support for dumping local symbols on this platform"); + } + + if ( (features & EPICS_STACKTRACE_ADDRESSES) ) { + testOk( numFound > 0, "dumping addresses" ); + } else { + testSkip(1 , "no support for dumping addresses on this platform"); + } + + p = testData.buf; + while ( (nl = strchr(p,'\n')) ) { + *nl = 0; + testDiag("%s",p); + *nl = '\n'; + p = nl+1; + } + testDiag("%s", p); + + testDone(); + + return 0; +} diff --git a/modules/libcom/test/epicsStdioTest.c b/modules/libcom/test/epicsStdioTest.c new file mode 100644 index 000000000..bf25784a5 --- /dev/null +++ b/modules/libcom/test/epicsStdioTest.c @@ -0,0 +1,141 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsStdioTest.c + * + * Author Marty Kraimer + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsStdio.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +#define LINE_1 "# This is first line of sample report\n" +#define LINE_2 "# This is second and last line of sample report\n" + +static void testEpicsSnprintf(void) { + char exbuffer[80], buffer[80]; + const int ivalue = 1234; + const float fvalue = 1.23e4f; + const char *svalue = "OneTwoThreeFour"; + const char *format = "int %d float %8.2e string %s"; + const char *expected = exbuffer; + int size; + int rtn, rlen; + +#ifdef _WIN32 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || \ + (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) + _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +#endif + + sprintf(exbuffer, format, ivalue, fvalue, svalue); + rlen = strlen(expected)+1; + + strcpy(buffer, "AAAA"); + + for (size = 1; size < strlen(expected) + 5; ++size) { + rtn = epicsSnprintf(buffer, size, format, ivalue, fvalue, svalue); + testOk(rtn <= rlen-1, "epicsSnprintf(size=%d) = %d", size, rtn); + if (rtn != rlen-1) + testDiag("Return value does not indicate buffer size needed"); + testOk(strncmp(buffer, expected, size - 1) == 0, + "buffer = '%s'", buffer); + rtn = strlen(buffer); + testOk(rtn == (size < rlen ? size : rlen) - 1, + "length = %d", rtn); + } +} + +void testStdoutRedir (const char *report) +{ + FILE *realStdout = stdout; + FILE *stream = 0; + char linebuf[80]; + size_t buflen = sizeof linebuf; + + testOk1(epicsGetStdout() == stdout); + + errno = 0; + if (!testOk1((stream = fopen(report, "w")) != NULL)) { + testDiag("'%s' could not be opened for writing: %s", + report, strerror(errno)); + testSkip(11, "Can't create stream file"); + return; + } + + epicsSetThreadStdout(stream); + testOk1(stdout == stream); + + printf(LINE_1); + printf(LINE_2); + + epicsSetThreadStdout(0); + testOk1(epicsGetStdout() == realStdout); + testOk1(stdout == realStdout); + + errno = 0; + if (!testOk1(!fclose(stream))) { + testDiag("fclose error: %s", strerror(errno)); +#ifdef vxWorks + testDiag("The above test fails if you don't cd to a writable directory"); + testDiag("before running the test. The next test will also fail..."); +#endif + } + + if (!testOk1((stream = fopen(report, "r")) != NULL)) { + testDiag("'%s' could not be opened for reading: %s", + report, strerror(errno)); + testSkip(6, "Can't reopen stream file."); + return; + } + + if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { + testDiag("File read error: %s", strerror(errno)); + testSkip(5, "Read failed."); + fclose(stream); + return; + } + testOk(strcmp(linebuf, LINE_1) == 0, "First line correct"); + + if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { + testDiag("File read error: %s", strerror(errno)); + testSkip(1, "No line to compare."); + } else + testOk(strcmp(linebuf, LINE_2) == 0, "Second line"); + + testOk(!fgets(linebuf, buflen, stream), "File ends"); + + if (!testOk1(!fclose(stream))) + testDiag("fclose error: %s\n", strerror(errno)); +} + +MAIN(epicsStdioTest) +{ + testPlan(163); + testEpicsSnprintf(); +#ifdef __rtems__ + /* ensure there is a writeable area */ + mkdir( "/tmp", S_IRWXU ); + testStdoutRedir("/tmp/report"); +#else + testStdoutRedir("report"); +#endif + return testDone(); +} diff --git a/modules/libcom/test/epicsStdlibTest.c b/modules/libcom/test/epicsStdlibTest.c new file mode 100644 index 000000000..d82f834b8 --- /dev/null +++ b/modules/libcom/test/epicsStdlibTest.c @@ -0,0 +1,567 @@ +/*************************************************************************\ +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsStdlibTest.c + * + * Author Andrew Johnson + */ + +#include +#include +#include +#include + +#include "epicsTypes.h" +#include "epicsStdlib.h" +#include "epicsMath.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* Implement the epicsParseDouble() API for checking the native stdtod() + * so we can tell the user if our epicsStrtod() wrapper is unnecessary. + */ +int +parseStrtod(const char *str, double *to, char **units) +{ + int c; + char *endp; + double value; + + while ((c = *str) && isspace(c)) + ++str; + + errno = 0; + value = strtod(str, &endp); + + if (endp == str) + return S_stdlib_noConversion; + if (errno == ERANGE) + return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow; + + while ((c = *endp) && isspace(c)) + ++endp; + if (c && !units) + return S_stdlib_extraneous; + + *to = value; + if (units) + *units = endp; + return 0; +} +#define scanStrtod(str, to) !parseStrtod(str, to, NULL) + + +MAIN(epicsStdlibTest) +{ + unsigned long u; + long l; + unsigned long long ull; + long long ll; + double d; + float f; + char *endp; + epicsInt8 i8; + epicsUInt8 u8; + epicsInt16 i16; + epicsUInt16 u16; + epicsInt32 i32; + epicsUInt32 u32; + epicsInt64 i64; + epicsUInt64 u64; + + testPlan(199); + + testOk(epicsParseLong("", &l, 0, NULL) == S_stdlib_noConversion, + "Long '' => noConversion"); + testOk(epicsParseULong("", &u, 0, NULL) == S_stdlib_noConversion, + "ULong '' => noConversion"); + testOk(epicsParseLLong("", &ll, 0, NULL) == S_stdlib_noConversion, + "LLong '' => noConversion"); + testOk(epicsParseULLong("", &ull, 0, NULL) == S_stdlib_noConversion, + "ULLong '' => noConversion"); + testOk(epicsParseFloat("", &f, NULL) == S_stdlib_noConversion, + "Float '' => noConversion"); + testOk(epicsParseDouble("", &d, NULL) == S_stdlib_noConversion, + "Double '' => noConversion"); + + testOk(epicsParseLong("\t \n", &l, 0, NULL) == S_stdlib_noConversion, + "Long '\\t 1\\n' => noConversion"); + testOk(epicsParseULong("\t \n", &u, 0, NULL) == S_stdlib_noConversion, + "ULong '\\t 1\\n' => noConversion"); + testOk(epicsParseLLong("\t \n", &ll, 0, NULL) == S_stdlib_noConversion, + "LLong '\\t 1\\n' => noConversion"); + testOk(epicsParseULLong("\t \n", &ull, 0, NULL) == S_stdlib_noConversion, + "ULLong '\\t 1\\n' => noConversion"); + testOk(epicsParseFloat("\t \n", &f, NULL) == S_stdlib_noConversion, + "Float '\\t 1\\n' => noConversion"); + testOk(epicsParseDouble("\t \n", &d, NULL) == S_stdlib_noConversion, + "Double '\\t 1\\n' => noConversion"); + + testOk(epicsParseLong("!", &l, 0, NULL) == S_stdlib_noConversion, + "Long '!' => noConversion"); + testOk(epicsParseULong("!", &u, 0, NULL) == S_stdlib_noConversion, + "ULong '!' => noConversion"); + testOk(epicsParseLLong("!", &ll, 0, NULL) == S_stdlib_noConversion, + "LLong '!' => noConversion"); + testOk(epicsParseULLong("!", &ull, 0, NULL) == S_stdlib_noConversion, + "ULLong '!' => noConversion"); + testOk(epicsParseFloat("!", &f, NULL) == S_stdlib_noConversion, + "Float '!' => noConversion"); + testOk(epicsParseDouble("!", &d, NULL) == S_stdlib_noConversion, + "Double '!' => noConversion"); + + testOk(epicsScanLong("0", &l, 0) && l == 0, "Long '0'"); + testOk(epicsScanULong("0", &u, 0) && u == 0, "ULong '0'"); + testOk(epicsScanLLong("0", &ll, 0) && ll == 0, "LLong '0'"); + testOk(epicsScanULLong("0", &ull, 0) && ull == 0, "ULLong '0'"); + testOk(epicsScanFloat("0", &f) && f == 0, "Float '0'"); + testOk(epicsScanDouble("0", &d) && d == 0, "Double '0'"); + + testOk(epicsScanLong("\t 1\n", &l, 0) && l == 1, "Long '\\t 1\\n'"); + testOk(epicsScanULong("\t 1\n", &u, 0) && u == 1, "ULong '\\t 1\\n'"); + testOk(epicsScanLLong("\t 1\n", &ll, 0) && ll == 1, "LLong '\\t 1\\n'"); + testOk(epicsScanULLong("\t 1\n", &ull, 0) && ull == 1, "ULLong '\\t 1\\n'"); + testOk(epicsScanFloat("\t 1\n", &f) && f == 1, "Float '\\t 1\\n'"); + testOk(epicsScanDouble("\t 1\n", &d) && d == 1, "Double '\\t 1\\n'"); + + testOk(epicsScanLong("-1", &l, 0) && l == -1, "Long '-1'"); + testOk(epicsScanULong("-1", &u, 0) && u + 1 == 0, "ULong '-1'"); + testOk(epicsScanLLong("-1", &ll, 0) && ll == -1, "LLong '-1'"); + testOk(epicsScanULLong("-1", &ull, 0) && ull + 1 == 0, "ULLong '-1'"); + testOk(epicsScanFloat("-1", &f) && f == -1, "Float '-1'"); + testOk(epicsScanDouble("-1", &d) && d == -1, "Double '-1'"); + + testOk(epicsParseLong("2!", &l, 0, NULL) == S_stdlib_extraneous, + "Long '2!' => extraneous"); + testOk(epicsParseULong("2!", &u, 0, NULL) == S_stdlib_extraneous, + "ULong '2!' => extraneous"); + testOk(epicsParseLLong("2!", &ll, 0, NULL) == S_stdlib_extraneous, + "LLong '2!' => extraneous"); + testOk(epicsParseULLong("2!", &ull, 0, NULL) == S_stdlib_extraneous, + "ULLong '2!' => extraneous"); + testOk(epicsParseFloat("2!", &f, NULL) == S_stdlib_extraneous, + "Float '2!' => extraneous"); + testOk(epicsParseDouble("2!", &d, NULL) == S_stdlib_extraneous, + "Double '2!' => extraneous"); + + testOk(epicsParseLong("3 \n\t!", &l, 0, NULL) == S_stdlib_extraneous, + "Long '3 \\n\\t!' => extraneous"); + testOk(epicsParseULong("3 \n\t!", &u, 0, NULL) == S_stdlib_extraneous, + "ULong '3 \\n\\t!' => extraneous"); + testOk(epicsParseLLong("3 \n\t!", &ll, 0, NULL) == S_stdlib_extraneous, + "LLong '3 \\n\\t!' => extraneous"); + testOk(epicsParseULLong("3 \n\t!", &ull, 0, NULL) == S_stdlib_extraneous, + "ULLong '3 \\n\\t!' => extraneous"); + testOk(epicsParseFloat("3 \n\t!", &f, NULL) == S_stdlib_extraneous, + "Float '3 \\n\\t!' => extraneous"); + testOk(epicsParseDouble("3 \n\t!", &d, NULL) == S_stdlib_extraneous, + "Double '3 \\n\\t!' => extraneous"); + + testOk(!epicsParseLong("2!", &l, 0, &endp) && *endp == '!', + "Long '2!' => units='!'"); + testOk(!epicsParseULong("2!", &u, 0, &endp) && *endp == '!', + "ULong '2!' => units='!'"); + testOk(!epicsParseLLong("2!", &ll, 0, &endp) && *endp == '!', + "LLong '2!' => units='!'"); + testOk(!epicsParseULLong("2!", &ull, 0, &endp) && *endp == '!', + "ULLong '2!' => units='!'"); + testOk(!epicsParseFloat("2!", &f, &endp) && *endp == '!', + "Float '2!' => units='!'"); + testOk(!epicsParseDouble("2!", &d, &endp) && *endp == '!', + "Double '2!' => units='!'"); + + testOk(!epicsParseLong("3 \n\t!", &l, 0, &endp) && *endp == '!', + "Long '3 \\n\\t!' => units='!'"); + testOk(!epicsParseULong("3 \n\t!", &u, 0, &endp) && *endp == '!', + "ULong '3 \\n\\t!' => units='!'"); + testOk(!epicsParseLLong("3 \n\t!", &ll, 0, &endp) && *endp == '!', + "LLong '3 \\n\\t!' => units='!'"); + testOk(!epicsParseULLong("3 \n\t!", &ull, 0, &endp) && *endp == '!', + "ULLong '3 \\n\\t!' => units='!'"); + testOk(!epicsParseFloat("3 \n\t!", &f, &endp) && *endp == '!', + "Float '3 \\n\\t!' => units='!'"); + testOk(!epicsParseDouble("3 \n\t!", &d, &endp) && *endp == '!', + "Double '3 \\n\\t!' => units='!'"); + + testOk(epicsScanLong("0x0", &l, 0) && l == 0, "Long '0x0'"); + testOk(epicsScanULong("0x0", &u, 0) && u == 0, "ULong '0x0'"); + testOk(epicsScanLLong("0x0", &ll, 0) && ll == 0, "LLong '0x0'"); + testOk(epicsScanULLong("0x0", &ull, 0) && ull == 0, "ULLong '0x0'"); + testOk(epicsScanFloat("0x0", &f) && f == 0, "Float '0x0'"); + testOk(epicsScanDouble("0x0", &d) && d == 0, "Double '0x0'"); + + testOk(epicsScanLong("0x1", &l, 0) && l == 1, "Long '0x1'"); + testOk(epicsScanULong("0x1", &u, 0) && u == 1, "ULong '0x1'"); + testOk(epicsScanLLong("0x1", &ll, 0) && ll == 1, "LLong '0x1'"); + testOk(epicsScanULLong("0x1", &ull, 0) && ull == 1, "ULLong '0x1'"); + testOk(epicsScanFloat("0x1", &f) && f == 1, "Float '0x1'"); + testOk(epicsScanDouble("0x1", &d) && d == 1, "Double '0x1'"); + + testOk(epicsScanLong("+0x1", &l, 0) && l == 1, "Long '+0x1'"); + testOk(epicsScanULong("+0x1", &u, 0) && u == 1, "ULong '+0x1'"); + testOk(epicsScanLLong("+0x1", &ll, 0) && ll == 1, "LLong '+0x1'"); + testOk(epicsScanULLong("+0x1", &ull, 0) && ull == 1, "ULLong '+0x1'"); + testOk(epicsScanFloat("+0x1", &f) && f == 1, "Float '+0x1'"); + testOk(epicsScanDouble("+0x1", &d) && d == 1, "Double '+0x1'"); + + testOk(epicsScanLong("-0x1", &l, 0) && l == -1, "Long '-0x1'"); + testOk(epicsScanULong("-0x1", &u, 0) && u == -1, "ULong '-0x1'"); + testOk(epicsScanLLong("-0x1", &ll, 0) && ll == -1, "LLong '-0x1'"); + testOk(epicsScanULLong("-0x1", &ull, 0) && ull == -1, "ULLong '-0x1'"); + testOk(epicsScanFloat("-0x1", &f) && f == -1, "Float '-0x1'"); + testOk(epicsScanDouble("-0x1", &d) && d == -1, "Double '-0x1'"); + + testOk(epicsScanLong("0xf", &l, 0) && l == 15, "Long '0xf'"); + testOk(epicsScanULong("0xf", &u, 0) && u == 15, "ULong '0xf'"); + testOk(epicsScanLLong("0xf", &ll, 0) && ll == 15, "LLong '0xf'"); + testOk(epicsScanULLong("0xf", &ull, 0) && ull == 15, "ULLong '0xf'"); + testOk(epicsScanFloat("0xf", &f) && f == 15, "Float '0xf'"); + testOk(epicsScanDouble("0xf", &d) && d == 15, "Double '0xf'"); + + testOk(epicsScanLong("0XF", &l, 0) && l == 15, "Long '0XF'"); + testOk(epicsScanULong("0XF", &u, 0) && u == 15, "ULong '0XF'"); + testOk(epicsScanLLong("0XF", &ll, 0) && ll == 15, "LLong '0XF'"); + testOk(epicsScanULLong("0XF", &ull, 0) && ull == 15, "ULLong '0XF'"); + testOk(epicsScanFloat("0XF", &f) && f == 15, "Float '0XF'"); + testOk(epicsScanDouble("0XF", &d) && d == 15, "Double '0XF'"); + + testOk(epicsParseLong("0x0", &l, 10, NULL) == S_stdlib_extraneous, + "Long '0x0' in base 10 => extraneous"); + testOk(epicsParseULong("0x0", &u, 10, NULL) == S_stdlib_extraneous, + "ULong '0x0' in base 10 => extraneous"); + testOk(epicsParseLLong("0x0", &ll, 10, NULL) == S_stdlib_extraneous, + "LLong '0x0' in base 10 => extraneous"); + testOk(epicsParseULLong("0x0", &ull, 10, NULL) == S_stdlib_extraneous, + "ULLong '0x0' in base 10 => extraneous"); + + testOk(epicsScanLong("0x10", &l, 0) && l == 0x10, + "Long '0x10' in base 0"); + testOk(epicsScanULong("0x10", &u, 0) && u == 0x10, + "ULong '0x10' in base 0"); + testOk(epicsScanLong("0x10", &l, 16) && l == 0x10, + "Long '0x10' in base 16"); + testOk(epicsScanULong("0x10", &u, 16) && u == 0x10, + "ULong '0x10' in base 16"); + testOk(epicsScanLong("10", &l, 16) && l == 0x10, + "Long '10' in base 16"); + testOk(epicsScanULong("10", &u, 16) && u == 0x10, + "ULong '10' in base 16"); + + testOk(epicsScanLong("0x7fffffff", &l, 0) && l == 0x7fffffff, + "Long '0x7fffffff'"); + testOk(epicsScanULong("0xffffffff", &u, 0) && u == 0xffffffff, + "ULong '0xffffffff'"); + testOk(epicsScanLLong("0x7fffffffffffffff", &ll, 0) && + ll == 0x7fffffffffffffffLL, "LLong '0x7fffffffffffffff'"); + testOk(epicsScanULLong("0xffffffffffffffff", &ull, 0) && + ull == 0xffffffffffffffffULL, "ULLong '0xffffffffffffffff'"); + testOk(epicsScanFloat("0xffffff", &f) && f == 0xffffff, + "Float '0xffffff'"); + testOk(epicsScanDouble("0xffffffff", &d) && d == 0xffffffff, + "Double '0xffffffff'"); + + testOk(epicsScanLong("-0x7fffffff", &l, 0) && l == -0x7fffffff, + "Long '-0x7fffffff'"); + testOk(epicsScanULong("-0x7fffffff", &u, 0) && u == -0x7fffffff, + "ULong '-0x7fffffff'"); + testOk(epicsScanLLong("-0x7fffffffffffffff", &ll, 0) + && ll == -0x7fffffffffffffffLL, "LLong '-0x7fffffffffffffff'"); + testOk(epicsScanULLong("-0x7fffffffffffffff", &ull, 0) && + ull == -0x7fffffffffffffffULL, "ULLong '-0x7fffffffffffffff'"); + testOk(epicsScanFloat("-0xffffff", &f) && f == -0xffffff, + "Float '-0xffffff'"); + testOk(epicsScanDouble("-0x7fffffff", &d) && d == -0x7fffffff, + "Double '-0x7fffffff'"); + + testOk(!epicsParseInt8("0x7f", &i8, 0, NULL) && i8 == 0x7f, + "Int8 '0x7f'"); + testOk(!epicsParseInt8("-0x80", &i8, 0, NULL) && ((i8 + 0x80) & 0xff) == 0, + "Int8 '-0x80'"); + testOk(!epicsParseUInt8("0xff", &u8, 0, NULL) && u8 == 0xff, + "UInt8 '0xff'"); + testOk(!epicsParseUInt8("-1", &u8, 0, NULL) && u8 == 0xff, + "UInt8 '-1'"); + testOk(epicsParseInt8("0x80", &i8, 0, NULL) == S_stdlib_overflow, + "Int8 '0x80' => overflow"); + testOk(epicsParseInt8("-0x81", &i8, 0, NULL) == S_stdlib_overflow, + "Int8 '-0x81' => overflow"); + testOk(epicsParseUInt8("0x100", &u8, 0, NULL) == S_stdlib_overflow, + "UInt8 '0x100' => overflow"); + testOk(epicsParseUInt8("-0x100", &u8, 0, NULL) == S_stdlib_overflow, + "UInt8 '-0x100' => overflow"); + + testOk(!epicsParseInt16("0x7fff", &i16, 0, NULL) && i16 == 0x7fff, + "Int16 '0x7fff'"); + testOk(!epicsParseInt16("-0x8000", &i16, 0, NULL) && i16 == -0x8000, + "Int16 '-0x8000'"); + testOk(!epicsParseUInt16("0xffff", &u16, 0, NULL) && u16 == 0xffff, + "UInt16 '0xffff'"); + testOk(!epicsParseUInt16("-1", &u16, 0, NULL) && u16 == 0xffff, + "UInt16 '-1'"); + testOk(epicsParseInt16("0x8000", &i16, 0, NULL) == S_stdlib_overflow, + "Int16 '0x8000' => overflow"); + testOk(epicsParseInt16("-0x8001", &i16, 0, NULL) == S_stdlib_overflow, + "Int16 '-0x8001' => overflow"); + testOk(epicsParseUInt16("0x10000", &u16, 0, NULL) == S_stdlib_overflow, + "UInt16 '0x10000' => overflow"); + testOk(epicsParseUInt16("-0x10000", &u16, 0, NULL) == S_stdlib_overflow, + "UInt16 '-0x10000' => overflow"); + + testOk(!epicsParseInt32("0x7fffffff", &i32, 0, NULL) && i32 == 0x7fffffff, + "Int32 '0x7fffffff'"); + testOk(!epicsParseInt32("-0x80000000", &i32, 0, NULL) && i32 == -0x80000000L, + "Int32 '-0x80000000'"); + testOk(!epicsParseUInt32("0xffffffff", &u32, 0, NULL) && u32 == 0xffffffff, + "UInt32 '0xffffffff'"); + testOk(!epicsParseUInt32("-1", &u32, 0, NULL) && u32 == 0xffffffffU, + "UInt32 '-1'"); + testOk(epicsParseInt32("0x80000000", &i32, 0, NULL) == S_stdlib_overflow, + "Int32 '0x80000000' => overflow"); + testOk(epicsParseInt32("-0x80000001", &i32, 0, NULL) == S_stdlib_overflow, + "Int32 '-0x80000001' => overflow"); + testOk(epicsParseUInt32("0x100000000", &u32, 0, NULL) == S_stdlib_overflow, + "UInt32 '0x100000000' => overflow"); + testOk(epicsParseUInt32("-0x100000000", &u32, 0, NULL) == S_stdlib_overflow, + "UInt32 '-0x100000000' => overflow"); + + testOk(!epicsParseInt64("0x7fffffffffffffff", &i64, 0, NULL) && + i64 == 0x7fffffffffffffffLL, "Int64 '0x7fffffffffffffff'"); + testOk(!epicsParseInt64("-0x8000000000000000", &i64, 0, NULL) && + i64 == -0x8000000000000000LL, "Int64 '-0x8000000000000000'"); + testOk(!epicsParseUInt64("0xffffffffffffffff", &u64, 0, NULL) && + u64 == 0xffffffffffffffffULL, "UInt64 '0xffffffffffffffff'"); + testOk(!epicsParseUInt64("-1", &u64, 0, NULL) && u64 == 0xffffffffffffffffULL, + "UInt64 '-1'"); + testOk(epicsParseInt64("0x8000000000000000", &i64, 0, NULL) == S_stdlib_overflow, + "Int64 '0x8000000000000000' => overflow"); + testOk(epicsParseInt64("-0x8000000000000001", &i64, 0, NULL) == S_stdlib_overflow, + "Int64 '-0x8000000000000001' => overflow"); + testOk(epicsParseUInt64("0x10000000000000000", &u64, 0, NULL) == S_stdlib_overflow, + "UInt64 '0x10000000000000000' => overflow"); + testOk(epicsParseUInt64("-0x10000000000000000", &u64, 0, NULL) == S_stdlib_overflow, + "UInt64 '-0x10000000000000000' => overflow"); + + testOk(epicsScanFloat(".1", &f) && fabs(f - 0.1) < 1e-7, + "Float '.1'"); + testOk(epicsScanDouble(".1", &d) && fabs(d - 0.1) < 1e-15, + "Double '.1'"); + + testOk(epicsScanFloat("0.1", &f) && fabs(f - 0.1) < 1e-7, + "Float '0.1'"); + testOk(epicsScanDouble("0.1", &d) && fabs(d - 0.1) < 1e-15, + "Double '0.1'"); + + testOk(epicsScanFloat("1e-1", &f) && fabs(f - 1e-1) < 1e-7, + "Float '1e-1'"); + testOk(epicsScanDouble("1e-1", &d) && fabs(d - 1e-1) < 1e-15, + "Double '1e-1'"); + + testOk(epicsScanFloat("-.1", &f) && fabs(f + 0.1) < 1e-7, + "Float '-.1'"); + testOk(epicsScanDouble("-.1", &d) && fabs(d + 0.1) < 1e-15, + "Double '-.1'"); + + testOk(epicsScanFloat("-0.1", &f) && fabs(f + 0.1) < 1e-7, + "Float '-0.1'"); + testOk(epicsScanDouble("-0.1", &d) && fabs(d + 0.1) < 1e-15, + "Double '-0.1'"); + + testOk(epicsScanFloat("-1e-1", &f) && fabs(f + 1e-1) < 1e-7, + "Float '-1e-1'"); + testOk(epicsScanDouble("-1e-1", &d) && fabs(d + 1e-1) < 1e-15, + "Double '-1e-1'"); + + testOk(epicsScanFloat("1e-30", &f) && fabs(f - 1e-30) < 1e-36, + "Float '1e-30'"); + testOk(epicsScanDouble("1e-300", &d) && fabs(d - 1e-300) < 1e-306, + "Double '1e-300'"); + + testOk(epicsParseFloat("1e-40", &f, NULL) == S_stdlib_underflow, + "Float '1e-40' => underflow"); + testOk(epicsParseDouble("1e-330", &d, NULL) == S_stdlib_underflow, + "Double '1e-330' => underflow"); + + testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24, + "Float '1e30'"); + testOk(epicsScanDouble("1e300", &d) && fabs(d - 1e300) < 1e285, + "Double '1e300'"); + + testOk(epicsParseFloat("1e40", &f, NULL) == S_stdlib_overflow, + "Float '1e40' => overflow"); + testOk(epicsParseDouble("1e310", &d, NULL) == S_stdlib_overflow, + "Double '1e330' => overflow"); + + testOk(epicsScanLong("2147483647", &l, 0) && l == 2147483647, + "Long '2147483647'"); + testOk(epicsScanLong("-2147483647", &l, 0) && l == -2147483647, + "Long '-2147483647'"); + testOk(epicsScanULong("4294967295", &u, 0) && u == 4294967295u, + "ULong '4294967295'"); + testOk(epicsScanFloat("16777214", &f) && f == 16777214.0, + "Float '16777214'"); + testOk(epicsScanFloat("16777215", &f) && f == 16777215.0, + "Float '16777215'"); + testOk(epicsScanFloat("-16777215", &f) && f == -16777215.0, + "Float '-16777215'"); + testOk(epicsScanFloat("-16777216", &f) && f == -16777216.0, + "Float '-16777216'"); + testOk(epicsScanDouble("4294967294", &d) && d == 4294967294.0, + "Double '4294967294'"); + testOk(epicsScanDouble("4294967295", &d) && d == 4294967295.0, + "Double '4294967295'"); + testOk(epicsScanDouble("-4294967295", &d) && d == -4294967295.0, + "Double '-4294967295'"); + testOk(epicsScanDouble("-4294967296", &d) && d == -4294967296.0, + "Double '-4294967296'"); + + testOk(epicsScanFloat("NAN", &f) && isnan(f), "Float 'NAN'"); + testOk(epicsScanDouble("NAN", &d) && isnan(d), "Double 'NAN'"); + testOk(epicsScanFloat("Nan", &f) && isnan(f), "Float 'Nan'"); + testOk(epicsScanDouble("Nan", &d) && isnan(d), "Double 'Nan'"); +#ifdef __rtems__ + testTodoBegin("RTEMS (newlib) parser doesn't recognise 'nan()'"); +#endif + testOk(epicsScanFloat("nan()", &f) && isnan(f), "Float 'nan()'"); + testOk(epicsScanDouble("nan()", &d) && isnan(d), "Double 'nan()'"); +#ifdef __rtems__ + testTodoEnd(); +#endif + + testOk(epicsScanFloat("INF", &f) && f == epicsINF, + "Float 'INF'"); + testOk(epicsScanDouble("INF", &d) && d == epicsINF, + "Double 'INF'"); + testOk(epicsScanFloat("Infinity", &f) && f == epicsINF, + "Float 'Infinity'"); + testOk(epicsScanDouble("Infinity", &d) && d == epicsINF, + "Double 'Infinity'"); + + testOk(epicsScanFloat("+INF", &f) && f == epicsINF, + "Float '+INF'"); + testOk(epicsScanDouble("+INF", &d) && d == epicsINF, + "Double '+INF'"); + testOk(epicsScanFloat("+Infinity", &f) && f == epicsINF, + "Float '+Infinity'"); + testOk(epicsScanDouble("+Infinity", &d) && d == epicsINF, + "Double '+Infinity'"); + + testOk(epicsScanFloat("-INF", &f) && f == -epicsINF, + "Float '-INF'"); + testOk(epicsScanDouble("-INF", &d) && d == -epicsINF, + "Double '-INF'"); + testOk(epicsScanFloat("-Infinity", &f) && f == -epicsINF, + "Float '-Infinity'"); + testOk(epicsScanDouble("-Infinity", &d) && d == -epicsINF, + "Double '-Infinity'"); + +#ifdef epicsStrtod +#define CHECK_STRTOD epicsStrtod != strtod + if (epicsStrtod == strtod) + testDiag("This target defines epicsStrtod as strtod"); + else + testDiag("This target defines its own epicsStrtod macro"); +#else +#define CHECK_STRTOD 1 + testDiag("This target compiles epicsStrtod()"); +#endif + + if (CHECK_STRTOD) { + int pass = 0, fail = 0; + +#define CHECK(ok, msg) if (ok) pass++; else fail++, testDiag(msg) + + testDiag("Checking the native strtod(), only failures shown:"); + + CHECK(parseStrtod("", &d, NULL) == S_stdlib_noConversion, + " not ok - strtod('') => noConversion"); + CHECK(parseStrtod("\t \n", &d, NULL) == S_stdlib_noConversion, + " not ok - strtod('\\t 1\\n') => noConversion"); + CHECK(parseStrtod("!", &d, NULL) == S_stdlib_noConversion, + " not ok - strtod('!') => noConversion"); + CHECK(scanStrtod("0", &d) && d == 0, + " not ok - strtod('0')"); + CHECK(scanStrtod("\t 1\n", &d) && d == 1, + " not ok - strtod('\\t 1\\n')"); + CHECK(parseStrtod("2!", &d, NULL) == S_stdlib_extraneous, + " not ok - strtod('2!') => extraneous"); + CHECK(parseStrtod("3 !", &d, NULL) == S_stdlib_extraneous, + " not ok - strtod('3 !') => extraneous"); + CHECK(!parseStrtod("2!", &d, &endp) && *endp == '!', + " not ok - strtod('2!') => units='!'"); + CHECK(!parseStrtod("3 \n\t!", &d, &endp) && *endp == '!', + " not ok - strtod('3 \\n\\t!') => units='!'"); + CHECK(scanStrtod("0x0", &d) && d == 0, + " not ok - strtod('0x0')"); + CHECK(scanStrtod("0x1", &d) && d == 1, + " not ok - strtod('0x1')"); + CHECK(scanStrtod("+0x1", &d) && d == 1, + " not ok - strtod('+0x1')"); + CHECK(scanStrtod("-0x1", &d) && d == -1, + " not ok - strtod('-0x1')"); + CHECK(scanStrtod("0xf", &d) && d == 15, + " not ok - strtod('0xf')"); + CHECK(scanStrtod("0XF", &d) && d == 15, + " not ok - strtod('0XF')"); + CHECK(scanStrtod("0xffffffff", &d) && d == 0xffffffff, + " not ok - strtod('0xffffffff')"); + CHECK(scanStrtod("-0x7fffffff", &d) && d == -0x7fffffffl, + " not ok - strtod('-0x7fffffff')"); + CHECK(scanStrtod(".1", &d) && fabs(d - 0.1) < 1e-15, + " not ok - strtod('.1')"); + CHECK(scanStrtod("0.1", &d) && fabs(d - 0.1) < 1e-15, + " not ok - strtod('0.1')"); + CHECK(scanStrtod("1e-1", &d) && fabs(d - 1e-1) < 1e-15, + " not ok - strtod('1e-1')"); + CHECK(scanStrtod("-.1", &d) && fabs(d + 0.1) < 1e-15, + " not ok - strtod('-.1')"); + CHECK(scanStrtod("-0.1", &d) && fabs(d + 0.1) < 1e-15, + " not ok - strtod('-0.1')"); + CHECK(scanStrtod("-1e-1", &d) && fabs(d + 1e-1) < 1e-15, + " not ok - strtod('-1e-1')"); + CHECK(scanStrtod("1e-300", &d) && fabs(d - 1e-300) < 1e-306, + " not ok - strtod('1e-300')"); + CHECK(parseStrtod("1e-400", &d, NULL) == S_stdlib_underflow, + " not ok - strtod('1e-400') => underflow"); + CHECK(scanStrtod("4294967294", &d) && d == 4294967294.0, + " not ok - strtod('4294967294')"); + CHECK(scanStrtod("4294967295", &d) && d == 4294967295.0, + " not ok - strtod('4294967295')"); + CHECK(scanStrtod("-4294967295", &d) && d == -4294967295.0, + " not ok - strtod('-4294967295')"); + CHECK(scanStrtod("-4294967296", &d) && d == -4294967296.0, + " not ok - strtod('-4294967296')"); + CHECK(scanStrtod("NAN", &d) && isnan(d), + " not ok - strtod('NAN')"); + CHECK(scanStrtod("Nan", &d) && isnan(d), + " not ok - strtod('Nan')"); + CHECK(scanStrtod("nan()", &d) && isnan(d), + " not ok - strtod('nan()')"); + CHECK(scanStrtod("INF", &d) && d == epicsINF, + " not ok - strtod('INF')"); + CHECK(scanStrtod("Infinity", &d) && d == epicsINF, + " not ok - strtod('Infinity')"); + CHECK(scanStrtod("+INF", &d) && d == epicsINF, + " not ok - strtod('+INF')"); + CHECK(scanStrtod("+Infinity", &d) && d == epicsINF, + " not ok - strtod('+Infinity')"); + CHECK(scanStrtod("-INF", &d) && d == -epicsINF, + " not ok - strtod('-INF')"); + CHECK(scanStrtod("-Infinity", &d) && d == -epicsINF, + " not ok - strtod('-Infinity')"); + + if (fail) + testDiag("The native strtod() passed %d and failed %d tests", + pass, fail); + else { + testDiag("The native strtod() passed all %d tests", pass); +#ifndef epicsStrtod + testDiag("The compiled epicsStrtod() should not be needed"); +#endif + } + } + + return testDone(); +} diff --git a/modules/libcom/test/epicsStringTest.c b/modules/libcom/test/epicsStringTest.c new file mode 100644 index 000000000..3e4aed7e7 --- /dev/null +++ b/modules/libcom/test/epicsStringTest.c @@ -0,0 +1,327 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Marty Kraimer + */ + +#include +#include +#include + +#include "epicsUnitTest.h" +#include "epicsString.h" +#include "testMain.h" + +static +void testChars(void) { + int i; + char input[2] = {0, 0}; + char escaped[20]; + char result[20]; + size_t s, t, needed; + + for (i = 255; i >= 0; --i) { + input[0] = i; + needed = epicsStrnEscapedFromRawSize(input, 1); + s = epicsStrnEscapedFromRaw(escaped, sizeof(escaped), input, 1); + t = epicsStrnRawFromEscaped(result, sizeof(result), escaped, s); + testOk(needed == s && t == 1 && + result[0] == input[0] && result[1] == 0, + "escaped char 0x%2.2x -> \"%s\" (%d) -> 0x%2.2x", + input[0] & 0xff, escaped, (int) needed, result[0] & 0xff); + } +} + +static +void testGlob(void) { + testOk1(epicsStrGlobMatch("xyz","xyz")); + testOk1(!epicsStrGlobMatch("xyz","xyzm")); + testOk1(!epicsStrGlobMatch("xyzm","xyz")); + testOk1(!epicsStrGlobMatch("","xyzm")); + testOk1(!epicsStrGlobMatch("xyz","")); + testOk1(epicsStrGlobMatch("","")); + + testOk1(epicsStrGlobMatch("","*")); + testOk1(!epicsStrGlobMatch("","?")); + testOk1(!epicsStrGlobMatch("","?*")); + + testOk1(epicsStrGlobMatch("hello","h*o")); + testOk1(!epicsStrGlobMatch("hello","h*x")); + testOk1(!epicsStrGlobMatch("hellx","h*o")); + + testOk1(epicsStrGlobMatch("hello","he?lo")); + testOk1(!epicsStrGlobMatch("hello","he?xo")); + testOk1(epicsStrGlobMatch("hello","he??o")); + testOk1(!epicsStrGlobMatch("helllo","he?lo")); + + testOk1(epicsStrGlobMatch("hello world","he*o w*d")); + testOk1(!epicsStrGlobMatch("hello_world","he*o w*d")); + testOk1(epicsStrGlobMatch("hello world","he**d")); + + testOk1(epicsStrGlobMatch("hello hello world","he*o w*d")); + + testOk1(!epicsStrGlobMatch("hello hello xorld","he*o w*d")); + + testOk1(epicsStrGlobMatch("hello","he*")); + testOk1(epicsStrGlobMatch("hello","*lo")); + testOk1(epicsStrGlobMatch("hello","*")); +} + +MAIN(epicsStringTest) +{ + const char * const empty = ""; + const char * const space = " "; + const char * const A = "A"; + const char * const ABCD = "ABCD"; + const char * const ABCDE = "ABCDE"; + const char * const a = "a"; + const char * const abcd = "abcd"; + const char * const abcde = "abcde"; + char result[8]; + char *s; + int status; + + testPlan(406); + + testChars(); + + testOk1(epicsStrnCaseCmp(empty, "", 0) == 0); + testOk1(epicsStrnCaseCmp(empty, "", 1) == 0); + testOk1(epicsStrnCaseCmp(space, empty, 1) > 0); + testOk1(epicsStrnCaseCmp(empty, space, 1) < 0); + testOk1(epicsStrnCaseCmp(a, A, 1) == 0); + testOk1(epicsStrnCaseCmp(a, A, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 4) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 1000) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 4) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 1000) < 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 2) == 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 4) == 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 1000) > 0); + + testOk1(epicsStrCaseCmp(empty, "") == 0); + testOk1(epicsStrCaseCmp(a, A) == 0); + testOk1(epicsStrCaseCmp(abcd, ABCD) == 0); + testOk1(epicsStrCaseCmp(abcd, ABCDE) < 0); + testOk1(epicsStrCaseCmp(abcde, ABCD) > 0); + testOk1(epicsStrCaseCmp(abcde, "ABCDF") < 0); + + s = epicsStrDup(abcd); + testOk(strcmp(s, abcd) == 0 && s != abcd, "epicsStrDup"); + free(s); + + testOk1(epicsStrHash(abcd, 0) != epicsStrHash("bacd", 0)); + testOk1(epicsStrHash(abcd, 0) == epicsMemHash(abcde, 4, 0)); + testOk1(epicsStrHash(abcd, 0) != epicsMemHash("abcd\0", 5, 0)); + + testOk1(epicsStrnLen("abcd", 5)==4); + testOk1(epicsStrnLen("abcd", 4)==4); + testOk1(epicsStrnLen("abcd", 3)==3); + testOk1(epicsStrnLen("abcd", 0)==0); + + testGlob(); + + memset(result, 'x', sizeof(result)); + status = epicsStrnEscapedFromRaw(result, 0, ABCD, 4); + testOk(status == 4, "epicsStrnEscapedFromRaw(out, 0) -> %d (exp. 4)", status); + testOk(result[0] == 'x', " No output"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 0, ABCD, 4); + testOk(status == 0, "epicsStrnRawFromEscaped(out, 0) -> %d (exp. 0)", status); + testOk(result[0] == 'x', " No output"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnEscapedFromRaw(result, 1, ABCD, 4); + testOk(status == 4, "epicsStrnEscapedFromRaw(out, 1) -> %d (exp. 4)", status); + testOk(result[0] == 0, " 0-terminated"); + testOk(result[1] == 'x', " No overrun"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 1, ABCD, 4); + testOk(status == 0, "epicsStrnRawFromEscaped(out, 1) -> %d (exp. 0)", status); + testOk(result[0] == 0, " 0-terminated"); + testOk(result[1] == 'x', " No overrun"); + + testDiag("Testing size = epicsStrnEscapedFromRawSize"); + + status = epicsStrnEscapedFromRawSize(ABCD, 3); + testOk(status == 3, "size(\"ABCD\", 3) -> %d (exp. 3)", status); + status = epicsStrnEscapedFromRawSize(ABCD, 4); + testOk(status == 4, "size(\"ABCD\", 4) -> %d (exp. 4)", status); + status = epicsStrnEscapedFromRawSize(ABCD, 5); + testOk(status == 8, "size(\"ABCD\", 5) -> %d (exp. 8)", status); + + testDiag("Testing esc = epicsStrnEscapedFromRaw(out, 4, ...)"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnEscapedFromRaw(result, 4, ABCD, 3); + testOk(status == 3, "esc(\"ABCD\", 3) -> %d (exp. 3)", status); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No overrun"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnEscapedFromRaw(result, 4, ABCD, 4); + testOk(status == 4, "esc(\"ABCD\", 4) -> %d (exp. 4)", status); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No overrun"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnEscapedFromRaw(result, 4, ABCD, 5); + testOk(status == 8, "esc(\"ABCD\", 5) -> %d (exp. 8)", status); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No overrun"); + + testDiag("Testing raw = epicsStrnRawFromEscaped(out, 4, ...)"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCD, 0); + testOk(status == 0, "raw(\"ABCD\", 0) -> %d (exp. 0)", status); + testOk(result[0] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCD, 1); + testOk(status == 1, "raw(\"ABCD\", 1) -> %d (exp. 1)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[1] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCD, 2); + testOk(status == 2, "raw(\"ABCD\", 2) -> %d (exp. 2)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); + testOk(result[2] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCD, 3); + testOk(status == 3, "raw(\"ABCD\", 3) -> %d (exp. 3)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); + testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No write outside buffer"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCD, 4); + testOk(status == 3, "raw(\"ABCD\", 4) -> %d (exp. 3)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); + testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No write outside buffer"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, ABCDE, 5); + testOk(status == 3, "raw(\"ABCDE\", 5) -> %d (exp. 3)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[1] == 'B', " Char '%c' (exp. 'B')", result[1]); + testOk(result[2] == 'C', " Char '%c' (exp. 'C')", result[2]); + testOk(result[3] == 0, " 0-terminated"); + testOk(result[4] == 'x', " No write outside buffer"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "A", 2); + testOk(status == 1, "raw(\"A\", 2) -> %d (exp. 1)", status); + testOk(result[0] == 'A', " Char '%c' (exp. 'A')", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\123", 1); + testOk(status == 0, "raw(\"\\123\", 1) -> %d (exp. 0)", status); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\123", 2); + testOk(status == 1, "raw(\"\\123\", 2) -> %d (exp. 1)", status); + testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\123", 3); + testOk(status == 1, "raw(\"\\123\", 3) -> %d (exp. 1)", status); + testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\123", 4); + testOk(status == 1, "raw(\"\\123\", 4) -> %d (exp. 1)", status); + testOk(result[0] == 0123, " Octal escape (got \\%03o)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\812", 2); + testOk(status == 1, "raw(\"\\812\", 2) -> %d (exp. 1)", status); + testOk(result[0] == '8', " Escaped '%c')", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\182", 3); + testOk(status == 2, "raw(\"\\182\", 3) -> %d (exp. 2)", status); + testOk(result[0] == 1, " Octal escape (got \\%03o)", result[0]); + testOk(result[1] == '8', " Terminated with '%c'", result[1]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\128", 4); + testOk(status == 2, "raw(\"\\128\", 4) -> %d (exp. 2)", status); + testOk(result[0] == 012, " Octal escape (got \\%03o)", result[0]); + testOk(result[1] == '8', " Terminator char got '%c'", result[1]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x12", 1); + testOk(status == 0, "raw(\"\\x12\", 1) -> %d (exp. 0)", status); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x12", 2); + testOk(status == 0, "raw(\"\\x12\", 2) -> %d (exp. 0)", status); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x12", 3); + testOk(status == 1, "raw(\"\\x12\", 3) -> %d (exp. 1)", status); + testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x12", 4); + testOk(status == 1, "raw(\"\\x12\", 4) -> %d (exp. 1)", status); + testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\xaF", 4); + testOk(status == 1, "raw(\"\\xaF\", 4) -> %d (exp. 1)", status); + testOk(result[0] == '\xaF'," Hex escape (got \\x%x)", result[0] & 0xFF); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x012", 5); + testOk(status == 1, "raw(\"\\x012\", 5) -> %d (exp. 1)", status); + testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x0012", 6); + testOk(status == 1, "raw(\"\\x0012\", 6) -> %d (exp. 1)", status); + testOk(result[0] == 0x12," Hex escape (got \\x%x)", result[0]); + testOk(result[status] == 0, " 0-terminated"); + + memset(result, 'x', sizeof(result)); + status = epicsStrnRawFromEscaped(result, 4, "\\x1g", 4); + testOk(status == 2, "raw(\"\\x1g\", 4) -> %d (exp. 2)", status); + testOk(result[0] == 1, " Hex escape (got \\x%x)", result[0]); + testOk(result[1] == 'g', " Terminator char got '%c'", result[1]); + testOk(result[status] == 0, " 0-terminated"); + + return testDone(); +} diff --git a/modules/libcom/test/epicsThreadHooksTest.c b/modules/libcom/test/epicsThreadHooksTest.c new file mode 100644 index 000000000..b6ba833e0 --- /dev/null +++ b/modules/libcom/test/epicsThreadHooksTest.c @@ -0,0 +1,176 @@ +/*************************************************************************\ +* Copyright (c) 2012 ITER Organization +* Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsThreadHooksTest.c */ + +#include + +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define MAX_THREADS 16 +#define TEST_THREADS 8 +#define NUM_HOOKS 4 + +static int order[MAX_THREADS][NUM_HOOKS]; +static int cnt[MAX_THREADS]; +static int mine[MAX_THREADS]; +static int mapped[MAX_THREADS]; +static epicsThreadId tid[MAX_THREADS]; + +static epicsMutexId tidLock; +static epicsEventId shutdown[TEST_THREADS]; + +static int newThreadIndex(epicsThreadId id) +{ + int i = 0; + + if (epicsMutexLock(tidLock)) + testAbort("newThreadIndex: Locking problem"); + + while (i < MAX_THREADS && tid[i] != 0) + i++; + if (i < MAX_THREADS) + tid[i] = id; + else + testAbort("newThreadIndex: Too many threads!"); + + epicsMutexUnlock(tidLock); + return i; +} + +static int findThreadIndex(epicsThreadId id) +{ + int i = 0; + + while (i < MAX_THREADS && tid[i] != id) + i++; + return i; +} + +static void atExitHook1 (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + + if (no < MAX_THREADS) + order[no][3] = cnt[no]++; +} + +static void atExitHook2 (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + + if (no < MAX_THREADS) + order[no][2] = cnt[no]++; +} + +static void startHook1 (epicsThreadId id) +{ + int no = newThreadIndex(id); + + if (no < MAX_THREADS) + order[no][0] = cnt[no]++; + epicsAtThreadExit(atExitHook1, NULL); +} + +static void startHook2 (epicsThreadId id) +{ + int no = findThreadIndex(id); + + if (no < MAX_THREADS) + order[no][1] = cnt[no]++; + epicsAtThreadExit(atExitHook2, NULL); +} + +static void my_thread (void *arg) +{ + int no = findThreadIndex(epicsThreadGetIdSelf()); + + if (no < MAX_THREADS) + mine[no] = 1; + epicsEventMustWait((epicsEventId) arg); +} + +static void mapper (epicsThreadId id) +{ + int no = findThreadIndex(id); + + if (no < MAX_THREADS) + mapped[no]++; +} + +MAIN(epicsThreadHooksTest) +{ + int i; + int ok; + + testPlan(TEST_THREADS + 1); + + tidLock = epicsMutexMustCreate(); + + if (epicsThreadHookAdd(startHook1)) + testAbort("startHook1 registration failed"); + if (epicsThreadHookAdd(startHook2)) + testAbort("startHook2 registration failed"); + + for (i = 0; i < TEST_THREADS; i++) { + char name[10]; + + shutdown[i] = epicsEventCreate(epicsEventEmpty); + sprintf(name, "t%d", (int) i); + epicsThreadCreate(name, epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + my_thread, shutdown[i]); + } + epicsThreadSleep(1.0); + + epicsThreadMap(mapper); + + ok = 1; + for (i = 0; i < MAX_THREADS; i++) { + if (!mine[i]) continue; + + if (mapped[i] != 1) { + ok = 0; + testDiag("mapped[%d] = %d", i, mapped[i]); + } + } + testOk(ok, "All my tasks covered once by epicsThreadMap"); + + for (i = 0; i < TEST_THREADS; i++) { + epicsEventSignal(shutdown[i]); + } + + epicsThreadSleep(1.0); + + for (i = 0; i < MAX_THREADS; i++) { + int j; + + if (!mine[i]) continue; + + ok = 1; + for (j = 0; j < NUM_HOOKS; j++) { + if (order[i][j] != j) { + ok = 0; + testDiag("order[%d][%d] = %d", i, j, order[i][j]); + } + } + testOk(ok, "All hooks for task %d called in correct order", i); + } + + epicsThreadHookDelete(startHook1); + epicsThreadHookDelete(startHook2); + + return testDone(); +} diff --git a/modules/libcom/test/epicsThreadOnceTest.c b/modules/libcom/test/epicsThreadOnceTest.c new file mode 100644 index 000000000..2ecdf3a68 --- /dev/null +++ b/modules/libcom/test/epicsThreadOnceTest.c @@ -0,0 +1,116 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "errlog.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define NUM_ONCE_THREADS 8 + +epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; +epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT; +epicsMutexId lock; +epicsEventId go; +epicsEventId done; + +int runCount = 0; +int initCount = 0; +char initBy[20]; +int doneCount = 0; + +void onceInit(void *ctx) +{ + initCount++; + strcpy(initBy, epicsThreadGetNameSelf()); +} + +void onceThread(void *ctx) +{ + epicsMutexMustLock(lock); + runCount++; + epicsMutexUnlock(lock); + + epicsEventMustWait(go); + epicsEventSignal(go); + + epicsThreadOnce(&onceFlag, onceInit, ctx); + testOk(initCount == 1, "%s: initCount = %d", + epicsThreadGetNameSelf(), initCount); + + epicsMutexMustLock(lock); + doneCount++; + if (doneCount == runCount) + epicsEventSignal(done); + epicsMutexUnlock(lock); +} + + +void recurseInit(void); +void onceRecurse(void *ctx) +{ + recurseInit(); +} + +void recurseInit(void) +{ + epicsThreadOnce(&twiceFlag, onceRecurse, 0); +} + +void recurseThread(void *ctx) +{ + recurseInit(); + testFail("Recursive epicsThreadOnce() not detected"); +} + + +MAIN(epicsThreadOnceTest) +{ + int i; + epicsThreadId tid; + + testPlan(3 + NUM_ONCE_THREADS); + + go = epicsEventMustCreate(epicsEventEmpty); + done = epicsEventMustCreate(epicsEventEmpty); + lock = epicsMutexMustCreate(); + + for (i = 0; i < NUM_ONCE_THREADS; i++) { + char name[20]; + + sprintf(name, "once-%d", i); + epicsThreadCreate(name, epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + onceThread, 0); + } + epicsThreadSleep(0.1); + + testOk(runCount == NUM_ONCE_THREADS, "runCount = %d", runCount); + epicsEventSignal(go); /* Use epicsEventBroadcast(go) when available */ + epicsEventMustWait(done); + + testOk(doneCount == NUM_ONCE_THREADS, "doneCount = %d", doneCount); + testDiag("init was run by %s", initBy); + + eltc(0); + tid = epicsThreadCreate("recurse", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + recurseThread, 0); + do { + epicsThreadSleep(0.1); + } while (!epicsThreadIsSuspended(tid)); + testPass("Recursive epicsThreadOnce() detected"); + + eltc(1); + return testDone(); +} diff --git a/modules/libcom/test/epicsThreadPerform.cpp b/modules/libcom/test/epicsThreadPerform.cpp new file mode 100644 index 000000000..c8fd34515 --- /dev/null +++ b/modules/libcom/test/epicsThreadPerform.cpp @@ -0,0 +1,227 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsThreadPerform.cpp */ + +/* sleep accuracy and sleep quantum tests by Jeff Hill */ +/* epicsThreadGetIdSelf performance by Jeff Hill */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "errlog.h" +#include "testMain.h" + +static void testPriority(const char *who) +{ + epicsThreadId id; + unsigned int oldPriority,newPriority; + + id = epicsThreadGetIdSelf(); + oldPriority = epicsThreadGetPriority(id); + epicsThreadSetPriority(id,epicsThreadPriorityMax); + newPriority = epicsThreadGetPriority(id); + epicsThreadSetPriority(id,oldPriority); + printf("testPriority %s\n id %p old %u new %u\n", + who,id,oldPriority,newPriority); +} + +extern "C" void testPriorityThread(void *arg) +{ + testPriority("thread"); +} + +static void epicsThreadPriorityTest() +{ + testPriority("main error expected from epicsThreadSetPriority"); + epicsThreadCreate("testPriorityThread",epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium),testPriorityThread,0); + epicsThreadSleep(0.5); +} + + +static double threadSleepMeasureDelayError ( const double & delay ) +{ + epicsTime beg = epicsTime::getCurrent(); + epicsThreadSleep ( delay ); + epicsTime end = epicsTime::getCurrent(); + double meas = end - beg; + double error = fabs ( delay - meas ); + return error; +} + +static double measureSleepQuantum ( + const unsigned iterations, const double testInterval ) +{ + double errorSum = 0.0; + printf ( "Estimating sleep quantum" ); + fflush ( stdout ); + for ( unsigned i = 0u; i < iterations; i++ ) { + // try to guarantee a uniform probability density function + // by intentionally burning some CPU until we are less + // likely to be aligned with the schedualing clock + double interval = rand (); + interval /= RAND_MAX; + interval *= testInterval; + epicsTime start = epicsTime::getCurrent (); + epicsTime current = start; + while ( current - start < interval ) { + current = epicsTime::getCurrent (); + } + errorSum += threadSleepMeasureDelayError ( testInterval ); + if ( i % ( iterations / 10 ) == 0 ) { + printf ( "." ); + fflush ( stdout ); + } + } + printf ( "done\n" ); + + // with a uniform probability density function the + // sleep delay error mean is one half of the quantum + double quantumEstimate = 2 * errorSum / iterations; + return quantumEstimate; +} + +static void threadSleepQuantumTest () +{ + const double quantum = epicsThreadSleepQuantum (); + + double quantumEstimate = measureSleepQuantum ( 10, 10 * quantum ); + quantumEstimate = measureSleepQuantum ( 100, 2 * quantumEstimate ); + + double quantumError = fabs ( quantumEstimate - quantum ) / quantum; + const char * pTol = 0; + if ( quantumError > 0.1 ) { + pTol = "10%"; + } + else if ( quantumError > 0.01 ) { + pTol = "1%"; + } + else if ( quantumError > 0.001 ) { + pTol = "0.1%"; + } + if ( pTol ) { + printf ( + "The epicsThreadSleepQuantum() call returns %f sec.\n", + quantum ); + printf ( + "This doesnt match the quantum estimate " + "of %f sec within %s.\n", + quantumEstimate, pTol ); + } +} + + +static void threadSleepTest () +{ + static const int iterations = 20; + const double quantum = epicsThreadSleepQuantum (); + double errorSum = threadSleepMeasureDelayError ( 0.0 ); + for ( int i = 0u; i < iterations; i++ ) { + double delay = ldexp ( 1.0 , -i ); + double error = + threadSleepMeasureDelayError ( delay ); + errorSum += error; + if ( error > quantum + quantum / 8.0 ) { + printf ( "epicsThreadSleep ( %10f ) delay err %10f sec\n", + delay, error ); + } + } + double averageError = errorSum / ( iterations + 1 ); + if ( averageError > quantum ) { + printf ( "Average sleep delay error was %f sec\n", averageError ); + } +} + +static void epicsThreadGetIdSelfPerfTest () +{ + static const unsigned N = 10000; + static const double microSecPerSec = 1e6; + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0u; i < N; i++ ) { + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + }; + epicsTime end = epicsTime::getCurrent (); + printf ( "It takes %f micro sec to call epicsThreadGetIdSelf ()\n", + microSecPerSec * ( end - begin ) / (10 * N) ); +} + + +static epicsThreadPrivate < bool > priv; + +static inline void callItTenTimes () +{ + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); + (void) priv.get (); +} + +static inline void callItTenTimesSquared () +{ + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); +} + +static void timeEpicsThreadPrivateGet () +{ + priv.set ( 0 ); + + epicsTime begin = epicsTime::getCurrent (); + static const unsigned N = 1000u; + for ( unsigned i = 0u; i < N; i++ ) { + callItTenTimesSquared (); + } + double delay = epicsTime::getCurrent() - begin; + delay /= N * 100u; // convert to sec per call + delay *= 1e6; // convert to micro sec + printf("epicsThreadPrivateGet() takes %f microseconds\n", delay); +} + + +MAIN(epicsThreadPerform) +{ + epicsThreadPriorityTest (); + threadSleepQuantumTest (); + threadSleepTest (); + epicsThreadGetIdSelfPerfTest (); + timeEpicsThreadPrivateGet (); + return 0; +} diff --git a/modules/libcom/test/epicsThreadPoolTest.c b/modules/libcom/test/epicsThreadPoolTest.c new file mode 100644 index 000000000..b949ec9cd --- /dev/null +++ b/modules/libcom/test/epicsThreadPoolTest.c @@ -0,0 +1,447 @@ +/*************************************************************************\ +* Copyright (c) 2014 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "epicsThreadPool.h" + +/* included to allow tests to peek */ +#include "../../src/pool/poolPriv.h" + +#include "testMain.h" +#include "epicsUnitTest.h" + +#include "cantProceed.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsThread.h" + +/* Do nothing */ +static void nullop(void) +{ + epicsThreadPool *pool; + testDiag("nullop()"); + { + epicsThreadPoolConfig conf; + epicsThreadPoolConfigDefaults(&conf); + testOk1(conf.maxThreads>0); + + testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); + if(!pool) + return; + } + + epicsThreadPoolDestroy(pool); +} + +/* Just create and destroy worker threads */ +static void oneop(void) +{ + epicsThreadPool *pool; + testDiag("oneop()"); + { + epicsThreadPoolConfig conf; + epicsThreadPoolConfigDefaults(&conf); + conf.initialThreads=2; + testOk1(conf.maxThreads>0); + + testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); + if(!pool) + return; + } + + epicsThreadPoolDestroy(pool); +} + +/* Test that Bursts of jobs will create enough threads to + * run all in parallel + */ +typedef struct { + epicsMutexId guard; + unsigned int count; + epicsEventId allrunning; + epicsEventId done; + epicsJob **job; +} countPriv; + +static void countjob(void *param, epicsJobMode mode) +{ + countPriv *cnt=param; + testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + return; + + epicsMutexMustLock(cnt->guard); + testDiag("Job %lu", (unsigned long)cnt->count); + cnt->count--; + if(cnt->count==0) { + testDiag("All jobs running"); + epicsEventSignal(cnt->allrunning); + } + epicsMutexUnlock(cnt->guard); + + epicsEventMustWait(cnt->done); + epicsEventSignal(cnt->done); /* pass along to next thread */ +} + +/* Starts "mcnt" jobs in a pool with initial and max + * thread counts "icnt" and "mcnt". + * The test ensures that all jobs run in parallel. + * "cork" checks the function of pausing the run queue + * with epicsThreadPoolQueueRun + */ +static void postjobs(size_t icnt, size_t mcnt, int cork) +{ + size_t i; + epicsThreadPool *pool; + countPriv *priv=callocMustSucceed(1, sizeof(*priv), "postjobs priv alloc"); + priv->guard=epicsMutexMustCreate(); + priv->done=epicsEventMustCreate(epicsEventEmpty); + priv->allrunning=epicsEventMustCreate(epicsEventEmpty); + priv->count=mcnt; + priv->job=callocMustSucceed(mcnt, sizeof(*priv->job), "postjobs job array"); + + testDiag("postjobs(%lu,%lu)", (unsigned long)icnt, (unsigned long)mcnt); + + { + epicsThreadPoolConfig conf; + epicsThreadPoolConfigDefaults(&conf); + conf.initialThreads=icnt; + conf.maxThreads=mcnt; + + testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); + if(!pool) + return; + } + + if(cork) + epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 0); + + for(i=0; ijob[i] = epicsJobCreate(pool, &countjob, priv); + testOk1(priv->job[i]!=NULL); + testOk1(epicsJobQueue(priv->job[i])==0); + } + + if(cork) { + /* no jobs should have run */ + epicsMutexMustLock(priv->guard); + testOk1(priv->count==mcnt); + epicsMutexUnlock(priv->guard); + + epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 1); + } + + testDiag("Waiting for all jobs to start"); + epicsEventMustWait(priv->allrunning); + testDiag("Stop all"); + epicsEventSignal(priv->done); + + for(i=0; ijob[i]); + } + + epicsThreadPoolDestroy(pool); + epicsMutexDestroy(priv->guard); + epicsEventDestroy(priv->allrunning); + epicsEventDestroy(priv->done); + free(priv->job); + free(priv); +} + +static unsigned int flag0 = 0; + +/* Test cancel from job (no-op) + * and destroy from job (lazy free) + */ +static void cleanupjob0(void* arg, epicsJobMode mode) +{ + epicsJob *job=arg; + testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + return; + + assert(flag0==0); + flag0=1; + + testOk1(epicsJobQueue(job)==0); + + epicsJobDestroy(job); /* delete while job is running */ +} +static void cleanupjob1(void* arg, epicsJobMode mode) +{ + epicsJob *job=arg; + testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + return; + + testOk1(epicsJobQueue(job)==0); + + testOk1(epicsJobUnqueue(job)==0); + /* delete later after job finishes, but before pool is destroyed */ +} +static void cleanupjob2(void* arg, epicsJobMode mode) +{ + epicsJob *job=arg; + testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + epicsJobDestroy(job); /* delete when threadpool is destroyed */ + else if(mode==epicsJobModeRun) + testOk1(epicsJobUnqueue(job)==S_pool_jobIdle); +} +static epicsJobFunction cleanupjobs[3] = {&cleanupjob0,&cleanupjob1,&cleanupjob2}; + +/* Tests three methods for job cleanup. + * 1. destroy which running + * 2. deferred cleanup after pool destroyed + * 3. immediate cleanup when pool destroyed + */ +static void testcleanup(void) +{ + int i=0; + epicsThreadPool *pool; + epicsJob *job[3]; + + testDiag("testcleanup()"); + + testOk1((pool=epicsThreadPoolCreate(NULL))!=NULL); + if(!pool) + return; + + /* unrolled so that valgrind can show which methods leaks */ + testOk1((job[0]=epicsJobCreate(pool, cleanupjobs[0], EPICSJOB_SELF))!=NULL); + testOk1((job[1]=epicsJobCreate(pool, cleanupjobs[1], EPICSJOB_SELF))!=NULL); + testOk1((job[2]=epicsJobCreate(pool, cleanupjobs[2], EPICSJOB_SELF))!=NULL); + for(i=0; i<3; i++) { + testOk1(epicsJobQueue(job[i])==0); + } + + epicsThreadPoolWait(pool, -1); + epicsJobDestroy(job[1]); + epicsThreadPoolDestroy(pool); +} + +/* Test re-add from inside job */ +typedef struct { + unsigned int count; + epicsEventId done; + epicsJob *job; + unsigned int inprogress; +} readdPriv; + +static void readdjob(void *arg, epicsJobMode mode) +{ + readdPriv *priv=arg; + testOk1(mode==epicsJobModeRun||mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + return; + testOk1(priv->inprogress==0); + testDiag("count==%u", priv->count); + + if(priv->count--) { + priv->inprogress=1; + epicsJobQueue(priv->job); + epicsThreadSleep(0.05); + priv->inprogress=0; + }else{ + epicsEventSignal(priv->done); + epicsJobDestroy(priv->job); + } +} + +/* Test re-queueing a job while it is running. + * Check that a single job won't run concurrently. + */ +static void testreadd(void) { + epicsThreadPoolConfig conf; + epicsThreadPool *pool; + readdPriv *priv=callocMustSucceed(1, sizeof(*priv), "testcleanup priv"); + readdPriv *priv2=callocMustSucceed(1, sizeof(*priv), "testcleanup priv"); + + testDiag("testreadd"); + + priv->done=epicsEventMustCreate(epicsEventEmpty); + priv->count=5; + priv2->done=epicsEventMustCreate(epicsEventEmpty); + priv2->count=5; + + epicsThreadPoolConfigDefaults(&conf); + conf.maxThreads = 2; + testOk1((pool=epicsThreadPoolCreate(&conf))!=NULL); + if(!pool) + return; + + testOk1((priv->job=epicsJobCreate(pool, &readdjob, priv))!=NULL); + testOk1((priv2->job=epicsJobCreate(pool, &readdjob, priv2))!=NULL); + + testOk1(epicsJobQueue(priv->job)==0); + testOk1(epicsJobQueue(priv2->job)==0); + epicsEventMustWait(priv->done); + epicsEventMustWait(priv2->done); + + testOk1(epicsThreadPoolNThreads(pool)==2); + testDiag("epicsThreadPoolNThreads = %d", epicsThreadPoolNThreads(pool)); + + epicsThreadPoolDestroy(pool); + epicsEventDestroy(priv->done); + epicsEventDestroy(priv2->done); + free(priv); + free(priv2); + +} + +static int shouldneverrun = 0; +static int numtoolate = 0; + +/* test job canceling */ +static +void neverrun(void *arg, epicsJobMode mode) +{ + epicsJob *job=arg; + testOk1(mode==epicsJobModeCleanup); + if(mode==epicsJobModeCleanup) + epicsJobDestroy(job); + else + shouldneverrun++; +} +static epicsEventId cancel[2]; +static +void toolate(void *arg, epicsJobMode mode) +{ + epicsJob *job=arg; + if(mode==epicsJobModeCleanup){ + epicsJobDestroy(job); + return; + } + testPass("Job runs"); + numtoolate++; + epicsEventSignal(cancel[0]); + epicsEventMustWait(cancel[1]); +} + +static +void testcancel(void) +{ + epicsJob *job[2]; + epicsThreadPool *pool; + testOk1((pool=epicsThreadPoolCreate(NULL))!=NULL); + if(!pool) + return; + + cancel[0]=epicsEventCreate(epicsEventEmpty); + cancel[1]=epicsEventCreate(epicsEventEmpty); + + testOk1((job[0]=epicsJobCreate(pool, &neverrun, EPICSJOB_SELF))!=NULL); + testOk1((job[1]=epicsJobCreate(pool, &toolate, EPICSJOB_SELF))!=NULL); + + /* freeze */ + epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 0); + + testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); /* not queued yet */ + + epicsJobQueue(job[0]); + testOk1(epicsJobUnqueue(job[0])==0); + testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); + + epicsThreadSleep(0.01); + epicsJobQueue(job[0]); + testOk1(epicsJobUnqueue(job[0])==0); + testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); + + epicsThreadPoolControl(pool, epicsThreadPoolQueueRun, 1); + + epicsJobQueue(job[1]); /* actually let it run this time */ + + epicsEventMustWait(cancel[0]); + testOk1(epicsJobUnqueue(job[0])==S_pool_jobIdle); + epicsEventSignal(cancel[1]); + + epicsThreadPoolDestroy(pool); + epicsEventDestroy(cancel[0]); + epicsEventDestroy(cancel[1]); + + testOk1(shouldneverrun==0); + testOk1(numtoolate==1); +} + +static +unsigned int sharedWasDeleted=0; + +static +void lastjob(void *arg, epicsJobMode mode) +{ + epicsJob *job=arg; + if(mode==epicsJobModeCleanup) { + sharedWasDeleted=1; + epicsJobDestroy(job); + } +} + +static +void testshared(void) +{ + epicsThreadPool *poolA, *poolB; + epicsThreadPoolConfig conf; + epicsJob *job; + + epicsThreadPoolConfigDefaults(&conf); + + testDiag("Check reference counting of shared pools"); + + testOk1((poolA=epicsThreadPoolGetShared(&conf))!=NULL); + + testOk1(poolA->sharedCount==1); + + testOk1((poolB=epicsThreadPoolGetShared(&conf))!=NULL); + + testOk1(poolA==poolB); + + testOk1(poolA->sharedCount==2); + + epicsThreadPoolReleaseShared(poolA); + + testOk1(poolB->sharedCount==1); + + testOk1((job=epicsJobCreate(poolB, &lastjob, EPICSJOB_SELF))!=NULL); + + epicsThreadPoolReleaseShared(poolB); + + testOk1(sharedWasDeleted==1); + + testOk1((poolA=epicsThreadPoolGetShared(&conf))!=NULL); + + testOk1(poolA->sharedCount==1); + + epicsThreadPoolReleaseShared(poolA); + +} + +MAIN(epicsThreadPoolTest) +{ + testPlan(171); + + nullop(); + oneop(); + testDiag("Queue with delayed start"); + postjobs(1,1,1); + postjobs(0,1,1); + postjobs(4,4,1); + postjobs(0,4,1); + postjobs(2,4,1); + testDiag("Queue with immediate start"); + postjobs(1,1,0); + postjobs(0,1,0); + postjobs(4,4,0); + postjobs(0,4,0); + postjobs(2,4,0); + testcleanup(); + testreadd(); + testcancel(); + testshared(); + + return testDone(); +} diff --git a/modules/libcom/test/epicsThreadPriorityTest.cpp b/modules/libcom/test/epicsThreadPriorityTest.cpp new file mode 100644 index 000000000..4f5a82d9e --- /dev/null +++ b/modules/libcom/test/epicsThreadPriorityTest.cpp @@ -0,0 +1,121 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsdThreadPriorityTest.cpp */ + +/* Author: Marty Kraimer Date: 21NOV2005 */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + epicsEventId waitForMaster; + epicsEventId waitForClient; +}info; + +extern "C" { + +static void client(void *arg) +{ + info *pinfo = (info *)arg; + epicsThreadId idSelf = epicsThreadGetIdSelf(); + int pass; + + for (pass = 0 ; pass < 3 ; pass++) { + epicsEventWaitStatus status; + status = epicsEventWait(pinfo->waitForMaster); + testOk(status == epicsEventWaitOK, + "task %p epicsEventWait returned %d", idSelf, status); + epicsThreadSleep(0.01); + epicsEventSignal(pinfo->waitForClient); + } +} + +static void runThreadPriorityTest(void *arg) +{ + epicsEventId testComplete = (epicsEventId) arg; + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + epicsThreadId myId = epicsThreadGetIdSelf(); + epicsThreadSetPriority(myId, 50); + + info *pinfo = (info *)calloc(1, sizeof(info)); + pinfo->waitForMaster = epicsEventMustCreate(epicsEventEmpty); + pinfo->waitForClient = epicsEventMustCreate(epicsEventEmpty); + + epicsThreadId clientId = epicsThreadCreate("client", + 50, stackSize, client, pinfo); + epicsEventSignal(pinfo->waitForMaster); + + epicsEventWaitStatus status; + status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); + if (!testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status)) { + testSkip(2, "epicsEventWaitWithTimeout failed"); + goto done; + } + + epicsThreadSetPriority(clientId, 20); + /* expect that client will not be able to run */ + epicsEventSignal(pinfo->waitForMaster); + + status = epicsEventTryWait(pinfo->waitForClient); + if (status != epicsEventWaitTimeout) { + testFail("epicsEventTryWait returned %d", status); + } else { + status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); + if (!testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status)) { + testSkip(1, "epicsEventWaitWithTimeout failed"); + goto done; + } + } + epicsThreadSetPriority(clientId, 80); + /* expect that client will be able to run */ + epicsEventSignal(pinfo->waitForMaster); + status = epicsEventTryWait(pinfo->waitForClient); + if (status==epicsEventWaitOK) { + testPass("Strict priority scheduler"); + } else { + testDiag("No strict priority scheduler"); + status = epicsEventWaitWithTimeout(pinfo->waitForClient,.1); + testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status); + } +done: + epicsThreadSleep(1.0); + epicsEventSignal(testComplete); +} + +} /* extern "C" */ + + +MAIN(epicsThreadPriorityTest) +{ + testPlan(7); + epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty); + epicsThreadMustCreate("threadPriorityTest", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + runThreadPriorityTest, testComplete); + epicsEventWaitStatus status = epicsEventWait(testComplete); + testOk(status == epicsEventWaitOK, + "epicsEventWait returned %d", status); + return testDone(); +} diff --git a/modules/libcom/test/epicsThreadPrivateTest.cpp b/modules/libcom/test/epicsThreadPrivateTest.cpp new file mode 100644 index 000000000..7ce28f4fd --- /dev/null +++ b/modules/libcom/test/epicsThreadPrivateTest.cpp @@ -0,0 +1,48 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Jeff Hill Date: March 28 2001 */ + +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static epicsThreadPrivate < bool > priv; + +extern "C" void epicsThreadPrivateTestThread ( void * ) +{ + testOk1 ( NULL == priv.get () ); + bool var = true; + priv.set ( &var ); + testOk1 ( &var == priv.get () ); +} + +MAIN(epicsThreadPrivateTest) +{ + testPlan(5); + + bool var = false; + priv.set ( &var ); + testOk1 ( &var == priv.get() ); + + epicsThreadCreate ( "epicsThreadPrivateTest", epicsThreadPriorityMax, + epicsThreadGetStackSize ( epicsThreadStackSmall ), + epicsThreadPrivateTestThread, 0 ); + epicsThreadSleep ( 1.0 ); + testOk1 ( &var == priv.get() ); + + priv.set ( NULL ); + testOk1 ( NULL == priv.get() ); + + return testDone(); +} + diff --git a/modules/libcom/test/epicsThreadTest.cpp b/modules/libcom/test/epicsThreadTest.cpp new file mode 100644 index 000000000..eb26cc8bf --- /dev/null +++ b/modules/libcom/test/epicsThreadTest.cpp @@ -0,0 +1,122 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsThreadTest.cpp */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static epicsThreadPrivate privateKey; + +class myThread: public epicsThreadRunable { +public: + myThread(int arg,const char *name); + virtual ~myThread(); + virtual void run(); + epicsThread thread; + epicsEvent startEvt; +private: + int *argvalue; +}; + +myThread::myThread(int arg,const char *name) : + thread(*this,name,epicsThreadGetStackSize(epicsThreadStackSmall),50+arg), + argvalue(0) +{ + argvalue = new int; + *argvalue = arg; + thread.start(); +} + +myThread::~myThread() {delete argvalue;} + +void myThread::run() +{ + startEvt.signal(); + int *pset = argvalue; + privateKey.set(argvalue); + epicsThreadSleep(2.0); + int *pget = privateKey.get(); + testOk1(pget == pset); + + epicsThreadId self = epicsThreadGetIdSelf(); + testOk1(thread.getPriority() == epicsThreadGetPriority(self)); +} + + +typedef struct info { + int isOkToBlock; +} info; + +extern "C" { +static void thread(void *arg) +{ + info *pinfo = (info *)arg; + + epicsThreadSetOkToBlock(pinfo->isOkToBlock); + epicsThreadSleep(1.0); + + testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock, + "%s epicsThreadIsOkToBlock() = %d", + epicsThreadGetNameSelf(), pinfo->isOkToBlock); + epicsThreadSleep(0.1); +} +} + + +MAIN(epicsThreadTest) +{ + testPlan(9); + + unsigned int ncpus = epicsThreadGetCPUs(); + testDiag("System has %u CPUs", ncpus); + testOk1(ncpus > 0); + + const int ntasks = 3; + myThread *myThreads[ntasks]; + + int startPriority = 0; + for (int i = 0; i < ntasks; i++) { + char name[10]; + sprintf(name, "t%d", i); + myThreads[i] = new myThread(i, name); + if (i == 0) + startPriority = myThreads[i]->thread.getPriority(); + myThreads[i]->thread.setPriority(startPriority + i); + } + + for (int i = 0; i < ntasks; i++) { + myThreads[i]->startEvt.wait(); + myThreads[i]->thread.exitWait(); + delete myThreads[i]; + } + + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + info infoA = {0}; + epicsThreadCreate("threadA", 50, stackSize, thread, &infoA); + + info infoB = {1}; + epicsThreadCreate("threadB", 50, stackSize, thread, &infoB); + + epicsThreadSleep(2.0); + + return testDone(); +} diff --git a/modules/libcom/test/epicsTimeTest.cpp b/modules/libcom/test/epicsTimeTest.cpp new file mode 100644 index 000000000..8eb8de6d4 --- /dev/null +++ b/modules/libcom/test/epicsTimeTest.cpp @@ -0,0 +1,288 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Authors: Jeff Hill, Marty Kraimer and Andrew Johnson + */ +#include +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +using namespace std; + +/* The functionality of the old invalidFormatTest () and badNanosecTest () + * routines is incorporated into epicsTimeTest () below. + */ + +struct l_fp { /* NTP time stamp */ + epicsUInt32 l_ui; /* sec past NTP epoch */ + epicsUInt32 l_uf; /* fractional seconds */ +}; + +static const unsigned mSecPerSec = 1000u; +static const unsigned uSecPerSec = 1000u * mSecPerSec; +static const unsigned nSecPerSec = 1000u * uSecPerSec; +static const double precisionEPICS = 1.0 / nSecPerSec; + +static void crossCheck(double delay) +{ + double mindelta = 2*epicsMonotonicResolution()*1e-9, + tres = epicsThreadSleepQuantum(); + epicsUInt64 A = epicsMonotonicGet(); + epicsThreadSleep(delay); + epicsUInt64 B = epicsMonotonicGet(); + + double actual = (B-A)*1e-9, percent; + + if(mindelta") == 0, "undefined => '%s'", buf); + + // This is Noon GMT, when all timezones have the same date + const epicsTimeStamp tTS = {12*60*60, 98765432}; + et = tTS; + pFormat = "%Y-%m-%d %S.%09f"; // %H and %M change with timezone + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "1990-01-01 00.098765432") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%03f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.099") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%04f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.0988") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.09877") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%05f %S.%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.09877 00.09877") == 0, "'%s' => '%s'", pFormat, buf); + + char smbuf[5]; + pFormat = "%S.%05f"; + et.strftime(smbuf, sizeof(smbuf), pFormat); + testOk(strcmp(smbuf, "00.*") == 0, "'%s' => '%s'", pFormat, smbuf); + + pFormat = "%S.%03f"; + (et + 0.9).strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.999") == 0, "0.998765 => '%s'", buf); + + pFormat = "%S.%03f"; + (et + 0.901).strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.999") == 0, "0.999765 => '%s'", buf); + + pFormat = "%%S.%%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "%S.%05f") == 0, "'%s' => '%s'", pFormat, buf); + + char bigBuf [512]; + memset(bigBuf, '\a', sizeof(bigBuf)); + bigBuf[ sizeof(bigBuf) - 1] = '\0'; + et.strftime(buf, sizeof(buf), bigBuf); + testOk(strcmp(buf, "") == 0, "bad format => '%s'", buf); + } + + epicsTime now; + try { + now = epicsTime::getCurrent(); + testPass("default time provider"); + } + catch ( ... ) { + testFail("epicsTime::getCurrent() throws"); + testAbort("Can't continue, check your time provider"); + } + + { + l_fp ntp = now; + epicsTime tsf = ntp; + const double diff = fabs(tsf - now); + // the difference in the precision of the two time formats + static const double precisionNTP = 1.0 / (1.0 + 0xffffffff); + testOk1(diff <= precisionEPICS + precisionNTP); + } + + testDiag("Running %d loops", nTimes); + + const epicsTime begin = epicsTime::getCurrent(); + + for (int loop = 0; loop < nTimes; ++loop) { + for (int i = 0; i < wasteTime; ++i) { + now = epicsTime::getCurrent(); + } + + const double diff = now - begin; + + if (loop == 0) { + testDiag ("%d calls to epicsTime::getCurrent() " + "averaged %6.3f usec each", wasteTime, + diff * 1e6 / wasteTime); + } + + epicsTime copy = now; + testOk1(copy == now); + testOk1(copy <= now); + testOk1(copy >= now); + + testOk1(now > begin); + testOk1(now >= begin); + testOk1(begin != now); + testOk1(begin < now); + testOk1(begin <= now); + + testOk1(now - now == 0); + testOk(fabs((now - begin) - diff) < precisionEPICS * 0.01, + "now - begin ~= diff"); + + testOk1(begin + 0 == begin); + testOk1(begin + diff == now); + testOk1(now - 0 == now); + testOk1(now - diff == begin); + + epicsTime end = begin; + end += diff; + testOk(end == now, "(begin += diff) == now"); + + end = now; + end -= diff; + testOk(end == begin, "(now -= diff) == begin"); + + // test struct tm round-trip conversion + local_tm_nano_sec ansiDate = begin; + epicsTime beginANSI = ansiDate; + testOk1(beginANSI + diff == now); + + // test struct gmtm round-trip conversion + gm_tm_nano_sec ansiGmDate = begin; + epicsTime beginGMANSI = ansiGmDate; + testOk1(beginGMANSI + diff == now); + + // test struct timespec round-trip conversion + struct timespec ts = begin; + epicsTime beginTS = ts; + testOk1(beginTS + diff == now); + } + + epicsTime ten_years_hence; + try { + now = epicsTime::getCurrent(); + ten_years_hence = now + 60 * 60 * 24 * 3652.5; + testPass("epicsTime can represent 10 years hence"); + } + catch ( ... ) { + testFail("epicsTime exception for value 10 years hence"); + } + + try { + /* This test exists because in libCom/osi/os/posix/osdTime.cpp + * the convertDoubleToWakeTime() routine limits the timeout delay + * to 10 years. libCom/timer/timerQueue.cpp returns DBL_MAX for + * queues with no timers present, and convertDoubleToWakeTime() + * has to return an absolute Posix timestamp. On 2028-01-19 any + * systems that still implement time_t as a signed 32-bit integer + * will be unable to represent that timestamp, so this will fail. + */ + time_t_wrapper os_time_t = ten_years_hence; + epicsTime then = os_time_t; // No fractional seconds + double delta = ten_years_hence - then; + + testOk(delta >= 0 && delta < 1.0, + "OS time_t can represent 10 years hence"); + } + catch ( ... ) { + testFail("OS time_t conversion exception for value 10 years hence"); + } + + testMonotonic(); + + return testDone(); +} diff --git a/modules/libcom/test/epicsTimeZoneTest.c b/modules/libcom/test/epicsTimeZoneTest.c new file mode 100644 index 000000000..685295fc4 --- /dev/null +++ b/modules/libcom/test/epicsTimeZoneTest.c @@ -0,0 +1,131 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "envDefs.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" + +#include "testMain.h" + +#if defined(_WIN32) +# define tzset _tzset +#endif + +static +void setTZ(const char *tz) +{ + testDiag("TZ = \"%s\"", tz); + epicsEnvSet("TZ", tz); + tzset(); +} + +static +void test_localtime(time_t T, int sec, int min, int hour, + int mday, int mon, int year, + int wday, int yday, int isdst) +{ + struct tm B; + testDiag("test_localtime(%ld, ...)", (long)T); + if(epicsTime_localtime(&T, &B)!=epicsTimeOK) { + testFail("epicsTime_localtime() error"); + testSkip(9, "epicsTime_localtime() failed"); + } else { + B.tm_year += 1900; /* for readability */ + testPass("epicsTime_localtime() success"); +#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) + TEST(sec); + TEST(min); + TEST(hour); + TEST(mday); + TEST(mon); + TEST(year); + TEST(wday); + TEST(yday); + TEST(isdst); +#undef TEST + } +} + +static +void test_gmtime(time_t T, int sec, int min, int hour, + int mday, int mon, int year, + int wday, int yday, int isdst) +{ + struct tm B; + testDiag("test_gmtime(%ld, ...)", (long)T); + if(epicsTime_gmtime(&T, &B)!=epicsTimeOK) { + testFail("epicsTime_localtime() error"); + testSkip(9, "epicsTime_localtime() failed"); + } else { + B.tm_year += 1900; /* for readability */ + testPass("epicsTime_localtime() success"); +#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) + TEST(sec); + TEST(min); + TEST(hour); + TEST(mday); + TEST(mon); + TEST(year); + TEST(wday); + TEST(yday); + TEST(isdst); +#undef TEST + } +} + +MAIN(epicsTimeZoneTest) +{ + testPlan(160); + /* 1445259616 + * Mon Oct 19 09:00:16 2015 EDT + * Mon Oct 19 08:00:16 2015 CDT + * Mon Oct 19 03:00:16 2015 HST (no dst) + * Mon Oct 19 13:00:16 2015 UTC + */ + testDiag("POSIX 1445259616"); + setTZ("EST5EDT"); + test_localtime(1445259616ul, 16, 0, 9, 19, 9, 2015, 1, 291, 1); + test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + setTZ("CST6CDT"); + test_localtime(1445259616ul, 16, 0, 8, 19, 9, 2015, 1, 291, 1); + test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); +#if defined(__rtems__) + setTZ("HST10HST10"); +#else + setTZ("HST10"); +#endif + test_localtime(1445259616ul, 16, 0, 3, 19, 9, 2015, 1, 291, 0); + test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + setTZ("UTC0"); + test_localtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + test_gmtime (1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + /* 1421244931 + * Wed Jan 14 09:15:31 2015 EST + * Wed Jan 14 08:15:31 2015 CST + * Wed Jan 14 04:15:31 2015 HST + * Wed Jan 14 14:15:31 2015 UTC + */ + testDiag("POSIX 1421244931"); + setTZ("EST5EDT"); + test_localtime(1421244931ul, 31, 15, 9, 14, 0, 2015, 3, 13, 0); + test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + setTZ("CST6CDT"); + test_localtime(1421244931ul, 31, 15, 8, 14, 0, 2015, 3, 13, 0); + test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); +#if defined(__rtems__) + setTZ("HST10HST10"); +#else + setTZ("HST10"); +#endif + test_localtime(1421244931ul, 31, 15, 4, 14, 0, 2015, 3, 13, 0); + test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + setTZ("UTC0"); + test_localtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + test_gmtime (1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + return testDone(); +} diff --git a/modules/libcom/test/epicsTimerTest.cpp b/modules/libcom/test/epicsTimerTest.cpp new file mode 100644 index 000000000..f2a510819 --- /dev/null +++ b/modules/libcom/test/epicsTimerTest.cpp @@ -0,0 +1,462 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include +#include + +#include "epicsTimer.h" +#include "epicsEvent.h" +#include "epicsAssert.h" +#include "epicsGuard.h" +#include "tsFreeList.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +class notified : public epicsTimerNotify +{ +public: + bool done; + notified() : epicsTimerNotify(), done(false) {} + + expireStatus expire(const epicsTime ¤tTime) + {done=true; return expireStatus(noRestart);} +}; + +void testRefCount() +{ + notified action; + + epicsTimerQueueActive *Q1, *Q2; + epicsTimer *T1, *T2; + + Q1 = &epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + T1 = &Q1->createTimer(); + //timer->start(action, 0.0); + + Q2 = &epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + testOk1(Q1==Q2); + + T2 = &Q2->createTimer(); + + T2->destroy(); + Q2->release(); + + T1->destroy(); + Q1->release(); +} + +static const double delayVerifyOffset = 1.0; // sec + +class delayVerify : public epicsTimerNotify { +public: + delayVerify ( double expectedDelay, epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void setBegin ( const epicsTime & ); + double delay () const; + double checkError () const; +protected: + virtual ~delayVerify (); +private: + epicsTimer &timer; + epicsTime beginStamp; + epicsTime expireStamp; + double expectedDelay; + expireStatus expire ( const epicsTime & ); + delayVerify ( const delayVerify & ); + delayVerify & operator = ( const delayVerify & ); +}; + +static volatile unsigned expireCount; +static epicsEvent expireEvent; + +delayVerify::delayVerify ( double expectedDelayIn, epicsTimerQueue &queueIn ) : + timer ( queueIn.createTimer() ), expectedDelay ( expectedDelayIn ) +{ +} + +delayVerify::~delayVerify () +{ + this->timer.destroy (); +} + +inline void delayVerify::setBegin ( const epicsTime &beginIn ) +{ + this->beginStamp = beginIn; +} + +inline double delayVerify::delay () const +{ + return this->expectedDelay; +} + +double delayVerify::checkError () const +{ + const double messageThresh = 2.0; // percent + double actualDelay = this->expireStamp - this->beginStamp; + double measuredError = actualDelay - this->expectedDelay; + double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay; + if ( ! testOk1 ( percentError < messageThresh ) ) { + testDiag ( "delay = %f s, error = %f s (%.1f %%)", + this->expectedDelay, measuredError, percentError ); + } + return measuredError; +} + +inline void delayVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +epicsTimerNotify::expireStatus delayVerify::expire ( const epicsTime ¤tTime ) +{ + this->expireStamp = currentTime; + if ( --expireCount == 0u ) { + expireEvent.signal (); + } + return noRestart; +} + +// +// verify reasonable timer interval accuracy +// +void testAccuracy () +{ + static const unsigned nTimers = 25u; + delayVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + testDiag ( "Testing timer accuracy" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMax ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new delayVerify ( i * 0.1 + delayVerifyOffset, queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + + expireCount = nTimers; + for ( i = 0u; i < nTimers; i++ ) { + epicsTime cur = epicsTime::getCurrent (); + pTimers[i]->setBegin ( cur ); + pTimers[i]->start ( cur + pTimers[i]->delay () ); + } + while ( expireCount != 0u ) { + expireEvent.wait (); + } + double averageMeasuredError = 0.0; + for ( i = 0u; i < nTimers; i++ ) { + averageMeasuredError += pTimers[i]->checkError (); + } + averageMeasuredError /= nTimers; + testDiag ("average timer delay error %f ms", + averageMeasuredError * 1000 ); + queue.release (); +} + + +class cancelVerify : public epicsTimerNotify { +public: + cancelVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void cancel (); + static unsigned cancelCount; + static unsigned expireCount; +protected: + virtual ~cancelVerify (); +private: + epicsTimer &timer; + expireStatus expire ( const epicsTime & ); + cancelVerify ( const cancelVerify & ); + cancelVerify & operator = ( const cancelVerify & ); +}; + +unsigned cancelVerify::cancelCount; +unsigned cancelVerify::expireCount; + +cancelVerify::cancelVerify ( epicsTimerQueue &queueIn ) : + timer ( queueIn.createTimer () ) +{ +} + +cancelVerify::~cancelVerify () +{ + this->timer.destroy (); +} + +inline void cancelVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +inline void cancelVerify::cancel () +{ + this->timer.cancel (); + ++cancelVerify::cancelCount; +} + +epicsTimerNotify::expireStatus cancelVerify::expire ( const epicsTime & ) +{ + ++cancelVerify::expireCount; + double root = 3.14159; + for ( unsigned i = 0u; i < 1000; i++ ) { + root = sqrt ( root ); + } + return noRestart; +} + +// +// verify that expire() won't be called after the timer is cancelled +// +void testCancel () +{ + static const unsigned nTimers = 25u; + cancelVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + cancelVerify::cancelCount = 0; + cancelVerify::expireCount = 0; + + testDiag ( "Testing timer cancellation" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new cancelVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + if ( ! testOk1 ( cancelVerify::expireCount == 0 ) ) + testDiag ( "expireCount = %u", cancelVerify::expireCount ); + if ( ! testOk1 ( cancelVerify::cancelCount == 0 ) ) + testDiag ( "cancelCount = %u", cancelVerify::cancelCount ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime exp = epicsTime::getCurrent () + 4.0; + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( exp ); + } + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == 0 ); + + testDiag ( "cancelling timers" ); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->cancel (); + } + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == nTimers ); + + testDiag ( "waiting until after timers should have expired" ); + epicsThreadSleep ( 5.0 ); + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == nTimers ); + + epicsThreadSleep ( 1.0 ); + queue.release (); +} + + +class expireDestroyVerify : public epicsTimerNotify { +public: + expireDestroyVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + static unsigned destroyCount; +protected: + virtual ~expireDestroyVerify (); +private: + epicsTimer & timer; + expireStatus expire ( const epicsTime & ); + expireDestroyVerify ( const expireDestroyVerify & ); + expireDestroyVerify & operator = ( const expireDestroyVerify & ); +}; + +unsigned expireDestroyVerify::destroyCount; + +expireDestroyVerify::expireDestroyVerify ( epicsTimerQueue & queueIn ) : + timer ( queueIn.createTimer () ) +{ +} + +expireDestroyVerify::~expireDestroyVerify () +{ + this->timer.destroy (); + ++expireDestroyVerify::destroyCount; +} + +inline void expireDestroyVerify::start ( const epicsTime & expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +epicsTimerNotify::expireStatus expireDestroyVerify::expire ( const epicsTime & ) +{ + delete this; + return noRestart; +} + +// +// verify that a timer can be destroyed in expire +// +void testExpireDestroy () +{ + static const unsigned nTimers = 25u; + expireDestroyVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + expireDestroyVerify::destroyCount = 0; + + testDiag ( "Testing timer destruction in expire()" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new expireDestroyVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + testOk1 ( expireDestroyVerify::destroyCount == 0 ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime cur = epicsTime::getCurrent (); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( cur ); + } + + testDiag ( "waiting until all timers should have expired" ); + epicsThreadSleep ( 5.0 ); + + testOk1 ( expireDestroyVerify::destroyCount == nTimers ); + queue.release (); +} + + +class periodicVerify : public epicsTimerNotify { +public: + periodicVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void cancel (); + bool verifyCount (); +protected: + virtual ~periodicVerify (); +private: + epicsTimer &timer; + unsigned nExpire; + bool cancelCalled; + expireStatus expire ( const epicsTime & ); + periodicVerify ( const periodicVerify & ); + periodicVerify & operator = ( const periodicVerify & ); +}; + +periodicVerify::periodicVerify ( epicsTimerQueue & queueIn ) : + timer ( queueIn.createTimer () ), nExpire ( 0u ), + cancelCalled ( false ) +{ +} + +periodicVerify::~periodicVerify () +{ + this->timer.destroy (); +} + +inline void periodicVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +inline void periodicVerify::cancel () +{ + this->timer.cancel (); + this->cancelCalled = true; +} + +inline bool periodicVerify::verifyCount () +{ + return ( this->nExpire > 1u ); +} + +epicsTimerNotify::expireStatus periodicVerify::expire ( const epicsTime & ) +{ + this->nExpire++; + double root = 3.14159; + for ( unsigned i = 0u; i < 1000; i++ ) { + root = sqrt ( root ); + } + verify ( ! this->cancelCalled ); + double delay = rand (); + delay = delay / RAND_MAX; + delay /= 10.0; + return expireStatus ( restart, delay ); +} + +// +// verify periodic timers +// +void testPeriodic () +{ + static const unsigned nTimers = 25u; + periodicVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + testDiag ( "Testing periodic timers" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new periodicVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime cur = epicsTime::getCurrent (); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( cur ); + } + + testDiag ( "waiting until all timers should have expired" ); + epicsThreadSleep ( 5.0 ); + + bool notWorking = false; + for ( i = 0u; i < nTimers; i++ ) { + notWorking |= ! pTimers[i]->verifyCount (); + pTimers[i]->cancel (); + } + testOk( ! notWorking, "All timers expiring" ); + epicsThreadSleep ( 1.0 ); + queue.release (); +} + +MAIN(epicsTimerTest) +{ + testPlan(41); + testRefCount(); + testAccuracy (); + testCancel (); + testExpireDestroy (); + testPeriodic (); + return testDone(); +} diff --git a/modules/libcom/test/epicsTypesTest.c b/modules/libcom/test/epicsTypesTest.c new file mode 100644 index 000000000..aa882aa9f --- /dev/null +++ b/modules/libcom/test/epicsTypesTest.c @@ -0,0 +1,53 @@ +/*************************************************************************\ +* Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTypesTest.c + * + * Size-check epicsTypes + * + */ + +#include "epicsUnitTest.h" +#include "epicsTypes.h" +#include "testMain.h" +#include "epicsAssert.h" + +/* + * Might as well check at compile-time too, since we can. + */ + +STATIC_ASSERT(sizeof(epicsInt8) == 1); +STATIC_ASSERT(sizeof(epicsUInt8) == 1); +STATIC_ASSERT(sizeof(epicsInt16) == 2); +STATIC_ASSERT(sizeof(epicsUInt16) == 2); +STATIC_ASSERT(sizeof(epicsInt32) == 4); +STATIC_ASSERT(sizeof(epicsUInt32) == 4); +STATIC_ASSERT(sizeof(epicsInt64) == 8); +STATIC_ASSERT(sizeof(epicsUInt64) == 8); +STATIC_ASSERT(sizeof(epicsFloat32) == 4); +STATIC_ASSERT(sizeof(epicsFloat64) == 8); + +MAIN(epicsTypesTest) +{ + testPlan(10); + + testOk1(sizeof(epicsInt8) == 1); + testOk1(sizeof(epicsUInt8) == 1); + + testOk1(sizeof(epicsInt16) == 2); + testOk1(sizeof(epicsUInt16) == 2); + + testOk1(sizeof(epicsInt32) == 4); + testOk1(sizeof(epicsUInt32) == 4); + + testOk1(sizeof(epicsInt64) == 8); + testOk1(sizeof(epicsUInt64) == 8); + + testOk1(sizeof(epicsFloat32) == 4); + testOk1(sizeof(epicsFloat64) == 8); + + return testDone(); +} diff --git a/modules/libcom/test/epicsUnitTestTest.c b/modules/libcom/test/epicsUnitTestTest.c new file mode 100644 index 000000000..b16bad52a --- /dev/null +++ b/modules/libcom/test/epicsUnitTestTest.c @@ -0,0 +1,36 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Andrew Johnson + * + * Test for the unit test module... + */ + +#include "epicsUnitTest.h" + +#define testOk1_success 1 +#define testOk1_failure 0 + +int main (void) { + testPlan(11); + testOk(1, "testOk(1)"); + testOk(0, "testOk(0)"); + testPass("testPass()"); + testFail("testFail()"); + testSkip(2, "Skipping two"); + testTodoBegin("Testing Todo"); + testOk(1, "Todo pass"); + testOk(0, "Todo fail"); + testSkip(1, "Todo skip"); + testTodoEnd(); + testOk1(testOk1_success); + testOk1(testOk1_failure); + testDiag("Diagnostic"); +/* testAbort("testAbort"); */ + return testDone(); +} diff --git a/modules/libcom/test/epicsUnitTestTest.plt b/modules/libcom/test/epicsUnitTestTest.plt new file mode 100644 index 000000000..1cb9e0c1b --- /dev/null +++ b/modules/libcom/test/epicsUnitTestTest.plt @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; +use Test; + +BEGIN {plan tests => 1} + +my $prog = "./$0"; +$prog =~ s/\.t$//; + +my $expected = join '', ; + +$ENV{HARNESS_ACTIVE} = 1; +my $result = `$prog`; + +ok($result, $expected); # test output matches + +__DATA__ +1..11 +ok 1 - testOk(1) +not ok 2 - testOk(0) +ok 3 - testPass() +not ok 4 - testFail() +ok 5 # SKIP Skipping two +ok 6 # SKIP Skipping two +ok 7 - Todo pass # TODO Testing Todo +not ok 8 - Todo fail # TODO Testing Todo +ok 9 # SKIP Todo skip +ok 10 - testOk1_success +not ok 11 - testOk1_failure +# Diagnostic diff --git a/modules/libcom/test/fdmgrTest.c b/modules/libcom/test/fdmgrTest.c new file mode 100644 index 000000000..e41784ccf --- /dev/null +++ b/modules/libcom/test/fdmgrTest.c @@ -0,0 +1,138 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "fdmgr.h" +#include "epicsTime.h" +#include "epicsAssert.h" +#include "cadef.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +static const unsigned uSecPerSec = 1000000; + +typedef struct cbStructCreateDestroyFD { + fdctx *pfdm; + int trig; +} cbStructCreateDestroyFD; + +void fdHandler (void *pArg) +{ + cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; + + printf ("triggered\n"); + pCBFD->trig = 1; +} + +void fdCreateDestroyHandler (void *pArg, int fd, int open) +{ + cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; + int status; + + if (open) { + printf ("new fd = %d\n", fd); + status = fdmgr_add_callback (pCBFD->pfdm, fd, fdi_read, fdHandler, pArg); + verify (status==0); + } + else { + printf ("terminated fd = %d\n", fd); + status = fdmgr_clear_callback (pCBFD->pfdm, fd, fdi_read); + verify (status==0); + } +} + +typedef struct cbStuctTimer { + epicsTimeStamp time; + int done; +} cbStruct; + +void alarmCB (void *parg) +{ + cbStruct *pCBS = (cbStruct *) parg; + epicsTimeGetCurrent (&pCBS->time); + pCBS->done = 1; +} + +void testTimer (fdctx *pfdm, double delay) +{ + int status; + fdmgrAlarmId aid; + struct timeval tmo; + epicsTimeStamp begin; + cbStruct cbs; + double measuredDelay; + double measuredError; + + epicsTimeGetCurrent (&begin); + cbs.done = 0; + tmo.tv_sec = (time_t) delay; + tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); + aid = fdmgr_add_timeout (pfdm, &tmo, alarmCB, &cbs); + verify (aid!=fdmgrNoAlarm); + + while (!cbs.done) { + tmo.tv_sec = (time_t) delay; + tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); + status = fdmgr_pend_event (pfdm, &tmo); + verify (status==0); + } + + measuredDelay = epicsTimeDiffInSeconds (&cbs.time, &begin); + measuredError = fabs (measuredDelay-delay); + printf ("measured delay for %lf sec was off by %lf sec (%lf %%)\n", + delay, measuredError, 100.0*measuredError/delay); +} + +int main (int argc, char **argv) +{ + int status; + fdctx *pfdm; + cbStructCreateDestroyFD cbsfd; + struct timeval tmo; + chid chan; + + pfdm = fdmgr_init (); + verify (pfdm); + + SEVCHK (ca_task_initialize(), NULL); + cbsfd.pfdm = pfdm; + SEVCHK (ca_add_fd_registration (fdCreateDestroyHandler, &cbsfd), NULL); + + /* + * timer test + */ + testTimer (pfdm, 0.001); + testTimer (pfdm, 0.01); + testTimer (pfdm, 0.1); + testTimer (pfdm, 1.0); + + if (argc==2) { + SEVCHK(ca_search (argv[1], &chan), NULL); + } + + while (1) { + tmo.tv_sec = 0; + tmo.tv_usec = 100000; + cbsfd.trig = 0; + status = fdmgr_pend_event (pfdm, &tmo); + verify (status==0); + ca_poll (); + } + + status = fdmgr_delete (pfdm); + verify (status==0); + + printf ( "Test Complete\n" ); + + return 0; +} + diff --git a/modules/libcom/test/ipAddrToAsciiTest.cpp b/modules/libcom/test/ipAddrToAsciiTest.cpp new file mode 100644 index 000000000..f9323374d --- /dev/null +++ b/modules/libcom/test/ipAddrToAsciiTest.cpp @@ -0,0 +1,165 @@ +/*************************************************************************\ +* Copyright (c) 2017 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define EPICS_PRIVATE_API + +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "ipAddrToAsciiAsynchronous.h" + +#include "epicsUnitTest.h" +#include "testMain.h" + +namespace { + +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +struct CB : public ipAddrToAsciiCallBack +{ + const char *name; + epicsMutex mutex; + epicsEvent starter, blocker, complete; + bool started, cont, done; + CB(const char *name) : name(name), started(false), cont(false), done(false) {} + virtual ~CB() {} + virtual void transactionComplete ( const char * pHostName ) + { + Guard G(mutex); + started = true; + starter.signal(); + testDiag("In transactionComplete(%s) for %s", pHostName, name); + while(!cont) { + UnGuard U(G); + if(!blocker.wait(2.0)) + break; + } + done = true; + complete.signal(); + } + void waitStart() + { + Guard G(mutex); + while(!started) { + UnGuard U(G); + if(!starter.wait(2.0)) + break; + } + } + void poke() + { + testDiag("Poke"); + Guard G(mutex); + cont = true; + blocker.signal(); + } + void finish() + { + testDiag("Finish"); + Guard G(mutex); + while(!done) { + UnGuard U(G); + if(!complete.wait(2.0)) + break; + } + testDiag("Finished"); + } +}; + +// ensure that lookup of 127.0.0.1 works +void doLookup(ipAddrToAsciiEngine& engine) +{ + testDiag("In doLookup"); + + ipAddrToAsciiTransaction& trn(engine.createTransaction()); + CB cb("cb"); + osiSockAddr addr; + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.ia.sin_port = htons(42); + + testDiag("Start lookup"); + trn.ipAddrToAscii(addr, cb); + cb.poke(); + cb.finish(); + testOk1(cb.cont); + testOk1(cb.done); + + trn.release(); +} + +// Test cancel of pending transaction +void doCancel() +{ + testDiag("In doCancel"); + + ipAddrToAsciiEngine& engine1(ipAddrToAsciiEngine::allocate()); + ipAddrToAsciiEngine& engine2(ipAddrToAsciiEngine::allocate()); + + ipAddrToAsciiTransaction& trn1(engine1.createTransaction()), + & trn2(engine2.createTransaction()); + testOk1(&trn1!=&trn2); + CB cb1("cb1"), cb2("cb2"); + + osiSockAddr addr; + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.ia.sin_port = htons(42); + + // ensure that the worker thread is blocked with a transaction from engine1 + testDiag("Start lookup1"); + trn1.ipAddrToAscii(addr, cb1); + testDiag("Wait start1"); + cb1.waitStart(); + + testDiag("Start lookup2"); + trn2.ipAddrToAscii(addr, cb2); + + testDiag("release engine2, implicitly cancels lookup2"); + engine2.release(); + + cb2.poke(); + testDiag("Wait for lookup2 timeout"); + cb2.finish(); + testOk1(!cb2.done); + + testDiag("Complete lookup1"); + cb1.poke(); + cb1.finish(); + testOk1(cb1.done); + + engine1.release(); + + trn1.release(); + trn2.release(); +} + +} // namespace + +MAIN(ipAddrToAsciiTest) +{ + testPlan(5); + { + ipAddrToAsciiEngine& engine(ipAddrToAsciiEngine::allocate()); + doLookup(engine); + engine.release(); + } + doCancel(); + // TODO: somehow test cancel of in-progress callback + // allow time for any un-canceled transcations to crash us... + epicsThreadSleep(1.0); + +#ifdef __linux__ + ipAddrToAsciiEngine::cleanup(); +#endif + + return testDone(); +} diff --git a/modules/libcom/test/macDefExpandTest.c b/modules/libcom/test/macDefExpandTest.c new file mode 100644 index 000000000..a5e45a420 --- /dev/null +++ b/modules/libcom/test/macDefExpandTest.c @@ -0,0 +1,254 @@ +/*************************************************************************\ +* Copyright (c) 2014 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include + +#include "macLib.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +static void checkMac(MAC_HANDLE *handle, const char *name, const char *value) +{ + char buf[20]; + + buf[19]='\0'; + if(macGetValue(handle, name, buf, 19)<0) { + if(value) + testFail("Macro %s undefined, expected %s", name, value); + else + testPass("Macro %s undefined", name); + } else { + if(!value) + testFail("Macro %s is %s, expected undefined", name, buf); + else if(strcmp(value, buf)==0) + testPass("Macro %s is %s", name, value); + else + testFail("Macro %s is %s, expected %s", name, buf, value); + } +} + +static void macEnvScope(void) +{ + MAC_HANDLE *handle; + char **defines; + static const char *pairs[] = { "", "environ", NULL, NULL }; + + epicsEnvSet("C","3"); + epicsEnvSet("D","4"); + epicsEnvSet("E","5"); + + macCreateHandle(&handle, pairs); + macParseDefns(NULL, "A=1,B=2,E=15", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "A", "1"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "3"); + checkMac(handle, "D", "4"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", NULL); + + { + macPushScope(handle); + + macParseDefns(NULL, "A=11,C=13,D=14,G=7", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "A", "11"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "13"); + checkMac(handle, "D", "14"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", NULL); + checkMac(handle, "G", "7"); + + epicsEnvSet("D", "24"); + macPutValue(handle, "D", NULL); /* implicit when called through in iocshBody */ + epicsEnvSet("F", "6"); + macPutValue(handle, "F", NULL); /* implicit */ + epicsEnvSet("G", "17"); + macPutValue(handle, "G", NULL); /* implicit */ + + checkMac(handle, "D", "24"); + checkMac(handle, "F", "6"); + checkMac(handle, "G", "17"); + + macPopScope(handle); + } + + checkMac(handle, "A", "1"); + checkMac(handle, "B", "2"); + checkMac(handle, "C", "3"); + checkMac(handle, "D", "24"); + checkMac(handle, "E", "15"); + checkMac(handle, "F", "6"); + checkMac(handle, "G", "17"); + + { + macPushScope(handle); + + macParseDefns(NULL, "D=34,G=27", &defines); + macInstallMacros(handle, defines); + + checkMac(handle, "D", "34"); + checkMac(handle, "G", "27"); + + macPopScope(handle); + } + + checkMac(handle, "D", "24"); + + macDeleteHandle(handle); +} + +static void check(const char *str, const char *macros, const char *expect) +{ + MAC_HANDLE *handle; + char **defines; + static const char *pairs[] = { "", "environ", NULL, NULL }; + char *got; + int pass = 1; + + macCreateHandle(&handle, pairs); + macParseDefns(NULL, macros, &defines); + macInstallMacros(handle, defines); + + got = macDefExpand(str, handle); + + if (expect && !got) { + testDiag("Got NULL, expected \"%s\".\n", expect); + pass = 0; + } + else if (!expect && got) { + testDiag("Got \"%s\", expected NULL.\n", got); + pass = 0; + } + else if (expect && got && strcmp(got, expect)) { + testDiag("Got \"%s\", expected \"%s\".\n", got, expect); + pass = 0; + } + testOk(pass, "%s", str); + + macDeleteHandle(handle); +} + +MAIN(macDefExpandTest) +{ + eltc(0); + testPlan(97); + + check("FOO", "", "FOO"); + + check("${FOO}", "", NULL); + check("${FOO,BAR}", "", NULL); + check("${FOO,BAR=baz}", "", NULL); + check("${FOO,BAR=$(FOO)}", "", NULL); + check("${FOO,FOO}", "", NULL); + check("${FOO,FOO=$(FOO)}", "", NULL); + check("${FOO,BAR=baz,FUM}", "", NULL); + + check("${=}", "", ""); + check("x${=}y", "", "xy"); + + check("${,=}", "", ""); + check("x${,=}y", "", "xy"); + + check("${FOO=}", "", ""); + check("x${FOO=}y", "", "xy"); + + check("${FOO=,}", "", ""); + check("x${FOO=,}y", "", "xy"); + + check("${FOO,FOO=}", "", ""); + check("x${FOO,FOO=}y", "", "xy"); + + check("${FOO=,BAR}", "", ""); + check("x${FOO=,BAR}y", "", "xy"); + + check("${FOO=$(BAR=)}", "", ""); + check("x${FOO=$(BAR=)}y", "", "xy"); + + check("${FOO=,BAR=baz}", "", ""); + check("x${FOO=,BAR=baz}y", "", "xy"); + + check("${FOO=$(BAR),BAR=}", "", ""); + check("x${FOO=$(BAR),BAR=}y", "", "xy"); + + check("${=BAR}", "", "BAR"); + check("x${=BAR}y", "", "xBARy"); + + check("${FOO=BAR}", "", "BAR"); + check("x${FOO=BAR}y", "", "xBARy"); + + epicsEnvSet("FOO","BLETCH"); + check("${FOO}", "", "BLETCH"); + check("${FOO,FOO}", "", "BLETCH"); + check("x${FOO}y", "", "xBLETCHy"); + check("x${FOO}y${FOO}z", "", "xBLETCHyBLETCHz"); + check("${FOO=BAR}", "", "BLETCH"); + check("x${FOO=BAR}y", "", "xBLETCHy"); + check("${FOO=${BAZ}}", "", "BLETCH"); + check("${FOO=${BAZ},BAR=$(BAZ)}", "", "BLETCH"); + check("x${FOO=${BAZ}}y", "", "xBLETCHy"); + check("x${FOO=${BAZ},BAR=$(BAZ)}y", "", "xBLETCHy"); + check("${BAR=${FOO}}", "", "BLETCH"); + check("x${BAR=${FOO}}y", "", "xBLETCHy"); + check("w${BAR=x${FOO}y}z", "", "wxBLETCHyz"); + + check("${FOO,FOO=BAR}", "", "BAR"); + check("x${FOO,FOO=BAR}y", "", "xBARy"); + check("${BAR,BAR=$(FOO)}", "", "BLETCH"); + check("x${BAR,BAR=$(FOO)}y", "", "xBLETCHy"); + check("${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); + check("x${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); + check("${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}", "", "GRIBBLE"); + check("x${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}y", "", "xGRIBBLEy"); + + check("${FOO}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("x${FOO}/${BAR}y", "BAR=GLEEP", "xBLETCH/GLEEPy"); + check("${FOO,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${FOO,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR=x}/${BAR}", "BAR=GLEEP", "BLETCH/GLEEP"); + + check("${${FOO}}", "BAR=GLEEP,BLETCH=BAR", "BAR"); + check("x${${FOO}}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); + check("${${FOO}=GRIBBLE}", "BAR=GLEEP,BLETCH=BAR", "BAR"); + check("x${${FOO}=GRIBBLE}y", "BAR=GLEEP,BLETCH=BAR", "xBARy"); + + check("${${FOO}}", "BAR=GLEEP,BLETCH=${BAR}", "GLEEP"); + + epicsEnvSet("FOO","${BAR}"); + check("${FOO}", "BAR=GLEEP,BLETCH=${BAR}" ,"GLEEP"); + + check("${FOO}", "BAR=${BAZ},BLETCH=${BAR}", NULL); + + check("${FOO}", "BAR=${BAZ=GRIBBLE},BLETCH=${BAR}", "GRIBBLE"); + + check("${FOO}", "BAR=${STR1},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL1"); + + check("${FOO}", "BAR=${STR2},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", "VAL2"); + + check("${FOO}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO,FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + check("${FOO=$(BAR),BAR=$(FOO)}", "BAR=${FOO},BLETCH=${BAR},STR1=VAL1,STR2=VAL2", NULL); + + macEnvScope(); + + errlogFlush(); + eltc(1); + return testDone(); +} diff --git a/modules/libcom/test/macLib.plt b/modules/libcom/test/macLib.plt new file mode 100644 index 000000000..559f27df8 --- /dev/null +++ b/modules/libcom/test/macLib.plt @@ -0,0 +1,79 @@ +#!/usr/bin/perl + +use lib '@TOP@/lib/perl'; + +use Test::More tests => 35; + +use EPICS::macLib; + +use Data::Dumper; + +my $m = EPICS::macLib->new; +isa_ok $m, 'EPICS::macLib'; +is $m->expandString(''), '', 'Empty string'; + +{ + local *STDERR; + my $output; + open STDERR, '>', \$output; + is $m->expandString('$(undef)'), undef, 'Warning $(undef)'; + chomp $output; + is $output, q/macLib: macro 'undef' is undefined (expanding string '$(undef)')/, 'macLib error message'; +} + +$m->suppressWarning(1); +is $m->expandString('$(undef)'), '$(undef)', 'Suppressed $(undef)'; + +$m->putValue('a', 'foo'); +is $m->expandString('$(a)'), 'foo', '$(a)'; +is $m->expandString('${a}'), 'foo', '${a}'; +is $m->expandString('$(a=bar)'), 'foo', '$(a=bar)'; +is $m->expandString('${a=bar}'), 'foo', '${a=bar}'; +is $m->expandString('$(undef)'), '$(undef)', '$(undef) again'; +is $m->expandString('${undef}'), '$(undef)', '${undef} again'; + +$m->suppressWarning(0); +is $m->expandString('$(undef=$(a))'), 'foo', '$(undef=$(a))'; +is $m->expandString('${undef=${a}}'), 'foo', '${undef=${a}}'; +is $m->expandString('${undef=$(a)}'), 'foo', '${undef=$(a)}'; +is $m->expandString('$(undef=${a})'), 'foo', '$(undef=${a})'; +is $m->expandString('$(a=$(undef))'), 'foo', '$(a=$(undef))'; + +$m->putValue('b', 'baz'); +is $m->expandString('$(b)'), 'baz', '$(b)'; +is $m->expandString('$(a)'), 'foo', '$(a)'; +is $m->expandString('$(a)$(b)'), 'foobaz', '$(a)$(b)'; +is $m->expandString('$(a)/$(b)'), 'foo/baz', '$(a)/$(b)'; +is $m->expandString('$(a)\$(b)'), 'foo\$(b)', '$(a)\$(b)'; +is $m->expandString('$(a)$$(b)'), 'foo$baz', '$(a)$$(b)'; + +$m->putValue('c', '$(a)'); +is $m->expandString('$(c)'), 'foo', '$(c)'; +is $m->expandString('$(undef=$(c))'), 'foo', '$(undef=$(c))'; + +$m->putValue('d', 'c'); +is $m->expandString('$(d)'), 'c', '$(d)'; +is $m->expandString('$($(d))'), 'foo', '$($(d))'; +is $m->expandString('$($(b)=$(a))'), 'foo', '$($(b)=$(a))'; + +$m->suppressWarning(1); +$m->putValue('c', undef); +is $m->expandString('$(c)'), '$(c)', '$(c) deleted'; + +$m->installMacros('c=fum,d'); +is $m->expandString('$(c)'), 'fum', 'installMacros, $(c)'; + +is $m->expandString('$(d)'), '$(d)', 'installMacros deletion'; + +$m->pushScope; +is $m->expandString('$(a)'), 'foo', 'pushScope, $(a)'; +$m->putValue('a', 'grinch'); +is $m->expandString('$(a)'), 'grinch', 'new $(a) in child'; + +$m->putValue('b', undef); +is $m->expandString('$(b)'), '$(b)', '$(b) deleted in child'; + +$m->popScope; +is $m->expandString('$(a)'), 'foo', 'popScope, $(a) restored'; +is $m->expandString('$(b)'), 'baz', '$(b) restored'; + diff --git a/modules/libcom/test/macLibTest.c b/modules/libcom/test/macLibTest.c new file mode 100644 index 000000000..7f1e2c544 --- /dev/null +++ b/modules/libcom/test/macLibTest.c @@ -0,0 +1,221 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include + +#include "macLib.h" +#include "dbDefs.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +MAC_HANDLE *h; + +static void check(const char *str, const char *expect) +{ + char output[MAC_SIZE] = {'\0'}; + long status = macExpandString(h, str, output, MAC_SIZE); + long expect_len = strlen(expect+1); + int expect_error = (expect[0] == '!'); + int statBad = expect_error ^ (status < 0); + int strBad = strcmp(output, expect+1); + + testOk(!statBad && !strBad, "%s => %s", str, output); + + if (strBad) { + testDiag("Got \"%s\", expected \"%s\"", output, expect+1); + } + if (statBad) { + testDiag("Return status was %ld, expected %ld", + status, expect_error ? -expect_len : expect_len); + } +} + +static void ovcheck(void) +{ + char output[54]; + long status; + + macPutValue(h, "OVVAR","abcdefghijklmnopqrstuvwxyz"); + + memset(output, '~', sizeof output); + status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 52); + testOk(status == 51, "expansion returned %ld, expected 51", status); + testOk(output[50] == 'y', "final character %x, expect 79 (y)", output[50]); + testOk(output[51] == '\0', "terminator character %x, expect 0", output[51]); + testOk(output[52] == '~', "sentinel character %x, expect 7e, (~)", output[52]); + + memset(output, '~', sizeof output); + status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 53); + testOk(status == 52, "expansion returned %ld, expected 52", status); + testOk(output[51] == 'z', "final character %x, expect 7a (z)", output[51]); + testOk(output[52] == '\0', "terminator character %x, expect 0", output[52]); + testOk(output[53] == '~', "sentinel character %x, expect 7e, (~)", output[53]); +} + +MAIN(macLibTest) +{ + testPlan(91); + + if (macCreateHandle(&h, NULL)) + testAbort("macCreateHandle() failed"); + macSuppressWarning(h, TRUE); + + check("FOO", " FOO"); + + check("$(FOO)", "!$(FOO,undefined)"); + check("${FOO}", "!$(FOO,undefined)"); + check("${FOO=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,FOO}", "!$(FOO,undefined)"); + check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); + check("${FOO,BAR}", "!$(FOO,undefined)"); + check("${FOO,BAR=baz}", "!$(FOO,undefined)"); + check("${FOO,BAR=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,BAR=baz,FUM}", "!$(FOO,undefined)"); + check("${FOO=${BAR},BAR=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,FOO=${BAR},BAR=${FOO}}", "!$(BAR,recursive)"); + + check("${=}", " "); + check("x${=}y", " xy"); + + check("${,=}", " "); + check("x${,=}y", " xy"); + + check("${FOO=}", " "); + check("x${FOO=}y", " xy"); + + check("${FOO=,}", " "); + check("x${FOO=,}y", " xy"); + + check("${FOO,FOO=}", " "); + check("x${FOO,FOO=}y", " xy"); + + check("${FOO=,BAR}", " "); + check("x${FOO=,BAR}y", " xy"); + + check("${FOO=${BAR=}}", " "); + check("x${FOO=${BAR=}}y", " xy"); + + check("${FOO=,BAR=baz}", " "); + check("x${FOO=,BAR=baz}y", " xy"); + + check("${FOO=${BAR},BAR=}", " "); + check("x${FOO=${BAR},BAR=}y", " xy"); + + check("${=BAR}", " BAR"); + check("x${=BAR}y", " xBARy"); + + check("${FOO=BAR}", " BAR"); + check("x${FOO=BAR}y", " xBARy"); + + macPutValue(h, "FOO", "BLETCH"); + /* FOO = "BLETCH" */ + check("${FOO}", " BLETCH"); + check("${FOO,FOO}", " BLETCH"); + check("x${FOO}y", " xBLETCHy"); + check("x${FOO}y${FOO}z", " xBLETCHyBLETCHz"); + check("${FOO=BAR}", " BLETCH"); + check("x${FOO=BAR}y", " xBLETCHy"); + check("${FOO=${BAZ}}", " BLETCH"); + check("${FOO=${BAZ},BAR=${BAZ}}", " BLETCH"); + check("x${FOO=${BAZ}}y", " xBLETCHy"); + check("x${FOO=${BAZ},BAR=${BAZ}}y", " xBLETCHy"); + check("${BAR=${FOO}}", " BLETCH"); + check("x${BAR=${FOO}}y", " xBLETCHy"); + check("w${BAR=x${FOO}y}z", " wxBLETCHyz"); + + check("${FOO,FOO=BAR}", " BAR"); + check("x${FOO,FOO=BAR}y", " xBARy"); + check("${BAR,BAR=${FOO}}", " BLETCH"); + check("x${BAR,BAR=${FOO}}y", " xBLETCHy"); + check("${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); + check("x${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); + check("${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); + check("x${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); + check("${N=${FOO}/${BAR},BAR=GLEEP}", " BLETCH/GLEEP"); + + macPutValue(h, "BAR","GLEEP"); + /* FOO = "BLETCH" */ + /* BAR = "GLEEP" */ + check("${FOO}/${BAR}", " BLETCH/GLEEP"); + check("x${FOO}/${BAR}y", " xBLETCH/GLEEPy"); + check("${FOO,BAR}/${BAR}", " BLETCH/GLEEP"); + check("${FOO,BAR=x}/${BAR}", " BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR}/${BAR}", " BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR=x}/${BAR}", " BLETCH/GLEEP"); + check("${N=${FOO}/${BAR}}", " BLETCH/GLEEP"); + + macPutValue(h, "BLETCH","BAR"); + /* FOO = "BLETCH" */ + /* BLETCH = "BAR" */ + check("${${FOO}}", " BAR"); + check("x${${FOO}}y", " xBARy"); + check("${${FOO}=GRIBBLE}", " BAR"); + check("x${${FOO}=GRIBBLE}y", " xBARy"); + + macPutValue(h, "BLETCH","${BAR}"); + /* FOO = "BLETCH" */ + /* BLETCH = "${BAR}" */ + /* BAR = "GLEEP" */ + check("${${FOO}}", " GLEEP"); + check("${BLETCH=${FOO}}", " GLEEP"); + + macPutValue(h, "FOO","${BAR}"); + /* FOO = "${BAR}" */ + /* BAR = "GLEEP" */ + check("${FOO}", " GLEEP"); + check("${FOO=BLETCH,BAR=BAZ}", " BAZ"); + + macPutValue(h, "BAR","${BAZ}"); + /* FOO = "${BAR}" */ + /* BAR = "${BAZ}" */ + check("${FOO}", "!$(BAZ,undefined)"); + + macPutValue(h, "BAR","${BAZ=GRIBBLE}"); + /* FOO = "${BAR}" */ + /* BAR = "${BAZ=GRIBBLE}" */ + check("${FOO}", " GRIBBLE"); + check("${FOO,BAZ=GEEK}", " GEEK"); + + macPutValue(h, "BAR","${STR1}"); + macPutValue(h, "STR1","VAL1"); + macPutValue(h, "STR2","VAL2"); + /* FOO = "${BAR}" */ + /* BAR = "${STR1}" */ + /* STR1 = "VAL1" */ + /* STR2 = "VAL2" */ + check("${FOO}", " VAL1"); + + macPutValue(h, "BAR","${STR2}"); + /* FOO = "${BAR}" */ + /* BAR = "${STR2}" */ + /* STR1 = "VAL1" */ + /* STR2 = "VAL2" */ + check("${FOO}", " VAL2"); + + check("$(FOO)$(FOO1)", "!VAL2$(FOO1,undefined)"); + check("$(FOO1)$(FOO)", "!$(FOO1,undefined)VAL2"); + + macPutValue(h, "BAR","${FOO}"); + /* FOO = "${BAR}" */ + /* BAR = "${FOO}" */ + check("${FOO}", "!$(BAR,recursive)"); + check("${FOO=GRIBBLE}", "!$(BAR,recursive)"); + check("${FOO=GRIBBLE,BAR=${FOO}}", "!$(BAR,recursive)"); + check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); + check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)"); + + ovcheck(); + + return testDone(); +} diff --git a/modules/libcom/test/osiSockTest.c b/modules/libcom/test/osiSockTest.c new file mode 100644 index 000000000..39eb0ba2b --- /dev/null +++ b/modules/libcom/test/osiSockTest.c @@ -0,0 +1,74 @@ +/*************************************************************************\ +* Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "osiSock.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* This could easily be generalized to test more options */ +void udpBroadcast(SOCKET s, int put) +{ + int status; + int flag = put; + osiSocklen_t len = sizeof(flag); + + status = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, len); + testOk(status >= 0, "setsockopt BROADCAST := %d", put); + + status = getsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&flag, &len); + testOk(status >= 0 && len == sizeof(flag) && !flag == !put, + "getsockopt BROADCAST => %d", flag); +} + +void multiCastLoop(SOCKET s, int put) +{ + int status; + osiSockOptMcastLoop_t flag = put; + osiSocklen_t len = sizeof(flag); + + status = setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, + (char *)&flag, len); + testOk(status >= 0, "setsockopt MULTICAST_LOOP := %d", put); + + status = getsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&flag, &len); + testOk(status >= 0 && len == sizeof(flag) && !flag == !put, + "getsockopt MULTICAST_LOOP => %d", (int) flag); +} + +void udpSockTest(void) +{ + SOCKET s; + + s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0"); + + udpBroadcast(s, 1); + udpBroadcast(s, 0); + + multiCastLoop(s, 1); + multiCastLoop(s, 0); + + epicsSocketDestroy(s); +} + + +MAIN(osiSockTest) +{ + int status; + testPlan(10); + + status = osiSockAttach(); + testOk(status, "osiSockAttach"); + + udpSockTest(); + + osiSockRelease(); + return testDone(); +} diff --git a/modules/libcom/test/ringBytesTest.c b/modules/libcom/test/ringBytesTest.c new file mode 100644 index 000000000..6cef93334 --- /dev/null +++ b/modules/libcom/test/ringBytesTest.c @@ -0,0 +1,123 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ringBytesTest.c */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsRingBytes.h" +#include "errlog.h" +#include "epicsEvent.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define RINGSIZE 10 + +typedef struct info { + epicsEventId consumerEvent; + epicsRingBytesId ring; +}info; + +static void check(epicsRingBytesId ring, int expectedFree) +{ + int expectedUsed = RINGSIZE - expectedFree; + int expectedEmpty = (expectedUsed == 0); + int expectedFull = (expectedFree == 0); + int nFree = epicsRingBytesFreeBytes(ring); + int nUsed = epicsRingBytesUsedBytes(ring); + int isEmpty = epicsRingBytesIsEmpty(ring); + int isFull = epicsRingBytesIsFull(ring); + + testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); + testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); + testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); + testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); +} + +MAIN(ringBytesTest) +{ + int i, n; + info *pinfo; + epicsEventId consumerEvent; + char put[RINGSIZE+1]; + char get[RINGSIZE+1]; + epicsRingBytesId ring; + + testPlan(245); + + pinfo = calloc(1,sizeof(info)); + if (!pinfo) { + testAbort("calloc failed"); + } + pinfo->consumerEvent = consumerEvent = epicsEventCreate(epicsEventEmpty); + if (!consumerEvent) { + testAbort("epicsEventCreate failed"); + } + + pinfo->ring = ring = epicsRingBytesCreate(RINGSIZE); + if (!ring) { + testAbort("epicsRingBytesCreate failed"); + } + check(ring, RINGSIZE); + + for (i = 0 ; i < sizeof(put) ; i++) + put[i] = i; + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesPut(ring, put, i); + testOk(n==i, "ring put %d", i); + check(ring, RINGSIZE-i); + n = epicsRingBytesGet(ring, get, i); + testOk(n==i, "ring get %d", i); + check(ring, RINGSIZE); + testOk(memcmp(put,get,i)==0, "get matches write"); + } + + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesPut(ring, put+i, 1); + testOk(n==1, "ring put 1, %d", i); + check(ring, RINGSIZE-1-i); + } + n = epicsRingBytesPut(ring, put+RINGSIZE, 1); + testOk(n==0, "put to full ring"); + check(ring, 0); + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesGet(ring, get+i, 1); + testOk(n==1, "ring get 1, %d", i); + check(ring, 1+i); + } + testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); + n = epicsRingBytesGet(ring, get+RINGSIZE, 1); + testOk(n==0, "get from empty ring"); + check(ring, RINGSIZE); + + n = epicsRingBytesPut(ring, put, RINGSIZE+1); + testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); + check(ring, RINGSIZE); + n = epicsRingBytesPut(ring, put, 1); + testOk(n==1, "ring put %d", 1); + check(ring, RINGSIZE-1); + n = epicsRingBytesPut(ring, put, RINGSIZE); + testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); + check(ring, RINGSIZE-1); + n = epicsRingBytesGet(ring, get, 1); + testOk(n==1, "ring get %d", 1); + check(ring, RINGSIZE); + + epicsRingBytesDelete(ring); + epicsEventDestroy(consumerEvent); + free(pinfo); + + return testDone(); +} diff --git a/modules/libcom/test/ringPointerTest.c b/modules/libcom/test/ringPointerTest.c new file mode 100644 index 000000000..65a349489 --- /dev/null +++ b/modules/libcom/test/ringPointerTest.c @@ -0,0 +1,248 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ringPointerTest.c */ + +/* Author: Marty Kraimer Date: 13OCT2000 */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsRingPointer.h" +#include "errlog.h" +#include "epicsEvent.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static +void *int2ptr(size_t i) +{ + char *zero = 0; + i = i|(i<<16); + return zero+i; +} + +static int foundCorruption; + +static +size_t ptr2int(void *p) +{ + char *zero = 0, *p2 = p; + size_t i = p2-zero; + if((i&0xffff)!=((i>>16)&0xffff)) { + testDiag("Pointer value corruption %p", p); + foundCorruption = 1; + } + return i&0xffff; +} + +static void testSingle(void) +{ + int i; + const int rsize = 100; + void *addr = 0; + epicsRingPointerId ring = epicsRingPointerCreate(rsize); + + foundCorruption = 0; + + testDiag("Testing operations w/o threading"); + + testOk1(epicsRingPointerIsEmpty(ring)); + testOk1(!epicsRingPointerIsFull(ring)); + testOk1(epicsRingPointerGetFree(ring)==rsize); + testOk1(epicsRingPointerGetSize(ring)==rsize); + testOk1(epicsRingPointerGetUsed(ring)==0); + + testOk1(epicsRingPointerPop(ring)==NULL); + + addr = int2ptr(1); + testOk1(epicsRingPointerPush(ring, addr)==1); + + testOk1(!epicsRingPointerIsEmpty(ring)); + testOk1(!epicsRingPointerIsFull(ring)); + testOk1(epicsRingPointerGetFree(ring)==rsize-1); + testOk1(epicsRingPointerGetSize(ring)==rsize); + testOk1(epicsRingPointerGetUsed(ring)==1); + + testDiag("Fill it up"); + for(i=2; i<2*rsize; i++) { + int ret; + addr = int2ptr(i); + ret = epicsRingPointerPush(ring, addr); + if(!ret) + break; + } + + /* Note: +1 because we started with 1 */ + testOk(i==rsize+1, "%d == %d", i, rsize+1); + testOk1(!epicsRingPointerIsEmpty(ring)); + testOk1(epicsRingPointerIsFull(ring)); + testOk1(epicsRingPointerGetFree(ring)==0); + testOk1(epicsRingPointerGetSize(ring)==rsize); + testOk1(epicsRingPointerGetUsed(ring)==rsize); + + testDiag("Drain it out"); + for(i=1; i<2*rsize; i++) { + addr = epicsRingPointerPop(ring); + if(addr==NULL || ptr2int(addr)!=i) + break; + } + + testOk1(!foundCorruption); + + testOk(i==rsize+1, "%d == %d", i, rsize+1); + testOk1(epicsRingPointerIsEmpty(ring)); + testOk1(!epicsRingPointerIsFull(ring)); + testOk1(epicsRingPointerGetFree(ring)==rsize); + testOk1(epicsRingPointerGetSize(ring)==rsize); + testOk1(epicsRingPointerGetUsed(ring)==0); + + testDiag("Fill it up again"); + for(i=2; i<2*rsize; i++) { + int ret; + addr = int2ptr(i); + ret = epicsRingPointerPush(ring, addr); + if(!ret) + break; + } + + testDiag("flush"); + testOk1(epicsRingPointerIsFull(ring)); + epicsRingPointerFlush(ring); + + testOk1(epicsRingPointerIsEmpty(ring)); + testOk1(!epicsRingPointerIsFull(ring)); + testOk1(epicsRingPointerGetFree(ring)==rsize); + testOk1(epicsRingPointerGetSize(ring)==rsize); + testOk1(epicsRingPointerGetUsed(ring)==0); + + epicsRingPointerDelete(ring); +} + +typedef struct { + epicsRingPointerId ring; + epicsEventId sync, wait; + int stop; + void *lastaddr; +} pairPvt; + +static void pairConsumer(void *raw) +{ + pairPvt *pvt = raw; + void *prev = pvt->lastaddr; + + epicsEventMustTrigger(pvt->sync); + + while(1) { + size_t c,p; + + epicsEventMustWait(pvt->wait); + if(pvt->stop) + break; + + pvt->lastaddr = epicsRingPointerPop(pvt->ring); + c = ptr2int(pvt->lastaddr); + p = ptr2int(prev); + if(p+1!=c) { + testFail("consumer skip %p %p", prev, pvt->lastaddr); + break; + } + prev = pvt->lastaddr; + } + + pvt->stop = 1; + epicsEventMustTrigger(pvt->sync); +} + +static void testPair(int locked) +{ + unsigned int myprio = epicsThreadGetPrioritySelf(), consumerprio; + pairPvt pvt; + const int rsize = 100; + int i, expect; + epicsRingPointerId ring; + if(locked) + ring = epicsRingPointerLockedCreate(rsize); + else + ring = epicsRingPointerCreate(rsize); + + pvt.ring = ring; + pvt.sync = epicsEventCreate(epicsEventEmpty); + pvt.wait = epicsEventCreate(epicsEventEmpty); + pvt.stop = 0; + pvt.lastaddr = 0; + + foundCorruption = 0; + + testDiag("single producer, single consumer with%s locking", locked?"":"out"); + + /* give the consumer thread a slightly higher priority so that + * it can preempt us on RTOS targets. On non-RTOS targets + * we expect to be preempted at some random time + */ + if(!epicsThreadLowestPriorityLevelAbove(myprio, &consumerprio)) + testAbort("Can't run test from thread with highest priority"); + + epicsThreadMustCreate("pair", consumerprio, + epicsThreadGetStackSize(epicsThreadStackSmall), + &pairConsumer, &pvt); + /* wait for worker to start */ + epicsEventMustWait(pvt.sync); + + i=1; + while(i +#include + +#include "taskwd.h" +#include "errlog.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* This is a unique prefix used for the names of the test threads */ +#define baseName "testTask" + +void monInsert(void *usr, epicsThreadId tid) +{ + char tname[32]; + + epicsThreadGetName(tid, tname, sizeof(tname)); + if (strncmp(tname, baseName, strlen(baseName)) == 0) + testPass("monInsert(thread='%s')", tname); + else + testDiag("monInsert(thread='%s')", tname); +} + +void monNotify(void *usr, epicsThreadId tid, int suspended) +{ + char tname[32]; + + epicsThreadGetName(tid, tname, sizeof(tname)); + testPass("monNotify(thread='%s', suspended=%d)", tname, suspended); + epicsThreadResume(tid); +} + +void monRemove(void *usr, epicsThreadId tid) +{ + char tname[32]; + + epicsThreadGetName(tid, tname, sizeof(tname)); + if (strncmp(tname, baseName, strlen(baseName)) == 0) + testPass("monRemove(thread='%s')", tname); + else + testDiag("monRemove(thread='%s')", tname); +} + +taskwdMonitor monFuncs = {monInsert, monNotify, monRemove}; + +void anyNotify(void *usr, epicsThreadId tid) +{ + char tname[32]; + + epicsThreadGetName(tid, tname, sizeof(tname)); + if (strncmp(tname, baseName, strlen(baseName)) == 0) + testPass("anyNotify(thread='%s')", tname); + else + testDiag("anyNotify(thread='%s')", tname); +} + +void taskNotify(void *usr) +{ + const char *id = (const char *) usr; + + testPass("taskNotify id='%s'", id); +} + +void testTask1(void *arg) +{ + taskwdInsert(0, taskNotify, "1"); + epicsThreadSleep(14.0); + testDiag("Task 1 cleaning up"); + taskwdRemove(0); +} + +void testTask2(void *arg) +{ + taskwdInsert(0, taskNotify, "2"); + epicsThreadSleep(1.0); + testDiag("Task 2 suspending"); + epicsThreadSuspendSelf(); + epicsThreadSleep(1.0); + testDiag("Task 2 alive again"); + epicsThreadSleep(6.0); + testDiag("Task 2 cleaning up"); + taskwdRemove(0); +} + +MAIN(taskwdTest) +{ + eltc(0); + testPlan(8); + + taskwdInit(); + taskwdMonitorAdd(&monFuncs, NULL); + taskwdAnyInsert(NULL, anyNotify, NULL); + + epicsThreadCreate(baseName "1", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask1, NULL); + epicsThreadSleep(1.0); + + epicsThreadCreate(baseName "2", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask2, NULL); + + /* taskwd checks tasks every 6 seconds */ + epicsThreadSleep(18.0); + + taskwdMonitorDel(&monFuncs, NULL); + taskwdAnyRemove(NULL); + + eltc(1); + return testDone(); +} diff --git a/modules/libcom/test/yajlTest.plt b/modules/libcom/test/yajlTest.plt new file mode 100644 index 000000000..5c9052032 --- /dev/null +++ b/modules/libcom/test/yajlTest.plt @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# +# This is a test director for running yajl JSON parser tests. +# The tests are actually defined in the yajlTestCases.pm module, +# which is generated from the yajl cases by yajlTestConverter.pl + +use strict; +use Test::More; +use IO::Handle; +use IPC::Open3; + +# Load test cases +use lib ".."; +use yajlTestCases; + +my @cases = cases(); +plan tests => scalar @cases; + +# The yajl_test program reads JSON from stdin and sends a description +# of what it got to stdout, with errors going to stderr. We merge the +# two output streams for the purpose of checking the test results. +my $prog = './yajl_test'; +$prog .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin'); + +foreach my $case (@cases) { + my $name = $case->{name}; + my @opts = @{$case->{opts}}; + my @input = @{$case->{input}}; + my @gives = @{$case->{gives}}; + + my ($rx, $tx); + my $pid = open3($tx, $rx, 0, $prog, @opts); + + # Send the test case, then EOF + print $tx join "\n", @input; + close $tx; + + # Receive the result + my @result; + while (!$rx->eof) { + chomp(my $line = <$rx>); + push @result, $line; + } + close $rx; + + # Clean up the child process + waitpid $pid, 0; + + # Report the result of this test case + is_deeply(\@result, \@gives, $name); +} diff --git a/modules/libcom/test/yajlTestCases.pm b/modules/libcom/test/yajlTestCases.pm new file mode 100644 index 000000000..40eaff6e7 --- /dev/null +++ b/modules/libcom/test/yajlTestCases.pm @@ -0,0 +1,3069 @@ +# Parser test cases from https://github.com/lloyd/yajl +# +# This file is generated, DO NOT EDIT! +# +# See comments in yajlTestConverter.pl for instructions on +# how to regenerate this file from the original yajl sources. + +sub cases { + my $VAR1 = [ + { + name => "difficult_json_c_test_case_with_comments", + opts => [ + "-c" + ], + input => [ + "{ \"glossary\": { /* you */ \"title\": /**/ \"example glossary\", /*should*/\"GlossDiv\": { \"title\": /*never*/\"S\", /*ever*/\"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", /*see*/\"GlossSeeAlso\"/*this*/:/*coming*/[/*out*/\"GML\"/*of*/,/*the*/\"XML\"/*parser!*/, \"markup\"] /*hey*/}/*ho*/]/*hey*/}/*ho*/} } // and the parser won't even get this far, so chill. /* hah!", + "" + ], + gives => [ + "map open '{'", + "key: 'glossary'", + "map open '{'", + "key: 'title'", + "string: 'example glossary'", + "key: 'GlossDiv'", + "map open '{'", + "key: 'title'", + "string: 'S'", + "key: 'GlossList'", + "array open '['", + "map open '{'", + "key: 'ID'", + "string: 'SGML'", + "key: 'SortAs'", + "string: 'SGML'", + "key: 'GlossTerm'", + "string: 'Standard Generalized Markup Language'", + "key: 'Acronym'", + "string: 'SGML'", + "key: 'Abbrev'", + "string: 'ISO 8879:1986'", + "key: 'GlossDef'", + "string: 'A meta-markup language, used to create markup languages such as DocBook.'", + "key: 'GlossSeeAlso'", + "array open '['", + "string: 'GML'", + "string: 'XML'", + "string: 'markup'", + "array close ']'", + "map close '}'", + "array close ']'", + "map close '}'", + "map close '}'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "simple_with_comments", + opts => [ + "-c" + ], + input => [ + "{", + " \"this\": \"is\", // ignore this", + " \"really\": \"simple\",", + " /* ignore", + "this", + "too * / ", + "** //", + "(/", + "******/", + " \"json\": \"right?\"", + "}", + "" + ], + gives => [ + "map open '{'", + "key: 'this'", + "string: 'is'", + "key: 'really'", + "string: 'simple'", + "key: 'json'", + "string: 'right?'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "false_then_garbage", + opts => [ + "-g" + ], + input => [ + "falsex" + ], + gives => [ + "bool: false", + "memory leaks:\t0" + ] + }, + { + name => "null_then_garbage", + opts => [ + "-g" + ], + input => [ + "nullx", + "" + ], + gives => [ + "null", + "memory leaks:\t0" + ] + }, + { + name => "true_then_garbage", + opts => [ + "-g" + ], + input => [ + "truex" + ], + gives => [ + "bool: true", + "memory leaks:\t0" + ] + }, + { + name => "eof", + opts => [ + "-m" + ], + input => [ + "{ \"123\":", + "" + ], + gives => [ + "map open '{'", + "key: '123'", + "parse error: premature EOF", + "memory leaks:\t0" + ] + }, + { + name => "integers", + opts => [ + "-m" + ], + input => [ + "1221 21", + "" + ], + gives => [ + "integer: 1221", + "integer: 21", + "memory leaks:\t0" + ] + }, + { + name => "multiple", + opts => [ + "-m" + ], + input => [ + "", + "{}", + "{}", + "" + ], + gives => [ + "map open '{'", + "map close '}'", + "map open '{'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "stuff", + opts => [ + "-m" + ], + input => [ + "{}", + "[]", + "[]", + "\"sdfasd\"", + 123, + "{ \"123\" : 123 }", + "3.1e124", + "" + ], + gives => [ + "map open '{'", + "map close '}'", + "array open '['", + "array close ']'", + "array open '['", + "array close ']'", + "string: 'sdfasd'", + "integer: 123", + "map open '{'", + "key: '123'", + "integer: 123", + "map close '}'", + "double: 3.1e+124", + "memory leaks:\t0" + ] + }, + { + name => "array_open", + opts => [ + "-p" + ], + input => [ + "[", + "" + ], + gives => [ + "array open '['", + "memory leaks:\t0" + ] + }, + { + name => "eof_str", + opts => [ + "-p" + ], + input => [ + "\"abc" + ], + gives => [ + "memory leaks:\t0" + ] + }, + { + name => "map_open", + opts => [ + "-p" + ], + input => [ + "{", + "" + ], + gives => [ + "map open '{'", + "memory leaks:\t0" + ] + }, + { + name => "partial_ok", + opts => [ + "-p" + ], + input => [ + "[ \"foo\", \"bar\"", + "" + ], + gives => [ + "array open '['", + "string: 'foo'", + "string: 'bar'", + "memory leaks:\t0" + ] + }, + { + name => "array", + opts => [], + input => [ + "[\"foo\",", + " \"bar\", \"baz\",", + " true,false,null,{\"key\":\"value\"},", + " [null,null,null,[]],", + " \"\\n\\r\\\\\"", + "]", + "" + ], + gives => [ + "array open '['", + "string: 'foo'", + "string: 'bar'", + "string: 'baz'", + "bool: true", + "bool: false", + "null", + "map open '{'", + "key: 'key'", + "string: 'value'", + "map close '}'", + "array open '['", + "null", + "null", + "null", + "array open '['", + "array close ']'", + "array close ']'", + "string: '", + "\r\\'", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "array_close", + opts => [], + input => [ + "]", + "" + ], + gives => [ + "parse error: unallowed token at this point in JSON text", + "memory leaks:\t0" + ] + }, + { + name => "bignums", + opts => [], + input => [ + "[ 9223372036854775807, -9223372036854775807 ]", + "" + ], + gives => [ + "array open '['", + "integer: 9223372036854775807", + "integer: -9223372036854775807", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "bogus_char", + opts => [], + input => [ + "[\"this\",\"is\",\"what\",\"should\",\"be\",", + " \"a happy bit of json\",", + " \"but someone, misspelled \\\"true\\\"\", ture,", + " \"who says JSON is easy for humans to generate?\"]", + "" + ], + gives => [ + "array open '['", + "string: 'this'", + "string: 'is'", + "string: 'what'", + "string: 'should'", + "string: 'be'", + "string: 'a happy bit of json'", + "string: 'but someone, misspelled \"true\"'", + "lexical error: invalid string in json text.", + "memory leaks:\t0" + ] + }, + { + name => "codepoints_from_unicode_org", + opts => [], + input => [ + "\"\\u004d\\u0430\\u4e8c\\ud800\\udf02\"", + "" + ], + gives => [ + "string: 'M\320\260\344\272\214\360\220\214\202'", + "memory leaks:\t0" + ] + }, + { + name => "deep_arrays", + opts => [], + input => [ + "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + ], + gives => [ + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array open '['", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "difficult_json_c_test_case", + opts => [], + input => [ + "{ \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }", + "" + ], + gives => [ + "map open '{'", + "key: 'glossary'", + "map open '{'", + "key: 'title'", + "string: 'example glossary'", + "key: 'GlossDiv'", + "map open '{'", + "key: 'title'", + "string: 'S'", + "key: 'GlossList'", + "array open '['", + "map open '{'", + "key: 'ID'", + "string: 'SGML'", + "key: 'SortAs'", + "string: 'SGML'", + "key: 'GlossTerm'", + "string: 'Standard Generalized Markup Language'", + "key: 'Acronym'", + "string: 'SGML'", + "key: 'Abbrev'", + "string: 'ISO 8879:1986'", + "key: 'GlossDef'", + "string: 'A meta-markup language, used to create markup languages such as DocBook.'", + "key: 'GlossSeeAlso'", + "array open '['", + "string: 'GML'", + "string: 'XML'", + "string: 'markup'", + "array close ']'", + "map close '}'", + "array close ']'", + "map close '}'", + "map close '}'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "doubles", + opts => [], + input => [ + "[ 0.1e2, 1e1, 3.141569, 10000000000000e-10]", + "" + ], + gives => [ + "array open '['", + "double: 10", + "double: 10", + "double: 3.14157", + "double: 1000", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "doubles_in_array", + opts => [], + input => [ + "[0.00011999999999999999, 6E-06, 6E-06, 1E-06, 1E-06]", + "" + ], + gives => [ + "array open '['", + "double: 0.00012", + "double: 6e-06", + "double: 6e-06", + "double: 1e-06", + "double: 1e-06", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "empty_array", + opts => [], + input => [ + "[]" + ], + gives => [ + "array open '['", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "empty_string", + opts => [], + input => [ + "\"\"", + "" + ], + gives => [ + "string: ''", + "memory leaks:\t0" + ] + }, + { + name => "escaped_bulgarian", + opts => [], + input => [ + "[\"\\u0414\\u0430\",", + " \"\\u041c\\u0443\",", + " \"\\u0415\\u0431\\u0430\",", + " \"\\u041c\\u0430\\u0439\\u043a\\u0430\\u0442\\u0430\"]", + "" + ], + gives => [ + "array open '['", + "string: '\320\224\320\260'", + "string: '\320\234\321\203'", + "string: '\320\225\320\261\320\260'", + "string: '\320\234\320\260\320\271\320\272\320\260\321\202\320\260'", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "escaped_foobar", + opts => [], + input => [ + "\"\\u0066\\u006f\\u006f\\u0062\\u0061\\u0072\"", + "" + ], + gives => [ + "string: 'foobar'", + "memory leaks:\t0" + ] + }, + { + name => "false", + opts => [], + input => [ + "false", + "" + ], + gives => [ + "bool: false", + "memory leaks:\t0" + ] + }, + { + name => "false_then_garbage", + opts => [], + input => [ + "falsex" + ], + gives => [ + "bool: false", + "parse error: trailing garbage", + "memory leaks:\t0" + ] + }, + { + name => "issue_7", + opts => [], + input => [ + "2009-10-20\@20:38:21.539575", + "" + ], + gives => [ + "integer: 2009", + "parse error: trailing garbage", + "memory leaks:\t0" + ] + }, + { + name => "null_then_garbage", + opts => [], + input => [ + "nullx", + "" + ], + gives => [ + "null", + "parse error: trailing garbage", + "memory leaks:\t0" + ] + }, + { + name => "true_then_garbage", + opts => [], + input => [ + "truex", + "" + ], + gives => [ + "bool: true", + "parse error: trailing garbage", + "memory leaks:\t0" + ] + }, + { + name => "four_byte_utf8", + opts => [], + input => [ + "{ \"U+10ABCD\": \"\364\212\257\215\" }", + "" + ], + gives => [ + "map open '{'", + "key: 'U+10ABCD'", + "string: '\364\212\257\215'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "high_overflow", + opts => [], + input => [ + "9223372036854775808" + ], + gives => [ + "parse error: integer overflow", + "memory leaks:\t0" + ] + }, + { + name => "integers", + opts => [], + input => [ + "[ 1,2,3,4,5,6,7,", + " 123456789 , -123456789,", + " 2147483647, -2147483647 ]", + "" + ], + gives => [ + "array open '['", + "integer: 1", + "integer: 2", + "integer: 3", + "integer: 4", + "integer: 5", + "integer: 6", + "integer: 7", + "integer: 123456789", + "integer: -123456789", + "integer: 2147483647", + "integer: -2147483647", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "invalid_utf8", + opts => [], + input => [ + "[\"\320\224\320\260 \320\234\321 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260\"]", + "" + ], + gives => [ + "array open '['", + "lexical error: invalid bytes in UTF8 string.", + "memory leaks:\t0" + ] + }, + { + name => "isolated_surrogate_marker", + opts => [], + input => [ + "\"\\ud800\"", + "" + ], + gives => [ + "string: '?'", + "memory leaks:\t0" + ] + }, + { + name => "leading_zero_in_number", + opts => [], + input => [ + "{ \"bad thing\": 01 }", + "" + ], + gives => [ + "map open '{'", + "key: 'bad thing'", + "integer: 0", + "parse error: after key and value, inside map, I expect ',' or '}'", + "memory leaks:\t0" + ] + }, + { + name => "lonely_minus_sign", + opts => [], + input => [ + "[", + "\t\t\"foo\", true,", + "\t\ttrue, \"blue\",", + "\t\t\"baby where are you?\", \"oh boo hoo!\",", + " - ", + "]", + "" + ], + gives => [ + "array open '['", + "string: 'foo'", + "bool: true", + "bool: true", + "string: 'blue'", + "string: 'baby where are you?'", + "string: 'oh boo hoo!'", + "lexical error: malformed number, a digit is required after the minus sign.", + "memory leaks:\t0" + ] + }, + { + name => "lonely_number", + opts => [], + input => [ + 123456789 + ], + gives => [ + "integer: 123456789", + "memory leaks:\t0" + ] + }, + { + name => "low_overflow", + opts => [], + input => [ + "-9223372036854775808" + ], + gives => [ + "parse error: integer overflow", + "memory leaks:\t0" + ] + }, + { + name => "map_close", + opts => [], + input => [ + "}", + "" + ], + gives => [ + "parse error: unallowed token at this point in JSON text", + "memory leaks:\t0" + ] + }, + { + name => "missing_integer_after_decimal_point", + opts => [], + input => [ + "10.e2", + "" + ], + gives => [ + "lexical error: malformed number, a digit is required after the decimal point.", + "memory leaks:\t0" + ] + }, + { + name => "missing_integer_after_exponent", + opts => [], + input => [ + "10e", + "" + ], + gives => [ + "lexical error: malformed number, a digit is required after the exponent.", + "memory leaks:\t0" + ] + }, + { + name => "multiple", + opts => [], + input => [ + "", + "{}", + "{}", + "" + ], + gives => [ + "map open '{'", + "map close '}'", + "parse error: trailing garbage", + "memory leaks:\t0" + ] + }, + { + name => "non_utf8_char_in_string", + opts => [], + input => [ + "{\"CoreletAPIVersion\":2,\"CoreletType\":\"standalone\",\"documentation\":\"A corelet that provides the capability to upload a folder\222s contents into a user\222s locker.\",\"functions\":[{\"documentation\":\"Displays a dialog box that allows user to select a folder on the local system.\",\"name\":\"ShowBrowseDialog\",\"parameters\":[{\"documentation\":\"The callback function for results.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]},{\"documentation\":\"Uploads all mp3 files in the folder provided.\",\"name\":\"UploadFolder\",\"parameters\":[{\"documentation\":\"The path to upload mp3 files from.\",\"name\":\"path\",\"required\":true,\"type\":\"string\"},{\"documentation\":\"The callback function for progress.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]},{\"documentation\":\"Returns the server name to the current locker service.\",\"name\":\"GetLockerService\",\"parameters\":[]},{\"documentation\":\"Changes the name of the locker service.\",\"name\":\"SetLockerService\",\"parameters\":[{\"documentation\":\"The value of the locker service to set active.\",\"name\":\"LockerService\",\"required\":true,\"type\":\"string\"}]},{\"documentation\":\"Downloads locker files to the suggested folder.\",\"name\":\"DownloadFile\",\"parameters\":[{\"documentation\":\"The origin path of the locker file.\",\"name\":\"path\",\"required\":true,\"type\":\"string\"},{\"documentation\":\"The Window destination path of the locker file.\",\"name\":\"destination\",\"required\":true,\"type\":\"integer\"},{\"documentation\":\"The callback function for progress.\",\"name\":\"callback\",\"required\":true,\"type\":\"callback\"}]}],\"name\":\"LockerUploader\",\"version\":{\"major\":0,\"micro\":1,\"minor\":0},\"versionString\":\"0.0.1\"}" + ], + gives => [ + "map open '{'", + "key: 'CoreletAPIVersion'", + "integer: 2", + "key: 'CoreletType'", + "string: 'standalone'", + "key: 'documentation'", + "lexical error: invalid bytes in UTF8 string.", + "memory leaks:\t0" + ] + }, + { + name => "partial_bad", + opts => [], + input => [ + "[ \"foo\", \"bar\"", + "" + ], + gives => [ + "array open '['", + "string: 'foo'", + "string: 'bar'", + "parse error: premature EOF", + "memory leaks:\t0" + ] + }, + { + name => "null", + opts => [], + input => [ + "null", + "" + ], + gives => [ + "null", + "memory leaks:\t0" + ] + }, + { + name => "nulls_and_bools", + opts => [], + input => [ + "{", + "\t\"boolean, true\": true,", + "\t\"boolean, false\": false,", + "\t\"null\": null", + "}", + "" + ], + gives => [ + "map open '{'", + "key: 'boolean, true'", + "bool: true", + "key: 'boolean, false'", + "bool: false", + "key: 'null'", + "null", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "simple", + opts => [], + input => [ + "{", + " \"this\": \"is\",", + " \"really\": \"simple\",", + " \"json\": \"right?\"", + "}", + "" + ], + gives => [ + "map open '{'", + "key: 'this'", + "string: 'is'", + "key: 'really'", + "string: 'simple'", + "key: 'json'", + "string: 'right?'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "simple_with_comments", + opts => [], + input => [ + "{", + " \"this\": \"is\", // ignore this", + " \"really\": \"simple\",", + " /* ignore", + "this", + "too * / ", + "** //", + "(/", + "******/", + " \"json\": \"right?\"", + "}", + "" + ], + gives => [ + "map open '{'", + "key: 'this'", + "string: 'is'", + "lexical error: probable comment found in input text, comments are not enabled.", + "memory leaks:\t0" + ] + }, + { + name => "string_invalid_escape", + opts => [], + input => [ + "[\"\\n foo \\/ bar \\r\\f\\\\\\uffff\\t\\b\\\"\\\\ and you can't escape thi\\s\"]", + "" + ], + gives => [ + "array open '['", + "lexical error: inside a string, '\\' occurs before a character which it may not.", + "memory leaks:\t0" + ] + }, + { + name => "string_invalid_hex_char", + opts => [], + input => [ + "\"foo foo, blah blah \\u0123 \\u4567 \\u89ab \\uc/ef \\uABCD \\uEFFE bar baz bing\"", + "" + ], + gives => [ + "lexical error: invalid (non-hex) character occurs after '\\u' inside string.", + "memory leaks:\t0" + ] + }, + { + name => "string_with_escapes", + opts => [], + input => [ + "[\"\\n foo \\/ bar \\r\\f\\\\\\uffff\\t\\b\\\"\\\\\",", + " \"\\\"and this string has an escape at the beginning\",", + " \"and this string has no escapes\" ]", + "" + ], + gives => [ + "array open '['", + "string: '", + " foo / bar \r\f\\\357\277\277\t\b\"\\'", + "string: '\"and this string has an escape at the beginning'", + "string: 'and this string has no escapes'", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "string_with_invalid_newline", + opts => [], + input => [ + "\"la di dah. this is a string, and I can do this, \\n, but not this", + "\"", + "" + ], + gives => [ + "lexical error: invalid character inside string.", + "memory leaks:\t0" + ] + }, + { + name => "three_byte_utf8", + opts => [], + input => [ + "{\"matzue\": \"\346\235\276\346\261\237\", \"asakusa\": \"\346\265\205\350\215\211\"}", + "" + ], + gives => [ + "map open '{'", + "key: 'matzue'", + "string: '\346\235\276\346\261\237'", + "key: 'asakusa'", + "string: '\346\265\205\350\215\211'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "trailing_commas", + opts => [], + input => [ + "{\"array\":[1,2,],\"map\":{\"a\":1,},}", + "" + ], + gives => [ + "map open '{'", + "key: 'array'", + "array open '['", + "integer: 1", + "integer: 2", + "array close ']'", + "key: 'map'", + "map open '{'", + "key: 'a'", + "integer: 1", + "map close '}'", + "map close '}'", + "memory leaks:\t0" + ] + }, + { + name => "true", + opts => [], + input => [ + "true", + "" + ], + gives => [ + "bool: true", + "memory leaks:\t0" + ] + }, + { + name => "unescaped_bulgarian", + opts => [], + input => [ + "[\"\320\224\320\260 \320\234\321\203 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260\"]", + "" + ], + gives => [ + "array open '['", + "string: '\320\224\320\260 \320\234\321\203 \320\225\320\261\320\260 \320\234\320\260\320\271\320\272\320\260\321\202\320\260'", + "array close ']'", + "memory leaks:\t0" + ] + }, + { + name => "zerobyte", + opts => [], + input => [ + "\"\\u0000\"", + "" + ], + gives => [ + "string: '\0'", + "memory leaks:\t0" + ] + } + ]; + + return @{$VAR1}; +} + +1; diff --git a/modules/libcom/test/yajlTestConverter.pl b/modules/libcom/test/yajlTestConverter.pl new file mode 100755 index 000000000..c3d2b54b6 --- /dev/null +++ b/modules/libcom/test/yajlTestConverter.pl @@ -0,0 +1,98 @@ +#!/usr/bin/perl +# +# This script converts the parsing test cases from the yajl release tree +# into the yajlTestCases module as used by libCom/test/yajlTest.plt +# +# Re-do this conversion and commit after checking out a new version of yajl +# from https://github.com/lloyd/yajl as follows: +# $ cd /src/libCom/test +# $ perl yajlTestConverter.pl /path/to/yajl +# The tests are saved into the file yajlTestCases.pm in the src/libCom/test +# directory which will be read by the yajlTest.t test script. + +use Data::Dumper; + +my $yajl = shift @ARGV + or die "Usage: $0 /path/to/yajl\n"; + +my @files = glob "$yajl/test/parsing/cases/*.json"; + +my $caseFile = 'yajlTestCases.pm'; + +my @cases; + +for my $file (@files) { + $file =~ m|/([afn][cgmp]_)?([^/]*)\.json$|; + my $allow = $1; + my $name = $2; + next if $name eq ''; + + my $case = { name => $name }; + + if ($allow eq 'ac_') { + $case->{opts} = ['-c']; + } + elsif ($allow eq 'ag_') { + $case->{opts} = ['-g']; + } + elsif ($allow eq 'am_') { + $case->{opts} = ['-m']; + } + elsif ($allow eq 'ap_') { + $case->{opts} = ['-p']; + } + else { + $case->{opts} = []; + } + + my $input = slurp($file); + my @input = split "\n", $input; + push @input, '' if $input =~ m/\n$/; + $case->{input} = \@input; + + my @gives = split "\n", slurp("$file.gold"); + $case->{gives} = \@gives; + + push @cases, $case; +} + +# Configure Dumper() output +$Data::Dumper::Pad = ' '; +$Data::Dumper::Indent = 1; +$Data::Dumper::Useqq = 1; +$Data::Dumper::Quotekeys = 0; +$Data::Dumper::Sortkeys = sub { return ['name', 'opts', 'input', 'gives'] }; + +my $data = Dumper(\@cases); + +open my $out, '>', $caseFile + or die "Can't open/create $caseFile: $@\n"; +print $out <<"EOF"; +# Parser test cases from https://github.com/lloyd/yajl +# +# This file is generated, DO NOT EDIT! +# +# See comments in yajlTestConverter.pl for instructions on +# how to regenerate this file from the original yajl sources. + +sub cases { + my$data + return \@{\$VAR1}; +} + +1; +EOF + +close $out + or die "Problem writing $caseFile: $@\n"; + +exit 0; + + +sub slurp { + my ($file) = @_; + open my $in, '<', $file + or die "Can't open file $file: $!\n"; + my $contents = do { local $/; <$in> }; + return $contents; +} diff --git a/modules/libcom/test/yajl_test.c b/modules/libcom/test/yajl_test.c new file mode 100644 index 000000000..cacfdcb5c --- /dev/null +++ b/modules/libcom/test/yajl_test.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2007-2014, Lloyd Hilaiel + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include + +/* memory debugging routines */ +typedef struct +{ + unsigned int numFrees; + unsigned int numMallocs; + /* XXX: we really need a hash table here with per-allocation + * information */ +} yajlTestMemoryContext; + +/* cast void * into context */ +#define TEST_CTX(vptr) ((yajlTestMemoryContext *) (vptr)) + +static void yajlTestFree(void * ctx, void * ptr) +{ + assert(ptr != NULL); + TEST_CTX(ctx)->numFrees++; + free(ptr); +} + +static void * yajlTestMalloc(void * ctx, size_t sz) +{ + assert(sz != 0); + TEST_CTX(ctx)->numMallocs++; + return malloc(sz); +} + +static void * yajlTestRealloc(void * ctx, void * ptr, size_t sz) +{ + if (ptr == NULL) { + assert(sz != 0); + TEST_CTX(ctx)->numMallocs++; + } else if (sz == 0) { + TEST_CTX(ctx)->numFrees++; + } + + return realloc(ptr, sz); +} + + +/* begin parsing callback routines */ +#define BUF_SIZE 2048 + +static int test_yajl_null(void *ctx) +{ + printf("null\n"); + return 1; +} + +static int test_yajl_boolean(void * ctx, int boolVal) +{ + printf("bool: %s\n", boolVal ? "true" : "false"); + return 1; +} + +static int test_yajl_integer(void *ctx, long long integerVal) +{ + printf("integer: %lld\n", integerVal); + return 1; +} + +static int test_yajl_double(void *ctx, double doubleVal) +{ + printf("double: %g\n", doubleVal); + return 1; +} + +static int test_yajl_string(void *ctx, const unsigned char * stringVal, + size_t stringLen) +{ + printf("string: '"); + fwrite(stringVal, 1, stringLen, stdout); + printf("'\n"); + return 1; +} + +static int test_yajl_map_key(void *ctx, const unsigned char * stringVal, + size_t stringLen) +{ + char * str = (char *) malloc(stringLen + 1); + str[stringLen] = 0; + memcpy(str, stringVal, stringLen); + printf("key: '%s'\n", str); + free(str); + return 1; +} + +static int test_yajl_start_map(void *ctx) +{ + printf("map open '{'\n"); + return 1; +} + + +static int test_yajl_end_map(void *ctx) +{ + printf("map close '}'\n"); + return 1; +} + +static int test_yajl_start_array(void *ctx) +{ + printf("array open '['\n"); + return 1; +} + +static int test_yajl_end_array(void *ctx) +{ + printf("array close ']'\n"); + return 1; +} + +static yajl_callbacks callbacks = { + test_yajl_null, + test_yajl_boolean, + test_yajl_integer, + test_yajl_double, + NULL, + test_yajl_string, + test_yajl_start_map, + test_yajl_map_key, + test_yajl_end_map, + test_yajl_start_array, + test_yajl_end_array +}; + +static void usage(const char * progname) +{ + fprintf(stderr, + "usage: %s [options]\n" + "Parse input from stdin as JSON and ouput parsing details " + "to stdout\n" + " -b set the read buffer size\n" + " -c allow comments\n" + " -g allow *g*arbage after valid JSON text\n" + " -h print this help message\n" + " -m allows the parser to consume multiple JSON values\n" + " from a single string separated by whitespace\n" + " -p partial JSON documents should not cause errors\n", + progname); + exit(1); +} + +int +main(int argc, char ** argv) +{ + yajl_handle hand; + const char * fileName = NULL; + static unsigned char * fileData = NULL; + FILE *file; + size_t bufSize = BUF_SIZE; + yajl_status stat; + size_t rd; + int i, j; + + /* memory allocation debugging: allocate a structure which collects + * statistics */ + yajlTestMemoryContext memCtx = { 0,0 }; + + /* memory allocation debugging: allocate a structure which holds + * allocation routines */ + yajl_alloc_funcs allocFuncs = { + yajlTestMalloc, + yajlTestRealloc, + yajlTestFree, + (void *) NULL + }; + + allocFuncs.ctx = (void *) &memCtx; + + /* allocate the parser */ + hand = yajl_alloc(&callbacks, &allocFuncs, NULL); + + /* check arguments. We expect exactly one! */ + for (i=1;i= argc) usage(argv[0]); + + /* validate integer */ + for (j=0;j<(int)strlen(argv[i]);j++) { + if (argv[i][j] <= '9' && argv[i][j] >= '0') continue; + fprintf(stderr, "-b requires an integer argument. '%s' " + "is invalid\n", argv[i]); + usage(argv[0]); + } + + bufSize = atoi(argv[i]); + if (!bufSize) { + fprintf(stderr, "%zu is an invalid buffer size\n", + bufSize); + } + } else if (!strcmp("-g", argv[i])) { + yajl_config(hand, yajl_allow_trailing_garbage, 1); + } else if (!strcmp("-h", argv[i])) { + usage(argv[0]); + } else if (!strcmp("-m", argv[i])) { + yajl_config(hand, yajl_allow_multiple_values, 1); + } else if (!strcmp("-p", argv[i])) { + yajl_config(hand, yajl_allow_partial_values, 1); + } else { + fileName = argv[i]; + break; + } + } + + fileData = (unsigned char *) malloc(bufSize); + + if (fileData == NULL) { + fprintf(stderr, + "failed to allocate read buffer of %zu bytes, exiting.", + bufSize); + yajl_free(hand); + exit(2); + } + + if (fileName) + { + file = fopen(fileName, "r"); + } + else + { + file = stdin; + } + for (;;) { + rd = fread((void *) fileData, 1, bufSize, file); + + if (rd == 0) { + if (!feof(file)) { + fprintf(stderr, "error reading from '%s'\n", fileName); + } + break; + } + /* read file data, now pass to parser */ + stat = yajl_parse(hand, fileData, rd); + + if (stat != yajl_status_ok) break; + } + + stat = yajl_complete_parse(hand); + if (stat != yajl_status_ok) + { + unsigned char * str = yajl_get_error(hand, 0, fileData, rd); + fflush(stdout); + fprintf(stderr, "%s", (char *) str); + yajl_free_error(hand, str); + } + + yajl_free(hand); + free(fileData); + + if (fileName) + { + fclose(file); + } + /* finally, print out some memory statistics */ + +/* (lth) only print leaks here, as allocations and frees may vary depending + * on read buffer size, causing false failures. + * + * printf("allocations:\t%u\n", memCtx.numMallocs); + * printf("frees:\t\t%u\n", memCtx.numFrees); +*/ + fflush(stderr); + fflush(stdout); + printf("memory leaks:\t%u\n", memCtx.numMallocs - memCtx.numFrees); + + return 0; +} diff --git a/modules/libcom/vxWorks/Makefile b/modules/libcom/vxWorks/Makefile new file mode 100644 index 000000000..e2c0b5d2d --- /dev/null +++ b/modules/libcom/vxWorks/Makefile @@ -0,0 +1,63 @@ +#************************************************************************* +# Copyright (c) 2017 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP = .. +include $(TOP)/configure/CONFIG + +# Install Boost smart_ptr headers needed by VxWorks 6.x + +INC_vxWorks += boost/assert.hpp +INC_vxWorks += boost/checked_delete.hpp +INC_vxWorks += boost/config/auto_link.hpp +INC_vxWorks += boost/config/compiler/gcc.hpp +INC_vxWorks += boost/config.hpp +INC_vxWorks += boost/config/no_tr1/memory.hpp +INC_vxWorks += boost/config/no_tr1/utility.hpp +INC_vxWorks += boost/config/platform/vxworks.hpp +INC_vxWorks += boost/config/posix_features.hpp +INC_vxWorks += boost/config/select_compiler_config.hpp +INC_vxWorks += boost/config/select_platform_config.hpp +INC_vxWorks += boost/config/select_stdlib_config.hpp +INC_vxWorks += boost/config/stdlib/dinkumware.hpp +INC_vxWorks += boost/config/suffix.hpp +INC_vxWorks += boost/config/user.hpp +INC_vxWorks += boost/current_function.hpp +INC_vxWorks += boost/detail/sp_typeinfo.hpp +INC_vxWorks += boost/detail/workaround.hpp +INC_vxWorks += boost/enable_shared_from_this.hpp +INC_vxWorks += boost/exception/detail/attribute_noreturn.hpp +INC_vxWorks += boost/exception/exception.hpp +INC_vxWorks += boost/memory_order.hpp +INC_vxWorks += boost/shared_ptr.hpp +INC_vxWorks += boost/smart_ptr/bad_weak_ptr.hpp +INC_vxWorks += boost/smart_ptr/detail/operator_bool.hpp +INC_vxWorks += boost/smart_ptr/detail/shared_count.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_convertible.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_counted_base.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_counted_impl.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_has_sync.hpp +INC_vxWorks += boost/smart_ptr/detail/spinlock.hpp +INC_vxWorks += boost/smart_ptr/detail/spinlock_pool.hpp +INC_vxWorks += boost/smart_ptr/detail/spinlock_pt.hpp +INC_vxWorks += boost/smart_ptr/detail/spinlock_sync.hpp +INC_vxWorks += boost/smart_ptr/detail/sp_nullptr_t.hpp +INC_vxWorks += boost/smart_ptr/detail/yield_k.hpp +INC_vxWorks += boost/smart_ptr/enable_shared_from_this.hpp +INC_vxWorks += boost/smart_ptr/shared_ptr.hpp +INC_vxWorks += boost/smart_ptr/weak_ptr.hpp +INC_vxWorks += boost/throw_exception.hpp +INC_vxWorks += boost/tr1/detail/config.hpp +INC_vxWorks += boost/tr1/memory.hpp +INC_vxWorks += boost/type_traits/is_signed.hpp +INC_vxWorks += boost/weak_ptr.hpp + +# Base doesn't have a vpath for .hpp files +vpath %.hpp $(USR_VPATH) $(ALL_SRC_DIRS) + +include $(TOP)/configure/RULES diff --git a/modules/libcom/vxWorks/boost/assert.hpp b/modules/libcom/vxWorks/boost/assert.hpp new file mode 100644 index 000000000..174f0846f --- /dev/null +++ b/modules/libcom/vxWorks/boost/assert.hpp @@ -0,0 +1,131 @@ +// +// boost/assert.hpp - BOOST_ASSERT(expr) +// BOOST_ASSERT_MSG(expr, msg) +// BOOST_VERIFY(expr) +// +// Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd. +// Copyright (c) 2007 Peter Dimov +// Copyright (c) Beman Dawes 2011 +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Note: There are no include guards. This is intentional. +// +// See http://www.boost.org/libs/utility/assert.html for documentation. +// + +// +// Stop inspect complaining about use of 'assert': +// +// boostinspect:naassert_macro +// + +//--------------------------------------------------------------------------------------// +// BOOST_ASSERT // +//--------------------------------------------------------------------------------------// + +#undef BOOST_ASSERT + +#if defined(BOOST_DISABLE_ASSERTS) + +# define BOOST_ASSERT(expr) ((void)0) + +#elif defined(BOOST_ENABLE_ASSERT_HANDLER) + +#include + +namespace boost +{ + void assertion_failed(char const * expr, + char const * function, char const * file, long line); // user defined +} // namespace boost + +#define BOOST_ASSERT(expr) ((expr) \ + ? ((void)0) \ + : ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) + +#else +# include // .h to support old libraries w/o - effect is the same +# define BOOST_ASSERT(expr) assert(expr) +#endif + +//--------------------------------------------------------------------------------------// +// BOOST_ASSERT_MSG // +//--------------------------------------------------------------------------------------// + +# undef BOOST_ASSERT_MSG + +#if defined(BOOST_DISABLE_ASSERTS) || defined(NDEBUG) + + #define BOOST_ASSERT_MSG(expr, msg) ((void)0) + +#elif defined(BOOST_ENABLE_ASSERT_HANDLER) + + #include + + namespace boost + { + void assertion_failed_msg(char const * expr, char const * msg, + char const * function, char const * file, long line); // user defined + } // namespace boost + + #define BOOST_ASSERT_MSG(expr, msg) ((expr) \ + ? ((void)0) \ + : ::boost::assertion_failed_msg(#expr, msg, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) + +#else + #ifndef BOOST_ASSERT_HPP + #define BOOST_ASSERT_HPP + #include + #include + #include + + // IDE's like Visual Studio perform better if output goes to std::cout or + // some other stream, so allow user to configure output stream: + #ifndef BOOST_ASSERT_MSG_OSTREAM + # define BOOST_ASSERT_MSG_OSTREAM std::cerr + #endif + + namespace boost + { + namespace assertion + { + namespace detail + { + inline void assertion_failed_msg(char const * expr, char const * msg, char const * function, + char const * file, long line) + { + BOOST_ASSERT_MSG_OSTREAM + << "***** Internal Program Error - assertion (" << expr << ") failed in " + << function << ":\n" + << file << '(' << line << "): " << msg << std::endl; + std::abort(); + } + } // detail + } // assertion + } // detail + #endif + + #define BOOST_ASSERT_MSG(expr, msg) ((expr) \ + ? ((void)0) \ + : ::boost::assertion::detail::assertion_failed_msg(#expr, msg, \ + BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) +#endif + +//--------------------------------------------------------------------------------------// +// BOOST_VERIFY // +//--------------------------------------------------------------------------------------// + +#undef BOOST_VERIFY + +#if defined(BOOST_DISABLE_ASSERTS) || ( !defined(BOOST_ENABLE_ASSERT_HANDLER) && defined(NDEBUG) ) + +# define BOOST_VERIFY(expr) ((void)(expr)) + +#else + +# define BOOST_VERIFY(expr) BOOST_ASSERT(expr) + +#endif diff --git a/modules/libcom/vxWorks/boost/checked_delete.hpp b/modules/libcom/vxWorks/boost/checked_delete.hpp new file mode 100644 index 000000000..9bb84e8e1 --- /dev/null +++ b/modules/libcom/vxWorks/boost/checked_delete.hpp @@ -0,0 +1,69 @@ +#ifndef BOOST_CHECKED_DELETE_HPP_INCLUDED +#define BOOST_CHECKED_DELETE_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/checked_delete.hpp +// +// Copyright (c) 2002, 2003 Peter Dimov +// Copyright (c) 2003 Daniel Frey +// Copyright (c) 2003 Howard Hinnant +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/utility/checked_delete.html for documentation. +// + +namespace boost +{ + +// verify that types are complete for increased safety + +template inline void checked_delete(T * x) +{ + // intentionally complex - simplification causes regressions + typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; + (void) sizeof(type_must_be_complete); + delete x; +} + +template inline void checked_array_delete(T * x) +{ + typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; + (void) sizeof(type_must_be_complete); + delete [] x; +} + +template struct checked_deleter +{ + typedef void result_type; + typedef T * argument_type; + + void operator()(T * x) const + { + // boost:: disables ADL + boost::checked_delete(x); + } +}; + +template struct checked_array_deleter +{ + typedef void result_type; + typedef T * argument_type; + + void operator()(T * x) const + { + boost::checked_array_delete(x); + } +}; + +} // namespace boost + +#endif // #ifndef BOOST_CHECKED_DELETE_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/config.hpp b/modules/libcom/vxWorks/boost/config.hpp new file mode 100644 index 000000000..f37585ebb --- /dev/null +++ b/modules/libcom/vxWorks/boost/config.hpp @@ -0,0 +1,70 @@ +// Boost config.hpp configuration header file ------------------------------// + +// (C) Copyright John Maddock 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for most recent version. + +// Boost config.hpp policy and rationale documentation has been moved to +// http://www.boost.org/libs/config +// +// CAUTION: This file is intended to be completely stable - +// DO NOT MODIFY THIS FILE! +// + +#ifndef BOOST_CONFIG_HPP +#define BOOST_CONFIG_HPP + +// if we don't have a user config, then use the default location: +#if !defined(BOOST_USER_CONFIG) && !defined(BOOST_NO_USER_CONFIG) +# define BOOST_USER_CONFIG +#endif +// include it first: +#ifdef BOOST_USER_CONFIG +# include BOOST_USER_CONFIG +#endif + +// if we don't have a compiler config set, try and find one: +#if !defined(BOOST_COMPILER_CONFIG) && !defined(BOOST_NO_COMPILER_CONFIG) && !defined(BOOST_NO_CONFIG) +# include +#endif +// if we have a compiler config, include it now: +#ifdef BOOST_COMPILER_CONFIG +# include BOOST_COMPILER_CONFIG +#endif + +// if we don't have a std library config set, try and find one: +#if !defined(BOOST_STDLIB_CONFIG) && !defined(BOOST_NO_STDLIB_CONFIG) && !defined(BOOST_NO_CONFIG) && defined(__cplusplus) +# include +#endif +// if we have a std library config, include it now: +#ifdef BOOST_STDLIB_CONFIG +# include BOOST_STDLIB_CONFIG +#endif + +// if we don't have a platform config set, try and find one: +#if !defined(BOOST_PLATFORM_CONFIG) && !defined(BOOST_NO_PLATFORM_CONFIG) && !defined(BOOST_NO_CONFIG) +# include +#endif +// if we have a platform config, include it now: +#ifdef BOOST_PLATFORM_CONFIG +# include BOOST_PLATFORM_CONFIG +#endif + +// get config suffix code: +#include + +#endif // BOOST_CONFIG_HPP + + + + + + + + + + + diff --git a/modules/libcom/vxWorks/boost/config/auto_link.hpp b/modules/libcom/vxWorks/boost/config/auto_link.hpp new file mode 100644 index 000000000..e36d06a05 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/auto_link.hpp @@ -0,0 +1,423 @@ +// (C) Copyright John Maddock 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + /* + * LOCATION: see http://www.boost.org for most recent version. + * FILE auto_link.hpp + * VERSION see + * DESCRIPTION: Automatic library inclusion for Borland/Microsoft compilers. + */ + +/************************************************************************* + +USAGE: +~~~~~~ + +Before including this header you must define one or more of define the following macros: + +BOOST_LIB_NAME: Required: A string containing the basename of the library, + for example boost_regex. +BOOST_LIB_TOOLSET: Optional: the base name of the toolset. +BOOST_DYN_LINK: Optional: when set link to dll rather than static library. +BOOST_LIB_DIAGNOSTIC: Optional: when set the header will print out the name + of the library selected (useful for debugging). +BOOST_AUTO_LINK_NOMANGLE: Specifies that we should link to BOOST_LIB_NAME.lib, + rather than a mangled-name version. +BOOST_AUTO_LINK_TAGGED: Specifies that we link to libraries built with the --layout=tagged option. + This is essentially the same as the default name-mangled version, but without + the compiler name and version, or the Boost version. Just the build options. + +These macros will be undef'ed at the end of the header, further this header +has no include guards - so be sure to include it only once from your library! + +Algorithm: +~~~~~~~~~~ + +Libraries for Borland and Microsoft compilers are automatically +selected here, the name of the lib is selected according to the following +formula: + +BOOST_LIB_PREFIX + + BOOST_LIB_NAME + + "_" + + BOOST_LIB_TOOLSET + + BOOST_LIB_THREAD_OPT + + BOOST_LIB_RT_OPT + "-" + + BOOST_LIB_VERSION + +These are defined as: + +BOOST_LIB_PREFIX: "lib" for static libraries otherwise "". + +BOOST_LIB_NAME: The base name of the lib ( for example boost_regex). + +BOOST_LIB_TOOLSET: The compiler toolset name (vc6, vc7, bcb5 etc). + +BOOST_LIB_THREAD_OPT: "-mt" for multithread builds, otherwise nothing. + +BOOST_LIB_RT_OPT: A suffix that indicates the runtime library used, + contains one or more of the following letters after + a hyphen: + + s static runtime (dynamic if not present). + g debug/diagnostic runtime (release if not present). + y Python debug/diagnostic runtime (release if not present). + d debug build (release if not present). + p STLport build. + n STLport build without its IOStreams. + +BOOST_LIB_VERSION: The Boost version, in the form x_y, for Boost version x.y. + + +***************************************************************************/ + +#ifdef __cplusplus +# ifndef BOOST_CONFIG_HPP +# include +# endif +#elif defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__EDG_VERSION__) +// +// C language compatability (no, honestly) +// +# define BOOST_MSVC _MSC_VER +# define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) +# define BOOST_DO_STRINGIZE(X) #X +#endif +// +// Only include what follows for known and supported compilers: +// +#if defined(BOOST_MSVC) \ + || defined(__BORLANDC__) \ + || (defined(__MWERKS__) && defined(_WIN32) && (__MWERKS__ >= 0x3000)) \ + || (defined(__ICL) && defined(_MSC_EXTENSIONS) && (_MSC_VER >= 1200)) + +#ifndef BOOST_VERSION_HPP +# include +#endif + +#ifndef BOOST_LIB_NAME +# error "Macro BOOST_LIB_NAME not set (internal error)" +#endif + +// +// error check: +// +#if defined(__MSVC_RUNTIME_CHECKS) && !defined(_DEBUG) +# pragma message("Using the /RTC option without specifying a debug runtime will lead to linker errors") +# pragma message("Hint: go to the code generation options and switch to one of the debugging runtimes") +# error "Incompatible build options" +#endif +// +// select toolset if not defined already: +// +#ifndef BOOST_LIB_TOOLSET +# if defined(BOOST_MSVC) && (BOOST_MSVC < 1200) + // Note: no compilers before 1200 are supported +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1300) + +# ifdef UNDER_CE + // eVC4: +# define BOOST_LIB_TOOLSET "evc4" +# else + // vc6: +# define BOOST_LIB_TOOLSET "vc6" +# endif + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1310) + + // vc7: +# define BOOST_LIB_TOOLSET "vc7" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1400) + + // vc71: +# define BOOST_LIB_TOOLSET "vc71" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1500) + + // vc80: +# define BOOST_LIB_TOOLSET "vc80" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1600) + + // vc90: +# define BOOST_LIB_TOOLSET "vc90" + +# elif defined(BOOST_MSVC) && (BOOST_MSVC < 1700) + + // vc10: +# define BOOST_LIB_TOOLSET "vc100" + +# elif defined(BOOST_MSVC) + + // vc11: +# define BOOST_LIB_TOOLSET "vc110" + +# elif defined(__BORLANDC__) + + // CBuilder 6: +# define BOOST_LIB_TOOLSET "bcb" + +# elif defined(__ICL) + + // Intel C++, no version number: +# define BOOST_LIB_TOOLSET "iw" + +# elif defined(__MWERKS__) && (__MWERKS__ <= 0x31FF ) + + // Metrowerks CodeWarrior 8.x +# define BOOST_LIB_TOOLSET "cw8" + +# elif defined(__MWERKS__) && (__MWERKS__ <= 0x32FF ) + + // Metrowerks CodeWarrior 9.x +# define BOOST_LIB_TOOLSET "cw9" + +# endif +#endif // BOOST_LIB_TOOLSET + +// +// select thread opt: +// +#if defined(_MT) || defined(__MT__) +# define BOOST_LIB_THREAD_OPT "-mt" +#else +# define BOOST_LIB_THREAD_OPT +#endif + +#if defined(_MSC_VER) || defined(__MWERKS__) + +# ifdef _DLL + +# if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydp" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-gdp" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gdp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-p" +# endif + +# elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydpn" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-gdpn" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gydpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gdpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-pn" +# endif + +# else + +# if defined(_DEBUG) && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-gyd" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-gd" +# else +# define BOOST_LIB_RT_OPT +# endif + +# endif + +# else + +# if (defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)) && (defined(_STLP_OWN_IOSTREAMS) || defined(__STL_OWN_IOSTREAMS)) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydp" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-sgdp" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgdp" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-sp" +# endif + +# elif defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) + +# if defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG))\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydpn" +# elif defined(_DEBUG) && (defined(__STL_DEBUG) || defined(_STLP_DEBUG)) +# define BOOST_LIB_RT_OPT "-sgdpn" +# elif defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgydpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgdpn" +# pragma message("warning: STLport debug versions are built with /D_STLP_DEBUG=1") +# error "Build options aren't compatible with pre-built libraries" +# else +# define BOOST_LIB_RT_OPT "-spn" +# endif + +# else + +# if defined(_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sgyd" +# elif defined(_DEBUG) +# define BOOST_LIB_RT_OPT "-sgd" +# else +# define BOOST_LIB_RT_OPT "-s" +# endif + +# endif + +# endif + +#elif defined(__BORLANDC__) + +// +// figure out whether we want the debug builds or not: +// +#if __BORLANDC__ > 0x561 +#pragma defineonoption BOOST_BORLAND_DEBUG -v +#endif +// +// sanity check: +// +#if defined(__STL_DEBUG) || defined(_STLP_DEBUG) +#error "Pre-built versions of the Boost libraries are not provided in STLport-debug form" +#endif + +# ifdef _RTLDLL + +# if defined(BOOST_BORLAND_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-yd" +# elif defined(BOOST_BORLAND_DEBUG) +# define BOOST_LIB_RT_OPT "-d" +# elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT -y +# else +# define BOOST_LIB_RT_OPT +# endif + +# else + +# if defined(BOOST_BORLAND_DEBUG)\ + && defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-syd" +# elif defined(BOOST_BORLAND_DEBUG) +# define BOOST_LIB_RT_OPT "-sd" +# elif defined(BOOST_DEBUG_PYTHON) && defined(BOOST_LINKING_PYTHON) +# define BOOST_LIB_RT_OPT "-sy" +# else +# define BOOST_LIB_RT_OPT "-s" +# endif + +# endif + +#endif + +// +// select linkage opt: +// +#if (defined(_DLL) || defined(_RTLDLL)) && defined(BOOST_DYN_LINK) +# define BOOST_LIB_PREFIX +#elif defined(BOOST_DYN_LINK) +# error "Mixing a dll boost library with a static runtime is a really bad idea..." +#else +# define BOOST_LIB_PREFIX "lib" +#endif + +// +// now include the lib: +// +#if defined(BOOST_LIB_NAME) \ + && defined(BOOST_LIB_PREFIX) \ + && defined(BOOST_LIB_TOOLSET) \ + && defined(BOOST_LIB_THREAD_OPT) \ + && defined(BOOST_LIB_RT_OPT) \ + && defined(BOOST_LIB_VERSION) + +#ifdef BOOST_AUTO_LINK_TAGGED +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT ".lib") +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT ".lib") +# endif +#elif defined(BOOST_AUTO_LINK_NOMANGLE) +# pragma comment(lib, BOOST_STRINGIZE(BOOST_LIB_NAME) ".lib") +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_STRINGIZE(BOOST_LIB_NAME) ".lib") +# endif +#else +# pragma comment(lib, BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT "-" BOOST_LIB_VERSION ".lib") +# ifdef BOOST_LIB_DIAGNOSTIC +# pragma message ("Linking to lib file: " BOOST_LIB_PREFIX BOOST_STRINGIZE(BOOST_LIB_NAME) "-" BOOST_LIB_TOOLSET BOOST_LIB_THREAD_OPT BOOST_LIB_RT_OPT "-" BOOST_LIB_VERSION ".lib") +# endif +#endif + +#else +# error "some required macros where not defined (internal logic error)." +#endif + + +#endif // _MSC_VER || __BORLANDC__ + +// +// finally undef any macros we may have set: +// +#ifdef BOOST_LIB_PREFIX +# undef BOOST_LIB_PREFIX +#endif +#if defined(BOOST_LIB_NAME) +# undef BOOST_LIB_NAME +#endif +// Don't undef this one: it can be set by the user and should be the +// same for all libraries: +//#if defined(BOOST_LIB_TOOLSET) +//# undef BOOST_LIB_TOOLSET +//#endif +#if defined(BOOST_LIB_THREAD_OPT) +# undef BOOST_LIB_THREAD_OPT +#endif +#if defined(BOOST_LIB_RT_OPT) +# undef BOOST_LIB_RT_OPT +#endif +#if defined(BOOST_LIB_LINK_OPT) +# undef BOOST_LIB_LINK_OPT +#endif +#if defined(BOOST_LIB_DEBUG_OPT) +# undef BOOST_LIB_DEBUG_OPT +#endif +#if defined(BOOST_DYN_LINK) +# undef BOOST_DYN_LINK +#endif + diff --git a/modules/libcom/vxWorks/boost/config/compiler/gcc.hpp b/modules/libcom/vxWorks/boost/config/compiler/gcc.hpp new file mode 100644 index 000000000..c0ac30af2 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/compiler/gcc.hpp @@ -0,0 +1,260 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Darin Adler 2001 - 2002. +// (C) Copyright Jens Maurer 2001 - 2002. +// (C) Copyright Beman Dawes 2001 - 2003. +// (C) Copyright Douglas Gregor 2002. +// (C) Copyright David Abrahams 2002 - 2003. +// (C) Copyright Synge Todo 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// GNU C++ compiler setup: + +#if __GNUC__ < 3 +# if __GNUC_MINOR__ == 91 + // egcs 1.1 won't parse shared_ptr.hpp without this: +# define BOOST_NO_AUTO_PTR +# endif +# if __GNUC_MINOR__ < 95 + // + // Prior to gcc 2.95 member templates only partly + // work - define BOOST_MSVC6_MEMBER_TEMPLATES + // instead since inline member templates mostly work. + // +# define BOOST_NO_MEMBER_TEMPLATES +# if __GNUC_MINOR__ >= 9 +# define BOOST_MSVC6_MEMBER_TEMPLATES +# endif +# endif + +# if __GNUC_MINOR__ < 96 +# define BOOST_NO_SFINAE +# endif + +# if __GNUC_MINOR__ <= 97 +# define BOOST_NO_MEMBER_TEMPLATE_FRIENDS +# define BOOST_NO_OPERATORS_IN_NAMESPACE +# endif + +# define BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +# define BOOST_NO_IS_ABSTRACT +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +// Variadic macros do not exist for gcc versions before 3.0 +# define BOOST_NO_CXX11_VARIADIC_MACROS +#elif __GNUC__ == 3 +# if defined (__PATHSCALE__) +# define BOOST_NO_TWO_PHASE_NAME_LOOKUP +# define BOOST_NO_IS_ABSTRACT +# endif + // + // gcc-3.x problems: + // + // Bug specific to gcc 3.1 and 3.2: + // +# if ((__GNUC_MINOR__ == 1) || (__GNUC_MINOR__ == 2)) +# define BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS +# endif +# if __GNUC_MINOR__ < 4 +# define BOOST_NO_IS_ABSTRACT +# endif +# define BOOST_NO_CXX11_EXTERN_TEMPLATE +#endif +#if __GNUC__ < 4 +// +// All problems to gcc-3.x and earlier here: +// +#define BOOST_NO_TWO_PHASE_NAME_LOOKUP +# ifdef __OPEN64__ +# define BOOST_NO_IS_ABSTRACT +# endif +#endif + +#if __GNUC__ < 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ < 4 ) +// Previous versions of GCC did not completely implement value-initialization: +// GCC Bug 30111, "Value-initialization of POD base class doesn't initialize +// members", reported by Jonathan Wakely in 2006, +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30111 (fixed for GCC 4.4) +// GCC Bug 33916, "Default constructor fails to initialize array members", +// reported by Michael Elizabeth Chastain in 2007, +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33916 (fixed for GCC 4.2.4) +// See also: http://www.boost.org/libs/utility/value_init.htm#compiler_issues +#define BOOST_NO_COMPLETE_VALUE_INITIALIZATION +#endif + +#if !defined(__EXCEPTIONS) && !defined(BOOST_NO_EXCEPTIONS) +# define BOOST_NO_EXCEPTIONS +#endif + + +// +// Threading support: Turn this on unconditionally here (except for +// those platforms where we can know for sure). It will get turned off again +// later if no threading API is detected. +// +#if !defined(__MINGW32__) && !defined(linux) && !defined(__linux) && !defined(__linux__) +# define BOOST_HAS_THREADS +#endif + +// +// gcc has "long long" +// +#define BOOST_HAS_LONG_LONG + +// +// gcc implements the named return value optimization since version 3.1 +// +#if __GNUC__ > 3 || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) +#define BOOST_HAS_NRVO +#endif + +// +// Dynamic shared object (DSO) and dynamic-link library (DLL) support +// +#if __GNUC__ >= 4 +# if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && !defined(__CYGWIN__) + // All Win32 development environments, including 64-bit Windows and MinGW, define + // _WIN32 or one of its variant spellings. Note that Cygwin is a POSIX environment, + // so does not define _WIN32 or its variants. +# define BOOST_HAS_DECLSPEC +# define BOOST_SYMBOL_EXPORT __attribute__((dllexport)) +# define BOOST_SYMBOL_IMPORT __attribute__((dllimport)) +# else +# define BOOST_SYMBOL_EXPORT __attribute__((visibility("default"))) +# define BOOST_SYMBOL_IMPORT +# endif +# define BOOST_SYMBOL_VISIBLE __attribute__((visibility("default"))) +#else +// config/platform/win32.hpp will define BOOST_SYMBOL_EXPORT, etc., unless already defined +# define BOOST_SYMBOL_EXPORT +#endif + +// +// RTTI and typeinfo detection is possible post gcc-4.3: +// +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 403 +# ifndef __GXX_RTTI +# ifndef BOOST_NO_TYPEID +# define BOOST_NO_TYPEID +# endif +# ifndef BOOST_NO_RTTI +# define BOOST_NO_RTTI +# endif +# endif +#endif + +// +// Recent GCC versions have __int128 when in 64-bit mode: +// +#if defined(__SIZEOF_INT128__) +# define BOOST_HAS_INT128 +#endif + +// C++0x features in 4.3.n and later +// +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && defined(__GXX_EXPERIMENTAL_CXX0X__) +// C++0x features are only enabled when -std=c++0x or -std=gnu++0x are +// passed on the command line, which in turn defines +// __GXX_EXPERIMENTAL_CXX0X__. +# define BOOST_HAS_DECLTYPE +# define BOOST_HAS_RVALUE_REFS +# define BOOST_HAS_STATIC_ASSERT +# define BOOST_HAS_VARIADIC_TMPL +#else +# define BOOST_NO_CXX11_DECLTYPE +# define BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS +# define BOOST_NO_CXX11_RVALUE_REFERENCES +# define BOOST_NO_CXX11_STATIC_ASSERT + +// Variadic templates compiler: +// http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html +# if defined(__VARIADIC_TEMPLATES) || (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +# define BOOST_HAS_VARIADIC_TMPL +# else +# define BOOST_NO_CXX11_VARIADIC_TEMPLATES +# endif +#endif + +// C++0x features in 4.4.n and later +// +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4) || !defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_NO_CXX11_AUTO_DECLARATIONS +# define BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS +# define BOOST_NO_CXX11_CHAR16_T +# define BOOST_NO_CXX11_CHAR32_T +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_DEFAULTED_FUNCTIONS +# define BOOST_NO_CXX11_DELETED_FUNCTIONS +#endif + +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +# define BOOST_NO_SFINAE_EXPR +#endif + +// C++0x features in 4.5.0 and later +// +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) || !defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS +# define BOOST_NO_CXX11_LAMBDAS +# define BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS +# define BOOST_NO_CXX11_RAW_LITERALS +# define BOOST_NO_CXX11_UNICODE_LITERALS +#endif + +// C++0x features in 4.5.1 and later +// +#if (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__ < 40501) || !defined(__GXX_EXPERIMENTAL_CXX0X__) +// scoped enums have a serious bug in 4.4.0, so define BOOST_NO_CXX11_SCOPED_ENUMS before 4.5.1 +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38064 +# define BOOST_NO_CXX11_SCOPED_ENUMS +#endif + +// C++0x features in 4.6.n and later +// +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6) || !defined(__GXX_EXPERIMENTAL_CXX0X__) +#define BOOST_NO_CXX11_CONSTEXPR +#define BOOST_NO_CXX11_NOEXCEPT +#define BOOST_NO_CXX11_NULLPTR +#define BOOST_NO_CXX11_RANGE_BASED_FOR +#define BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX +#endif + +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) || !defined(__GXX_EXPERIMENTAL_CXX0X__) +# define BOOST_NO_CXX11_TEMPLATE_ALIASES +#endif +// C++0x features not supported at all yet +// +#define BOOST_NO_CXX11_DECLTYPE_N3276 + +#ifndef BOOST_COMPILER +# define BOOST_COMPILER "GNU C++ version " __VERSION__ +#endif + +// ConceptGCC compiler: +// http://www.generic-programming.org/software/ConceptGCC/ +#ifdef __GXX_CONCEPTS__ +# define BOOST_HAS_CONCEPTS +# define BOOST_COMPILER "ConceptGCC version " __VERSION__ +#endif + +// versions check: +// we don't know gcc prior to version 2.90: +#if (__GNUC__ == 2) && (__GNUC_MINOR__ < 90) +# error "Compiler not configured - please reconfigure" +#endif +// +// last known and checked version is 4.6 (Pre-release): +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6)) +# if defined(BOOST_ASSERT_CONFIG) +# error "Unknown compiler version - please run the configure tests and report the results" +# else +// we don't emit warnings here anymore since there are no defect macros defined for +// gcc post 3.4, so any failures are gcc regressions... +//# warning "Unknown compiler version - please run the configure tests and report the results" +# endif +#endif + + diff --git a/modules/libcom/vxWorks/boost/config/no_tr1/memory.hpp b/modules/libcom/vxWorks/boost/config/no_tr1/memory.hpp new file mode 100644 index 000000000..2b5d20802 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/no_tr1/memory.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/memory is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_MEMORY +# define BOOST_CONFIG_MEMORY + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_MEMORY_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_MEMORY_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_MEMORY_RECURSION +# endif + +#endif diff --git a/modules/libcom/vxWorks/boost/config/no_tr1/utility.hpp b/modules/libcom/vxWorks/boost/config/no_tr1/utility.hpp new file mode 100644 index 000000000..dea8f115b --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/no_tr1/utility.hpp @@ -0,0 +1,28 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// The aim of this header is just to include but to do +// so in a way that does not result in recursive inclusion of +// the Boost TR1 components if boost/tr1/tr1/utility is in the +// include search path. We have to do this to avoid circular +// dependencies: +// + +#ifndef BOOST_CONFIG_UTILITY +# define BOOST_CONFIG_UTILITY + +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_CONFIG_NO_UTILITY_RECURSION +# endif + +# include + +# ifdef BOOST_CONFIG_NO_UTILITY_RECURSION +# undef BOOST_TR1_NO_RECURSION +# undef BOOST_CONFIG_NO_UTILITY_RECURSION +# endif + +#endif diff --git a/modules/libcom/vxWorks/boost/config/platform/vxworks.hpp b/modules/libcom/vxWorks/boost/config/platform/vxworks.hpp new file mode 100644 index 000000000..6ec5171e3 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/platform/vxworks.hpp @@ -0,0 +1,31 @@ +// (C) Copyright Dustin Spicuzza 2009. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// vxWorks specific config options: + +#define BOOST_PLATFORM "vxWorks" + +#define BOOST_NO_CWCHAR +#define BOOST_NO_INTRINSIC_WCHAR_T + +#if defined(__GNUC__) && defined(__STRICT_ANSI__) +#define BOOST_NO_INT64_T +#endif + +#define BOOST_HAS_UNISTD_H + +// these allow posix_features to work, since vxWorks doesn't +// define them itself +#define _POSIX_TIMERS 1 +#define _POSIX_THREADS 1 + +// vxworks doesn't work with asio serial ports +#define BOOST_ASIO_DISABLE_SERIAL_PORT + +// boilerplate code: +#include + diff --git a/modules/libcom/vxWorks/boost/config/posix_features.hpp b/modules/libcom/vxWorks/boost/config/posix_features.hpp new file mode 100644 index 000000000..d12954797 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/posix_features.hpp @@ -0,0 +1,95 @@ +// (C) Copyright John Maddock 2001 - 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// All POSIX feature tests go in this file, +// Note that we test _POSIX_C_SOURCE and _XOPEN_SOURCE as well +// _POSIX_VERSION and _XOPEN_VERSION: on some systems POSIX API's +// may be present but none-functional unless _POSIX_C_SOURCE and +// _XOPEN_SOURCE have been defined to the right value (it's up +// to the user to do this *before* including any header, although +// in most cases the compiler will do this for you). + +# if defined(BOOST_HAS_UNISTD_H) +# include + + // XOpen has , but is this the correct version check? +# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 3) +# define BOOST_HAS_NL_TYPES_H +# endif + + // POSIX version 6 requires +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200100) +# define BOOST_HAS_STDINT_H +# endif + + // POSIX version 2 requires +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199009L) +# define BOOST_HAS_DIRENT_H +# endif + + // POSIX version 3 requires to have sigaction: +# if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 199506L) +# define BOOST_HAS_SIGACTION +# endif + // POSIX defines _POSIX_THREADS > 0 for pthread support, + // however some platforms define _POSIX_THREADS without + // a value, hence the (_POSIX_THREADS+0 >= 0) check. + // Strictly speaking this may catch platforms with a + // non-functioning stub , but such occurrences should + // occur very rarely if at all. +# if defined(_POSIX_THREADS) && (_POSIX_THREADS+0 >= 0) && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_MPTASKS) +# define BOOST_HAS_PTHREADS +# endif + + // BOOST_HAS_NANOSLEEP: + // This is predicated on _POSIX_TIMERS or _XOPEN_REALTIME: +# if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) \ + || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) +# define BOOST_HAS_NANOSLEEP +# endif + + // BOOST_HAS_CLOCK_GETTIME: + // This is predicated on _POSIX_TIMERS (also on _XOPEN_REALTIME + // but at least one platform - linux - defines that flag without + // defining clock_gettime): +# if (defined(_POSIX_TIMERS) && (_POSIX_TIMERS+0 >= 0)) +# define BOOST_HAS_CLOCK_GETTIME +# endif + + // BOOST_HAS_SCHED_YIELD: + // This is predicated on _POSIX_PRIORITY_SCHEDULING or + // on _POSIX_THREAD_PRIORITY_SCHEDULING or on _XOPEN_REALTIME. +# if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING+0 > 0)\ + || (defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING+0 > 0))\ + || (defined(_XOPEN_REALTIME) && (_XOPEN_REALTIME+0 >= 0)) +# define BOOST_HAS_SCHED_YIELD +# endif + + // BOOST_HAS_GETTIMEOFDAY: + // BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE: + // These are predicated on _XOPEN_VERSION, and appears to be first released + // in issue 4, version 2 (_XOPEN_VERSION > 500). + // Likewise for the functions log1p and expm1. +# if defined(_XOPEN_VERSION) && (_XOPEN_VERSION+0 >= 500) +# define BOOST_HAS_GETTIMEOFDAY +# if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE+0 >= 500) +# define BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# endif +# ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +# endif +# ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +# endif +# endif + +# endif + + + + diff --git a/modules/libcom/vxWorks/boost/config/select_compiler_config.hpp b/modules/libcom/vxWorks/boost/config/select_compiler_config.hpp new file mode 100644 index 000000000..0d47b2542 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/select_compiler_config.hpp @@ -0,0 +1,112 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Martin Wille 2003. +// (C) Copyright Guillaume Melquiond 2003. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for most recent version. + +// locate which compiler we are using and define +// BOOST_COMPILER_CONFIG as needed: + +#if defined(__GCCXML__) +// GCC-XML emulates other compilers, it has to appear first here! +# define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc_xml.hpp" + +#elif defined(_CRAYC) +// EDG based Cray compiler: +# define BOOST_COMPILER_CONFIG "boost/config/compiler/cray.hpp" + +#elif defined __CUDACC__ +// NVIDIA CUDA C++ compiler for GPU +# define BOOST_COMPILER_CONFIG "boost/config/compiler/nvcc.hpp" + +#elif defined __COMO__ +// Comeau C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/comeau.hpp" + +#elif defined(__PATHSCALE__) && (__PATHCC__ >= 4) +// PathScale EKOPath compiler (has to come before clang and gcc) +# define BOOST_COMPILER_CONFIG "boost/config/compiler/pathscale.hpp" + +#elif defined __clang__ +// Clang C++ emulates GCC, so it has to appear early. +# define BOOST_COMPILER_CONFIG "boost/config/compiler/clang.hpp" + +#elif defined __DMC__ +// Digital Mars C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/digitalmars.hpp" + +#elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) +// Intel +# define BOOST_COMPILER_CONFIG "boost/config/compiler/intel.hpp" + +# elif defined __GNUC__ +// GNU C++: +# define BOOST_COMPILER_CONFIG "boost/config/compiler/gcc.hpp" + +#elif defined __KCC +// Kai C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/kai.hpp" + +#elif defined __sgi +// SGI MIPSpro C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/sgi_mipspro.hpp" + +#elif defined __DECCXX +// Compaq Tru64 Unix cxx +# define BOOST_COMPILER_CONFIG "boost/config/compiler/compaq_cxx.hpp" + +#elif defined __ghs +// Greenhills C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/greenhills.hpp" + +#elif defined __CODEGEARC__ +// CodeGear - must be checked for before Borland +# define BOOST_COMPILER_CONFIG "boost/config/compiler/codegear.hpp" + +#elif defined __BORLANDC__ +// Borland +# define BOOST_COMPILER_CONFIG "boost/config/compiler/borland.hpp" + +#elif defined __MWERKS__ +// Metrowerks CodeWarrior +# define BOOST_COMPILER_CONFIG "boost/config/compiler/metrowerks.hpp" + +#elif defined __SUNPRO_CC +// Sun Workshop Compiler C++ +# define BOOST_COMPILER_CONFIG "boost/config/compiler/sunpro_cc.hpp" + +#elif defined __HP_aCC +// HP aCC +# define BOOST_COMPILER_CONFIG "boost/config/compiler/hp_acc.hpp" + +#elif defined(__MRC__) || defined(__SC__) +// MPW MrCpp or SCpp +# define BOOST_COMPILER_CONFIG "boost/config/compiler/mpw.hpp" + +#elif defined(__IBMCPP__) +// IBM Visual Age +# define BOOST_COMPILER_CONFIG "boost/config/compiler/vacpp.hpp" + +#elif defined(__PGI) +// Portland Group Inc. +# define BOOST_COMPILER_CONFIG "boost/config/compiler/pgi.hpp" + +#elif defined _MSC_VER +// Microsoft Visual C++ +// +// Must remain the last #elif since some other vendors (Metrowerks, for +// example) also #define _MSC_VER +# define BOOST_COMPILER_CONFIG "boost/config/compiler/visualc.hpp" + +#elif defined (BOOST_ASSERT_CONFIG) +// this must come last - generate an error if we don't +// recognise the compiler: +# error "Unknown compiler - please configure (http://www.boost.org/libs/config/config.htm#configuring) and report the results to the main boost mailing list (http://www.boost.org/more/mailing_lists.htm#main)" + +#endif diff --git a/modules/libcom/vxWorks/boost/config/select_platform_config.hpp b/modules/libcom/vxWorks/boost/config/select_platform_config.hpp new file mode 100644 index 000000000..2af61d2d4 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/select_platform_config.hpp @@ -0,0 +1,105 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2002. +// (C) Copyright Jens Maurer 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// locate which platform we are on and define BOOST_PLATFORM_CONFIG as needed. +// Note that we define the headers to include using "header_name" not +// in order to prevent macro expansion within the header +// name (for example "linux" is a macro on linux systems). + +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && !defined(_CRAYC) +// linux, also other platforms (Hurd etc) that use GLIBC, should these really have their own config headers though? +# define BOOST_PLATFORM_CONFIG "boost/config/platform/linux.hpp" + +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +// BSD: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/bsd.hpp" + +#elif defined(sun) || defined(__sun) +// solaris: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/solaris.hpp" + +#elif defined(__sgi) +// SGI Irix: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/irix.hpp" + +#elif defined(__hpux) +// hp unix: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/hpux.hpp" + +#elif defined(__CYGWIN__) +// cygwin is not win32: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/cygwin.hpp" + +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +// win32: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/win32.hpp" + +#elif defined(__BEOS__) +// BeOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/beos.hpp" + +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +// MacOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/macos.hpp" + +#elif defined(__IBMCPP__) || defined(_AIX) +// IBM +# define BOOST_PLATFORM_CONFIG "boost/config/platform/aix.hpp" + +#elif defined(__amigaos__) +// AmigaOS +# define BOOST_PLATFORM_CONFIG "boost/config/platform/amigaos.hpp" + +#elif defined(__QNXNTO__) +// QNX: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/qnxnto.hpp" + +#elif defined(__VXWORKS__) +// vxWorks: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/vxworks.hpp" + +#elif defined(__SYMBIAN32__) +// Symbian: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/symbian.hpp" + +#elif defined(_CRAYC) +// Cray: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/cray.hpp" + +#elif defined(__VMS) +// VMS: +# define BOOST_PLATFORM_CONFIG "boost/config/platform/vms.hpp" +#else + +# if defined(unix) \ + || defined(__unix) \ + || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) + + // generic unix platform: + +# ifndef BOOST_HAS_UNISTD_H +# define BOOST_HAS_UNISTD_H +# endif + +# include + +# endif + +# if defined (BOOST_ASSERT_CONFIG) + // this must come last - generate an error if we don't + // recognise the platform: +# error "Unknown platform - please configure and report the results to boost.org" +# endif + +#endif + + + diff --git a/modules/libcom/vxWorks/boost/config/select_stdlib_config.hpp b/modules/libcom/vxWorks/boost/config/select_stdlib_config.hpp new file mode 100644 index 000000000..96ede0022 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/select_stdlib_config.hpp @@ -0,0 +1,85 @@ +// Boost compiler configuration selection header file + +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001 - 2002. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// See http://www.boost.org for most recent version. + +// locate which std lib we are using and define BOOST_STDLIB_CONFIG as needed: + +// First include to determine if some version of STLport is in use as the std lib +// (do not rely on this header being included since users can short-circuit this header +// if they know whose std lib they are using.) +#ifdef __cplusplus +# include +#else +# include +#endif + +#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION) +// STLPort library; this _must_ come first, otherwise since +// STLport typically sits on top of some other library, we +// can end up detecting that first rather than STLport: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/stlport.hpp" + +#else + +// If our std lib was not some version of STLport, then include as it is about +// the smallest of the std lib headers that includes real C++ stuff. (Some std libs do not +// include their C++-related macros in so this additional include makes sure +// we get those definitions) +// (again do not rely on this header being included since users can short-circuit this +// header if they know whose std lib they are using.) +#include + +#if defined(__LIBCOMO__) +// Comeau STL: +#define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcomo.hpp" + +#elif defined(__STD_RWCOMPILER_H__) || defined(_RWSTD_VER) +// Rogue Wave library: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/roguewave.hpp" + +#elif defined(_LIBCPP_VERSION) +// libc++ +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/libcpp.hpp" + +#elif defined(__GLIBCPP__) || defined(__GLIBCXX__) +// GNU libstdc++ 3 +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/libstdcpp3.hpp" + +#elif defined(__STL_CONFIG_H) +// generic SGI STL +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/sgi.hpp" + +#elif defined(__MSL_CPP__) +// MSL standard lib: +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/msl.hpp" + +#elif defined(__IBMCPP__) +// take the default VACPP std lib +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/vacpp.hpp" + +#elif defined(MSIPL_COMPILE_H) +// Modena C++ standard library +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/modena.hpp" + +#elif (defined(_YVALS) && !defined(__IBMCPP__)) || defined(_CPPLIB_VER) +// Dinkumware Library (this has to appear after any possible replacement libraries): +# define BOOST_STDLIB_CONFIG "boost/config/stdlib/dinkumware.hpp" + +#elif defined (BOOST_ASSERT_CONFIG) +// this must come last - generate an error if we don't +// recognise the library: +# error "Unknown standard library - please configure and report the results to boost.org" + +#endif + +#endif + + + diff --git a/modules/libcom/vxWorks/boost/config/stdlib/dinkumware.hpp b/modules/libcom/vxWorks/boost/config/stdlib/dinkumware.hpp new file mode 100644 index 000000000..305885177 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/stdlib/dinkumware.hpp @@ -0,0 +1,155 @@ +// (C) Copyright John Maddock 2001 - 2003. +// (C) Copyright Jens Maurer 2001. +// (C) Copyright Peter Dimov 2001. +// (C) Copyright David Abrahams 2002. +// (C) Copyright Guillaume Melquiond 2003. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org for most recent version. + +// Dinkumware standard library config: + +#if !defined(_YVALS) && !defined(_CPPLIB_VER) +#include +#if !defined(_YVALS) && !defined(_CPPLIB_VER) +#error This is not the Dinkumware lib! +#endif +#endif + + +#if defined(_CPPLIB_VER) && (_CPPLIB_VER >= 306) + // full dinkumware 3.06 and above + // fully conforming provided the compiler supports it: +# if !(defined(_GLOBAL_USING) && (_GLOBAL_USING+0 > 0)) && !defined(__BORLANDC__) && !defined(_STD) && !(defined(__ICC) && (__ICC >= 700)) // can be defined in yvals.h +# define BOOST_NO_STDC_NAMESPACE +# endif +# if !(defined(_HAS_MEMBER_TEMPLATES_REBIND) && (_HAS_MEMBER_TEMPLATES_REBIND+0 > 0)) && !(defined(_MSC_VER) && (_MSC_VER > 1300)) && defined(BOOST_MSVC) +# define BOOST_NO_STD_ALLOCATOR +# endif +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +# if defined(BOOST_MSVC) && (BOOST_MSVC < 1300) + // if this lib version is set up for vc6 then there is no std::use_facet: +# define BOOST_NO_STD_USE_FACET +# define BOOST_HAS_TWO_ARG_USE_FACET + // C lib functions aren't in namespace std either: +# define BOOST_NO_STDC_NAMESPACE + // and nor is +# define BOOST_NO_EXCEPTION_STD_NAMESPACE +# endif +// There's no numeric_limits support unless _LONGLONG is defined: +# if !defined(_LONGLONG) && (_CPPLIB_VER <= 310) +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# endif +// 3.06 appears to have (non-sgi versions of) & , +// and no at all +#else +# define BOOST_MSVC_STD_ITERATOR 1 +# define BOOST_NO_STD_ITERATOR +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# define BOOST_NO_STD_ALLOCATOR +# define BOOST_NO_STDC_NAMESPACE +# define BOOST_NO_STD_USE_FACET +# define BOOST_NO_STD_OUTPUT_ITERATOR_ASSIGN +# define BOOST_HAS_MACRO_USE_FACET +# ifndef _CPPLIB_VER + // Updated Dinkum library defines this, and provides + // its own min and max definitions, as does MTA version. +# ifndef __MTA__ +# define BOOST_NO_STD_MIN_MAX +# endif +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# endif +#endif + +// +// std extension namespace is stdext for vc7.1 and later, +// the same applies to other compilers that sit on top +// of vc7.1 (Intel and Comeau): +// +#if defined(_MSC_VER) && (_MSC_VER >= 1310) && !defined(__BORLANDC__) +# define BOOST_STD_EXTENSION_NAMESPACE stdext +#endif + + +#if (defined(_MSC_VER) && (_MSC_VER <= 1300) && !defined(__BORLANDC__)) || !defined(_CPPLIB_VER) || (_CPPLIB_VER < 306) + // if we're using a dinkum lib that's + // been configured for VC6/7 then there is + // no iterator traits (true even for icl) +# define BOOST_NO_STD_ITERATOR_TRAITS +#endif + +#if defined(__ICL) && (__ICL < 800) && defined(_CPPLIB_VER) && (_CPPLIB_VER <= 310) +// Intel C++ chokes over any non-trivial use of +// this may be an overly restrictive define, but regex fails without it: +# define BOOST_NO_STD_LOCALE +#endif + +#include +#if ( (!_HAS_EXCEPTIONS && !defined(__ghs__)) || (!_HAS_NAMESPACE && defined(__ghs__)) ) +# define BOOST_NO_STD_TYPEINFO +#endif + +// C++0x headers implemented in 520 (as shipped by Microsoft) +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 520 +# define BOOST_NO_CXX11_HDR_ARRAY +# define BOOST_NO_CXX11_HDR_CODECVT +# define BOOST_NO_CXX11_HDR_FORWARD_LIST +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# define BOOST_NO_CXX11_HDR_RANDOM +# define BOOST_NO_CXX11_HDR_REGEX +# define BOOST_NO_CXX11_HDR_SYSTEM_ERROR +# define BOOST_NO_CXX11_HDR_UNORDERED_MAP +# define BOOST_NO_CXX11_HDR_UNORDERED_SET +# define BOOST_NO_CXX11_HDR_TUPLE +# define BOOST_NO_CXX11_HDR_TYPEINDEX +# define BOOST_NO_CXX11_HDR_FUNCTIONAL +# define BOOST_NO_CXX11_NUMERIC_LIMITS +# define BOOST_NO_CXX11_SMART_PTR +#endif + +#if (!defined(_HAS_TR1_IMPORTS) || (_HAS_TR1_IMPORTS+0 == 0)) && !defined(BOOST_NO_CXX11_HDR_TUPLE) +# define BOOST_NO_CXX11_HDR_TUPLE +#endif + +// C++0x headers implemented in 540 (as shipped by Microsoft) +// +#if !defined(_CPPLIB_VER) || _CPPLIB_VER < 540 +# define BOOST_NO_CXX11_HDR_TYPE_TRAITS +# define BOOST_NO_CXX11_HDR_CHRONO +# define BOOST_NO_CXX11_HDR_CONDITION_VARIABLE +# define BOOST_NO_CXX11_HDR_FUTURE +# define BOOST_NO_CXX11_HDR_MUTEX +# define BOOST_NO_CXX11_HDR_RATIO +# define BOOST_NO_CXX11_HDR_THREAD +# define BOOST_NO_CXX11_ALLOCATOR +# define BOOST_NO_CXX11_ATOMIC_SMART_PTR +#endif + +// +// C++0x headers not yet (fully) implemented: +// +# define BOOST_NO_CXX11_HDR_INITIALIZER_LIST + +#ifdef _CPPLIB_VER +# define BOOST_DINKUMWARE_STDLIB _CPPLIB_VER +#else +# define BOOST_DINKUMWARE_STDLIB 1 +#endif + +#ifdef _CPPLIB_VER +# define BOOST_STDLIB "Dinkumware standard library version " BOOST_STRINGIZE(_CPPLIB_VER) +#else +# define BOOST_STDLIB "Dinkumware standard library version 1.x" +#endif + + + + + + + + + diff --git a/modules/libcom/vxWorks/boost/config/suffix.hpp b/modules/libcom/vxWorks/boost/config/suffix.hpp new file mode 100644 index 000000000..8ee88d90f --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/suffix.hpp @@ -0,0 +1,920 @@ +// Boost config.hpp configuration header file ------------------------------// +// boostinspect:ndprecated_macros -- tell the inspect tool to ignore this file + +// Copyright (c) 2001-2003 John Maddock +// Copyright (c) 2001 Darin Adler +// Copyright (c) 2001 Peter Dimov +// Copyright (c) 2002 Bill Kempf +// Copyright (c) 2002 Jens Maurer +// Copyright (c) 2002-2003 David Abrahams +// Copyright (c) 2003 Gennaro Prota +// Copyright (c) 2003 Eric Friedman +// Copyright (c) 2010 Eric Jourdanneau, Joel Falcou +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/ for most recent version. + +// Boost config.hpp policy and rationale documentation has been moved to +// http://www.boost.org/libs/config/ +// +// This file is intended to be stable, and relatively unchanging. +// It should contain boilerplate code only - no compiler specific +// code unless it is unavoidable - no changes unless unavoidable. + +#ifndef BOOST_CONFIG_SUFFIX_HPP +#define BOOST_CONFIG_SUFFIX_HPP + +#if defined(__GNUC__) && (__GNUC__ >= 4) +// +// Some GCC-4.x versions issue warnings even when __extension__ is used, +// so use this as a workaround: +// +#pragma GCC system_header +#endif + +// +// ensure that visibility macros are always defined, thus symplifying use +// +#ifndef BOOST_SYMBOL_EXPORT +# define BOOST_SYMBOL_EXPORT +#endif +#ifndef BOOST_SYMBOL_IMPORT +# define BOOST_SYMBOL_IMPORT +#endif +#ifndef BOOST_SYMBOL_VISIBLE +# define BOOST_SYMBOL_VISIBLE +#endif + +// +// look for long long by looking for the appropriate macros in . +// Note that we use limits.h rather than climits for maximal portability, +// remember that since these just declare a bunch of macros, there should be +// no namespace issues from this. +// +#if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG) \ + && !defined(BOOST_MSVC) && !defined(__BORLANDC__) +# include +# if (defined(ULLONG_MAX) || defined(ULONG_LONG_MAX) || defined(ULONGLONG_MAX)) +# define BOOST_HAS_LONG_LONG +# else +# define BOOST_NO_LONG_LONG +# endif +#endif + +// GCC 3.x will clean up all of those nasty macro definitions that +// BOOST_NO_CTYPE_FUNCTIONS is intended to help work around, so undefine +// it under GCC 3.x. +#if defined(__GNUC__) && (__GNUC__ >= 3) && defined(BOOST_NO_CTYPE_FUNCTIONS) +# undef BOOST_NO_CTYPE_FUNCTIONS +#endif + +// +// Assume any extensions are in namespace std:: unless stated otherwise: +// +# ifndef BOOST_STD_EXTENSION_NAMESPACE +# define BOOST_STD_EXTENSION_NAMESPACE std +# endif + +// +// If cv-qualified specializations are not allowed, then neither are cv-void ones: +// +# if defined(BOOST_NO_CV_SPECIALIZATIONS) \ + && !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) +# define BOOST_NO_CV_VOID_SPECIALIZATIONS +# endif + +// +// If there is no numeric_limits template, then it can't have any compile time +// constants either! +// +# if defined(BOOST_NO_LIMITS) \ + && !defined(BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS) +# define BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +# define BOOST_NO_LONG_LONG_NUMERIC_LIMITS +# endif + +// +// if there is no long long then there is no specialisation +// for numeric_limits either: +// +#if !defined(BOOST_HAS_LONG_LONG) && !defined(BOOST_NO_LONG_LONG_NUMERIC_LIMITS) +# define BOOST_NO_LONG_LONG_NUMERIC_LIMITS +#endif + +// +// if there is no __int64 then there is no specialisation +// for numeric_limits<__int64> either: +// +#if !defined(BOOST_HAS_MS_INT64) && !defined(BOOST_NO_MS_INT64_NUMERIC_LIMITS) +# define BOOST_NO_MS_INT64_NUMERIC_LIMITS +#endif + +// +// if member templates are supported then so is the +// VC6 subset of member templates: +// +# if !defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) +# define BOOST_MSVC6_MEMBER_TEMPLATES +# endif + +// +// Without partial specialization, can't test for partial specialisation bugs: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_BCB_PARTIAL_SPECIALIZATION_BUG) +# define BOOST_BCB_PARTIAL_SPECIALIZATION_BUG +# endif + +// +// Without partial specialization, we can't have array-type partial specialisations: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS) +# define BOOST_NO_ARRAY_TYPE_SPECIALIZATIONS +# endif + +// +// Without partial specialization, std::iterator_traits can't work: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_STD_ITERATOR_TRAITS) +# define BOOST_NO_STD_ITERATOR_TRAITS +# endif + +// +// Without partial specialization, partial +// specialization with default args won't work either: +// +# if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) \ + && !defined(BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS) +# define BOOST_NO_PARTIAL_SPECIALIZATION_IMPLICIT_DEFAULT_ARGS +# endif + +// +// Without member template support, we can't have template constructors +// in the standard library either: +// +# if defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ + && !defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS) +# define BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS +# endif + +// +// Without member template support, we can't have a conforming +// std::allocator template either: +// +# if defined(BOOST_NO_MEMBER_TEMPLATES) \ + && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) \ + && !defined(BOOST_NO_STD_ALLOCATOR) +# define BOOST_NO_STD_ALLOCATOR +# endif + +// +// without ADL support then using declarations will break ADL as well: +// +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP) && !defined(BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL) +# define BOOST_FUNCTION_SCOPE_USING_DECLARATION_BREAKS_ADL +#endif + +// +// Without typeid support we have no dynamic RTTI either: +// +#if defined(BOOST_NO_TYPEID) && !defined(BOOST_NO_RTTI) +# define BOOST_NO_RTTI +#endif + +// +// If we have a standard allocator, then we have a partial one as well: +// +#if !defined(BOOST_NO_STD_ALLOCATOR) +# define BOOST_HAS_PARTIAL_STD_ALLOCATOR +#endif + +// +// We can't have a working std::use_facet if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_USE_FACET) +# define BOOST_NO_STD_USE_FACET +# endif + +// +// We can't have a std::messages facet if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_MESSAGES) +# define BOOST_NO_STD_MESSAGES +# endif + +// +// We can't have a working std::wstreambuf if there is no std::locale: +// +# if defined(BOOST_NO_STD_LOCALE) && !defined(BOOST_NO_STD_WSTREAMBUF) +# define BOOST_NO_STD_WSTREAMBUF +# endif + +// +// We can't have a if there is no : +// +# if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_CWCTYPE) +# define BOOST_NO_CWCTYPE +# endif + +// +// We can't have a swprintf if there is no : +// +# if defined(BOOST_NO_CWCHAR) && !defined(BOOST_NO_SWPRINTF) +# define BOOST_NO_SWPRINTF +# endif + +// +// If Win32 support is turned off, then we must turn off +// threading support also, unless there is some other +// thread API enabled: +// +#if defined(BOOST_DISABLE_WIN32) && defined(_WIN32) \ + && !defined(BOOST_DISABLE_THREADS) && !defined(BOOST_HAS_PTHREADS) +# define BOOST_DISABLE_THREADS +#endif + +// +// Turn on threading support if the compiler thinks that it's in +// multithreaded mode. We put this here because there are only a +// limited number of macros that identify this (if there's any missing +// from here then add to the appropriate compiler section): +// +#if (defined(__MT__) || defined(_MT) || defined(_REENTRANT) \ + || defined(_PTHREADS) || defined(__APPLE__) || defined(__DragonFly__)) \ + && !defined(BOOST_HAS_THREADS) +# define BOOST_HAS_THREADS +#endif + +// +// Turn threading support off if BOOST_DISABLE_THREADS is defined: +// +#if defined(BOOST_DISABLE_THREADS) && defined(BOOST_HAS_THREADS) +# undef BOOST_HAS_THREADS +#endif + +// +// Turn threading support off if we don't recognise the threading API: +// +#if defined(BOOST_HAS_THREADS) && !defined(BOOST_HAS_PTHREADS)\ + && !defined(BOOST_HAS_WINTHREADS) && !defined(BOOST_HAS_BETHREADS)\ + && !defined(BOOST_HAS_MPTASKS) +# undef BOOST_HAS_THREADS +#endif + +// +// Turn threading detail macros off if we don't (want to) use threading +// +#ifndef BOOST_HAS_THREADS +# undef BOOST_HAS_PTHREADS +# undef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE +# undef BOOST_HAS_PTHREAD_YIELD +# undef BOOST_HAS_PTHREAD_DELAY_NP +# undef BOOST_HAS_WINTHREADS +# undef BOOST_HAS_BETHREADS +# undef BOOST_HAS_MPTASKS +#endif + +// +// If the compiler claims to be C99 conformant, then it had better +// have a : +// +# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) +# define BOOST_HAS_STDINT_H +# ifndef BOOST_HAS_LOG1P +# define BOOST_HAS_LOG1P +# endif +# ifndef BOOST_HAS_EXPM1 +# define BOOST_HAS_EXPM1 +# endif +# endif + +// +// Define BOOST_NO_SLIST and BOOST_NO_HASH if required. +// Note that this is for backwards compatibility only. +// +# if !defined(BOOST_HAS_SLIST) && !defined(BOOST_NO_SLIST) +# define BOOST_NO_SLIST +# endif + +# if !defined(BOOST_HAS_HASH) && !defined(BOOST_NO_HASH) +# define BOOST_NO_HASH +# endif + +// +// Set BOOST_SLIST_HEADER if not set already: +// +#if defined(BOOST_HAS_SLIST) && !defined(BOOST_SLIST_HEADER) +# define BOOST_SLIST_HEADER +#endif + +// +// Set BOOST_HASH_SET_HEADER if not set already: +// +#if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_SET_HEADER) +# define BOOST_HASH_SET_HEADER +#endif + +// +// Set BOOST_HASH_MAP_HEADER if not set already: +// +#if defined(BOOST_HAS_HASH) && !defined(BOOST_HASH_MAP_HEADER) +# define BOOST_HASH_MAP_HEADER +#endif + +// BOOST_HAS_ABI_HEADERS +// This macro gets set if we have headers that fix the ABI, +// and prevent ODR violations when linking to external libraries: +#if defined(BOOST_ABI_PREFIX) && defined(BOOST_ABI_SUFFIX) && !defined(BOOST_HAS_ABI_HEADERS) +# define BOOST_HAS_ABI_HEADERS +#endif + +#if defined(BOOST_HAS_ABI_HEADERS) && defined(BOOST_DISABLE_ABI_HEADERS) +# undef BOOST_HAS_ABI_HEADERS +#endif + +// BOOST_NO_STDC_NAMESPACE workaround --------------------------------------// +// Because std::size_t usage is so common, even in boost headers which do not +// otherwise use the C library, the workaround is included here so +// that ugly workaround code need not appear in many other boost headers. +// NOTE WELL: This is a workaround for non-conforming compilers; +// must still be #included in the usual places so that inclusion +// works as expected with standard conforming compilers. The resulting +// double inclusion of is harmless. + +# if defined(BOOST_NO_STDC_NAMESPACE) && defined(__cplusplus) +# include + namespace std { using ::ptrdiff_t; using ::size_t; } +# endif + +// Workaround for the unfortunate min/max macros defined by some platform headers + +#define BOOST_PREVENT_MACRO_SUBSTITUTION + +#ifndef BOOST_USING_STD_MIN +# define BOOST_USING_STD_MIN() using std::min +#endif + +#ifndef BOOST_USING_STD_MAX +# define BOOST_USING_STD_MAX() using std::max +#endif + +// BOOST_NO_STD_MIN_MAX workaround -----------------------------------------// + +# if defined(BOOST_NO_STD_MIN_MAX) && defined(__cplusplus) + +namespace std { + template + inline const _Tp& min BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { + return __b < __a ? __b : __a; + } + template + inline const _Tp& max BOOST_PREVENT_MACRO_SUBSTITUTION (const _Tp& __a, const _Tp& __b) { + return __a < __b ? __b : __a; + } +} + +# endif + +// BOOST_STATIC_CONSTANT workaround --------------------------------------- // +// On compilers which don't allow in-class initialization of static integral +// constant members, we must use enums as a workaround if we want the constants +// to be available at compile-time. This macro gives us a convenient way to +// declare such constants. + +# ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION +# define BOOST_STATIC_CONSTANT(type, assignment) enum { assignment } +# else +# define BOOST_STATIC_CONSTANT(type, assignment) static const type assignment +# endif + +// BOOST_USE_FACET / HAS_FACET workaround ----------------------------------// +// When the standard library does not have a conforming std::use_facet there +// are various workarounds available, but they differ from library to library. +// The same problem occurs with has_facet. +// These macros provide a consistent way to access a locale's facets. +// Usage: +// replace +// std::use_facet(loc); +// with +// BOOST_USE_FACET(Type, loc); +// Note do not add a std:: prefix to the front of BOOST_USE_FACET! +// Use for BOOST_HAS_FACET is analogous. + +#if defined(BOOST_NO_STD_USE_FACET) +# ifdef BOOST_HAS_TWO_ARG_USE_FACET +# define BOOST_USE_FACET(Type, loc) std::use_facet(loc, static_cast(0)) +# define BOOST_HAS_FACET(Type, loc) std::has_facet(loc, static_cast(0)) +# elif defined(BOOST_HAS_MACRO_USE_FACET) +# define BOOST_USE_FACET(Type, loc) std::_USE(loc, Type) +# define BOOST_HAS_FACET(Type, loc) std::_HAS(loc, Type) +# elif defined(BOOST_HAS_STLP_USE_FACET) +# define BOOST_USE_FACET(Type, loc) (*std::_Use_facet(loc)) +# define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) +# endif +#else +# define BOOST_USE_FACET(Type, loc) std::use_facet< Type >(loc) +# define BOOST_HAS_FACET(Type, loc) std::has_facet< Type >(loc) +#endif + +// BOOST_NESTED_TEMPLATE workaround ------------------------------------------// +// Member templates are supported by some compilers even though they can't use +// the A::template member syntax, as a workaround replace: +// +// typedef typename A::template rebind binder; +// +// with: +// +// typedef typename A::BOOST_NESTED_TEMPLATE rebind binder; + +#ifndef BOOST_NO_MEMBER_TEMPLATE_KEYWORD +# define BOOST_NESTED_TEMPLATE template +#else +# define BOOST_NESTED_TEMPLATE +#endif + +// BOOST_UNREACHABLE_RETURN(x) workaround -------------------------------------// +// Normally evaluates to nothing, unless BOOST_NO_UNREACHABLE_RETURN_DETECTION +// is defined, in which case it evaluates to return x; Use when you have a return +// statement that can never be reached. + +#ifdef BOOST_NO_UNREACHABLE_RETURN_DETECTION +# define BOOST_UNREACHABLE_RETURN(x) return x; +#else +# define BOOST_UNREACHABLE_RETURN(x) +#endif + +// BOOST_DEDUCED_TYPENAME workaround ------------------------------------------// +// +// Some compilers don't support the use of `typename' for dependent +// types in deduced contexts, e.g. +// +// template void f(T, typename T::type); +// ^^^^^^^^ +// Replace these declarations with: +// +// template void f(T, BOOST_DEDUCED_TYPENAME T::type); + +#ifndef BOOST_NO_DEDUCED_TYPENAME +# define BOOST_DEDUCED_TYPENAME typename +#else +# define BOOST_DEDUCED_TYPENAME +#endif + +#ifndef BOOST_NO_TYPENAME_WITH_CTOR +# define BOOST_CTOR_TYPENAME typename +#else +# define BOOST_CTOR_TYPENAME +#endif + +// long long workaround ------------------------------------------// +// On gcc (and maybe other compilers?) long long is alway supported +// but it's use may generate either warnings (with -ansi), or errors +// (with -pedantic -ansi) unless it's use is prefixed by __extension__ +// +#if defined(BOOST_HAS_LONG_LONG) && defined(__cplusplus) +namespace boost{ +# ifdef __GNUC__ + __extension__ typedef long long long_long_type; + __extension__ typedef unsigned long long ulong_long_type; +# else + typedef long long long_long_type; + typedef unsigned long long ulong_long_type; +# endif +} +#endif +// same again for __int128: +#if defined(BOOST_HAS_INT128) && defined(__cplusplus) +namespace boost{ +# ifdef __GNUC__ + __extension__ typedef __int128 int128_type; + __extension__ typedef unsigned __int128 uint128_type; +# else + typedef __int128 int128_type; + typedef unsigned __int128 uint128_type; +# endif +} +#endif + +// BOOST_[APPEND_]EXPLICIT_TEMPLATE_[NON_]TYPE macros --------------------------// +// +// Some compilers have problems with function templates whose template +// parameters don't appear in the function parameter list (basically +// they just link one instantiation of the template in the final +// executable). These macros provide a uniform way to cope with the +// problem with no effects on the calling syntax. + +// Example: +// +// #include +// #include +// #include +// +// template +// void f() { std::cout << n << ' '; } +// +// template +// void g() { std::cout << typeid(T).name() << ' '; } +// +// int main() { +// f<1>(); +// f<2>(); +// +// g(); +// g(); +// } +// +// With VC++ 6.0 the output is: +// +// 2 2 double double +// +// To fix it, write +// +// template +// void f(BOOST_EXPLICIT_TEMPLATE_NON_TYPE(int, n)) { ... } +// +// template +// void g(BOOST_EXPLICIT_TEMPLATE_TYPE(T)) { ... } +// + + +#if defined(BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS) && defined(__cplusplus) + +# include "boost/type.hpp" +# include "boost/non_type.hpp" + +# define BOOST_EXPLICIT_TEMPLATE_TYPE(t) boost::type* = 0 +# define BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) boost::type* +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) boost::non_type* = 0 +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) boost::non_type* + +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t) \ + , BOOST_EXPLICIT_TEMPLATE_TYPE(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE_SPEC(t) \ + , BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v) \ + , BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) \ + , BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) + +#else + +// no workaround needed: expand to nothing + +# define BOOST_EXPLICIT_TEMPLATE_TYPE(t) +# define BOOST_EXPLICIT_TEMPLATE_TYPE_SPEC(t) +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE(t, v) +# define BOOST_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) + +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE_SPEC(t) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(t, v) +# define BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE_SPEC(t, v) + + +#endif // defined BOOST_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS + +// When BOOST_NO_STD_TYPEINFO is defined, we can just import +// the global definition into std namespace: +#if defined(BOOST_NO_STD_TYPEINFO) && defined(__cplusplus) +#include +namespace std{ using ::type_info; } +#endif + +// ---------------------------------------------------------------------------// + +// +// Helper macro BOOST_STRINGIZE: +// Converts the parameter X to a string after macro replacement +// on X has been performed. +// +#define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X) +#define BOOST_DO_STRINGIZE(X) #X + +// +// Helper macro BOOST_JOIN: +// The following piece of macro magic joins the two +// arguments together, even when one of the arguments is +// itself a macro (see 16.3.1 in C++ standard). The key +// is that macro expansion of macro arguments does not +// occur in BOOST_DO_JOIN2 but does in BOOST_DO_JOIN. +// +#define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y ) +#define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2(X,Y) +#define BOOST_DO_JOIN2( X, Y ) X##Y + +// +// Set some default values for compiler/library/platform names. +// These are for debugging config setup only: +// +# ifndef BOOST_COMPILER +# define BOOST_COMPILER "Unknown ISO C++ Compiler" +# endif +# ifndef BOOST_STDLIB +# define BOOST_STDLIB "Unknown ISO standard library" +# endif +# ifndef BOOST_PLATFORM +# if defined(unix) || defined(__unix) || defined(_XOPEN_SOURCE) \ + || defined(_POSIX_SOURCE) +# define BOOST_PLATFORM "Generic Unix" +# else +# define BOOST_PLATFORM "Unknown" +# endif +# endif + +// +// Set some default values GPU support +// +# ifndef BOOST_GPU_ENABLED +# define BOOST_GPU_ENABLED +# endif + +// BOOST_FORCEINLINE ---------------------------------------------// +// Macro to use in place of 'inline' to force a function to be inline +#if !defined(BOOST_FORCEINLINE) +# if defined(_MSC_VER) +# define BOOST_FORCEINLINE __forceinline +# elif defined(__GNUC__) && __GNUC__ > 3 +# define BOOST_FORCEINLINE inline __attribute__ ((always_inline)) +# else +# define BOOST_FORCEINLINE inline +# endif +#endif + +// +// Set BOOST_NO_DECLTYPE_N3276 when BOOST_NO_DECLTYPE is defined +// +#if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_CXX11_DECLTYPE_N3276) +#define BOOST_NO_CXX11_DECLTYPE_N3276 BOOST_NO_CXX11_DECLTYPE +#endif + +// -------------------- Deprecated macros for 1.50 --------------------------- +// These will go away in a future release + +// Use BOOST_NO_CXX11_HDR_UNORDERED_SET or BOOST_NO_CXX11_HDR_UNORDERED_MAP +// instead of BOOST_NO_STD_UNORDERED +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) || defined (BOOST_NO_CXX11_HDR_UNORDERED_SET) +# ifndef BOOST_NO_CXX11_STD_UNORDERED +# define BOOST_NO_CXX11_STD_UNORDERED +# endif +#endif + +// Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST instead of BOOST_NO_INITIALIZER_LISTS +#if defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) && !defined(BOOST_NO_INITIALIZER_LISTS) +# define BOOST_NO_INITIALIZER_LISTS +#endif + +// Use BOOST_NO_CXX11_HDR_ARRAY instead of BOOST_NO_0X_HDR_ARRAY +#if defined(BOOST_NO_CXX11_HDR_ARRAY) && !defined(BOOST_NO_BOOST_NO_0X_HDR_ARRAY) +# define BOOST_NO_0X_HDR_ARRAY +#endif +// Use BOOST_NO_CXX11_HDR_CHRONO instead of BOOST_NO_0X_HDR_CHRONO +#if defined(BOOST_NO_CXX11_HDR_CHRONO) && !defined(BOOST_NO_0X_HDR_CHRONO) +# define BOOST_NO_0X_HDR_CHRONO +#endif +// Use BOOST_NO_CXX11_HDR_CODECVT instead of BOOST_NO_0X_HDR_CODECVT +#if defined(BOOST_NO_CXX11_HDR_CODECVT) && !defined(BOOST_NO_0X_HDR_CODECVT) +# define BOOST_NO_0X_HDR_CODECVT +#endif +// Use BOOST_NO_CXX11_HDR_CONDITION_VARIABLE instead of BOOST_NO_0X_HDR_CONDITION_VARIABLE +#if defined(BOOST_NO_CXX11_HDR_CONDITION_VARIABLE) && !defined(BOOST_NO_0X_HDR_CONDITION_VARIABLE) +# define BOOST_NO_0X_HDR_CONDITION_VARIABLE +#endif +// Use BOOST_NO_CXX11_HDR_FORWARD_LIST instead of BOOST_NO_0X_HDR_FORWARD_LIST +#if defined(BOOST_NO_CXX11_HDR_FORWARD_LIST) && !defined(BOOST_NO_0X_HDR_FORWARD_LIST) +# define BOOST_NO_0X_HDR_FORWARD_LIST +#endif +// Use BOOST_NO_CXX11_HDR_FUTURE instead of BOOST_NO_0X_HDR_FUTURE +#if defined(BOOST_NO_CXX11_HDR_FUTURE) && !defined(BOOST_NO_0X_HDR_FUTURE) +# define BOOST_NO_0X_HDR_FUTURE +#endif + +// Use BOOST_NO_CXX11_HDR_INITIALIZER_LIST +// instead of BOOST_NO_0X_HDR_INITIALIZER_LIST or BOOST_NO_INITIALIZER_LISTS +#ifdef BOOST_NO_CXX11_HDR_INITIALIZER_LIST +# ifndef BOOST_NO_0X_HDR_INITIALIZER_LIST +# define BOOST_NO_0X_HDR_INITIALIZER_LIST +# endif +# ifndef BOOST_NO_INITIALIZER_LISTS +# define BOOST_NO_INITIALIZER_LISTS +# endif +#endif + +// Use BOOST_NO_CXX11_HDR_MUTEX instead of BOOST_NO_0X_HDR_MUTEX +#if defined(BOOST_NO_CXX11_HDR_MUTEX) && !defined(BOOST_NO_0X_HDR_MUTEX) +# define BOOST_NO_0X_HDR_MUTEX +#endif +// Use BOOST_NO_CXX11_HDR_RANDOM instead of BOOST_NO_0X_HDR_RANDOM +#if defined(BOOST_NO_CXX11_HDR_RANDOM) && !defined(BOOST_NO_0X_HDR_RANDOM) +# define BOOST_NO_0X_HDR_RANDOM +#endif +// Use BOOST_NO_CXX11_HDR_RATIO instead of BOOST_NO_0X_HDR_RATIO +#if defined(BOOST_NO_CXX11_HDR_RATIO) && !defined(BOOST_NO_0X_HDR_RATIO) +# define BOOST_NO_0X_HDR_RATIO +#endif +// Use BOOST_NO_CXX11_HDR_REGEX instead of BOOST_NO_0X_HDR_REGEX +#if defined(BOOST_NO_CXX11_HDR_REGEX) && !defined(BOOST_NO_0X_HDR_REGEX) +# define BOOST_NO_0X_HDR_REGEX +#endif +// Use BOOST_NO_CXX11_HDR_SYSTEM_ERROR instead of BOOST_NO_0X_HDR_SYSTEM_ERROR +#if defined(BOOST_NO_CXX11_HDR_SYSTEM_ERROR) && !defined(BOOST_NO_0X_HDR_SYSTEM_ERROR) +# define BOOST_NO_0X_HDR_SYSTEM_ERROR +#endif +// Use BOOST_NO_CXX11_HDR_THREAD instead of BOOST_NO_0X_HDR_THREAD +#if defined(BOOST_NO_CXX11_HDR_THREAD) && !defined(BOOST_NO_0X_HDR_THREAD) +# define BOOST_NO_0X_HDR_THREAD +#endif +// Use BOOST_NO_CXX11_HDR_TUPLE instead of BOOST_NO_0X_HDR_TUPLE +#if defined(BOOST_NO_CXX11_HDR_TUPLE) && !defined(BOOST_NO_0X_HDR_TUPLE) +# define BOOST_NO_0X_HDR_TUPLE +#endif +// Use BOOST_NO_CXX11_HDR_TYPE_TRAITS instead of BOOST_NO_0X_HDR_TYPE_TRAITS +#if defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) && !defined(BOOST_NO_0X_HDR_TYPE_TRAITS) +# define BOOST_NO_0X_HDR_TYPE_TRAITS +#endif +// Use BOOST_NO_CXX11_HDR_TYPEINDEX instead of BOOST_NO_0X_HDR_TYPEINDEX +#if defined(BOOST_NO_CXX11_HDR_TYPEINDEX) && !defined(BOOST_NO_0X_HDR_TYPEINDEX) +# define BOOST_NO_0X_HDR_TYPEINDEX +#endif +// Use BOOST_NO_CXX11_HDR_UNORDERED_MAP instead of BOOST_NO_0X_HDR_UNORDERED_MAP +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_MAP) && !defined(BOOST_NO_0X_HDR_UNORDERED_MAP) +# define BOOST_NO_0X_HDR_UNORDERED_MAP +#endif +// Use BOOST_NO_CXX11_HDR_UNORDERED_SET instead of BOOST_NO_0X_HDR_UNORDERED_SET +#if defined(BOOST_NO_CXX11_HDR_UNORDERED_SET) && !defined(BOOST_NO_0X_HDR_UNORDERED_SET) +# define BOOST_NO_0X_HDR_UNORDERED_SET +#endif + +// ------------------ End of deprecated macros for 1.50 --------------------------- + +// -------------------- Deprecated macros for 1.51 --------------------------- +// These will go away in a future release + +// Use BOOST_NO_CXX11_AUTO_DECLARATIONS instead of BOOST_NO_AUTO_DECLARATIONS +#if defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) && !defined(BOOST_NO_AUTO_DECLARATIONS) +# define BOOST_NO_AUTO_DECLARATIONS +#endif +// Use BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS instead of BOOST_NO_AUTO_MULTIDECLARATIONS +#if defined(BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS) && !defined(BOOST_NO_AUTO_MULTIDECLARATIONS) +# define BOOST_NO_AUTO_MULTIDECLARATIONS +#endif +// Use BOOST_NO_CXX11_CHAR16_T instead of BOOST_NO_CHAR16_T +#if defined(BOOST_NO_CXX11_CHAR16_T) && !defined(BOOST_NO_CHAR16_T) +# define BOOST_NO_CHAR16_T +#endif +// Use BOOST_NO_CXX11_CHAR32_T instead of BOOST_NO_CHAR32_T +#if defined(BOOST_NO_CXX11_CHAR32_T) && !defined(BOOST_NO_CHAR32_T) +# define BOOST_NO_CHAR32_T +#endif +// Use BOOST_NO_CXX11_TEMPLATE_ALIASES instead of BOOST_NO_TEMPLATE_ALIASES +#if defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) && !defined(BOOST_NO_TEMPLATE_ALIASES) +# define BOOST_NO_TEMPLATE_ALIASES +#endif +// Use BOOST_NO_CXX11_CONSTEXPR instead of BOOST_NO_CONSTEXPR +#if defined(BOOST_NO_CXX11_CONSTEXPR) && !defined(BOOST_NO_CONSTEXPR) +# define BOOST_NO_CONSTEXPR +#endif +// Use BOOST_NO_CXX11_DECLTYPE_N3276 instead of BOOST_NO_DECLTYPE_N3276 +#if defined(BOOST_NO_CXX11_DECLTYPE_N3276) && !defined(BOOST_NO_DECLTYPE_N3276) +# define BOOST_NO_DECLTYPE_N3276 +#endif +// Use BOOST_NO_CXX11_DECLTYPE instead of BOOST_NO_DECLTYPE +#if defined(BOOST_NO_CXX11_DECLTYPE) && !defined(BOOST_NO_DECLTYPE) +# define BOOST_NO_DECLTYPE +#endif +// Use BOOST_NO_CXX11_DEFAULTED_FUNCTIONS instead of BOOST_NO_DEFAULTED_FUNCTIONS +#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) && !defined(BOOST_NO_DEFAULTED_FUNCTIONS) +# define BOOST_NO_DEFAULTED_FUNCTIONS +#endif +// Use BOOST_NO_CXX11_DELETED_FUNCTIONS instead of BOOST_NO_DELETED_FUNCTIONS +#if defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) && !defined(BOOST_NO_DELETED_FUNCTIONS) +# define BOOST_NO_DELETED_FUNCTIONS +#endif +// Use BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS instead of BOOST_NO_EXPLICIT_CONVERSION_OPERATORS +#if defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) && !defined(BOOST_NO_EXPLICIT_CONVERSION_OPERATORS) +# define BOOST_NO_EXPLICIT_CONVERSION_OPERATORS +#endif +// Use BOOST_NO_CXX11_EXTERN_TEMPLATE instead of BOOST_NO_EXTERN_TEMPLATE +#if defined(BOOST_NO_CXX11_EXTERN_TEMPLATE) && !defined(BOOST_NO_EXTERN_TEMPLATE) +# define BOOST_NO_EXTERN_TEMPLATE +#endif +// Use BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS instead of BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS +#if defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) && !defined(BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) +# define BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS +#endif +// Use BOOST_NO_CXX11_LAMBDAS instead of BOOST_NO_LAMBDAS +#if defined(BOOST_NO_CXX11_LAMBDAS) && !defined(BOOST_NO_LAMBDAS) +# define BOOST_NO_LAMBDAS +#endif +// Use BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS instead of BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS +#if defined(BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS) && !defined(BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS) +# define BOOST_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS +#endif +// Use BOOST_NO_CXX11_NOEXCEPT instead of BOOST_NO_NOEXCEPT +#if defined(BOOST_NO_CXX11_NOEXCEPT) && !defined(BOOST_NO_NOEXCEPT) +# define BOOST_NO_NOEXCEPT +#endif +// Use BOOST_NO_CXX11_NULLPTR instead of BOOST_NO_NULLPTR +#if defined(BOOST_NO_CXX11_NULLPTR) && !defined(BOOST_NO_NULLPTR) +# define BOOST_NO_NULLPTR +#endif +// Use BOOST_NO_CXX11_RAW_LITERALS instead of BOOST_NO_RAW_LITERALS +#if defined(BOOST_NO_CXX11_RAW_LITERALS) && !defined(BOOST_NO_RAW_LITERALS) +# define BOOST_NO_RAW_LITERALS +#endif +// Use BOOST_NO_CXX11_RVALUE_REFERENCES instead of BOOST_NO_RVALUE_REFERENCES +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_NO_RVALUE_REFERENCES) +# define BOOST_NO_RVALUE_REFERENCES +#endif +// Use BOOST_NO_CXX11_SCOPED_ENUMS instead of BOOST_NO_SCOPED_ENUMS +#if defined(BOOST_NO_CXX11_SCOPED_ENUMS) && !defined(BOOST_NO_SCOPED_ENUMS) +# define BOOST_NO_SCOPED_ENUMS +#endif +// Use BOOST_NO_CXX11_STATIC_ASSERT instead of BOOST_NO_STATIC_ASSERT +#if defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_NO_STATIC_ASSERT) +# define BOOST_NO_STATIC_ASSERT +#endif +// Use BOOST_NO_CXX11_STD_UNORDERED instead of BOOST_NO_STD_UNORDERED +#if defined(BOOST_NO_CXX11_STD_UNORDERED) && !defined(BOOST_NO_STD_UNORDERED) +# define BOOST_NO_STD_UNORDERED +#endif +// Use BOOST_NO_CXX11_UNICODE_LITERALS instead of BOOST_NO_UNICODE_LITERALS +#if defined(BOOST_NO_CXX11_UNICODE_LITERALS) && !defined(BOOST_NO_UNICODE_LITERALS) +# define BOOST_NO_UNICODE_LITERALS +#endif +// Use BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX instead of BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX +#if defined(BOOST_NO_CXX11_UNIFIED_INITIALIZATION_SYNTAX) && !defined(BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX) +# define BOOST_NO_UNIFIED_INITIALIZATION_SYNTAX +#endif +// Use BOOST_NO_CXX11_VARIADIC_TEMPLATES instead of BOOST_NO_VARIADIC_TEMPLATES +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_NO_VARIADIC_TEMPLATES) +# define BOOST_NO_VARIADIC_TEMPLATES +#endif +// Use BOOST_NO_CXX11_VARIADIC_MACROS instead of BOOST_NO_VARIADIC_MACROS +#if defined(BOOST_NO_CXX11_VARIADIC_MACROS) && !defined(BOOST_NO_VARIADIC_MACROS) +# define BOOST_NO_VARIADIC_MACROS +#endif +// Use BOOST_NO_CXX11_NUMERIC_LIMITS instead of BOOST_NO_NUMERIC_LIMITS_LOWEST +#if defined(BOOST_NO_CXX11_NUMERIC_LIMITS) && !defined(BOOST_NO_NUMERIC_LIMITS_LOWEST) +# define BOOST_NO_NUMERIC_LIMITS_LOWEST +#endif +// ------------------ End of deprecated macros for 1.51 --------------------------- + + + +// +// Helper macros BOOST_NOEXCEPT, BOOST_NOEXCEPT_IF, BOOST_NOEXCEPT_EXPR +// These aid the transition to C++11 while still supporting C++03 compilers +// +#ifdef BOOST_NO_CXX11_NOEXCEPT +# define BOOST_NOEXCEPT +# define BOOST_NOEXCEPT_IF(Predicate) +# define BOOST_NOEXCEPT_EXPR(Expression) false +#else +# define BOOST_NOEXCEPT noexcept +# define BOOST_NOEXCEPT_IF(Predicate) noexcept((Predicate)) +# define BOOST_NOEXCEPT_EXPR(Expression) noexcept((Expression)) +#endif + +// +// constexpr workarounds +// +#if defined(BOOST_NO_CXX11_CONSTEXPR) +#define BOOST_CONSTEXPR +#define BOOST_CONSTEXPR_OR_CONST const +#else +#define BOOST_CONSTEXPR constexpr +#define BOOST_CONSTEXPR_OR_CONST constexpr +#endif + +#define BOOST_STATIC_CONSTEXPR static BOOST_CONSTEXPR_OR_CONST + +// +// Set BOOST_HAS_STATIC_ASSERT when BOOST_NO_CXX11_STATIC_ASSERT is not defined +// +#if !defined(BOOST_NO_CXX11_STATIC_ASSERT) && !defined(BOOST_HAS_STATIC_ASSERT) +# define BOOST_HAS_STATIC_ASSERT +#endif + +// +// Set BOOST_HAS_RVALUE_REFS when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined +// +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && !defined(BOOST_HAS_RVALUE_REFS) +#define BOOST_HAS_RVALUE_REFS +#endif + +// +// Set BOOST_HAS_VARIADIC_TMPL when BOOST_NO_CXX11_VARIADIC_TEMPLATES is not defined +// +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && !defined(BOOST_HAS_VARIADIC_TMPL) +#define BOOST_HAS_VARIADIC_TMPL +#endif + + +#endif diff --git a/modules/libcom/vxWorks/boost/config/user.hpp b/modules/libcom/vxWorks/boost/config/user.hpp new file mode 100644 index 000000000..5a4a9d477 --- /dev/null +++ b/modules/libcom/vxWorks/boost/config/user.hpp @@ -0,0 +1,124 @@ +// boost/config/user.hpp ---------------------------------------------------// + +// (C) Copyright John Maddock 2001. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Do not check in modified versions of this file, +// This file may be customized by the end user, but not by boost. + +// +// Use this file to define a site and compiler specific +// configuration policy: +// + +// define this to locate a compiler config file: +// #define BOOST_COMPILER_CONFIG + +// define this to locate a stdlib config file: +// #define BOOST_STDLIB_CONFIG + +// define this to locate a platform config file: +// #define BOOST_PLATFORM_CONFIG + +// define this to disable compiler config, +// use if your compiler config has nothing to set: +// #define BOOST_NO_COMPILER_CONFIG + +// define this to disable stdlib config, +// use if your stdlib config has nothing to set: +// #define BOOST_NO_STDLIB_CONFIG + +// define this to disable platform config, +// use if your platform config has nothing to set: +// #define BOOST_NO_PLATFORM_CONFIG + +// define this to disable all config options, +// excluding the user config. Use if your +// setup is fully ISO compliant, and has no +// useful extensions, or for autoconf generated +// setups: +// #define BOOST_NO_CONFIG + +// define this to make the config "optimistic" +// about unknown compiler versions. Normally +// unknown compiler versions are assumed to have +// all the defects of the last known version, however +// setting this flag, causes the config to assume +// that unknown compiler versions are fully conformant +// with the standard: +// #define BOOST_STRICT_CONFIG + +// define this to cause the config to halt compilation +// with an #error if it encounters anything unknown -- +// either an unknown compiler version or an unknown +// compiler/platform/library: +// #define BOOST_ASSERT_CONFIG + + +// define if you want to disable threading support, even +// when available: +// #define BOOST_DISABLE_THREADS + +// define when you want to disable Win32 specific features +// even when available: +// #define BOOST_DISABLE_WIN32 + +// BOOST_DISABLE_ABI_HEADERS: Stops boost headers from including any +// prefix/suffix headers that normally control things like struct +// packing and alignment. +// #define BOOST_DISABLE_ABI_HEADERS + +// BOOST_ABI_PREFIX: A prefix header to include in place of whatever +// boost.config would normally select, any replacement should set up +// struct packing and alignment options as required. +// #define BOOST_ABI_PREFIX my-header-name + +// BOOST_ABI_SUFFIX: A suffix header to include in place of whatever +// boost.config would normally select, any replacement should undo +// the effects of the prefix header. +// #define BOOST_ABI_SUFFIX my-header-name + +// BOOST_ALL_DYN_LINK: Forces all libraries that have separate source, +// to be linked as dll's rather than static libraries on Microsoft Windows +// (this macro is used to turn on __declspec(dllimport) modifiers, so that +// the compiler knows which symbols to look for in a dll rather than in a +// static library). Note that there may be some libraries that can only +// be statically linked (Boost.Test for example) and others which may only +// be dynamically linked (Boost.Threads for example), in these cases this +// macro has no effect. +// #define BOOST_ALL_DYN_LINK + +// BOOST_WHATEVER_DYN_LINK: Forces library "whatever" to be linked as a dll +// rather than a static library on Microsoft Windows: replace the WHATEVER +// part of the macro name with the name of the library that you want to +// dynamically link to, for example use BOOST_DATE_TIME_DYN_LINK or +// BOOST_REGEX_DYN_LINK etc (this macro is used to turn on __declspec(dllimport) +// modifiers, so that the compiler knows which symbols to look for in a dll +// rather than in a static library). +// Note that there may be some libraries that can only be statically linked +// (Boost.Test for example) and others which may only be dynamically linked +// (Boost.Threads for example), in these cases this macro is unsupported. +// #define BOOST_WHATEVER_DYN_LINK + +// BOOST_ALL_NO_LIB: Tells the config system not to automatically select +// which libraries to link against. +// Normally if a compiler supports #pragma lib, then the correct library +// build variant will be automatically selected and linked against, +// simply by the act of including one of that library's headers. +// This macro turns that feature off. +// #define BOOST_ALL_NO_LIB + +// BOOST_WHATEVER_NO_LIB: Tells the config system not to automatically +// select which library to link against for library "whatever", +// replace WHATEVER in the macro name with the name of the library; +// for example BOOST_DATE_TIME_NO_LIB or BOOST_REGEX_NO_LIB. +// Normally if a compiler supports #pragma lib, then the correct library +// build variant will be automatically selected and linked against, simply +// by the act of including one of that library's headers. This macro turns +// that feature off. +// #define BOOST_WHATEVER_NO_LIB + + + diff --git a/modules/libcom/vxWorks/boost/current_function.hpp b/modules/libcom/vxWorks/boost/current_function.hpp new file mode 100644 index 000000000..cb36e35c3 --- /dev/null +++ b/modules/libcom/vxWorks/boost/current_function.hpp @@ -0,0 +1,68 @@ +#ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED +#define BOOST_CURRENT_FUNCTION_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/current_function.hpp - BOOST_CURRENT_FUNCTION +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// http://www.boost.org/libs/utility/current_function.html +// + +namespace boost +{ + +namespace detail +{ + +inline void current_function_helper() +{ + +#if defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__) + +# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ + +#elif defined(__DMC__) && (__DMC__ >= 0x810) + +# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__ + +#elif defined(__FUNCSIG__) + +# define BOOST_CURRENT_FUNCTION __FUNCSIG__ + +#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500)) + +# define BOOST_CURRENT_FUNCTION __FUNCTION__ + +#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550) + +# define BOOST_CURRENT_FUNCTION __FUNC__ + +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901) + +# define BOOST_CURRENT_FUNCTION __func__ + +#else + +# define BOOST_CURRENT_FUNCTION "(unknown)" + +#endif + +} + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_CURRENT_FUNCTION_HPP_INCLUDED + diff --git a/modules/libcom/vxWorks/boost/detail/sp_typeinfo.hpp b/modules/libcom/vxWorks/boost/detail/sp_typeinfo.hpp new file mode 100644 index 000000000..43fae78ef --- /dev/null +++ b/modules/libcom/vxWorks/boost/detail/sp_typeinfo.hpp @@ -0,0 +1,135 @@ +#ifndef BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED +#define BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// detail/sp_typeinfo.hpp +// +// Copyright 2007 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#if defined( BOOST_NO_TYPEID ) + +#include +#include + +namespace boost +{ + +namespace detail +{ + +class sp_typeinfo +{ +private: + + sp_typeinfo( sp_typeinfo const& ); + sp_typeinfo& operator=( sp_typeinfo const& ); + + char const * name_; + +public: + + explicit sp_typeinfo( char const * name ): name_( name ) + { + } + + bool operator==( sp_typeinfo const& rhs ) const + { + return this == &rhs; + } + + bool operator!=( sp_typeinfo const& rhs ) const + { + return this != &rhs; + } + + bool before( sp_typeinfo const& rhs ) const + { + return std::less< sp_typeinfo const* >()( this, &rhs ); + } + + char const* name() const + { + return name_; + } +}; + +template struct sp_typeid_ +{ + static sp_typeinfo ti_; + + static char const * name() + { + return BOOST_CURRENT_FUNCTION; + } +}; + +#if defined(__SUNPRO_CC) +// see #4199, the Sun Studio compiler gets confused about static initialization +// constructor arguments. But an assignment works just fine. +template sp_typeinfo sp_typeid_< T >::ti_ = sp_typeid_< T >::name(); +#else +template sp_typeinfo sp_typeid_< T >::ti_(sp_typeid_< T >::name()); +#endif + +template struct sp_typeid_< T & >: sp_typeid_< T > +{ +}; + +template struct sp_typeid_< T const >: sp_typeid_< T > +{ +}; + +template struct sp_typeid_< T volatile >: sp_typeid_< T > +{ +}; + +template struct sp_typeid_< T const volatile >: sp_typeid_< T > +{ +}; + +} // namespace detail + +} // namespace boost + +#define BOOST_SP_TYPEID(T) (boost::detail::sp_typeid_::ti_) + +#else + +#include + +namespace boost +{ + +namespace detail +{ + +#if defined( BOOST_NO_STD_TYPEINFO ) + +typedef ::type_info sp_typeinfo; + +#else + +typedef std::type_info sp_typeinfo; + +#endif + +} // namespace detail + +} // namespace boost + +#define BOOST_SP_TYPEID(T) typeid(T) + +#endif + +#endif // #ifndef BOOST_DETAIL_SP_TYPEINFO_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/detail/workaround.hpp b/modules/libcom/vxWorks/boost/detail/workaround.hpp new file mode 100644 index 000000000..40b3423b3 --- /dev/null +++ b/modules/libcom/vxWorks/boost/detail/workaround.hpp @@ -0,0 +1,267 @@ +// Copyright David Abrahams 2002. +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef WORKAROUND_DWA2002126_HPP +# define WORKAROUND_DWA2002126_HPP + +// Compiler/library version workaround macro +// +// Usage: +// +// #if BOOST_WORKAROUND(BOOST_MSVC, < 1300) +// // workaround for eVC4 and VC6 +// ... // workaround code here +// #endif +// +// When BOOST_STRICT_CONFIG is defined, expands to 0. Otherwise, the +// first argument must be undefined or expand to a numeric +// value. The above expands to: +// +// (BOOST_MSVC) != 0 && (BOOST_MSVC) < 1300 +// +// When used for workarounds that apply to the latest known version +// and all earlier versions of a compiler, the following convention +// should be observed: +// +// #if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1301)) +// +// The version number in this case corresponds to the last version in +// which the workaround was known to have been required. When +// BOOST_DETECT_OUTDATED_WORKAROUNDS is not the defined, the macro +// BOOST_TESTED_AT(x) expands to "!= 0", which effectively activates +// the workaround for any version of the compiler. When +// BOOST_DETECT_OUTDATED_WORKAROUNDS is defined, a compiler warning or +// error will be issued if the compiler version exceeds the argument +// to BOOST_TESTED_AT(). This can be used to locate workarounds which +// may be obsoleted by newer versions. + +# ifndef BOOST_STRICT_CONFIG + +#include + +#ifndef __BORLANDC__ +#define __BORLANDC___WORKAROUND_GUARD 1 +#else +#define __BORLANDC___WORKAROUND_GUARD 0 +#endif +#ifndef __CODEGEARC__ +#define __CODEGEARC___WORKAROUND_GUARD 1 +#else +#define __CODEGEARC___WORKAROUND_GUARD 0 +#endif +#ifndef _MSC_VER +#define _MSC_VER_WORKAROUND_GUARD 1 +#else +#define _MSC_VER_WORKAROUND_GUARD 0 +#endif +#ifndef _MSC_FULL_VER +#define _MSC_FULL_VER_WORKAROUND_GUARD 1 +#else +#define _MSC_FULL_VER_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_MSVC +#define BOOST_MSVC_WORKAROUND_GUARD 1 +#else +#define BOOST_MSVC_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_MSVC_FULL_VER +#define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 1 +#else +#define BOOST_MSVC_FULL_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC__ +#define __GNUC___WORKAROUND_GUARD 1 +#else +#define __GNUC___WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC_MINOR__ +#define __GNUC_MINOR___WORKAROUND_GUARD 1 +#else +#define __GNUC_MINOR___WORKAROUND_GUARD 0 +#endif +#ifndef __GNUC_PATCHLEVEL__ +#define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 1 +#else +#define __GNUC_PATCHLEVEL___WORKAROUND_GUARD 0 +#endif +#ifndef __IBMCPP__ +#define __IBMCPP___WORKAROUND_GUARD 1 +#else +#define __IBMCPP___WORKAROUND_GUARD 0 +#endif +#ifndef __SUNPRO_CC +#define __SUNPRO_CC_WORKAROUND_GUARD 1 +#else +#define __SUNPRO_CC_WORKAROUND_GUARD 0 +#endif +#ifndef __DECCXX_VER +#define __DECCXX_VER_WORKAROUND_GUARD 1 +#else +#define __DECCXX_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __MWERKS__ +#define __MWERKS___WORKAROUND_GUARD 1 +#else +#define __MWERKS___WORKAROUND_GUARD 0 +#endif +#ifndef __EDG__ +#define __EDG___WORKAROUND_GUARD 1 +#else +#define __EDG___WORKAROUND_GUARD 0 +#endif +#ifndef __EDG_VERSION__ +#define __EDG_VERSION___WORKAROUND_GUARD 1 +#else +#define __EDG_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef __HP_aCC +#define __HP_aCC_WORKAROUND_GUARD 1 +#else +#define __HP_aCC_WORKAROUND_GUARD 0 +#endif +#ifndef __hpxstd98 +#define __hpxstd98_WORKAROUND_GUARD 1 +#else +#define __hpxstd98_WORKAROUND_GUARD 0 +#endif +#ifndef _CRAYC +#define _CRAYC_WORKAROUND_GUARD 1 +#else +#define _CRAYC_WORKAROUND_GUARD 0 +#endif +#ifndef __DMC__ +#define __DMC___WORKAROUND_GUARD 1 +#else +#define __DMC___WORKAROUND_GUARD 0 +#endif +#ifndef MPW_CPLUS +#define MPW_CPLUS_WORKAROUND_GUARD 1 +#else +#define MPW_CPLUS_WORKAROUND_GUARD 0 +#endif +#ifndef __COMO__ +#define __COMO___WORKAROUND_GUARD 1 +#else +#define __COMO___WORKAROUND_GUARD 0 +#endif +#ifndef __COMO_VERSION__ +#define __COMO_VERSION___WORKAROUND_GUARD 1 +#else +#define __COMO_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef __INTEL_COMPILER +#define __INTEL_COMPILER_WORKAROUND_GUARD 1 +#else +#define __INTEL_COMPILER_WORKAROUND_GUARD 0 +#endif +#ifndef __ICL +#define __ICL_WORKAROUND_GUARD 1 +#else +#define __ICL_WORKAROUND_GUARD 0 +#endif +#ifndef _COMPILER_VERSION +#define _COMPILER_VERSION_WORKAROUND_GUARD 1 +#else +#define _COMPILER_VERSION_WORKAROUND_GUARD 0 +#endif + +#ifndef _RWSTD_VER +#define _RWSTD_VER_WORKAROUND_GUARD 1 +#else +#define _RWSTD_VER_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_RWSTD_VER +#define BOOST_RWSTD_VER_WORKAROUND_GUARD 1 +#else +#define BOOST_RWSTD_VER_WORKAROUND_GUARD 0 +#endif +#ifndef __GLIBCPP__ +#define __GLIBCPP___WORKAROUND_GUARD 1 +#else +#define __GLIBCPP___WORKAROUND_GUARD 0 +#endif +#ifndef _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC +#define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 1 +#else +#define _GLIBCXX_USE_C99_FP_MACROS_DYNAMIC_WORKAROUND_GUARD 0 +#endif +#ifndef __SGI_STL_PORT +#define __SGI_STL_PORT_WORKAROUND_GUARD 1 +#else +#define __SGI_STL_PORT_WORKAROUND_GUARD 0 +#endif +#ifndef _STLPORT_VERSION +#define _STLPORT_VERSION_WORKAROUND_GUARD 1 +#else +#define _STLPORT_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef __LIBCOMO_VERSION__ +#define __LIBCOMO_VERSION___WORKAROUND_GUARD 1 +#else +#define __LIBCOMO_VERSION___WORKAROUND_GUARD 0 +#endif +#ifndef _CPPLIB_VER +#define _CPPLIB_VER_WORKAROUND_GUARD 1 +#else +#define _CPPLIB_VER_WORKAROUND_GUARD 0 +#endif + +#ifndef BOOST_INTEL_CXX_VERSION +#define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_CXX_VERSION_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_INTEL_WIN +#define BOOST_INTEL_WIN_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_WIN_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_DINKUMWARE_STDLIB +#define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 1 +#else +#define BOOST_DINKUMWARE_STDLIB_WORKAROUND_GUARD 0 +#endif +#ifndef BOOST_INTEL +#define BOOST_INTEL_WORKAROUND_GUARD 1 +#else +#define BOOST_INTEL_WORKAROUND_GUARD 0 +#endif +// Always define to zero, if it's used it'll be defined my MPL: +#define BOOST_MPL_CFG_GCC_WORKAROUND_GUARD 0 + +# define BOOST_WORKAROUND(symbol, test) \ + ((symbol ## _WORKAROUND_GUARD + 0 == 0) && \ + (symbol != 0) && (1 % (( (symbol test) ) + 1))) +// ^ ^ ^ ^ +// The extra level of parenthesis nesting above, along with the +// BOOST_OPEN_PAREN indirection below, is required to satisfy the +// broken preprocessor in MWCW 8.3 and earlier. +// +// The basic mechanism works as follows: +// (symbol test) + 1 => if (symbol test) then 2 else 1 +// 1 % ((symbol test) + 1) => if (symbol test) then 1 else 0 +// +// The complication with % is for cooperation with BOOST_TESTED_AT(). +// When "test" is BOOST_TESTED_AT(x) and +// BOOST_DETECT_OUTDATED_WORKAROUNDS is #defined, +// +// symbol test => if (symbol <= x) then 1 else -1 +// (symbol test) + 1 => if (symbol <= x) then 2 else 0 +// 1 % ((symbol test) + 1) => if (symbol <= x) then 1 else divide-by-zero +// + +# ifdef BOOST_DETECT_OUTDATED_WORKAROUNDS +# define BOOST_OPEN_PAREN ( +# define BOOST_TESTED_AT(value) > value) ?(-1): BOOST_OPEN_PAREN 1 +# else +# define BOOST_TESTED_AT(value) != ((value)-(value)) +# endif + +# else + +# define BOOST_WORKAROUND(symbol, test) 0 + +# endif + +#endif // WORKAROUND_DWA2002126_HPP diff --git a/modules/libcom/vxWorks/boost/enable_shared_from_this.hpp b/modules/libcom/vxWorks/boost/enable_shared_from_this.hpp new file mode 100644 index 000000000..b1bb63d9a --- /dev/null +++ b/modules/libcom/vxWorks/boost/enable_shared_from_this.hpp @@ -0,0 +1,18 @@ +#ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED +#define BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED + +// +// enable_shared_from_this.hpp +// +// Copyright (c) 2002 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html +// + +#include + +#endif // #ifndef BOOST_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/exception/detail/attribute_noreturn.hpp b/modules/libcom/vxWorks/boost/exception/detail/attribute_noreturn.hpp new file mode 100644 index 000000000..ae9f031ef --- /dev/null +++ b/modules/libcom/vxWorks/boost/exception/detail/attribute_noreturn.hpp @@ -0,0 +1,17 @@ +//Copyright (c) 2009 Emil Dotchevski and Reverge Studios, Inc. + +//Distributed under the Boost Software License, Version 1.0. (See accompanying +//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef UUID_61531AB0680611DEADD5846855D89593 +#define UUID_61531AB0680611DEADD5846855D89593 + +#if defined(_MSC_VER) +#define BOOST_ATTRIBUTE_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define BOOST_ATTRIBUTE_NORETURN __attribute__((__noreturn__)) +#else +#define BOOST_ATTRIBUTE_NORETURN +#endif + +#endif diff --git a/modules/libcom/vxWorks/boost/exception/exception.hpp b/modules/libcom/vxWorks/boost/exception/exception.hpp new file mode 100644 index 000000000..42d278712 --- /dev/null +++ b/modules/libcom/vxWorks/boost/exception/exception.hpp @@ -0,0 +1,483 @@ +//Copyright (c) 2006-2009 Emil Dotchevski and Reverge Studios, Inc. + +//Distributed under the Boost Software License, Version 1.0. (See accompanying +//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef UUID_274DA366004E11DCB1DDFE2E56D89593 +#define UUID_274DA366004E11DCB1DDFE2E56D89593 +#if defined(__GNUC__) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma GCC system_header +#endif +#if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma warning(push,1) +#endif + +namespace +boost + { + namespace + exception_detail + { + template + class + refcount_ptr + { + public: + + refcount_ptr(): + px_(0) + { + } + + ~refcount_ptr() + { + release(); + } + + refcount_ptr( refcount_ptr const & x ): + px_(x.px_) + { + add_ref(); + } + + refcount_ptr & + operator=( refcount_ptr const & x ) + { + adopt(x.px_); + return *this; + } + + void + adopt( T * px ) + { + release(); + px_=px; + add_ref(); + } + + T * + get() const + { + return px_; + } + + private: + + T * px_; + + void + add_ref() + { + if( px_ ) + px_->add_ref(); + } + + void + release() + { + if( px_ && px_->release() ) + px_=0; + } + }; + } + + //////////////////////////////////////////////////////////////////////// + + template + class error_info; + + typedef error_info throw_function; + typedef error_info throw_file; + typedef error_info throw_line; + + template <> + class + error_info + { + public: + typedef char const * value_type; + value_type v_; + explicit + error_info( value_type v ): + v_(v) + { + } + }; + + template <> + class + error_info + { + public: + typedef char const * value_type; + value_type v_; + explicit + error_info( value_type v ): + v_(v) + { + } + }; + + template <> + class + error_info + { + public: + typedef int value_type; + value_type v_; + explicit + error_info( value_type v ): + v_(v) + { + } + }; + +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif +#endif + class exception; +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif +#endif + + template + class shared_ptr; + + namespace + exception_detail + { + class error_info_base; + struct type_info_; + + struct + error_info_container + { + virtual char const * diagnostic_information( char const * ) const = 0; + virtual shared_ptr get( type_info_ const & ) const = 0; + virtual void set( shared_ptr const &, type_info_ const & ) = 0; + virtual void add_ref() const = 0; + virtual bool release() const = 0; + virtual refcount_ptr clone() const = 0; + + protected: + + ~error_info_container() throw() + { + } + }; + + template + struct get_info; + + template <> + struct get_info; + + template <> + struct get_info; + + template <> + struct get_info; + + char const * get_diagnostic_information( exception const &, char const * ); + + void copy_boost_exception( exception *, exception const * ); + + template + E const & set_info( E const &, error_info const & ); + + template + E const & set_info( E const &, throw_function const & ); + + template + E const & set_info( E const &, throw_file const & ); + + template + E const & set_info( E const &, throw_line const & ); + } + +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif +#endif + class + exception + { + protected: + + exception(): + throw_function_(0), + throw_file_(0), + throw_line_(-1) + { + } + +#ifdef __HP_aCC + //On HP aCC, this protected copy constructor prevents throwing boost::exception. + //On all other platforms, the same effect is achieved by the pure virtual destructor. + exception( exception const & x ) throw(): + data_(x.data_), + throw_function_(x.throw_function_), + throw_file_(x.throw_file_), + throw_line_(x.throw_line_) + { + } +#endif + + virtual ~exception() throw() +#ifndef __HP_aCC + = 0 //Workaround for HP aCC, =0 incorrectly leads to link errors. +#endif + ; + +#if (defined(__MWERKS__) && __MWERKS__<=0x3207) || (defined(_MSC_VER) && _MSC_VER<=1310) + public: +#else + private: + + template + friend E const & exception_detail::set_info( E const &, throw_function const & ); + + template + friend E const & exception_detail::set_info( E const &, throw_file const & ); + + template + friend E const & exception_detail::set_info( E const &, throw_line const & ); + + template + friend E const & exception_detail::set_info( E const &, error_info const & ); + + friend char const * exception_detail::get_diagnostic_information( exception const &, char const * ); + + template + friend struct exception_detail::get_info; + friend struct exception_detail::get_info; + friend struct exception_detail::get_info; + friend struct exception_detail::get_info; + friend void exception_detail::copy_boost_exception( exception *, exception const * ); +#endif + mutable exception_detail::refcount_ptr data_; + mutable char const * throw_function_; + mutable char const * throw_file_; + mutable int throw_line_; + }; +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif +#endif + + inline + exception:: + ~exception() throw() + { + } + + namespace + exception_detail + { + template + E const & + set_info( E const & x, throw_function const & y ) + { + x.throw_function_=y.v_; + return x; + } + + template + E const & + set_info( E const & x, throw_file const & y ) + { + x.throw_file_=y.v_; + return x; + } + + template + E const & + set_info( E const & x, throw_line const & y ) + { + x.throw_line_=y.v_; + return x; + } + } + + //////////////////////////////////////////////////////////////////////// + + namespace + exception_detail + { +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif +#endif + template + struct + error_info_injector: + public T, + public exception + { + explicit + error_info_injector( T const & x ): + T(x) + { + } + + ~error_info_injector() throw() + { + } + }; +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif +#endif + + struct large_size { char c[256]; }; + large_size dispatch_boost_exception( exception const * ); + + struct small_size { }; + small_size dispatch_boost_exception( void const * ); + + template + struct enable_error_info_helper; + + template + struct + enable_error_info_helper + { + typedef T type; + }; + + template + struct + enable_error_info_helper + { + typedef error_info_injector type; + }; + + template + struct + enable_error_info_return_type + { + typedef typename enable_error_info_helper(0)))>::type type; + }; + } + + template + inline + typename + exception_detail::enable_error_info_return_type::type + enable_error_info( T const & x ) + { + typedef typename exception_detail::enable_error_info_return_type::type rt; + return rt(x); + } + + //////////////////////////////////////////////////////////////////////// + + namespace + exception_detail + { +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility push (default) +# endif +#endif + class + clone_base + { + public: + + virtual clone_base const * clone() const = 0; + virtual void rethrow() const = 0; + + virtual + ~clone_base() throw() + { + } + }; +#if defined(__GNUC__) +# if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4) +# pragma GCC visibility pop +# endif +#endif + + inline + void + copy_boost_exception( exception * a, exception const * b ) + { + refcount_ptr data; + if( error_info_container * d=b->data_.get() ) + data = d->clone(); + a->throw_file_ = b->throw_file_; + a->throw_line_ = b->throw_line_; + a->throw_function_ = b->throw_function_; + a->data_ = data; + } + + inline + void + copy_boost_exception( void *, void const * ) + { + } + + template + class + clone_impl: + public T, + public virtual clone_base + { + struct clone_tag { }; + clone_impl( clone_impl const & x, clone_tag ): + T(x) + { + copy_boost_exception(this,&x); + } + + public: + + explicit + clone_impl( T const & x ): + T(x) + { + copy_boost_exception(this,&x); + } + + ~clone_impl() throw() + { + } + + private: + + clone_base const * + clone() const + { + return new clone_impl(*this,clone_tag()); + } + + void + rethrow() const + { + throw*this; + } + }; + } + + template + inline + exception_detail::clone_impl + enable_current_exception( T const & x ) + { + return exception_detail::clone_impl(x); + } + } + +#if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma warning(pop) +#endif +#endif diff --git a/modules/libcom/vxWorks/boost/memory_order.hpp b/modules/libcom/vxWorks/boost/memory_order.hpp new file mode 100644 index 000000000..4945af623 --- /dev/null +++ b/modules/libcom/vxWorks/boost/memory_order.hpp @@ -0,0 +1,53 @@ +#ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED +#define BOOST_MEMORY_ORDER_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/memory_order.hpp +// +// Defines enum boost::memory_order per the C++0x working draft +// +// Copyright (c) 2008, 2009 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +namespace boost +{ + +// +// Enum values are chosen so that code that needs to insert +// a trailing fence for acquire semantics can use a single +// test such as: +// +// if( mo & memory_order_acquire ) { ...fence... } +// +// For leading fences one can use: +// +// if( mo & memory_order_release ) { ...fence... } +// +// Architectures such as Alpha that need a fence on consume +// can use: +// +// if( mo & ( memory_order_acquire | memory_order_consume ) ) { ...fence... } +// + +enum memory_order +{ + memory_order_relaxed = 0, + memory_order_acquire = 1, + memory_order_release = 2, + memory_order_acq_rel = 3, // acquire | release + memory_order_seq_cst = 7, // acq_rel | 4 + memory_order_consume = 8 +}; + +} // namespace boost + +#endif // #ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/shared_ptr.hpp b/modules/libcom/vxWorks/boost/shared_ptr.hpp new file mode 100644 index 000000000..d31978c92 --- /dev/null +++ b/modules/libcom/vxWorks/boost/shared_ptr.hpp @@ -0,0 +1,19 @@ +#ifndef BOOST_SHARED_PTR_HPP_INCLUDED +#define BOOST_SHARED_PTR_HPP_INCLUDED + +// +// shared_ptr.hpp +// +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. +// Copyright (c) 2001-2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/smart_ptr/shared_ptr.htm for documentation. +// + +#include + +#endif // #ifndef BOOST_SHARED_PTR_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/bad_weak_ptr.hpp b/modules/libcom/vxWorks/boost/smart_ptr/bad_weak_ptr.hpp new file mode 100644 index 000000000..3e0a1b728 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/bad_weak_ptr.hpp @@ -0,0 +1,59 @@ +#ifndef BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED +#define BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/smart_ptr/bad_weak_ptr.hpp +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#ifdef __BORLANDC__ +# pragma warn -8026 // Functions with excep. spec. are not expanded inline +#endif + +namespace boost +{ + +// The standard library that comes with Borland C++ 5.5.1, 5.6.4 +// defines std::exception and its members as having C calling +// convention (-pc). When the definition of bad_weak_ptr +// is compiled with -ps, the compiler issues an error. +// Hence, the temporary #pragma option -pc below. + +#if defined(__BORLANDC__) && __BORLANDC__ <= 0x564 +# pragma option push -pc +#endif + +class bad_weak_ptr: public std::exception +{ +public: + + virtual char const * what() const throw() + { + return "tr1::bad_weak_ptr"; + } +}; + +#if defined(__BORLANDC__) && __BORLANDC__ <= 0x564 +# pragma option pop +#endif + +} // namespace boost + +#ifdef __BORLANDC__ +# pragma warn .8026 // Functions with excep. spec. are not expanded inline +#endif + +#endif // #ifndef BOOST_SMART_PTR_BAD_WEAK_PTR_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/operator_bool.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/operator_bool.hpp new file mode 100644 index 000000000..8ae1527e7 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/operator_bool.hpp @@ -0,0 +1,63 @@ +// This header intentionally has no include guards. +// +// Copyright (c) 2001-2009, 2012 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +#if !defined( BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS ) && !defined( BOOST_NO_CXX11_NULLPTR ) + + explicit operator bool () const BOOST_NOEXCEPT + { + return px != 0; + } + +#elif ( defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, < 0x570) ) || defined(__CINT__) + + operator bool () const BOOST_NOEXCEPT + { + return px != 0; + } + +#elif defined( _MANAGED ) + + static void unspecified_bool( this_type*** ) + { + } + + typedef void (*unspecified_bool_type)( this_type*** ); + + operator unspecified_bool_type() const BOOST_NOEXCEPT + { + return px == 0? 0: unspecified_bool; + } + +#elif \ + ( defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, < 0x3200) ) || \ + ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 304) ) || \ + ( defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x590) ) + + typedef element_type * (this_type::*unspecified_bool_type)() const; + + operator unspecified_bool_type() const BOOST_NOEXCEPT + { + return px == 0? 0: &this_type::get; + } + +#else + + typedef element_type * this_type::*unspecified_bool_type; + + operator unspecified_bool_type() const BOOST_NOEXCEPT + { + return px == 0? 0: &this_type::px; + } + +#endif + + // operator! is redundant, but some compilers need it + bool operator! () const BOOST_NOEXCEPT + { + return px == 0; + } diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/shared_count.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/shared_count.hpp new file mode 100644 index 000000000..f055fd0df --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/shared_count.hpp @@ -0,0 +1,603 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// detail/shared_count.hpp +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. +// Copyright 2004-2005 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifdef __BORLANDC__ +# pragma warn -8027 // Functions containing try are not expanded inline +#endif + +#include +#include +#include +#include +#include +#include +#include +// In order to avoid circular dependencies with Boost.TR1 +// we make sure that our include of doesn't try to +// pull in the TR1 headers: that's why we use this header +// rather than including directly: +#include // std::auto_ptr +#include // std::less + +#ifdef BOOST_NO_EXCEPTIONS +# include // std::bad_alloc +#endif + +#if !defined( BOOST_NO_CXX11_SMART_PTR ) +# include +#endif + +namespace boost +{ + +namespace detail +{ + +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + +int const shared_count_id = 0x2C35F101; +int const weak_count_id = 0x298C38A4; + +#endif + +struct sp_nothrow_tag {}; + +template< class D > struct sp_inplace_tag +{ +}; + +#if !defined( BOOST_NO_CXX11_SMART_PTR ) + +template< class T > class sp_reference_wrapper +{ +public: + + explicit sp_reference_wrapper( T & t): t_( boost::addressof( t ) ) + { + } + + template< class Y > void operator()( Y * p ) const + { + (*t_)( p ); + } + +private: + + T * t_; +}; + +template< class D > struct sp_convert_reference +{ + typedef D type; +}; + +template< class D > struct sp_convert_reference< D& > +{ + typedef sp_reference_wrapper< D > type; +}; + +#endif + +class weak_count; + +class shared_count +{ +private: + + sp_counted_base * pi_; + +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + int id_; +#endif + + friend class weak_count; + +public: + + shared_count(): pi_(0) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + } + + template explicit shared_count( Y * p ): pi_( 0 ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { +#ifndef BOOST_NO_EXCEPTIONS + + try + { + pi_ = new sp_counted_impl_p( p ); + } + catch(...) + { + boost::checked_delete( p ); + throw; + } + +#else + + pi_ = new sp_counted_impl_p( p ); + + if( pi_ == 0 ) + { + boost::checked_delete( p ); + boost::throw_exception( std::bad_alloc() ); + } + +#endif + } + +#if defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) + template shared_count( Y * p, D d ): pi_(0) +#else + template shared_count( P p, D d ): pi_(0) +#endif +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { +#if defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) + typedef Y* P; +#endif +#ifndef BOOST_NO_EXCEPTIONS + + try + { + pi_ = new sp_counted_impl_pd(p, d); + } + catch(...) + { + d(p); // delete p + throw; + } + +#else + + pi_ = new sp_counted_impl_pd(p, d); + + if(pi_ == 0) + { + d(p); // delete p + boost::throw_exception(std::bad_alloc()); + } + +#endif + } + +#if !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) + + template< class P, class D > shared_count( P p, sp_inplace_tag ): pi_( 0 ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { +#ifndef BOOST_NO_EXCEPTIONS + + try + { + pi_ = new sp_counted_impl_pd< P, D >( p ); + } + catch( ... ) + { + D()( p ); // delete p + throw; + } + +#else + + pi_ = new sp_counted_impl_pd< P, D >( p ); + + if( pi_ == 0 ) + { + D()( p ); // delete p + boost::throw_exception( std::bad_alloc() ); + } + +#endif // #ifndef BOOST_NO_EXCEPTIONS + } + +#endif // !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) + + template shared_count( P p, D d, A a ): pi_( 0 ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + typedef sp_counted_impl_pda impl_type; + typedef typename A::template rebind< impl_type >::other A2; + + A2 a2( a ); + +#ifndef BOOST_NO_EXCEPTIONS + + try + { + pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); + new( static_cast< void* >( pi_ ) ) impl_type( p, d, a ); + } + catch(...) + { + d( p ); + + if( pi_ != 0 ) + { + a2.deallocate( static_cast< impl_type* >( pi_ ), 1 ); + } + + throw; + } + +#else + + pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); + + if( pi_ != 0 ) + { + new( static_cast< void* >( pi_ ) ) impl_type( p, d, a ); + } + else + { + d( p ); + boost::throw_exception( std::bad_alloc() ); + } + +#endif + } + +#if !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) + + template< class P, class D, class A > shared_count( P p, sp_inplace_tag< D >, A a ): pi_( 0 ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + typedef sp_counted_impl_pda< P, D, A > impl_type; + typedef typename A::template rebind< impl_type >::other A2; + + A2 a2( a ); + +#ifndef BOOST_NO_EXCEPTIONS + + try + { + pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); + new( static_cast< void* >( pi_ ) ) impl_type( p, a ); + } + catch(...) + { + D()( p ); + + if( pi_ != 0 ) + { + a2.deallocate( static_cast< impl_type* >( pi_ ), 1 ); + } + + throw; + } + +#else + + pi_ = a2.allocate( 1, static_cast< impl_type* >( 0 ) ); + + if( pi_ != 0 ) + { + new( static_cast< void* >( pi_ ) ) impl_type( p, a ); + } + else + { + D()( p ); + boost::throw_exception( std::bad_alloc() ); + } + +#endif // #ifndef BOOST_NO_EXCEPTIONS + } + +#endif // !defined( BOOST_NO_FUNCTION_TEMPLATE_ORDERING ) + +#ifndef BOOST_NO_AUTO_PTR + + // auto_ptr is special cased to provide the strong guarantee + + template + explicit shared_count( std::auto_ptr & r ): pi_( new sp_counted_impl_p( r.get() ) ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { +#ifdef BOOST_NO_EXCEPTIONS + + if( pi_ == 0 ) + { + boost::throw_exception(std::bad_alloc()); + } + +#endif + + r.release(); + } + +#endif + +#if !defined( BOOST_NO_CXX11_SMART_PTR ) + + template + explicit shared_count( std::unique_ptr & r ): pi_( 0 ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + typedef typename sp_convert_reference::type D2; + + D2 d2( r.get_deleter() ); + pi_ = new sp_counted_impl_pd< typename std::unique_ptr::pointer, D2 >( r.get(), d2 ); + +#ifdef BOOST_NO_EXCEPTIONS + + if( pi_ == 0 ) + { + boost::throw_exception( std::bad_alloc() ); + } + +#endif + + r.release(); + } + +#endif + + ~shared_count() // nothrow + { + if( pi_ != 0 ) pi_->release(); +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + id_ = 0; +#endif + } + + shared_count(shared_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + if( pi_ != 0 ) pi_->add_ref_copy(); + } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + shared_count(shared_count && r): pi_(r.pi_) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif + { + r.pi_ = 0; + } + +#endif + + explicit shared_count(weak_count const & r); // throws bad_weak_ptr when r.use_count() == 0 + shared_count( weak_count const & r, sp_nothrow_tag ); // constructs an empty *this when r.use_count() == 0 + + shared_count & operator= (shared_count const & r) // nothrow + { + sp_counted_base * tmp = r.pi_; + + if( tmp != pi_ ) + { + if( tmp != 0 ) tmp->add_ref_copy(); + if( pi_ != 0 ) pi_->release(); + pi_ = tmp; + } + + return *this; + } + + void swap(shared_count & r) // nothrow + { + sp_counted_base * tmp = r.pi_; + r.pi_ = pi_; + pi_ = tmp; + } + + long use_count() const // nothrow + { + return pi_ != 0? pi_->use_count(): 0; + } + + bool unique() const // nothrow + { + return use_count() == 1; + } + + bool empty() const // nothrow + { + return pi_ == 0; + } + + friend inline bool operator==(shared_count const & a, shared_count const & b) + { + return a.pi_ == b.pi_; + } + + friend inline bool operator<(shared_count const & a, shared_count const & b) + { + return std::less()( a.pi_, b.pi_ ); + } + + void * get_deleter( sp_typeinfo const & ti ) const + { + return pi_? pi_->get_deleter( ti ): 0; + } + + void * get_untyped_deleter() const + { + return pi_? pi_->get_untyped_deleter(): 0; + } +}; + + +class weak_count +{ +private: + + sp_counted_base * pi_; + +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + int id_; +#endif + + friend class shared_count; + +public: + + weak_count(): pi_(0) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(weak_count_id) +#endif + { + } + + weak_count(shared_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(weak_count_id) +#endif + { + if(pi_ != 0) pi_->weak_add_ref(); + } + + weak_count(weak_count const & r): pi_(r.pi_) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(weak_count_id) +#endif + { + if(pi_ != 0) pi_->weak_add_ref(); + } + +// Move support + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + weak_count(weak_count && r): pi_(r.pi_) // nothrow +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(weak_count_id) +#endif + { + r.pi_ = 0; + } + +#endif + + ~weak_count() // nothrow + { + if(pi_ != 0) pi_->weak_release(); +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + id_ = 0; +#endif + } + + weak_count & operator= (shared_count const & r) // nothrow + { + sp_counted_base * tmp = r.pi_; + + if( tmp != pi_ ) + { + if(tmp != 0) tmp->weak_add_ref(); + if(pi_ != 0) pi_->weak_release(); + pi_ = tmp; + } + + return *this; + } + + weak_count & operator= (weak_count const & r) // nothrow + { + sp_counted_base * tmp = r.pi_; + + if( tmp != pi_ ) + { + if(tmp != 0) tmp->weak_add_ref(); + if(pi_ != 0) pi_->weak_release(); + pi_ = tmp; + } + + return *this; + } + + void swap(weak_count & r) // nothrow + { + sp_counted_base * tmp = r.pi_; + r.pi_ = pi_; + pi_ = tmp; + } + + long use_count() const // nothrow + { + return pi_ != 0? pi_->use_count(): 0; + } + + bool empty() const // nothrow + { + return pi_ == 0; + } + + friend inline bool operator==(weak_count const & a, weak_count const & b) + { + return a.pi_ == b.pi_; + } + + friend inline bool operator<(weak_count const & a, weak_count const & b) + { + return std::less()(a.pi_, b.pi_); + } +}; + +inline shared_count::shared_count( weak_count const & r ): pi_( r.pi_ ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif +{ + if( pi_ == 0 || !pi_->add_ref_lock() ) + { + boost::throw_exception( boost::bad_weak_ptr() ); + } +} + +inline shared_count::shared_count( weak_count const & r, sp_nothrow_tag ): pi_( r.pi_ ) +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + , id_(shared_count_id) +#endif +{ + if( pi_ != 0 && !pi_->add_ref_lock() ) + { + pi_ = 0; + } +} + +} // namespace detail + +} // namespace boost + +#ifdef __BORLANDC__ +# pragma warn .8027 // Functions containing try are not expanded inline +#endif + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SHARED_COUNT_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_convertible.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_convertible.hpp new file mode 100644 index 000000000..31b262782 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_convertible.hpp @@ -0,0 +1,91 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// detail/sp_convertible.hpp +// +// Copyright 2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +#include + +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( BOOST_NO_SFINAE ) +# define BOOST_SP_NO_SP_CONVERTIBLE +#endif + +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ < 303 ) +# define BOOST_SP_NO_SP_CONVERTIBLE +#endif + +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) && defined( __BORLANDC__ ) && ( __BORLANDC__ < 0x630 ) +# define BOOST_SP_NO_SP_CONVERTIBLE +#endif + +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + +namespace boost +{ + +namespace detail +{ + +template< class Y, class T > struct sp_convertible +{ + typedef char (&yes) [1]; + typedef char (&no) [2]; + + static yes f( T* ); + static no f( ... ); + + enum _vt { value = sizeof( (f)( static_cast(0) ) ) == sizeof(yes) }; +}; + +template< class Y, class T > struct sp_convertible< Y, T[] > +{ + enum _vt { value = false }; +}; + +template< class Y, class T > struct sp_convertible< Y[], T[] > +{ + enum _vt { value = sp_convertible< Y[1], T[1] >::value }; +}; + +template< class Y, std::size_t N, class T > struct sp_convertible< Y[N], T[] > +{ + enum _vt { value = sp_convertible< Y[1], T[1] >::value }; +}; + +struct sp_empty +{ +}; + +template< bool > struct sp_enable_if_convertible_impl; + +template<> struct sp_enable_if_convertible_impl +{ + typedef sp_empty type; +}; + +template<> struct sp_enable_if_convertible_impl +{ +}; + +template< class Y, class T > struct sp_enable_if_convertible: public sp_enable_if_convertible_impl< sp_convertible< Y, T >::value > +{ +}; + +} // namespace detail + +} // namespace boost + +#endif // !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_CONVERTIBLE_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base.hpp new file mode 100644 index 000000000..9ced2b95e --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base.hpp @@ -0,0 +1,79 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// detail/sp_counted_base.hpp +// +// Copyright 2005, 2006 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#if defined( BOOST_SP_DISABLE_THREADS ) +# include + +#elif defined( BOOST_SP_USE_SPINLOCK ) +# include + +#elif defined( BOOST_SP_USE_PTHREADS ) +# include + +#elif defined( BOOST_DISABLE_THREADS ) && !defined( BOOST_SP_ENABLE_THREADS ) && !defined( BOOST_DISABLE_WIN32 ) +# include + +#elif defined( __SNC__ ) +# include + +#elif defined( __GNUC__ ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) && !defined(__PATHSCALE__) +# include + +#elif defined(__HP_aCC) && defined(__ia64) +# include + +#elif defined( __GNUC__ ) && defined( __ia64__ ) && !defined( __INTEL_COMPILER ) && !defined(__PATHSCALE__) +# include + +#elif defined( __IBMCPP__ ) && defined( __powerpc ) +# include + +#elif defined( __MWERKS__ ) && defined( __POWERPC__ ) +# include + +#elif defined( __GNUC__ ) && ( defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc ) ) && !defined(__PATHSCALE__) && !defined( _AIX ) +# include + +#elif defined( __GNUC__ ) && ( defined( __mips__ ) || defined( _mips ) ) && !defined(__PATHSCALE__) +# include + +#elif defined( BOOST_SP_HAS_SYNC ) +# include + +#elif defined(__GNUC__) && ( defined( __sparcv9 ) || ( defined( __sparcv8 ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 402 ) ) ) +# include + +#elif defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined(__CYGWIN__) +# include + +#elif defined( _AIX ) +# include + +#elif !defined( BOOST_HAS_THREADS ) +# include + +#else +# include + +#endif + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp new file mode 100644 index 000000000..2e5bc0e85 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_ppc.hpp @@ -0,0 +1,182 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// detail/sp_counted_base_gcc_ppc.hpp - g++ on PowerPC +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. +// Copyright 2004-2005 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// +// Lock-free algorithm by Alexander Terekhov +// +// Thanks to Ben Hitchings for the #weak + (#shared != 0) +// formulation +// + +#include + +namespace boost +{ + +namespace detail +{ + +inline void atomic_increment( int * pw ) +{ + // ++*pw; + + int tmp; + + __asm__ + ( + "0:\n\t" + "lwarx %1, 0, %2\n\t" + "addi %1, %1, 1\n\t" + "stwcx. %1, 0, %2\n\t" + "bne- 0b": + + "=m"( *pw ), "=&b"( tmp ): + "r"( pw ), "m"( *pw ): + "cc" + ); +} + +inline int atomic_decrement( int * pw ) +{ + // return --*pw; + + int rv; + + __asm__ __volatile__ + ( + "sync\n\t" + "0:\n\t" + "lwarx %1, 0, %2\n\t" + "addi %1, %1, -1\n\t" + "stwcx. %1, 0, %2\n\t" + "bne- 0b\n\t" + "isync": + + "=m"( *pw ), "=&b"( rv ): + "r"( pw ), "m"( *pw ): + "memory", "cc" + ); + + return rv; +} + +inline int atomic_conditional_increment( int * pw ) +{ + // if( *pw != 0 ) ++*pw; + // return *pw; + + int rv; + + __asm__ + ( + "0:\n\t" + "lwarx %1, 0, %2\n\t" + "cmpwi %1, 0\n\t" + "beq 1f\n\t" + "addi %1, %1, 1\n\t" + "1:\n\t" + "stwcx. %1, 0, %2\n\t" + "bne- 0b": + + "=m"( *pw ), "=&b"( rv ): + "r"( pw ), "m"( *pw ): + "cc" + ); + + return rv; +} + +class sp_counted_base +{ +private: + + sp_counted_base( sp_counted_base const & ); + sp_counted_base & operator= ( sp_counted_base const & ); + + int use_count_; // #shared + int weak_count_; // #weak + (#shared != 0) + +public: + + sp_counted_base(): use_count_( 1 ), weak_count_( 1 ) + { + } + + virtual ~sp_counted_base() // nothrow + { + } + + // dispose() is called when use_count_ drops to zero, to release + // the resources managed by *this. + + virtual void dispose() = 0; // nothrow + + // destroy() is called when weak_count_ drops to zero. + + virtual void destroy() // nothrow + { + delete this; + } + + virtual void * get_deleter( sp_typeinfo const & ti ) = 0; + virtual void * get_untyped_deleter() = 0; + + void add_ref_copy() + { + atomic_increment( &use_count_ ); + } + + bool add_ref_lock() // true on success + { + return atomic_conditional_increment( &use_count_ ) != 0; + } + + void release() // nothrow + { + if( atomic_decrement( &use_count_ ) == 0 ) + { + dispose(); + weak_release(); + } + } + + void weak_add_ref() // nothrow + { + atomic_increment( &weak_count_ ); + } + + void weak_release() // nothrow + { + if( atomic_decrement( &weak_count_ ) == 0 ) + { + destroy(); + } + } + + long use_count() const // nothrow + { + return static_cast( use_count_ ); + } +}; + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_PPC_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp new file mode 100644 index 000000000..173dce5c8 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp @@ -0,0 +1,174 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// detail/sp_counted_base_gcc_x86.hpp - g++ on 486+ or AMD64 +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. +// Copyright 2004-2005 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// +// Lock-free algorithm by Alexander Terekhov +// +// Thanks to Ben Hitchings for the #weak + (#shared != 0) +// formulation +// + +#include + +namespace boost +{ + +namespace detail +{ + +inline int atomic_exchange_and_add( int * pw, int dv ) +{ + // int r = *pw; + // *pw += dv; + // return r; + + int r; + + __asm__ __volatile__ + ( + "lock\n\t" + "xadd %1, %0": + "=m"( *pw ), "=r"( r ): // outputs (%0, %1) + "m"( *pw ), "1"( dv ): // inputs (%2, %3 == %1) + "memory", "cc" // clobbers + ); + + return r; +} + +inline void atomic_increment( int * pw ) +{ + //atomic_exchange_and_add( pw, 1 ); + + __asm__ + ( + "lock\n\t" + "incl %0": + "=m"( *pw ): // output (%0) + "m"( *pw ): // input (%1) + "cc" // clobbers + ); +} + +inline int atomic_conditional_increment( int * pw ) +{ + // int rv = *pw; + // if( rv != 0 ) ++*pw; + // return rv; + + int rv, tmp; + + __asm__ + ( + "movl %0, %%eax\n\t" + "0:\n\t" + "test %%eax, %%eax\n\t" + "je 1f\n\t" + "movl %%eax, %2\n\t" + "incl %2\n\t" + "lock\n\t" + "cmpxchgl %2, %0\n\t" + "jne 0b\n\t" + "1:": + "=m"( *pw ), "=&a"( rv ), "=&r"( tmp ): // outputs (%0, %1, %2) + "m"( *pw ): // input (%3) + "cc" // clobbers + ); + + return rv; +} + +class sp_counted_base +{ +private: + + sp_counted_base( sp_counted_base const & ); + sp_counted_base & operator= ( sp_counted_base const & ); + + int use_count_; // #shared + int weak_count_; // #weak + (#shared != 0) + +public: + + sp_counted_base(): use_count_( 1 ), weak_count_( 1 ) + { + } + + virtual ~sp_counted_base() // nothrow + { + } + + // dispose() is called when use_count_ drops to zero, to release + // the resources managed by *this. + + virtual void dispose() = 0; // nothrow + + // destroy() is called when weak_count_ drops to zero. + + virtual void destroy() // nothrow + { + delete this; + } + + virtual void * get_deleter( sp_typeinfo const & ti ) = 0; + virtual void * get_untyped_deleter() = 0; + + void add_ref_copy() + { + atomic_increment( &use_count_ ); + } + + bool add_ref_lock() // true on success + { + return atomic_conditional_increment( &use_count_ ) != 0; + } + + void release() // nothrow + { + if( atomic_exchange_and_add( &use_count_, -1 ) == 1 ) + { + dispose(); + weak_release(); + } + } + + void weak_add_ref() // nothrow + { + atomic_increment( &weak_count_ ); + } + + void weak_release() // nothrow + { + if( atomic_exchange_and_add( &weak_count_, -1 ) == 1 ) + { + destroy(); + } + } + + long use_count() const // nothrow + { + return static_cast( use_count_ ); + } +}; + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_BASE_GCC_X86_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_impl.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_impl.hpp new file mode 100644 index 000000000..d15cd3cf1 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_counted_impl.hpp @@ -0,0 +1,254 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// detail/sp_counted_impl.hpp +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov and Multi Media Ltd. +// Copyright 2004-2005 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#if defined(BOOST_SP_USE_STD_ALLOCATOR) && defined(BOOST_SP_USE_QUICK_ALLOCATOR) +# error BOOST_SP_USE_STD_ALLOCATOR and BOOST_SP_USE_QUICK_ALLOCATOR are incompatible. +#endif + +#include +#include + +#if defined(BOOST_SP_USE_QUICK_ALLOCATOR) +#include +#endif + +#if defined(BOOST_SP_USE_STD_ALLOCATOR) +#include // std::allocator +#endif + +#include // std::size_t + +namespace boost +{ + +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + +void sp_scalar_constructor_hook( void * px, std::size_t size, void * pn ); +void sp_scalar_destructor_hook( void * px, std::size_t size, void * pn ); + +#endif + +namespace detail +{ + +template class sp_counted_impl_p: public sp_counted_base +{ +private: + + X * px_; + + sp_counted_impl_p( sp_counted_impl_p const & ); + sp_counted_impl_p & operator= ( sp_counted_impl_p const & ); + + typedef sp_counted_impl_p this_type; + +public: + + explicit sp_counted_impl_p( X * px ): px_( px ) + { +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + boost::sp_scalar_constructor_hook( px, sizeof(X), this ); +#endif + } + + virtual void dispose() // nothrow + { +#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) + boost::sp_scalar_destructor_hook( px_, sizeof(X), this ); +#endif + boost::checked_delete( px_ ); + } + + virtual void * get_deleter( detail::sp_typeinfo const & ) + { + return 0; + } + + virtual void * get_untyped_deleter() + { + return 0; + } + +#if defined(BOOST_SP_USE_STD_ALLOCATOR) + + void * operator new( std::size_t ) + { + return std::allocator().allocate( 1, static_cast(0) ); + } + + void operator delete( void * p ) + { + std::allocator().deallocate( static_cast(p), 1 ); + } + +#endif + +#if defined(BOOST_SP_USE_QUICK_ALLOCATOR) + + void * operator new( std::size_t ) + { + return quick_allocator::alloc(); + } + + void operator delete( void * p ) + { + quick_allocator::dealloc( p ); + } + +#endif +}; + +// +// Borland's Codeguard trips up over the -Vx- option here: +// +#ifdef __CODEGUARD__ +# pragma option push -Vx- +#endif + +template class sp_counted_impl_pd: public sp_counted_base +{ +private: + + P ptr; // copy constructor must not throw + D del; // copy constructor must not throw + + sp_counted_impl_pd( sp_counted_impl_pd const & ); + sp_counted_impl_pd & operator= ( sp_counted_impl_pd const & ); + + typedef sp_counted_impl_pd this_type; + +public: + + // pre: d(p) must not throw + + sp_counted_impl_pd( P p, D & d ): ptr( p ), del( d ) + { + } + + sp_counted_impl_pd( P p ): ptr( p ), del() + { + } + + virtual void dispose() // nothrow + { + del( ptr ); + } + + virtual void * get_deleter( detail::sp_typeinfo const & ti ) + { + return ti == BOOST_SP_TYPEID(D)? &reinterpret_cast( del ): 0; + } + + virtual void * get_untyped_deleter() + { + return &reinterpret_cast( del ); + } + +#if defined(BOOST_SP_USE_STD_ALLOCATOR) + + void * operator new( std::size_t ) + { + return std::allocator().allocate( 1, static_cast(0) ); + } + + void operator delete( void * p ) + { + std::allocator().deallocate( static_cast(p), 1 ); + } + +#endif + +#if defined(BOOST_SP_USE_QUICK_ALLOCATOR) + + void * operator new( std::size_t ) + { + return quick_allocator::alloc(); + } + + void operator delete( void * p ) + { + quick_allocator::dealloc( p ); + } + +#endif +}; + +template class sp_counted_impl_pda: public sp_counted_base +{ +private: + + P p_; // copy constructor must not throw + D d_; // copy constructor must not throw + A a_; // copy constructor must not throw + + sp_counted_impl_pda( sp_counted_impl_pda const & ); + sp_counted_impl_pda & operator= ( sp_counted_impl_pda const & ); + + typedef sp_counted_impl_pda this_type; + +public: + + // pre: d( p ) must not throw + + sp_counted_impl_pda( P p, D & d, A a ): p_( p ), d_( d ), a_( a ) + { + } + + sp_counted_impl_pda( P p, A a ): p_( p ), d_(), a_( a ) + { + } + + virtual void dispose() // nothrow + { + d_( p_ ); + } + + virtual void destroy() // nothrow + { + typedef typename A::template rebind< this_type >::other A2; + + A2 a2( a_ ); + + this->~this_type(); + a2.deallocate( this, 1 ); + } + + virtual void * get_deleter( detail::sp_typeinfo const & ti ) + { + return ti == BOOST_SP_TYPEID( D )? &reinterpret_cast( d_ ): 0; + } + + virtual void * get_untyped_deleter() + { + return &reinterpret_cast( d_ ); + } +}; + +#ifdef __CODEGUARD__ +# pragma option pop +#endif + +} // namespace detail + +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_COUNTED_IMPL_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_has_sync.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_has_sync.hpp new file mode 100644 index 000000000..16de21d37 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_has_sync.hpp @@ -0,0 +1,69 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/smart_ptr/detail/sp_has_sync.hpp +// +// Copyright (c) 2008, 2009 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// Defines the BOOST_SP_HAS_SYNC macro if the __sync_* intrinsics +// are available. +// + +#ifndef BOOST_SP_NO_SYNC + +#if defined( __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 ) + +# define BOOST_SP_HAS_SYNC + +#elif defined( __IBMCPP__ ) && ( __IBMCPP__ >= 1210 ) + +# define BOOST_SP_HAS_SYNC + +#elif defined( __GNUC__ ) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) + +#define BOOST_SP_HAS_SYNC + +#if defined( __arm__ ) || defined( __armel__ ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined( __hppa ) || defined( __hppa__ ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined( __m68k__ ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined( __sh__ ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined( __sparc__ ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined( __INTEL_COMPILER ) && !defined( __ia64__ ) && ( __INTEL_COMPILER < 1110 ) +#undef BOOST_SP_HAS_SYNC +#endif + +#if defined(__PATHSCALE__) && ((__PATHCC__ == 4) && (__PATHCC_MINOR__ < 9)) +#undef BOOST_SP_HAS_SYNC +#endif + +#endif + +#endif // #ifndef BOOST_SP_NO_SYNC + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_HAS_SYNC_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_nullptr_t.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_nullptr_t.hpp new file mode 100644 index 000000000..ccbb1238f --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/sp_nullptr_t.hpp @@ -0,0 +1,45 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// detail/sp_nullptr_t.hpp +// +// Copyright 2013 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + +namespace boost +{ + +namespace detail +{ + +#if defined( __clang__ ) && !defined( _LIBCPP_VERSION ) && !defined( BOOST_NO_CXX11_DECLTYPE ) + + typedef decltype(nullptr) sp_nullptr_t; + +#else + + typedef std::nullptr_t sp_nullptr_t; + +#endif + +} // namespace detail + +} // namespace boost + +#endif // !defined( BOOST_NO_CXX11_NULLPTR ) + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SP_NULLPTR_T_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock.hpp new file mode 100644 index 000000000..88d7ad62c --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock.hpp @@ -0,0 +1,56 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/detail/spinlock.hpp +// +// Copyright (c) 2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// struct spinlock +// { +// void lock(); +// bool try_lock(); +// void unlock(); +// +// class scoped_lock; +// }; +// +// #define BOOST_DETAIL_SPINLOCK_INIT +// + +#include +#include + +#if defined( BOOST_SP_USE_PTHREADS ) +# include + +#elif defined(__GNUC__) && defined( __arm__ ) && !defined( __thumb__ ) +# include + +#elif defined( BOOST_SP_HAS_SYNC ) +# include + +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# include + +#elif defined(BOOST_HAS_PTHREADS) +# include + +#elif !defined(BOOST_HAS_THREADS) +# include + +#else +# error Unrecognized threading platform +#endif + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pool.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pool.hpp new file mode 100644 index 000000000..f09d5c640 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pool.hpp @@ -0,0 +1,91 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/detail/spinlock_pool.hpp +// +// Copyright (c) 2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// spinlock_pool<0> is reserved for atomic<>, when/if it arrives +// spinlock_pool<1> is reserved for shared_ptr reference counts +// spinlock_pool<2> is reserved for shared_ptr atomic access +// + +#include +#include +#include + +namespace boost +{ + +namespace detail +{ + +template< int I > class spinlock_pool +{ +private: + + static spinlock pool_[ 41 ]; + +public: + + static spinlock & spinlock_for( void const * pv ) + { +#if defined(__VMS) && __INITIAL_POINTER_SIZE == 64 + std::size_t i = reinterpret_cast< unsigned long long >( pv ) % 41; +#else + std::size_t i = reinterpret_cast< std::size_t >( pv ) % 41; +#endif + return pool_[ i ]; + } + + class scoped_lock + { + private: + + spinlock & sp_; + + scoped_lock( scoped_lock const & ); + scoped_lock & operator=( scoped_lock const & ); + + public: + + explicit scoped_lock( void const * pv ): sp_( spinlock_for( pv ) ) + { + sp_.lock(); + } + + ~scoped_lock() + { + sp_.unlock(); + } + }; +}; + +template< int I > spinlock spinlock_pool< I >::pool_[ 41 ] = +{ + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, BOOST_DETAIL_SPINLOCK_INIT, + BOOST_DETAIL_SPINLOCK_INIT +}; + +} // namespace detail +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_POOL_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pt.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pt.hpp new file mode 100644 index 000000000..f9cabfc3a --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_pt.hpp @@ -0,0 +1,79 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// Copyright (c) 2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost +{ + +namespace detail +{ + +class spinlock +{ +public: + + pthread_mutex_t v_; + +public: + + bool try_lock() + { + return pthread_mutex_trylock( &v_ ) == 0; + } + + void lock() + { + pthread_mutex_lock( &v_ ); + } + + void unlock() + { + pthread_mutex_unlock( &v_ ); + } + +public: + + class scoped_lock + { + private: + + spinlock & sp_; + + scoped_lock( scoped_lock const & ); + scoped_lock & operator=( scoped_lock const & ); + + public: + + explicit scoped_lock( spinlock & sp ): sp_( sp ) + { + sp.lock(); + } + + ~scoped_lock() + { + sp_.unlock(); + } + }; +}; + +} // namespace detail +} // namespace boost + +#define BOOST_DETAIL_SPINLOCK_INIT { PTHREAD_MUTEX_INITIALIZER } + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_PT_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_sync.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_sync.hpp new file mode 100644 index 000000000..a7145c5ac --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/spinlock_sync.hpp @@ -0,0 +1,87 @@ +#ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// Copyright (c) 2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#if defined( __ia64__ ) && defined( __INTEL_COMPILER ) +# include +#endif + +namespace boost +{ + +namespace detail +{ + +class spinlock +{ +public: + + int v_; + +public: + + bool try_lock() + { + int r = __sync_lock_test_and_set( &v_, 1 ); + return r == 0; + } + + void lock() + { + for( unsigned k = 0; !try_lock(); ++k ) + { + boost::detail::yield( k ); + } + } + + void unlock() + { + __sync_lock_release( &v_ ); + } + +public: + + class scoped_lock + { + private: + + spinlock & sp_; + + scoped_lock( scoped_lock const & ); + scoped_lock & operator=( scoped_lock const & ); + + public: + + explicit scoped_lock( spinlock & sp ): sp_( sp ) + { + sp.lock(); + } + + ~scoped_lock() + { + sp_.unlock(); + } + }; +}; + +} // namespace detail +} // namespace boost + +#define BOOST_DETAIL_SPINLOCK_INIT {0} + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_SPINLOCK_SYNC_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/detail/yield_k.hpp b/modules/libcom/vxWorks/boost/smart_ptr/detail/yield_k.hpp new file mode 100644 index 000000000..23eadd808 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/detail/yield_k.hpp @@ -0,0 +1,149 @@ +#ifndef BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED +#define BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// yield_k.hpp +// +// Copyright (c) 2008 Peter Dimov +// +// void yield( unsigned k ); +// +// Typical use: +// +// for( unsigned k = 0; !try_lock(); ++k ) yield( k ); +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// + +#include + +// BOOST_SMT_PAUSE + +#if defined(_MSC_VER) && _MSC_VER >= 1310 && ( defined(_M_IX86) || defined(_M_X64) ) + +extern "C" void _mm_pause(); +#pragma intrinsic( _mm_pause ) + +#define BOOST_SMT_PAUSE _mm_pause(); + +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +#define BOOST_SMT_PAUSE __asm__ __volatile__( "rep; nop" : : : "memory" ); + +#endif + +// + +#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) + +#if defined( BOOST_USE_WINDOWS_H ) +# include +#endif + +namespace boost +{ + +namespace detail +{ + +#if !defined( BOOST_USE_WINDOWS_H ) + extern "C" void __stdcall Sleep( unsigned long ms ); +#endif + +inline void yield( unsigned k ) +{ + if( k < 4 ) + { + } +#if defined( BOOST_SMT_PAUSE ) + else if( k < 16 ) + { + BOOST_SMT_PAUSE + } +#endif + else if( k < 32 ) + { + Sleep( 0 ); + } + else + { + Sleep( 1 ); + } +} + +} // namespace detail + +} // namespace boost + +#elif defined( BOOST_HAS_PTHREADS ) + +#include +#include + +namespace boost +{ + +namespace detail +{ + +inline void yield( unsigned k ) +{ + if( k < 4 ) + { + } +#if defined( BOOST_SMT_PAUSE ) + else if( k < 16 ) + { + BOOST_SMT_PAUSE + } +#endif + else if( k < 32 || k & 1 ) + { + sched_yield(); + } + else + { + // g++ -Wextra warns on {} or {0} + struct timespec rqtp = { 0, 0 }; + + // POSIX says that timespec has tv_sec and tv_nsec + // But it doesn't guarantee order or placement + + rqtp.tv_sec = 0; + rqtp.tv_nsec = 1000; + + nanosleep( &rqtp, 0 ); + } +} + +} // namespace detail + +} // namespace boost + +#else + +namespace boost +{ + +namespace detail +{ + +inline void yield( unsigned ) +{ +} + +} // namespace detail + +} // namespace boost + +#endif + +#endif // #ifndef BOOST_SMART_PTR_DETAIL_YIELD_K_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/enable_shared_from_this.hpp b/modules/libcom/vxWorks/boost/smart_ptr/enable_shared_from_this.hpp new file mode 100644 index 000000000..3230f0257 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/enable_shared_from_this.hpp @@ -0,0 +1,79 @@ +#ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED +#define BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED + +// +// enable_shared_from_this.hpp +// +// Copyright 2002, 2009 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html +// + +#include +#include +#include +#include + +namespace boost +{ + +template class enable_shared_from_this +{ +protected: + + enable_shared_from_this() BOOST_NOEXCEPT + { + } + + enable_shared_from_this(enable_shared_from_this const &) BOOST_NOEXCEPT + { + } + + enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_NOEXCEPT + { + return *this; + } + + ~enable_shared_from_this() BOOST_NOEXCEPT // ~weak_ptr newer throws, so this call also must not throw + { + } + +public: + + shared_ptr shared_from_this() + { + shared_ptr p( weak_this_ ); + BOOST_ASSERT( p.get() == this ); + return p; + } + + shared_ptr shared_from_this() const + { + shared_ptr p( weak_this_ ); + BOOST_ASSERT( p.get() == this ); + return p; + } + +public: // actually private, but avoids compiler template friendship issues + + // Note: invoked automatically by shared_ptr; do not call + template void _internal_accept_owner( shared_ptr const * ppx, Y * py ) const + { + if( weak_this_.expired() ) + { + weak_this_ = shared_ptr( *ppx, py ); + } + } + +private: + + mutable weak_ptr weak_this_; +}; + +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/shared_ptr.hpp b/modules/libcom/vxWorks/boost/smart_ptr/shared_ptr.hpp new file mode 100644 index 000000000..d2781c158 --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/shared_ptr.hpp @@ -0,0 +1,1035 @@ +#ifndef BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED +#define BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED + +// +// shared_ptr.hpp +// +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. +// Copyright (c) 2001-2008 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/smart_ptr/shared_ptr.htm for documentation. +// + +#include // for broken compiler workarounds + +#if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) +#include +#else + +// In order to avoid circular dependencies with Boost.TR1 +// we make sure that our include of doesn't try to +// pull in the TR1 headers: that's why we use this header +// rather than including directly: +#include // std::auto_ptr + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(BOOST_SP_NO_ATOMIC_ACCESS) +#include +#include +#endif + +#include // for std::swap +#include // for std::less +#include // for std::bad_cast +#include // for std::size_t + +#if !defined(BOOST_NO_IOSTREAM) +#if !defined(BOOST_NO_IOSFWD) +#include // for std::basic_ostream +#else +#include +#endif +#endif + +namespace boost +{ + +template class shared_ptr; +template class weak_ptr; +template class enable_shared_from_this; +class enable_shared_from_raw; + +namespace detail +{ + +// sp_element, element_type + +template< class T > struct sp_element +{ + typedef T type; +}; + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T > struct sp_element< T[] > +{ + typedef T type; +}; + +#if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) + +template< class T, std::size_t N > struct sp_element< T[N] > +{ + typedef T type; +}; + +#endif + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// sp_dereference, return type of operator* + +template< class T > struct sp_dereference +{ + typedef T & type; +}; + +template<> struct sp_dereference< void > +{ + typedef void type; +}; + +#if !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) + +template<> struct sp_dereference< void const > +{ + typedef void type; +}; + +template<> struct sp_dereference< void volatile > +{ + typedef void type; +}; + +template<> struct sp_dereference< void const volatile > +{ + typedef void type; +}; + +#endif // !defined(BOOST_NO_CV_VOID_SPECIALIZATIONS) + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T > struct sp_dereference< T[] > +{ + typedef void type; +}; + +#if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) + +template< class T, std::size_t N > struct sp_dereference< T[N] > +{ + typedef void type; +}; + +#endif + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// sp_member_access, return type of operator-> + +template< class T > struct sp_member_access +{ + typedef T * type; +}; + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T > struct sp_member_access< T[] > +{ + typedef void type; +}; + +#if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) + +template< class T, std::size_t N > struct sp_member_access< T[N] > +{ + typedef void type; +}; + +#endif + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// sp_array_access, return type of operator[] + +template< class T > struct sp_array_access +{ + typedef void type; +}; + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T > struct sp_array_access< T[] > +{ + typedef T & type; +}; + +#if !defined( __BORLANDC__ ) || !BOOST_WORKAROUND( __BORLANDC__, < 0x600 ) + +template< class T, std::size_t N > struct sp_array_access< T[N] > +{ + typedef T & type; +}; + +#endif + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// sp_extent, for operator[] index check + +template< class T > struct sp_extent +{ + enum _vt { value = 0 }; +}; + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T, std::size_t N > struct sp_extent< T[N] > +{ + enum _vt { value = N }; +}; + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// enable_shared_from_this support + +template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe ) +{ + if( pe != 0 ) + { + pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); + } +} + +template< class X, class Y > inline void sp_enable_shared_from_this( boost::shared_ptr * ppx, Y const * py, boost::enable_shared_from_raw const * pe ); + +#ifdef _MANAGED + +// Avoid C4793, ... causes native code generation + +struct sp_any_pointer +{ + template sp_any_pointer( T* ) {} +}; + +inline void sp_enable_shared_from_this( sp_any_pointer, sp_any_pointer, sp_any_pointer ) +{ +} + +#else // _MANAGED + +inline void sp_enable_shared_from_this( ... ) +{ +} + +#endif // _MANAGED + +#if !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) && !defined( BOOST_NO_AUTO_PTR ) + +// rvalue auto_ptr support based on a technique by Dave Abrahams + +template< class T, class R > struct sp_enable_if_auto_ptr +{ +}; + +template< class T, class R > struct sp_enable_if_auto_ptr< std::auto_ptr< T >, R > +{ + typedef R type; +}; + +#endif + +// sp_assert_convertible + +template< class Y, class T > inline void sp_assert_convertible() +{ +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + // static_assert( sp_convertible< Y, T >::value ); + typedef char tmp[ sp_convertible< Y, T >::value? 1: -1 ]; + (void)sizeof( tmp ); + +#else + + T* p = static_cast< Y* >( 0 ); + (void)p; + +#endif +} + +// pointer constructor helper + +template< class T, class Y > inline void sp_pointer_construct( boost::shared_ptr< T > * ppx, Y * p, boost::detail::shared_count & pn ) +{ + boost::detail::shared_count( p ).swap( pn ); + boost::detail::sp_enable_shared_from_this( ppx, p, p ); +} + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T, class Y > inline void sp_pointer_construct( boost::shared_ptr< T[] > * /*ppx*/, Y * p, boost::detail::shared_count & pn ) +{ + sp_assert_convertible< Y[], T[] >(); + boost::detail::shared_count( p, boost::checked_array_deleter< T >() ).swap( pn ); +} + +template< class T, std::size_t N, class Y > inline void sp_pointer_construct( boost::shared_ptr< T[N] > * /*ppx*/, Y * p, boost::detail::shared_count & pn ) +{ + sp_assert_convertible< Y[N], T[N] >(); + boost::detail::shared_count( p, boost::checked_array_deleter< T >() ).swap( pn ); +} + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +// deleter constructor helper + +template< class T, class Y > inline void sp_deleter_construct( boost::shared_ptr< T > * ppx, Y * p ) +{ + boost::detail::sp_enable_shared_from_this( ppx, p, p ); +} + +#if !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +template< class T, class Y > inline void sp_deleter_construct( boost::shared_ptr< T[] > * /*ppx*/, Y * /*p*/ ) +{ + sp_assert_convertible< Y[], T[] >(); +} + +template< class T, std::size_t N, class Y > inline void sp_deleter_construct( boost::shared_ptr< T[N] > * /*ppx*/, Y * /*p*/ ) +{ + sp_assert_convertible< Y[N], T[N] >(); +} + +#endif // !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + +} // namespace detail + + +// +// shared_ptr +// +// An enhanced relative of scoped_ptr with reference counted copy semantics. +// The object pointed to is deleted when the last shared_ptr pointing to it +// is destroyed or reset. +// + +template class shared_ptr +{ +private: + + // Borland 5.5.1 specific workaround + typedef shared_ptr this_type; + +public: + + typedef typename boost::detail::sp_element< T >::type element_type; + + shared_ptr() BOOST_NOEXCEPT : px( 0 ), pn() // never throws in 1.30+ + { + } + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + + shared_ptr( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT : px( 0 ), pn() // never throws + { + } + +#endif + + template + explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete + { + boost::detail::sp_pointer_construct( this, p, pn ); + } + + // + // Requirements: D's copy constructor must not throw + // + // shared_ptr will release p by calling d(p) + // + + template shared_ptr( Y * p, D d ): px( p ), pn( p, d ) + { + boost::detail::sp_deleter_construct( this, p ); + } + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + + template shared_ptr( boost::detail::sp_nullptr_t p, D d ): px( p ), pn( p, d ) + { + } + +#endif + + // As above, but with allocator. A's copy constructor shall not throw. + + template shared_ptr( Y * p, D d, A a ): px( p ), pn( p, d, a ) + { + boost::detail::sp_deleter_construct( this, p ); + } + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + + template shared_ptr( boost::detail::sp_nullptr_t p, D d, A a ): px( p ), pn( p, d, a ) + { + } + +#endif + +// generated copy constructor, destructor are fine... + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + +// ... except in C++0x, move disables the implicit copy + + shared_ptr( shared_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) + { + } + +#endif + + template + explicit shared_ptr( weak_ptr const & r ): pn( r.pn ) // may throw + { + boost::detail::sp_assert_convertible< Y, T >(); + + // it is now safe to copy r.px, as pn(r.pn) did not throw + px = r.px; + } + + template + shared_ptr( weak_ptr const & r, boost::detail::sp_nothrow_tag ) + BOOST_NOEXCEPT : px( 0 ), pn( r.pn, boost::detail::sp_nothrow_tag() ) + { + if( !pn.empty() ) + { + px = r.px; + } + } + + template +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + shared_ptr( shared_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) + +#else + + shared_ptr( shared_ptr const & r ) + +#endif + BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) + { + boost::detail::sp_assert_convertible< Y, T >(); + } + + // aliasing + template< class Y > + shared_ptr( shared_ptr const & r, element_type * p ) BOOST_NOEXCEPT : px( p ), pn( r.pn ) + { + } + +#ifndef BOOST_NO_AUTO_PTR + + template + explicit shared_ptr( std::auto_ptr & r ): px(r.get()), pn() + { + boost::detail::sp_assert_convertible< Y, T >(); + + Y * tmp = r.get(); + pn = boost::detail::shared_count( r ); + + boost::detail::sp_deleter_construct( this, tmp ); + } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + template + shared_ptr( std::auto_ptr && r ): px(r.get()), pn() + { + boost::detail::sp_assert_convertible< Y, T >(); + + Y * tmp = r.get(); + pn = boost::detail::shared_count( r ); + + boost::detail::sp_deleter_construct( this, tmp ); + } + +#elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + + template + explicit shared_ptr( Ap r, typename boost::detail::sp_enable_if_auto_ptr::type = 0 ): px( r.get() ), pn() + { + typedef typename Ap::element_type Y; + + boost::detail::sp_assert_convertible< Y, T >(); + + Y * tmp = r.get(); + pn = boost::detail::shared_count( r ); + + boost::detail::sp_deleter_construct( this, tmp ); + } + +#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +#endif // BOOST_NO_AUTO_PTR + +#if !defined( BOOST_NO_CXX11_SMART_PTR ) + + template< class Y, class D > + shared_ptr( std::unique_ptr< Y, D > && r ): px( r.get() ), pn() + { + boost::detail::sp_assert_convertible< Y, T >(); + + typename std::unique_ptr< Y, D >::pointer tmp = r.get(); + pn = boost::detail::shared_count( r ); + + boost::detail::sp_deleter_construct( this, tmp ); + } + +#endif + + // assignment + + shared_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT + { + this_type(r).swap(*this); + return *this; + } + +#if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1400) + + template + shared_ptr & operator=(shared_ptr const & r) BOOST_NOEXCEPT + { + this_type(r).swap(*this); + return *this; + } + +#endif + +#ifndef BOOST_NO_AUTO_PTR + + template + shared_ptr & operator=( std::auto_ptr & r ) + { + this_type( r ).swap( *this ); + return *this; + } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + template + shared_ptr & operator=( std::auto_ptr && r ) + { + this_type( static_cast< std::auto_ptr && >( r ) ).swap( *this ); + return *this; + } + +#elif !defined( BOOST_NO_SFINAE ) && !defined( BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION ) + + template + typename boost::detail::sp_enable_if_auto_ptr< Ap, shared_ptr & >::type operator=( Ap r ) + { + this_type( r ).swap( *this ); + return *this; + } + +#endif // BOOST_NO_SFINAE, BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + +#endif // BOOST_NO_AUTO_PTR + +#if !defined( BOOST_NO_CXX11_SMART_PTR ) + + template + shared_ptr & operator=( std::unique_ptr && r ) + { + this_type( static_cast< std::unique_ptr && >( r ) ).swap(*this); + return *this; + } + +#endif + +// Move support + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + shared_ptr( shared_ptr && r ) BOOST_NOEXCEPT : px( r.px ), pn() + { + pn.swap( r.pn ); + r.px = 0; + } + + template +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + shared_ptr( shared_ptr && r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) + +#else + + shared_ptr( shared_ptr && r ) + +#endif + BOOST_NOEXCEPT : px( r.px ), pn() + { + boost::detail::sp_assert_convertible< Y, T >(); + + pn.swap( r.pn ); + r.px = 0; + } + + shared_ptr & operator=( shared_ptr && r ) BOOST_NOEXCEPT + { + this_type( static_cast< shared_ptr && >( r ) ).swap( *this ); + return *this; + } + + template + shared_ptr & operator=( shared_ptr && r ) BOOST_NOEXCEPT + { + this_type( static_cast< shared_ptr && >( r ) ).swap( *this ); + return *this; + } + +#endif + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + + shared_ptr & operator=( boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT // never throws + { + this_type().swap(*this); + return *this; + } + +#endif + + void reset() BOOST_NOEXCEPT // never throws in 1.30+ + { + this_type().swap(*this); + } + + template void reset( Y * p ) // Y must be complete + { + BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors + this_type( p ).swap( *this ); + } + + template void reset( Y * p, D d ) + { + this_type( p, d ).swap( *this ); + } + + template void reset( Y * p, D d, A a ) + { + this_type( p, d, a ).swap( *this ); + } + + template void reset( shared_ptr const & r, element_type * p ) + { + this_type( r, p ).swap( *this ); + } + + // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) + typename boost::detail::sp_dereference< T >::type operator* () const + { + BOOST_ASSERT( px != 0 ); + return *px; + } + + // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) + typename boost::detail::sp_member_access< T >::type operator-> () const + { + BOOST_ASSERT( px != 0 ); + return px; + } + + // never throws (but has a BOOST_ASSERT in it, so not marked with BOOST_NOEXCEPT) + typename boost::detail::sp_array_access< T >::type operator[] ( std::ptrdiff_t i ) const + { + BOOST_ASSERT( px != 0 ); + BOOST_ASSERT( i >= 0 && ( i < boost::detail::sp_extent< T >::value || boost::detail::sp_extent< T >::value == 0 ) ); + + return px[ i ]; + } + + element_type * get() const BOOST_NOEXCEPT + { + return px; + } + +// implicit conversion to "bool" +#include + + bool unique() const BOOST_NOEXCEPT + { + return pn.unique(); + } + + long use_count() const BOOST_NOEXCEPT + { + return pn.use_count(); + } + + void swap( shared_ptr & other ) BOOST_NOEXCEPT + { + std::swap(px, other.px); + pn.swap(other.pn); + } + + template bool owner_before( shared_ptr const & rhs ) const BOOST_NOEXCEPT + { + return pn < rhs.pn; + } + + template bool owner_before( weak_ptr const & rhs ) const BOOST_NOEXCEPT + { + return pn < rhs.pn; + } + + void * _internal_get_deleter( boost::detail::sp_typeinfo const & ti ) const BOOST_NOEXCEPT + { + return pn.get_deleter( ti ); + } + + void * _internal_get_untyped_deleter() const BOOST_NOEXCEPT + { + return pn.get_untyped_deleter(); + } + + bool _internal_equiv( shared_ptr const & r ) const BOOST_NOEXCEPT + { + return px == r.px && pn == r.pn; + } + +// Tasteless as this may seem, making all members public allows member templates +// to work in the absence of member template friends. (Matthew Langston) + +#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS + +private: + + template friend class shared_ptr; + template friend class weak_ptr; + + +#endif + + element_type * px; // contained pointer + boost::detail::shared_count pn; // reference counter + +}; // shared_ptr + +template inline bool operator==(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT +{ + return a.get() == b.get(); +} + +template inline bool operator!=(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT +{ + return a.get() != b.get(); +} + +#if __GNUC__ == 2 && __GNUC_MINOR__ <= 96 + +// Resolve the ambiguity between our op!= and the one in rel_ops + +template inline bool operator!=(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT +{ + return a.get() != b.get(); +} + +#endif + +#if !defined( BOOST_NO_CXX11_NULLPTR ) + +template inline bool operator==( shared_ptr const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT +{ + return p.get() == 0; +} + +template inline bool operator==( boost::detail::sp_nullptr_t, shared_ptr const & p ) BOOST_NOEXCEPT +{ + return p.get() == 0; +} + +template inline bool operator!=( shared_ptr const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT +{ + return p.get() != 0; +} + +template inline bool operator!=( boost::detail::sp_nullptr_t, shared_ptr const & p ) BOOST_NOEXCEPT +{ + return p.get() != 0; +} + +#endif + +template inline bool operator<(shared_ptr const & a, shared_ptr const & b) BOOST_NOEXCEPT +{ + return a.owner_before( b ); +} + +template inline void swap(shared_ptr & a, shared_ptr & b) BOOST_NOEXCEPT +{ + a.swap(b); +} + +template shared_ptr static_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT +{ + (void) static_cast< T* >( static_cast< U* >( 0 ) ); + + typedef typename shared_ptr::element_type E; + + E * p = static_cast< E* >( r.get() ); + return shared_ptr( r, p ); +} + +template shared_ptr const_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT +{ + (void) const_cast< T* >( static_cast< U* >( 0 ) ); + + typedef typename shared_ptr::element_type E; + + E * p = const_cast< E* >( r.get() ); + return shared_ptr( r, p ); +} + +template shared_ptr dynamic_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT +{ + (void) dynamic_cast< T* >( static_cast< U* >( 0 ) ); + + typedef typename shared_ptr::element_type E; + + E * p = dynamic_cast< E* >( r.get() ); + return p? shared_ptr( r, p ): shared_ptr(); +} + +template shared_ptr reinterpret_pointer_cast( shared_ptr const & r ) BOOST_NOEXCEPT +{ + (void) reinterpret_cast< T* >( static_cast< U* >( 0 ) ); + + typedef typename shared_ptr::element_type E; + + E * p = reinterpret_cast< E* >( r.get() ); + return shared_ptr( r, p ); +} + +// get_pointer() enables boost::mem_fn to recognize shared_ptr + +template inline typename shared_ptr::element_type * get_pointer(shared_ptr const & p) BOOST_NOEXCEPT +{ + return p.get(); +} + +// operator<< + +#if !defined(BOOST_NO_IOSTREAM) + +#if defined(BOOST_NO_TEMPLATED_IOSTREAMS) || ( defined(__GNUC__) && (__GNUC__ < 3) ) + +template std::ostream & operator<< (std::ostream & os, shared_ptr const & p) +{ + os << p.get(); + return os; +} + +#else + +// in STLport's no-iostreams mode no iostream symbols can be used +#ifndef _STLP_NO_IOSTREAMS + +# if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, < 1300 && __SGI_STL_PORT) +// MSVC6 has problems finding std::basic_ostream through the using declaration in namespace _STL +using std::basic_ostream; +template basic_ostream & operator<< (basic_ostream & os, shared_ptr const & p) +# else +template std::basic_ostream & operator<< (std::basic_ostream & os, shared_ptr const & p) +# endif +{ + os << p.get(); + return os; +} + +#endif // _STLP_NO_IOSTREAMS + +#endif // __GNUC__ < 3 + +#endif // !defined(BOOST_NO_IOSTREAM) + +// get_deleter + +namespace detail +{ + +#if ( defined(__GNUC__) && BOOST_WORKAROUND(__GNUC__, < 3) ) || \ + ( defined(__EDG_VERSION__) && BOOST_WORKAROUND(__EDG_VERSION__, <= 238) ) || \ + ( defined(__HP_aCC) && BOOST_WORKAROUND(__HP_aCC, <= 33500) ) + +// g++ 2.9x doesn't allow static_cast(void *) +// apparently EDG 2.38 and HP aCC A.03.35 also don't accept it + +template D * basic_get_deleter(shared_ptr const & p) +{ + void const * q = p._internal_get_deleter(BOOST_SP_TYPEID(D)); + return const_cast(static_cast(q)); +} + +#else + +template D * basic_get_deleter( shared_ptr const & p ) BOOST_NOEXCEPT +{ + return static_cast( p._internal_get_deleter(BOOST_SP_TYPEID(D)) ); +} + +#endif + +class esft2_deleter_wrapper +{ +private: + + shared_ptr deleter_; + +public: + + esft2_deleter_wrapper() + { + } + + template< class T > void set_deleter( shared_ptr const & deleter ) + { + deleter_ = deleter; + } + + template D* get_deleter() const BOOST_NOEXCEPT + { + return boost::detail::basic_get_deleter( deleter_ ); + } + + template< class T> void operator()( T* ) + { + BOOST_ASSERT( deleter_.use_count() <= 1 ); + deleter_.reset(); + } +}; + +} // namespace detail + +template D * get_deleter( shared_ptr const & p ) BOOST_NOEXCEPT +{ + D *del = boost::detail::basic_get_deleter(p); + + if(del == 0) + { + boost::detail::esft2_deleter_wrapper *del_wrapper = boost::detail::basic_get_deleter(p); +// The following get_deleter method call is fully qualified because +// older versions of gcc (2.95, 3.2.3) fail to compile it when written del_wrapper->get_deleter() + if(del_wrapper) del = del_wrapper->::boost::detail::esft2_deleter_wrapper::get_deleter(); + } + + return del; +} + +// atomic access + +#if !defined(BOOST_SP_NO_ATOMIC_ACCESS) + +template inline bool atomic_is_lock_free( shared_ptr const * /*p*/ ) BOOST_NOEXCEPT +{ + return false; +} + +template shared_ptr atomic_load( shared_ptr const * p ) +{ + boost::detail::spinlock_pool<2>::scoped_lock lock( p ); + return *p; +} + +template inline shared_ptr atomic_load_explicit( shared_ptr const * p, memory_order /*mo*/ ) +{ + return atomic_load( p ); +} + +template void atomic_store( shared_ptr * p, shared_ptr r ) +{ + boost::detail::spinlock_pool<2>::scoped_lock lock( p ); + p->swap( r ); +} + +template inline void atomic_store_explicit( shared_ptr * p, shared_ptr r, memory_order /*mo*/ ) +{ + atomic_store( p, r ); // std::move( r ) +} + +template shared_ptr atomic_exchange( shared_ptr * p, shared_ptr r ) +{ + boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( p ); + + sp.lock(); + p->swap( r ); + sp.unlock(); + + return r; // return std::move( r ) +} + +template shared_ptr atomic_exchange_explicit( shared_ptr * p, shared_ptr r, memory_order /*mo*/ ) +{ + return atomic_exchange( p, r ); // std::move( r ) +} + +template bool atomic_compare_exchange( shared_ptr * p, shared_ptr * v, shared_ptr w ) +{ + boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( p ); + + sp.lock(); + + if( p->_internal_equiv( *v ) ) + { + p->swap( w ); + + sp.unlock(); + + return true; + } + else + { + shared_ptr tmp( *p ); + + sp.unlock(); + + tmp.swap( *v ); + return false; + } +} + +template inline bool atomic_compare_exchange_explicit( shared_ptr * p, shared_ptr * v, shared_ptr w, memory_order /*success*/, memory_order /*failure*/ ) +{ + return atomic_compare_exchange( p, v, w ); // std::move( w ) +} + +#endif // !defined(BOOST_SP_NO_ATOMIC_ACCESS) + +// hash_value + +template< class T > struct hash; + +template< class T > std::size_t hash_value( boost::shared_ptr const & p ) BOOST_NOEXCEPT +{ + return boost::hash< T* >()( p.get() ); +} + +} // namespace boost + +#endif // #if defined(BOOST_NO_MEMBER_TEMPLATES) && !defined(BOOST_MSVC6_MEMBER_TEMPLATES) + +#endif // #ifndef BOOST_SMART_PTR_SHARED_PTR_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/smart_ptr/weak_ptr.hpp b/modules/libcom/vxWorks/boost/smart_ptr/weak_ptr.hpp new file mode 100644 index 000000000..e3e9ad9bd --- /dev/null +++ b/modules/libcom/vxWorks/boost/smart_ptr/weak_ptr.hpp @@ -0,0 +1,253 @@ +#ifndef BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED +#define BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED + +// +// weak_ptr.hpp +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/smart_ptr/weak_ptr.htm for documentation. +// + +#include // boost.TR1 include order fix +#include +#include + +namespace boost +{ + +template class weak_ptr +{ +private: + + // Borland 5.5.1 specific workarounds + typedef weak_ptr this_type; + +public: + + typedef typename boost::detail::sp_element< T >::type element_type; + + weak_ptr() BOOST_NOEXCEPT : px(0), pn() // never throws in 1.30+ + { + } + +// generated copy constructor, assignment, destructor are fine... + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + +// ... except in C++0x, move disables the implicit copy + + weak_ptr( weak_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) + { + } + + weak_ptr & operator=( weak_ptr const & r ) BOOST_NOEXCEPT + { + px = r.px; + pn = r.pn; + return *this; + } + +#endif + +// +// The "obvious" converting constructor implementation: +// +// template +// weak_ptr(weak_ptr const & r): px(r.px), pn(r.pn) // never throws +// { +// } +// +// has a serious problem. +// +// r.px may already have been invalidated. The px(r.px) +// conversion may require access to *r.px (virtual inheritance). +// +// It is not possible to avoid spurious access violations since +// in multithreaded programs r.px may be invalidated at any point. +// + + template +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + weak_ptr( weak_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) + +#else + + weak_ptr( weak_ptr const & r ) + +#endif + BOOST_NOEXCEPT : px(r.lock().get()), pn(r.pn) + { + boost::detail::sp_assert_convertible< Y, T >(); + } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + template +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + weak_ptr( weak_ptr && r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) + +#else + + weak_ptr( weak_ptr && r ) + +#endif + BOOST_NOEXCEPT : px( r.lock().get() ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) + { + boost::detail::sp_assert_convertible< Y, T >(); + r.px = 0; + } + + // for better efficiency in the T == Y case + weak_ptr( weak_ptr && r ) + BOOST_NOEXCEPT : px( r.px ), pn( static_cast< boost::detail::weak_count && >( r.pn ) ) + { + r.px = 0; + } + + // for better efficiency in the T == Y case + weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT + { + this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); + return *this; + } + + +#endif + + template +#if !defined( BOOST_SP_NO_SP_CONVERTIBLE ) + + weak_ptr( shared_ptr const & r, typename boost::detail::sp_enable_if_convertible::type = boost::detail::sp_empty() ) + +#else + + weak_ptr( shared_ptr const & r ) + +#endif + BOOST_NOEXCEPT : px( r.px ), pn( r.pn ) + { + boost::detail::sp_assert_convertible< Y, T >(); + } + +#if !defined(BOOST_MSVC) || (BOOST_MSVC >= 1300) + + template + weak_ptr & operator=( weak_ptr const & r ) BOOST_NOEXCEPT + { + boost::detail::sp_assert_convertible< Y, T >(); + + px = r.lock().get(); + pn = r.pn; + + return *this; + } + +#if !defined( BOOST_NO_CXX11_RVALUE_REFERENCES ) + + template + weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT + { + this_type( static_cast< weak_ptr && >( r ) ).swap( *this ); + return *this; + } + +#endif + + template + weak_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT + { + boost::detail::sp_assert_convertible< Y, T >(); + + px = r.px; + pn = r.pn; + + return *this; + } + +#endif + + shared_ptr lock() const BOOST_NOEXCEPT + { + return shared_ptr( *this, boost::detail::sp_nothrow_tag() ); + } + + long use_count() const BOOST_NOEXCEPT + { + return pn.use_count(); + } + + bool expired() const BOOST_NOEXCEPT + { + return pn.use_count() == 0; + } + + bool _empty() const // extension, not in std::weak_ptr + { + return pn.empty(); + } + + void reset() BOOST_NOEXCEPT // never throws in 1.30+ + { + this_type().swap(*this); + } + + void swap(this_type & other) BOOST_NOEXCEPT + { + std::swap(px, other.px); + pn.swap(other.pn); + } + + template + void _internal_aliasing_assign(weak_ptr const & r, element_type * px2) + { + px = px2; + pn = r.pn; + } + + template bool owner_before( weak_ptr const & rhs ) const BOOST_NOEXCEPT + { + return pn < rhs.pn; + } + + template bool owner_before( shared_ptr const & rhs ) const BOOST_NOEXCEPT + { + return pn < rhs.pn; + } + +// Tasteless as this may seem, making all members public allows member templates +// to work in the absence of member template friends. (Matthew Langston) + +#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS + +private: + + template friend class weak_ptr; + template friend class shared_ptr; + +#endif + + element_type * px; // contained pointer + boost::detail::weak_count pn; // reference counter + +}; // weak_ptr + +template inline bool operator<(weak_ptr const & a, weak_ptr const & b) BOOST_NOEXCEPT +{ + return a.owner_before( b ); +} + +template void swap(weak_ptr & a, weak_ptr & b) BOOST_NOEXCEPT +{ + a.swap(b); +} + +} // namespace boost + +#endif // #ifndef BOOST_SMART_PTR_WEAK_PTR_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/throw_exception.hpp b/modules/libcom/vxWorks/boost/throw_exception.hpp new file mode 100644 index 000000000..c1bff4379 --- /dev/null +++ b/modules/libcom/vxWorks/boost/throw_exception.hpp @@ -0,0 +1,100 @@ +#ifndef UUID_AA15E74A856F11E08B8D93F24824019B +#define UUID_AA15E74A856F11E08B8D93F24824019B +#if defined(__GNUC__) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma GCC system_header +#endif +#if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma warning(push,1) +#endif + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// +// boost/throw_exception.hpp +// +// Copyright (c) 2002 Peter Dimov and Multi Media Ltd. +// Copyright (c) 2008-2009 Emil Dotchevski and Reverge Studios, Inc. +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// http://www.boost.org/libs/utility/throw_exception.html +// + +#include +#include +#include +#include + +#if !defined( BOOST_EXCEPTION_DISABLE ) && defined( __BORLANDC__ ) && BOOST_WORKAROUND( __BORLANDC__, BOOST_TESTED_AT(0x593) ) +# define BOOST_EXCEPTION_DISABLE +#endif + +#if !defined( BOOST_EXCEPTION_DISABLE ) && defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, < 1310 ) +# define BOOST_EXCEPTION_DISABLE +#endif + +#if !defined( BOOST_EXCEPTION_DISABLE ) +# include +# include +# define BOOST_THROW_EXCEPTION(x) ::boost::exception_detail::throw_exception_(x,BOOST_CURRENT_FUNCTION,__FILE__,__LINE__) +#else +# define BOOST_THROW_EXCEPTION(x) ::boost::throw_exception(x) +#endif + +namespace boost +{ +#ifdef BOOST_NO_EXCEPTIONS + +void throw_exception( std::exception const & e ); // user defined + +#else + +inline void throw_exception_assert_compatibility( std::exception const & ) { } + +template BOOST_ATTRIBUTE_NORETURN inline void throw_exception( E const & e ) +{ + //All boost exceptions are required to derive from std::exception, + //to ensure compatibility with BOOST_NO_EXCEPTIONS. + throw_exception_assert_compatibility(e); + +#ifndef BOOST_EXCEPTION_DISABLE + throw enable_current_exception(enable_error_info(e)); +#else + throw e; +#endif +} + +#endif + +#if !defined( BOOST_EXCEPTION_DISABLE ) + namespace + exception_detail + { + template + BOOST_ATTRIBUTE_NORETURN + void + throw_exception_( E const & x, char const * current_function, char const * file, int line ) + { + boost::throw_exception( + set_info( + set_info( + set_info( + enable_error_info(x), + throw_function(current_function)), + throw_file(file)), + throw_line(line))); + } + } +#endif +} // namespace boost + +#if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) +#pragma warning(pop) +#endif +#endif diff --git a/modules/libcom/vxWorks/boost/tr1/detail/config.hpp b/modules/libcom/vxWorks/boost/tr1/detail/config.hpp new file mode 100644 index 000000000..1b95148bb --- /dev/null +++ b/modules/libcom/vxWorks/boost/tr1/detail/config.hpp @@ -0,0 +1,169 @@ +// (C) Copyright John Maddock 2005-7. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED + +#include + +#if (defined(__GNUC__) && !(defined(linux) || defined(__linux) || defined(__linux__))) \ + || (!defined(_AIX) && defined(__IBMCPP__) && (__IBMCPP__ >= 800)) + // Disable use of #include_next on Linux as typically we are installed in a + // directory that is searched *after* the std lib include path. +#if !defined(BOOST_HAS_INCLUDE_NEXT) +# define BOOST_HAS_INCLUDE_NEXT +#endif +// Need to find out if we're using GLIBC: +#ifdef BOOST_TR1_UTILITY_INCLUDED +// Oops we're in a recursive include path!! +// Need to include utility, or some std lib header, +// but *not* via or +# ifndef BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_RECURSION +# define BOOST_TR1_NO_CONFIG_RECURSION +# endif +# if defined(BOOST_HAS_INCLUDE_NEXT) && !defined(BOOST_TR1_DISABLE_INCLUDE_NEXT) +# include_next +# else +# include BOOST_TR1_STD_HEADER(utility) +# endif +# ifdef BOOST_TR1_NO_CONFIG_RECURSION +# undef BOOST_TR1_NO_CONFIG_RECURSION +# undef BOOST_TR1_NO_RECURSION +# endif +#else +#include +#endif +#endif + +#if defined(__GLIBCXX__) && !defined(BOOST_TR1_PATH) +# define BOOST_TR1_PATH(name) tr1/name +#endif +#if !defined(BOOST_TR1_PATH) +# define BOOST_TR1_PATH(name) name +#endif + +#define BOOST_TR1_HEADER(name) + +// Can't use BOOST_WORKAROUND here, it leads to recursive includes: +#if (defined(__BORLANDC__) && (__BORLANDC__ <= 0x600)) || (defined(_MSC_VER) && (_MSC_VER < 1310)) +# define BOOST_TR1_USE_OLD_TUPLE +#endif + +#ifdef __IBMCPP_TR1__ + // turn on support for everything: +# define BOOST_HAS_TR1 +#endif + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +# define BOOST_HAS_TR1_COMPLEX_OVERLOADS +# define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG +#endif + +#ifdef BOOST_HAS_TR1 + // turn on support for everything: +# define BOOST_HAS_TR1_ARRAY +# define BOOST_HAS_TR1_COMPLEX_OVERLOADS +# define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG +# define BOOST_HAS_TR1_REFERENCE_WRAPPER +# define BOOST_HAS_TR1_RESULT_OF +# define BOOST_HAS_TR1_MEM_FN +# define BOOST_HAS_TR1_BIND +# define BOOST_HAS_TR1_FUNCTION +# define BOOST_HAS_TR1_HASH +# define BOOST_HAS_TR1_SHARED_PTR +# define BOOST_HAS_TR1_RANDOM +# define BOOST_HAS_TR1_REGEX +# define BOOST_HAS_TR1_TUPLE +# define BOOST_HAS_TR1_TYPE_TRAITS +# define BOOST_HAS_TR1_UTILITY +# define BOOST_HAS_TR1_UNORDERED_MAP +# define BOOST_HAS_TR1_UNORDERED_SET +# define BOOST_HAS_TR1_CMATH + +#endif + +#if defined(__MWERKS__) && (__MWERKS__ >= 0x3205) +// +// Very preliminary MWCW support, may not be right: +// +# define BOOST_HAS_TR1_SHARED_PTR +# define BOOST_HAS_TR1_REFERENCE_WRAPPER +# define BOOST_HAS_TR1_FUNCTION +# define BOOST_HAS_TR1_TUPLE +# define BOOST_HAS_TR1_RESULT_OF +#endif + +#ifdef BOOST_HAS_GCC_TR1 + // turn on support for everything in gcc 4.0.x: +# define BOOST_HAS_TR1_ARRAY +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 +//# define BOOST_HAS_TR1_COMPLEX_OVERLOADS +# define BOOST_HAS_TR1_COMPLEX_INVERSE_TRIG +#endif +# define BOOST_HAS_TR1_REFERENCE_WRAPPER +# define BOOST_HAS_TR1_RESULT_OF +# define BOOST_HAS_TR1_MEM_FN +# define BOOST_HAS_TR1_BIND +# define BOOST_HAS_TR1_FUNCTION +# define BOOST_HAS_TR1_HASH +# define BOOST_HAS_TR1_SHARED_PTR +#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 +# define BOOST_HAS_TR1_RANDOM +//# define BOOST_HAS_TR1_REGEX +#ifdef _GLIBCXX_USE_C99_MATH_TR1 +# define BOOST_HAS_TR1_CMATH +#endif +#endif +# define BOOST_HAS_TR1_TUPLE +# define BOOST_HAS_TR1_TYPE_TRAITS +# define BOOST_HAS_TR1_UTILITY +# define BOOST_HAS_TR1_UNORDERED_MAP +# define BOOST_HAS_TR1_UNORDERED_SET + +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1500) \ + && defined(_MSC_FULL_VER) && \ + !defined(__SGI_STL_PORT) && \ + !defined(_STLPORT_VERSION) && \ + !defined(_RWSTD_VER_STR) && \ + !defined(_RWSTD_VER) +// +// MSVC-9.0 defines a not-quite TR1 conforming hash +// function object in , so we must define +// this here, in addition the feature pack for VC9 +// provides a more or less full TR1 implementation: +// +# if defined(_HAS_TR1) && (_HAS_TR1 + 0) +# define BOOST_HAS_TR1_ARRAY +# define BOOST_HAS_TR1_REFERENCE_WRAPPER +# define BOOST_HAS_TR1_RESULT_OF +# define BOOST_HAS_TR1_MEM_FN +# define BOOST_HAS_TR1_BIND +# define BOOST_HAS_TR1_FUNCTION +# define BOOST_HAS_TR1_HASH +# define BOOST_HAS_TR1_SHARED_PTR +# define BOOST_HAS_TR1_RANDOM +# define BOOST_HAS_TR1_REGEX +# define BOOST_HAS_TR1_TUPLE +# define BOOST_HAS_TR1_TYPE_TRAITS +# define BOOST_HAS_TR1_UTILITY +# define BOOST_HAS_TR1_UNORDERED_MAP +# define BOOST_HAS_TR1_UNORDERED_SET +# else +# define BOOST_HAS_TR1_HASH +# endif +# if _MSC_VER >= 1600 +# define BOOST_HAS_CPP_0X +# endif +#endif + +#include + +#endif + + + diff --git a/modules/libcom/vxWorks/boost/tr1/memory.hpp b/modules/libcom/vxWorks/boost/tr1/memory.hpp new file mode 100644 index 000000000..169087746 --- /dev/null +++ b/modules/libcom/vxWorks/boost/tr1/memory.hpp @@ -0,0 +1,72 @@ +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED +# define BOOST_TR1_MEMORY_HPP_INCLUDED +# include +# include +# include + +#ifndef BOOST_HAS_TR1_SHARED_PTR + +// +// This header can get included by boost/shared_ptr.hpp which leads +// to cyclic dependencies, the workaround is to forward declare all +// the boost components, and then include the actual headers afterwards. +// This is fragile, but seems to work, and doesn't require modification +// of boost/shared_ptr.hpp. +// +namespace boost{ + +class bad_weak_ptr; +template class weak_ptr; +template class shared_ptr; +template void swap(weak_ptr & a, weak_ptr & b) BOOST_NOEXCEPT; +template void swap(shared_ptr & a, shared_ptr & b) BOOST_NOEXCEPT; +template shared_ptr static_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; +template shared_ptr dynamic_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; +template shared_ptr const_pointer_cast(shared_ptr const & r) BOOST_NOEXCEPT; +template D * get_deleter(shared_ptr const & p) BOOST_NOEXCEPT; +template class enable_shared_from_this; + +namespace detail{ +class shared_count; +class weak_count; +} + +} + +namespace std{ namespace tr1{ + + using ::boost::bad_weak_ptr; + using ::boost::shared_ptr; +#if !BOOST_WORKAROUND(__BORLANDC__, < 0x0582) + using ::boost::swap; +#endif + using ::boost::static_pointer_cast; + using ::boost::dynamic_pointer_cast; + using ::boost::const_pointer_cast; + using ::boost::get_deleter; + using ::boost::weak_ptr; + using ::boost::enable_shared_from_this; + +} } +#include +#include +#include + +#else + +# if defined(BOOST_HAS_INCLUDE_NEXT) && !defined(BOOST_TR1_DISABLE_INCLUDE_NEXT) +# include_next BOOST_TR1_HEADER(memory) +# else +# include +# include BOOST_TR1_STD_HEADER(BOOST_TR1_PATH(memory)) +# endif + +#endif + +#endif + diff --git a/modules/libcom/vxWorks/boost/type_traits/is_signed.hpp b/modules/libcom/vxWorks/boost/type_traits/is_signed.hpp new file mode 100644 index 000000000..ba7d6e97c --- /dev/null +++ b/modules/libcom/vxWorks/boost/type_traits/is_signed.hpp @@ -0,0 +1,140 @@ + +// (C) Copyright John Maddock 2005. +// Use, modification and distribution are subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt). +// +// See http://www.boost.org/libs/type_traits for most recent version including documentation. + + +#ifndef BOOST_TT_IS_SIGNED_HPP_INCLUDED +#define BOOST_TT_IS_SIGNED_HPP_INCLUDED + +#include +#include +#include +#include + +// should be the last #include +#include + +namespace boost { + +#if !defined( __CODEGEARC__ ) + +namespace detail{ + +#if !(defined(__EDG_VERSION__) && __EDG_VERSION__ <= 238) && !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) + +template +struct is_signed_values +{ + // + // Note that we cannot use BOOST_STATIC_CONSTANT here, using enum's + // rather than "real" static constants simply doesn't work or give + // the correct answer. + // + typedef typename remove_cv::type no_cv_t; + static const no_cv_t minus_one = (static_cast(-1)); + static const no_cv_t zero = (static_cast(0)); +}; + +template +struct is_signed_helper +{ + typedef typename remove_cv::type no_cv_t; + BOOST_STATIC_CONSTANT(bool, value = (!(::boost::detail::is_signed_values::minus_one > boost::detail::is_signed_values::zero))); +}; + +template +struct is_signed_select_helper +{ + template + struct rebind + { + typedef is_signed_helper type; + }; +}; + +template <> +struct is_signed_select_helper +{ + template + struct rebind + { + typedef false_type type; + }; +}; + +template +struct is_signed_imp +{ + typedef is_signed_select_helper< + ::boost::type_traits::ice_or< + ::boost::is_integral::value, + ::boost::is_enum::value>::value + > selector; + typedef typename selector::template rebind binder; + typedef typename binder::type type; +#if defined(BOOST_MSVC) && (BOOST_MSVC < 1300) + BOOST_STATIC_CONSTANT(bool, value = is_signed_imp::type::value); +#else + BOOST_STATIC_CONSTANT(bool, value = type::value); +#endif +}; + +#else + +template struct is_signed_imp : public false_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +#ifdef BOOST_HAS_LONG_LONG +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +#endif +#if defined(CHAR_MIN) && (CHAR_MIN != 0) +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +#endif +#if defined(WCHAR_MIN) && (WCHAR_MIN != 0) +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +template <> struct is_signed_imp : public true_type{}; +#endif + +#endif + +} + +#endif // !defined( __CODEGEARC__ ) + +#if defined( __CODEGEARC__ ) +BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_signed,T,__is_signed(T)) +#else +BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_signed,T,::boost::detail::is_signed_imp::value) +#endif + +} // namespace boost + +#include + +#endif // BOOST_TT_IS_MEMBER_FUNCTION_POINTER_HPP_INCLUDED diff --git a/modules/libcom/vxWorks/boost/weak_ptr.hpp b/modules/libcom/vxWorks/boost/weak_ptr.hpp new file mode 100644 index 000000000..dd2686905 --- /dev/null +++ b/modules/libcom/vxWorks/boost/weak_ptr.hpp @@ -0,0 +1,18 @@ +#ifndef BOOST_WEAK_PTR_HPP_INCLUDED +#define BOOST_WEAK_PTR_HPP_INCLUDED + +// +// weak_ptr.hpp +// +// Copyright (c) 2001, 2002, 2003 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// +// See http://www.boost.org/libs/smart_ptr/weak_ptr.htm for documentation. +// + +#include + +#endif // #ifndef BOOST_WEAK_PTR_HPP_INCLUDED