diff options
Diffstat (limited to 'gnu/lib')
93 files changed, 29436 insertions, 0 deletions
diff --git a/gnu/lib/Makefile b/gnu/lib/Makefile new file mode 100644 index 0000000..cae52c3 --- /dev/null +++ b/gnu/lib/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +SUBDIR= csu libgcc libgcov libdialog libgomp libodialog libregex libreadline \ + libssp + +# libsupc++ uses libstdc++ headers, although 'make includes' should +# have taken care of that already. +.if ${MK_CXX} != "no" +SUBDIR+= libstdc++ libsupc++ +.endif + +.if ${MK_OBJC} != "no" +SUBDIR+= libobjc +.endif + +.include <bsd.subdir.mk> diff --git a/gnu/lib/Makefile.inc b/gnu/lib/Makefile.inc new file mode 100644 index 0000000..265f86d --- /dev/null +++ b/gnu/lib/Makefile.inc @@ -0,0 +1,3 @@ +# $FreeBSD$ + +.include "../Makefile.inc" diff --git a/gnu/lib/csu/Makefile b/gnu/lib/csu/Makefile new file mode 100644 index 0000000..4999d06 --- /dev/null +++ b/gnu/lib/csu/Makefile @@ -0,0 +1,80 @@ +# $FreeBSD$ + +.include <bsd.own.mk> +MK_SSP= no + +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +CCDIR= ${.CURDIR}/../../usr.bin/cc +.include "${CCDIR}/Makefile.tgt" + +.PATH: ${GCCDIR}/config/${GCC_CPU} ${GCCDIR} + +SRCS= crtstuff.c tconfig.h tm.h options.h +OBJS= crtbegin.o crtend.o crtbeginT.o +SOBJS= crtbegin.So crtend.So +CSTD?= gnu89 +CFLAGS+= -DIN_GCC -DHAVE_LD_EH_FRAME_HDR -DDT_CONFIG -D__GLIBC__=3 +CFLAGS+= -finhibit-size-directive -fno-inline-functions \ + -fno-exceptions -fno-zero-initialized-in-bss \ + -fno-zero-initialized-in-bss -fno-toplevel-reorder \ + -fno-asynchronous-unwind-tables -fno-omit-frame-pointer +CFLAGS+= -I${GCCLIB}/include -I${GCCDIR}/config -I${GCCDIR} -I. \ + -I${CCDIR}/cc_tools +CRTS_CFLAGS= -DCRTSTUFFS_O -DSHARED ${PICFLAG} +MKDEP= -DCRT_BEGIN + +.if ${MACHINE_CPUARCH} == "ia64" +BEGINSRC= crtbegin.asm +ENDSRC= crtend.asm +CFLAGS+= -x assembler-with-cpp # Ugly hack +CFLAGS+= -include osreldate.h +.undef SRCS # hack for 'make depend' +.endif +.if ${MACHINE_CPUARCH} == "powerpc" +TGTOBJS= crtsavres.o +SRCS+= crtsavres.asm +.endif +.if ${MACHINE_CPUARCH} == "sparc64" +TGTOBJS= crtfastmath.o +SRCS+= crtfastmath.c +.endif +BEGINSRC?= crtstuff.c +ENDSRC?= crtstuff.c + +all: ${OBJS} ${SOBJS} ${TGTOBJS} +${OBJS} ${SOBJS}: ${SRCS:M*.h} + +CLEANFILES= ${OBJS} ${SOBJS} ${TGTOBJS} + +crtbegin.o: ${BEGINSRC} + ${CC} ${CFLAGS} -g0 -DCRT_BEGIN \ + -c -o ${.TARGET} ${.ALLSRC:N*.h} + +crtbeginT.o: ${BEGINSRC} + ${CC} ${CFLAGS} -g0 -DCRT_BEGIN -DCRTSTUFFT_O \ + -c -o ${.TARGET} ${.ALLSRC:N*.h} + +crtbegin.So: ${BEGINSRC} + ${CC} ${CFLAGS} -g0 -DCRT_BEGIN ${CRTS_CFLAGS} \ + -c -o ${.TARGET} ${.ALLSRC:N*.h} + +crtend.o: ${ENDSRC} + ${CC} ${CFLAGS} -g0 -DCRT_END \ + -c -o ${.TARGET} ${.ALLSRC:N*.h} + +crtend.So: ${ENDSRC} + ${CC} ${CFLAGS} -g0 -DCRT_END ${CRTS_CFLAGS} \ + -c -o ${.TARGET} ${.ALLSRC:N*.h} + +CLEANFILES+= tm.h tconfig.h options.h optionlist cs-tconfig.h cs-tm.h +tm.h tconfig.h options.h: ${CCDIR}/cc_tools/Makefile + ${MAKE} -f ${.ALLSRC} MFILE=${.ALLSRC} GCCDIR=${GCCDIR} ${.TARGET} + +realinstall: +.for file in ${OBJS} ${SOBJS} ${TGTOBJS} + ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ + ${file} ${DESTDIR}${LIBDIR}/${file:S/.So$/S.o/} +.endfor + +.include <bsd.lib.mk> diff --git a/gnu/lib/libdialog/Makefile b/gnu/lib/libdialog/Makefile new file mode 100644 index 0000000..7547a60 --- /dev/null +++ b/gnu/lib/libdialog/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +DIALOG= ${.CURDIR}/../../../contrib/dialog + +LIB= dialog +SHLIB_MAJOR= 7 +SRCS= argv.c arrows.c buttons.c calendar.c checklist.c columns.c \ + dlg_keys.c editbox.c fselect.c formbox.c guage.c inputbox.c \ + inputstr.c menubox.c mixedform.c mixedgauge.c mouse.c \ + mousewget.c msgbox.c pause.c prgbox.c progressbox.c rc.c \ + tailbox.c textbox.c timebox.c trace.c ui_getc.c util.c \ + version.c yesno.c +INCS= dialog.h dlg_colors.h dlg_config.h dlg_keys.h +MAN= dialog.3 + +CFLAGS+= -I${.CURDIR} -I${DIALOG} -D_XOPEN_SOURCE_EXTENDED +.PATH: ${DIALOG} +WARNS?= 3 + +.include <bsd.lib.mk> diff --git a/gnu/lib/libdialog/dlg_config.h b/gnu/lib/libdialog/dlg_config.h new file mode 100644 index 0000000..4dd6ab4 --- /dev/null +++ b/gnu/lib/libdialog/dlg_config.h @@ -0,0 +1,91 @@ +/* dlg_config.h. Generated automatically by configure. */ +/* + * The configure script expands this as a set of definitions + * + * $FreeBSD$ + */ + +#define DIALOG_PATCHDATE 20100428 +#define DIALOG_VERSION "1.1" +#define HAVE_ALLOCA 1 +#define HAVE_COLOR 1 +#define HAVE_DIRENT_H 1 +#define HAVE_DLG_FORMBOX 1 +#define HAVE_DLG_GAUGE 1 +#define HAVE_DLG_MIXEDFORM 1 +#define HAVE_DLG_TAILBOX 1 +#define HAVE_DLG_TRACE 1 +#define HAVE_FEOF_UNLOCKED 1 +#define HAVE_FLUSHINP 1 +#define HAVE_FSEEKO 1 +#define HAVE_GETBEGX 1 +#define HAVE_GETBEGY 1 +#define HAVE_GETBEGYX 1 +#define HAVE_GETCURX 1 +#define HAVE_GETCURY 1 +#define HAVE_GETCWD 1 +#define HAVE_GETEGID 1 +#define HAVE_GETEUID 1 +#define HAVE_GETGID 1 +#define HAVE_GETMAXX 1 +#define HAVE_GETMAXY 1 +#define HAVE_GETMAXYX 1 +#define HAVE_GETPAGESIZE 1 +#define HAVE_GETPARX 1 +#define HAVE_GETPARY 1 +#define HAVE_GETPARYX 1 +#define HAVE_GETUID 1 +#define HAVE_ICONV 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LANGINFO_CODESET 1 +#define HAVE_LC_MESSAGES 1 +#define HAVE_LIBNCURSESW 1 +#define HAVE_LIMITS_H 1 +#define HAVE_LOCALE_H 1 +#define HAVE_MBSTATE_T 1 +#define HAVE_MEMORY_H 1 +#define HAVE_MIXEDGAUGE 1 +#define HAVE_MMAP 1 +#define HAVE_MUNMAP 1 +#define HAVE_NL_TYPES_H 1 +#define HAVE_PUTENV 1 +#define HAVE_RC_FILE 1 +#define HAVE_SEARCH_H 1 +#define HAVE_SETENV 1 +#define HAVE_SETLOCALE 1 +#define HAVE_STDDEF_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STPCPY 1 +#define HAVE_STRCASECMP 1 +#define HAVE_STRCHR 1 +#define HAVE_STRDUP 1 +#define HAVE_STRFTIME 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRTOUL 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_TERM_H 1 +#define HAVE_TSEARCH 1 +#define HAVE_TYPE_CHTYPE 1 +#define HAVE_UNCTRL_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_USE_DEFAULT_COLORS 1 +#define HAVE_WAITPID 1 +#define HAVE_WGET_WCH 1 +#define HAVE_XDIALOG 1 +#define HAVE__NC_FREE_AND_EXIT 1 +#define ICONV_CONST const +#define MIXEDCASE_FILENAMES 1 +#define NCURSES 1 +#define NEED_WCHAR_H 1 +#define PACKAGE "dialog" +#define RETSIGTYPE void +#define STDC_HEADERS 1 +#define SYSTEM_NAME "freebsd9.0" +#define TIME_WITH_SYS_TIME 1 +#define TYPE_CHTYPE_IS_SCALAR 1 +#define USE_WIDE_CURSES 1 diff --git a/gnu/lib/libgcc/Makefile b/gnu/lib/libgcc/Makefile new file mode 100644 index 0000000..7f6fc0d --- /dev/null +++ b/gnu/lib/libgcc/Makefile @@ -0,0 +1,374 @@ +# $FreeBSD$ + +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs + +SHLIB_NAME= libgcc_s.so.1 +SHLIBDIR?= /lib + +.include <bsd.own.mk> +# +# libgcc is linked in last and thus cannot depend on ssp symbols coming +# from earlier libraries. Disable stack protection for this library. +# +MK_SSP= no + +.include "${.CURDIR}/../../usr.bin/cc/Makefile.tgt" + +.if ${TARGET_CPUARCH} == "sparc64" || ${TARGET_CPUARCH} == "mips" +LIB= gcc +.endif + +.PATH: ${GCCDIR}/config/${GCC_CPU} ${GCCDIR}/config ${GCCDIR} + +CFLAGS+= -DIN_GCC -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED \ + -DHAVE_GTHR_DEFAULT \ + -I${GCCLIB}/include \ + -I${GCCDIR}/config -I${GCCDIR} -I. \ + -I${.CURDIR}/../../usr.bin/cc/cc_tools + +LDFLAGS+= -nodefaultlibs +LDADD+= -lc + +OBJS= # added to below in various ways depending on TARGET_CPUARCH + +#--------------------------------------------------------------------------- +# +# When upgrading GCC, get the following defintions straight from Makefile.in +# +# Library members defined in libgcc2.c. +LIB2FUNCS= _muldi3 _negdi2 _lshrdi3 _ashldi3 _ashrdi3 \ + _cmpdi2 _ucmpdi2 _clear_cache \ + _enable_execute_stack _trampoline __main _absvsi2 _absvdi2 _addvsi3 \ + _addvdi3 _subvsi3 _subvdi3 _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 _ctors \ + _ffssi2 _ffsdi2 _clz _clzsi2 _clzdi2 _ctzsi2 _ctzdi2 _popcount_tab \ + _popcountsi2 _popcountdi2 _paritysi2 _paritydi2 _powisf2 _powidf2 \ + _powixf2 _powitf2 _mulsc3 _muldc3 _mulxc3 _multc3 _divsc3 _divdc3 \ + _divxc3 _divtc3 + +# The floating-point conversion routines that involve a single-word integer. +.for mode in sf df xf +LIB2FUNCS+= _fixuns${mode}si +.endfor + +# Likewise double-word routines. +.for mode in sf df xf tf +LIB2FUNCS+= _fix${mode}di _fixuns${mode}di +LIB2FUNCS+= _floatdi${mode} _floatundi${mode} +.endfor + +LIB2ADD = $(LIB2FUNCS_EXTRA) +LIB2ADD_ST = $(LIB2FUNCS_STATIC_EXTRA) + +# Additional sources to handle exceptions; overridden by targets as needed. +LIB2ADDEH = unwind-dw2.c unwind-dw2-fde-glibc.c unwind-sjlj.c gthr-gnat.c \ + unwind-c.c +LIB2ADDEHSTATIC = $(LIB2ADDEH) +LIB2ADDEHSHARED = $(LIB2ADDEH) + +# List of extra C and assembler files to add to static and shared libgcc2. +# Assembler files should have names ending in `.asm'. +LIB2FUNCS_EXTRA = + +# List of extra C and assembler files to add to static libgcc2. +# Assembler files should have names ending in `.asm'. +LIB2FUNCS_STATIC_EXTRA = + +# Defined in libgcc2.c, included only in the static library. +# KAN: Excluded _sf_to_tf and _df_to_tf as TPBIT_FUNCS are not +# built on any of our platforms. +LIB2FUNCS_ST = _eprintf __gcc_bcmp + +FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \ + _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \ + _lt_sf _le_sf _unord_sf _si_to_sf _sf_to_si _negate_sf _make_sf \ + _sf_to_df _thenan_sf _sf_to_usi _usi_to_sf + +DPBIT_FUNCS = _pack_df _unpack_df _addsub_df _mul_df _div_df \ + _fpcmp_parts_df _compare_df _eq_df _ne_df _gt_df _ge_df \ + _lt_df _le_df _unord_df _si_to_df _df_to_si _negate_df _make_df \ + _df_to_sf _thenan_df _df_to_usi _usi_to_df + +TPBIT_FUNCS = _pack_tf _unpack_tf _addsub_tf _mul_tf _div_tf \ + _fpcmp_parts_tf _compare_tf _eq_tf _ne_tf _gt_tf _ge_tf \ + _lt_tf _le_tf _unord_tf _si_to_tf _tf_to_si _negate_tf _make_tf \ + _tf_to_df _tf_to_sf _thenan_tf _tf_to_usi _usi_to_tf + +# These might cause a divide overflow trap and so are compiled with +# unwinder info. +LIB2_DIVMOD_FUNCS = _divdi3 _moddi3 _udivdi3 _umoddi3 _udiv_w_sdiv _udivmoddi4 + +#----------------------------------------------------------------------- +# +# Platform specific bits. +# When upgrading GCC, get the following definitions from config/<cpu>/t-* +# +.if ${TARGET_CPUARCH} == "arm" +# from config/arm/t-strongarm-elf +CFLAGS+= -Dinhibit_libc -fno-inline +LIB1ASMSRC = lib1funcs.asm +LIB1ASMFUNCS = _dvmd_tls _bb_init_func +LIB2FUNCS_EXTRA = floatunsidf.c floatunsisf.c + +# Not now +#LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func +#LIB1ASMFUNCS+= _call_via_rX _interwork_call_via_rX \ +# _lshrdi3 _ashrdi3 _ashldi3 \ +# _negdf2 _addsubdf3 _muldivdf3 _cmpdf2 _unorddf2 _fixdfsi _fixunsdfsi \ +# _truncdfsf2 _negsf2 _addsubsf3 _muldivsf3 _cmpsf2 _unordsf2 \ +# _fixsfsi _fixunssfsi _floatdidf _floatdisf +.endif + +.if ${TARGET_CPUARCH} == mips +LIB2FUNCS_EXTRA = floatunsidf.c floatunsisf.c +# ABIs other than o32 need this +.if ${TARGET_ARCH:Mmipse[lb]} == "" +LIB2FUNCS_EXTRA+= floatdidf.c fixunsdfsi.c +LIB2FUNCS_EXTRA+= floatdisf.c floatundidf.c +LIB2FUNCS_EXTRA+= fixsfdi.c floatundisf.c +LIB2FUNCS_EXTRA+= fixdfdi.c fixunssfsi.c +.endif +.endif + +.if ${TARGET_CPUARCH} == "ia64" +# from config/ia64/t-ia64 +LIB1ASMSRC = lib1funcs.asm +LIB1ASMFUNCS = __divxf3 __divdf3 __divsf3 \ + __divdi3 __moddi3 __udivdi3 __umoddi3 \ + __divsi3 __modsi3 __udivsi3 __umodsi3 __save_stack_nonlocal \ + __nonlocal_goto __restore_stack_nonlocal __trampoline \ + _fixtfdi _fixunstfdi _floatditf +LIB2ADDEH = unwind-ia64.c unwind-sjlj.c unwind-c.c +.endif + +.if ${TARGET_ARCH} == "powerpc" +# from config/rs6000/t-ppccomm +LIB2FUNCS_EXTRA = tramp.asm +LIB2FUNCS_STATIC_EXTRA = eabi.asm +.endif + +.if ${TARGET_ARCH} == "powerpc64" +# from config/rs6000/t-ppccomm +LIB2FUNCS_EXTRA = tramp.asm +.endif + +.if ${TARGET_CPUARCH} == "sparc64" +# from config/sparc/t-elf +LIB1ASMSRC = lb1spc.asm +LIB1ASMFUNCS = _mulsi3 _divsi3 _modsi3 +.endif + +#----------------------------------------------------------------------- + +# Remove any objects from LIB2FUNCS and LIB2_DIVMOD_FUNCS that are +# defined as optimized assembly code in LIB1ASMFUNCS. +.if defined(LIB1ASMFUNCS) +.for sym in ${LIB1ASMFUNCS} +LIB2FUNCS:= ${LIB2FUNCS:S/${sym}//g} +LIB2_DIVMOD_FUNCS:= ${LIB2_DIVMOD_FUNCS:S/${sym}//g} +.endfor +.endif + +COMMONHDRS= tm.h tconfig.h options.h unwind.h gthr-default.h + +#----------------------------------------------------------------------- +# +# Helpful shortcuts for compiler invocations. +# +HIDE = -fvisibility=hidden -DHIDE_EXPORTS +CC_T = ${CC} -c ${CFLAGS} ${HIDE} -fPIC +CC_P = ${CC} -c ${CFLAGS} ${HIDE} -p -fPIC +CC_S = ${CC} -c ${CFLAGS} ${PICFLAG} -DSHARED + +#----------------------------------------------------------------------- +# +# Functions from libgcc2.c +# +STD_CFLAGS = +DIV_CFLAGS = -fexceptions -fnon-call-exceptions + +STD_FUNCS = ${LIB2FUNCS} +DIV_FUNCS = ${LIB2_DIVMOD_FUNCS} + +STD_CFILE = libgcc2.c +DIV_CFILE = libgcc2.c + +OBJ_GRPS = STD DIV + +#----------------------------------------------------------------------- +# +# Floating point emulation functions +# +.if ${TARGET_CPUARCH} == "armNOT_YET" || \ + ${TARGET_CPUARCH} == "powerpc" || ${TARGET_CPUARCH} == "sparc64" + +FPBIT_CFLAGS = -DFINE_GRAINED_LIBRARIES -DFLOAT +DPBIT_CFLAGS = -DFINE_GRAINED_LIBRARIES + +FPBIT_CFILE = config/fp-bit.c +DPBIT_CFILE = config/fp-bit.c + +OBJ_GRPS += FPBIT DPBIT +.endif + +#----------------------------------------------------------------------- +# +# Generic build rules for object groups defined above +# +.for T in ${OBJ_GRPS} +${T}_OBJS_T = ${${T}_FUNCS:S/$/.o/} +${T}_OBJS_P = ${${T}_FUNCS:S/$/.po/} +${T}_OBJS_S = ${${T}_FUNCS:S/$/.So/} +OBJS += ${${T}_FUNCS:S/$/.o/} + +${${T}_OBJS_T}: ${${T}_CFILE} ${COMMONHDRS} + ${CC_T} ${${T}_CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +${${T}_OBJS_P}: ${${T}_CFILE} ${COMMONHDRS} + ${CC_P} ${${T}_CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +${${T}_OBJS_S}: ${${T}_CFILE} ${COMMONHDRS} + ${CC_S} ${${T}_CFLAGS} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +.endfor + +#----------------------------------------------------------------------- +# +# Extra objects coming from separate files +# +.if !empty(LIB2ADD) +OBJS += ${LIB2ADD:R:S/$/.o/} +SOBJS += ${LIB2ADD:R:S/$/.So/} +POBJS += ${LIB2ADD:R:S/$/.po/} +.endif + +#----------------------------------------------------------------------- +# +# Objects that should be in static library only. +# +SYMS_ST = ${LIB2FUNCS_ST} ${LIB2ADD_ST} +STAT_OBJS_T = ${SYMS_ST:S/$/.o/} +STAT_OBJS_P = ${SYMS_ST:S/$/.po/} +STATICOBJS = ${SYMS_ST:S/$/.o/} + +${STAT_OBJS_T}: ${STD_CFILE} ${COMMONHDRS} + ${CC_T} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +${STAT_OBJS_P}: ${STD_CFILE} ${COMMONHDRS} + ${CC_P} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} + +#----------------------------------------------------------------------- +# +# Assembler files. +# +.if defined(LIB1ASMSRC) +ASM_T = ${LIB1ASMFUNCS:S/$/.o/} +ASM_P = ${LIB1ASMFUNCS:S/$/.po/} +ASM_S = ${LIB1ASMFUNCS:S/$/.So/} +ASM_V = ${LIB1ASMFUNCS:S/$/.vis/} +OBJS += ${LIB1ASMFUNCS:S/$/.o/} + +${ASM_T}: ${LIB1ASMSRC} ${.PREFIX}.vis + ${CC} -x assembler-with-cpp -c ${CFLAGS} -DL${.PREFIX} \ + -o ${.TARGET} -include ${.PREFIX}.vis ${.ALLSRC:N*.h:N*.vis} +${ASM_P}: ${LIB1ASMSRC} ${.PREFIX}.vis + ${CC} -x assembler-with-cpp -p -c ${CFLAGS} -DL${.PREFIX} \ + -o ${.TARGET} -include ${.PREFIX}.vis ${.ALLSRC:N*.h:N*.vis} +${ASM_S}: ${LIB1ASMSRC} + ${CC} -x assembler-with-cpp -c ${PICFLAG} ${CFLAGS} -DL${.PREFIX} \ + -o ${.TARGET} ${.ALLSRC:N*.h} +${ASM_V}: ${LIB1ASMSRC} + ${CC} -x assembler-with-cpp -c ${CFLAGS} -DL${.PREFIX} \ + -o ${.PREFIX}.vo ${.ALLSRC:N*.h} + ( nm -pg ${.PREFIX}.vo | \ + awk 'NF == 3 && $$2 !~ /^[UN]$$/ { print "\t.hidden ", $$3 }'\ + ) > ${.TARGET} + +CLEANFILES += ${ASM_V} ${ASM_V:R:S/$/.vo/} +.endif + +#----------------------------------------------------------------------- +# +# Exception handling / unwinding support. +# +EH_OBJS_T = ${LIB2ADDEHSTATIC:R:S/$/.o/} +EH_OBJS_P = ${LIB2ADDEHSTATIC:R:S/$/.po/} +EH_OBJS_S = ${LIB2ADDEHSHARED:R:S/$/.So/} +EH_CFLAGS = -fexceptions -D__GLIBC__=3 -DElfW=__ElfN +SOBJS += ${EH_OBJS_S} + +.for _src in ${LIB2ADDEHSTATIC} +${_src:R:S/$/.o/}: ${_src} ${COMMONHDRS} + ${CC_T} ${EH_CFLAGS} -o ${.TARGET} ${.IMPSRC} +${_src:R:S/$/.po/}: ${_src} ${COMMONHDRS} + ${CC_P} ${EH_CFLAGS} -o ${.TARGET} ${.IMPSRC} +.endfor +.for _src in ${LIB2ADDEHSHARED} +${_src:R:S/$/.So/}: ${_src} ${COMMONHDRS} + ${CC_S} ${EH_CFLAGS} -o ${.TARGET} ${.IMPSRC} +.endfor + + +#----------------------------------------------------------------------- +# +# Generated headers +# +${COMMONHDRS}: ${.CURDIR}/../../usr.bin/cc/cc_tools/Makefile + ${MAKE} -f ${.ALLSRC} MFILE=${.ALLSRC} GCCDIR=${GCCDIR} ${.TARGET} + +CLEANFILES += ${COMMONHDRS} +CLEANFILES += cs-*.h option* + +#----------------------------------------------------------------------- +# +# Build symbol version map +# +SHLIB_MKMAP = ${GCCDIR}/mkmap-symver.awk +SHLIB_MKMAP_OPTS = +SHLIB_MAPFILES = ${GCCDIR}/libgcc-std.ver +VERSION_MAP = libgcc.map + +libgcc.map: ${SHLIB_MKMAP} ${SHLIB_MAPFILES} ${SOBJS} ${OBJS:R:S/$/.So/} + ( nm -pg ${SOBJS};echo %% ; \ + cat ${SHLIB_MAPFILES} \ + | sed -e '/^[ ]*#/d' \ + -e 's/^%\(if\|else\|elif\|endif\|define\)/#\1/' \ + | ${CC} ${CFLAGS} -E -xassembler-with-cpp -; \ + ) | awk -f ${SHLIB_MKMAP} ${SHLIB_MKMAP_OPTS} > ${.TARGET} + +CLEANFILES += libgcc.map + +#----------------------------------------------------------------------- +# +# Build additional static libgcc_eh[_p].a libraries. +# +libgcc_eh.a: ${EH_OBJS_T} + @${ECHO} building static gcc_eh library + @rm -f ${.TARGET} + @${AR} cq ${.TARGET} `lorder ${EH_OBJS_T} | tsort -q` + ${RANLIB} ${.TARGET} + +all: libgcc_eh.a + +.if ${MK_PROFILE} != "no" +libgcc_eh_p.a: ${EH_OBJS_P} + @${ECHO} building profiled gcc_eh library + @rm -f ${.TARGET} + @${AR} cq ${.TARGET} `lorder ${EH_OBJS_P} | tsort -q` + ${RANLIB} ${.TARGET} +all: libgcc_eh_p.a +.endif + +_libinstall: _lib-eh-install + +_lib-eh-install: +.if ${MK_INSTALLLIB} != "no" + ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ + ${_INSTALLFLAGS} libgcc_eh.a ${DESTDIR}${LIBDIR} +.endif +.if ${MK_PROFILE} != "no" + ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ + ${_INSTALLFLAGS} libgcc_eh_p.a ${DESTDIR}${LIBDIR} +.endif + +CLEANFILES+= libgcc_eh.a libgcc_eh_p.a ${EH_OBJS_T} ${EH_OBJS_P} + +.include <bsd.lib.mk> + +.SUFFIXES: .vis .vo diff --git a/gnu/lib/libgcov/Makefile b/gnu/lib/libgcov/Makefile new file mode 100644 index 0000000..680cdbf --- /dev/null +++ b/gnu/lib/libgcov/Makefile @@ -0,0 +1,60 @@ +# $FreeBSD$ + +NO_PROFILE= +.include <bsd.own.mk> +MK_SSP= no +.include "${.CURDIR}/../../usr.bin/cc/Makefile.tgt" + +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +.PATH: ${GCCDIR}/config/${GCC_CPU} ${GCCDIR} + +LIB= gcov + +CFLAGS+= -DIN_GCC -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED +CFLAGS+= -D_PTHREADS -DGTHREAD_USE_WEAK +CFLAGS+= -I${.CURDIR}/../../usr.bin/cc/cc_tools \ + -I${GCCLIB}/include -I${GCCDIR}/config -I${GCCDIR} -I. +# +# Library members defined in libgcov.c. +# Defined in libgcov.c, included only in gcov library +SYMS = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \ + _gcov_fork _gcov_execl _gcov_execlp _gcov_execle \ + _gcov_execv _gcov_execvp _gcov_execve \ + _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler + +OBJS= ${SYMS:S/$/.o/} +OBJS_T= ${SYMS:S/$/.o/} +OBJS_P= ${SYMS:S/$/.po/} +OBJS_S= ${SYMS:S/$/.So/} + +#----------------------------------------------------------------------- +# +# Helpful shortcuts for compiler invocations. +# +CC_T = ${CC} -c ${CFLAGS} -fPIC +CC_P = ${CC} -c ${CFLAGS} -p -fPIC +CC_S = ${CC} -c ${CFLAGS} ${PICFLAG} -DSHARED + +COMMONHDRS= tm.h tconfig.h gcov-iov.h options.h +CLEANFILES+= ${COMMONHDRS} cs-tm.h cs-tconfig.h options.h optionlist + +${COMMONHDRS}: ${.CURDIR}/../../usr.bin/cc/cc_tools/Makefile + ${MAKE} -f ${.ALLSRC} MFILE=${.ALLSRC} GCCDIR=${GCCDIR} ${.TARGET} + +${OBJS} beforedepend: ${COMMONHDRS} + +${OBJS_T}: libgcov.c + ${CC_T} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} + +.if !defined(NO_PIC) +${OBJS_S}: libgcov.c + ${CC_S} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +.endif + +.if ${MK_PROFILE} != "no" +${OBJS_P}: libgcov.c + ${CC_P} -DL${.PREFIX} -o ${.TARGET} ${.ALLSRC:M*.c} +.endif + +.include <bsd.lib.mk> diff --git a/gnu/lib/libgomp/Makefile b/gnu/lib/libgomp/Makefile new file mode 100644 index 0000000..237c14a --- /dev/null +++ b/gnu/lib/libgomp/Makefile @@ -0,0 +1,61 @@ +# $FreeBSD$ + +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +SRCDIR= ${GCCLIB}/libgomp + +.PATH: ${SRCDIR} ${SRCDIR}/config/posix + +LIB= gomp +SHLIB_MAJOR= 1 + +SRCS= alloc.c barrier.c critical.c env.c \ + error.c iter.c loop.c ordered.c parallel.c sections.c \ + single.c team.c work.c lock.c mutex.c proc.c sem.c \ + bar.c time.c fortran.c +SRCS+= gstdint.h libgomp_f.h omp.h omp_lib.h + +INCS+= omp.h + +CFLAGS+= -DHAVE_CONFIG_H +CFLAGS+= -I${.CURDIR} -I. -I${SRCDIR} -I${SRCDIR}/config/posix + +VERSION_MAP= ${SRCDIR}/libgomp.map + +# Target-specific OpenMP configuration +.if ${MACHINE_CPUARCH} == arm || ${MACHINE_CPUARCH} == i386 || \ + ${MACHINE_ARCH} == powerpc || \ + (${MACHINE_CPUARCH} == mips && ${MACHINE_ARCH:Mmips64*} == "") +OMP_LOCK_ALIGN = 4 +OMP_LOCK_KIND= 4 +OMP_LOCK_SIZE= 4 +OMP_NEST_LOCK_ALIGN= 4 +OMP_NEST_LOCK_KIND= 8 +OMP_NEST_LOCK_SIZE= 8 +.else +OMP_LOCK_ALIGN = 8 +OMP_LOCK_KIND= 8 +OMP_LOCK_SIZE= 8 +OMP_NEST_LOCK_ALIGN= 8 +OMP_NEST_LOCK_KIND= 8 +OMP_NEST_LOCK_SIZE= 16 +.endif + +gstdint.h: + echo '#include <sys/types.h>' > ${.TARGET} + echo '#include <stdint.h>' >> ${.TARGET} +CLEANFILES+= gstdint.h + +.for HFILE in libgomp_f.h omp.h omp_lib.h +${HFILE}: ${SRCDIR}/${HFILE}.in ${.CURDIR}/Makefile + sed -e 's/@OMP_LOCK_ALIGN@/${OMP_LOCK_ALIGN}/g' \ + -e 's/@OMP_LOCK_KIND@/${OMP_LOCK_KIND}/g' \ + -e 's/@OMP_LOCK_SIZE@/${OMP_LOCK_SIZE}/g' \ + -e 's/@OMP_NEST_LOCK_ALIGN@/${OMP_NEST_LOCK_ALIGN}/g' \ + -e 's/@OMP_NEST_LOCK_KIND@/${OMP_NEST_LOCK_KIND}/g' \ + -e 's/@OMP_NEST_LOCK_SIZE@/${OMP_NEST_LOCK_SIZE}/g' \ + < ${SRCDIR}/${HFILE}.in > ${.TARGET} +CLEANFILES+= ${HFILE} +.endfor + +.include <bsd.lib.mk> diff --git a/gnu/lib/libgomp/config.h b/gnu/lib/libgomp/config.h new file mode 100644 index 0000000..c56d860 --- /dev/null +++ b/gnu/lib/libgomp/config.h @@ -0,0 +1,109 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* $FreeBSD$ */ + +/* Define to 1 if the target supports __attribute__((alias(...))). */ +#define HAVE_ATTRIBUTE_ALIAS 1 + +/* Define to 1 if the target supports __attribute__((dllexport)). */ +/* #undef HAVE_ATTRIBUTE_DLLEXPORT */ + +/* Define to 1 if the target supports __attribute__((visibility(...))). */ +#define HAVE_ATTRIBUTE_VISIBILITY 1 + +/* Define if the POSIX Semaphores do not work on your system. */ +/* #undef HAVE_BROKEN_POSIX_SEMAPHORES */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the `getloadavg' function. */ +#define HAVE_GETLOADAVG 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the <semaphore.h> header file. */ +#define HAVE_SEMAPHORE_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if the target supports __sync_*_compare_and_swap */ +#ifdef __amd64__ +#define HAVE_SYNC_BUILTINS 1 +#endif + +/* Define to 1 if you have the <sys/loadavg.h> header file. */ +/* #undef HAVE_SYS_LOADAVG_H */ + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if the target supports thread-local storage. */ +#if !defined(__arm__) && !defined(__mips__) +#define HAVE_TLS 1 +#endif + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "libgomp" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "GNU OpenMP Runtime Library" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "GNU OpenMP Runtime Library 1.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libgomp" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0" + +/* The size of a `char', as computed by sizeof. */ +/* #undef SIZEOF_CHAR */ + +/* The size of a `int', as computed by sizeof. */ +/* #undef SIZEOF_INT */ + +/* The size of a `long', as computed by sizeof. */ +/* #undef SIZEOF_LONG */ + +/* The size of a `short', as computed by sizeof. */ +/* #undef SIZEOF_SHORT */ + +/* The size of a `void *', as computed by sizeof. */ +/* #undef SIZEOF_VOID_P */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ +#define TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define VERSION "1.0" diff --git a/gnu/lib/libobjc/Makefile b/gnu/lib/libobjc/Makefile new file mode 100644 index 0000000..80d0240 --- /dev/null +++ b/gnu/lib/libobjc/Makefile @@ -0,0 +1,50 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +OBJCDIR=${.CURDIR}/../../../contrib/libobjc +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs + +.PATH: ${OBJCDIR}/objc ${OBJCDIR} + +LIB= objc +SHLIB_MAJOR= 4 + +SRCS= archive.c class.c encoding.c gc.c hash.c init.c linking.m misc.c \ + nil_method.c NXConstStr.m Object.m objects.c Protocol.m sarray.c \ + selector.c sendmsg.c thr.c thr-objc.c exception.c + +# XXX: clang cannot compile libobjc yet +CC:=${CC:C/^(.*\/)?clang$/gcc/1} + +INCS= encoding.h hash.h objc-api.h objc-decls.h objc-list.h objc.h runtime.h \ + sarray.h thr.h typedstream.h NXConstStr.h Object.h Protocol.h +INCSDIR=${INCLUDEDIR}/objc + +CFLAGS+= -DHAVE_GTHR_DEFAULT -DIN_GCC -DIN_TARGET_LIBS +CFLAGS+= -I. -I${.CURDIR}/../../usr.bin/cc/cc_tools +CFLAGS+= -I${OBJCDIR}/objc -I${OBJCDIR} +CFLAGS+= -I${GCCDIR}/config -I${GCCDIR} +CFLAGS+= -I${GCCLIB}/include +CFLAGS+= -fexceptions -frandom-seed=RepeatabilityConsideredGood +OBJCFLAGS= -fgnu-runtime ${CFLAGS} + +GENHDRS= runtime-info.h + +runtime-info.h: + `${CC} --print-prog-name=cc1obj` -print-objc-runtime-info \ + < /dev/null > ${.TARGET} + +.for H in tconfig.h tm.h config.h options.h gthr-default.h unwind.h +$H: ${.CURDIR}/../../usr.bin/cc/cc_tools/Makefile + ${MAKE} -f ${.ALLSRC} MFILE=${.ALLSRC} GCCDIR=${GCCDIR} ${.TARGET} +GENHDRS+= $H +.endfor + +CLEANFILES+= ${GENHDRS} cs-* optionlist +SRCS+= ${GENHDRS} + +${OBJS}: ${GENHDRS} + +.include <bsd.lib.mk> diff --git a/gnu/lib/libodialog/CHANGES b/gnu/lib/libodialog/CHANGES new file mode 100644 index 0000000..1467a0e --- /dev/null +++ b/gnu/lib/libodialog/CHANGES @@ -0,0 +1,9 @@ +- Added two variables to call to dialog_menu() to save the position + in the menu when choosing a menu-option. + +- Added dialog_fselect(), implements a fileselector dialog +- Added ui-interface objects: Stringobject, Listobject and Buttonobject. + The fileselector dialog was built using these objects. +- changed dialog_menu to use PGUP and PGDN +- Added dialog_mesgbox, which display text given in a char buffer. +- diff --git a/gnu/lib/libodialog/COPYING b/gnu/lib/libodialog/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/gnu/lib/libodialog/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gnu/lib/libodialog/Makefile b/gnu/lib/libodialog/Makefile new file mode 100644 index 0000000..b03bb4a --- /dev/null +++ b/gnu/lib/libodialog/Makefile @@ -0,0 +1,24 @@ +# Makefile for libdialog +# $FreeBSD$ + +.include <bsd.own.mk> + +LIB= odialog +#MAN= NOMAN + +SHLIB_MAJOR= 7 +SRCS= kernel.c rc.c checklist.c inputbox.c menubox.c msgbox.c \ + lineedit.c radiolist.c textbox.c yesno.c prgbox.c raw_popen.c \ + fselect.c ui_objects.c dir.c notify.c help.c gauge.c tree.c + +CFLAGS+= -I${.CURDIR} -Wall -Wstrict-prototypes -DLOCALE + +DPADD= ${LIBNCURSES} +LDADD= -lncurses + +.if ${MK_HTML} != "no" +FILES= ${EXAMPLES:C;^;${.CURDIR}/TESTS/;} +FILESDIR= ${SHAREDIR}/examples/libdialog +.endif + +.include <bsd.lib.mk> diff --git a/gnu/lib/libodialog/README b/gnu/lib/libodialog/README new file mode 100644 index 0000000..e5e6d56 --- /dev/null +++ b/gnu/lib/libodialog/README @@ -0,0 +1,8 @@ +This library was split out from the `dialog' program for use +in C programs. For a list of interface functions, see dialog.h. +For usage examples, see the `dialog' program sources in +/usr/src/gnu/usr.bin/dialog. + +You can additionally use any ncurses functions after init_dialog(). + + Ache. diff --git a/gnu/lib/libodialog/TESTS/Makefile b/gnu/lib/libodialog/TESTS/Makefile new file mode 100644 index 0000000..65e3d71 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/Makefile @@ -0,0 +1,20 @@ +# Really quick and evil Makefile for building all the tests. I wish that +# bmake was friendlier to the concept of multiple progs/libs in the same +# directory. +# +# $FreeBSD$ + +PROGS= msg yesno prgbox gauge dselect fselect text menu1 menu2 menu3 \ + input1 input2 check1 check2 check3 radio1 radio2 radio3 \ + ftree1 ftree2 tree + +WARNS?= 2 +CFLAGS+= -Wall -Wstrict-prototypes +LDFLAGS+= -ldialog + +all: ${PROGS} + +clean: + rm -f ${PROGS} + +.include <bsd.prog.mk> diff --git a/gnu/lib/libodialog/TESTS/check1.c b/gnu/lib/libodialog/TESTS/check1.c new file mode 100644 index 0000000..a2bec62 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/check1.c @@ -0,0 +1,83 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int +getBool(dialogMenuItem *self) +{ + if (self->data && *((int *)self->data)) + return TRUE; + return FALSE; +} + +static int +setBool(dialogMenuItem *self) +{ + if (self->data) { + *((int *)self->data) = !*((int *)self->data); + return DITEM_SUCCESS; + } + return DITEM_FAILURE; +} + +static int german_book, italian_book, slang_book; + +static int +clearBooks(dialogMenuItem *self) +{ + german_book = italian_book = slang_book = FALSE; + return DITEM_SUCCESS | DITEM_REDRAW; +} + +/* menu2 - A more advanced way of using checked and fire hooks to manipulate the backing-variables directly */ +/* prompt title checked fire sel data */ +static dialogMenuItem menu2[] = { + { "German", "Buy book on learning German", getBool, setBool, NULL, &german_book}, + { "Italian", "Buy book on learning Italian", getBool, setBool, NULL, &italian_book }, + { "Slang", "Buy book on commonly used insults", getBool, setBool, NULL, &slang_book }, + { "Clear", "Clear book list", NULL, clearBooks, NULL, NULL, ' ', ' ', ' ' }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_checklist("this is a dialog_checklist() in action, test #1", + "this checklist menu shows off some of the straight-forward features\n" + "of the new menu system's check & fire dispatch hooks", -1, -1, 4, -4, menu2, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_checklist was %d (%d %d %d)\n", retval, german_book, italian_book, slang_book); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/check2.c b/gnu/lib/libodialog/TESTS/check2.c new file mode 100644 index 0000000..3608261 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/check2.c @@ -0,0 +1,105 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int +getBool(dialogMenuItem *self) +{ + if (self->data && *((int *)self->data)) + return TRUE; + return FALSE; +} + +static int +setBool(dialogMenuItem *self) +{ + if (self->data) { + *((int *)self->data) = !*((int *)self->data); + return DITEM_SUCCESS; + } + return DITEM_FAILURE; +} + +static int german_book, italian_book, slang_book; + +static int +clearBooks(dialogMenuItem *self) +{ + german_book = italian_book = slang_book = FALSE; + return DITEM_SUCCESS | DITEM_REDRAW; +} + +static int +buyBooks(dialogMenuItem *self) +{ + char foo[256]; + + if (german_book || italian_book || slang_book) { + strcpy(foo, "Ok, you're buying books on"); + if (german_book) + strcat(foo, " german"); + if (italian_book) + strcat(foo, " italian"); + if (slang_book) + strcat(foo, " slang"); + } + else + strcpy(foo, "You're not buying any books?"); + dialog_mesgbox("This is a direct callback for the `Buy' button", foo, -1, -1); + return DITEM_SUCCESS; +} + +/* menu3 - Look mom! We can finally use our own OK and Cancel buttons! */ +/* prompt title checked fire sel data */ +static dialogMenuItem menu3[] = { + { "Buy!", NULL, NULL, buyBooks }, /* New "OK" button */ + { "No Way!", NULL, NULL, NULL }, /* New "Cancel" button */ + { "German", "Buy books on learning German", getBool, setBool, NULL, &german_book }, + { "Italian", "Buy books on learning Italian", getBool, setBool, NULL, &italian_book }, + { "Slang", "Buy books on commonly used insults", getBool, setBool, NULL, &slang_book }, + { "Clear", "Clear book list", NULL, clearBooks, NULL, NULL, ' ', ' ', ' ' }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_checklist("this is dialog_checklist() in action, test #2", + "Same as before, but now we relabel the buttons and override the OK action.", + -1, -1, 4, -4, menu3 + 2, (char *)TRUE); + dialog_clear(); + fprintf(stderr, "returned value for dialog_checklist was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/check3.c b/gnu/lib/libodialog/TESTS/check3.c new file mode 100644 index 0000000..f117bd6 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/check3.c @@ -0,0 +1,92 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int +getBool(dialogMenuItem *self) +{ + if (self->data && *((int *)self->data)) + return TRUE; + return FALSE; +} + +static int +setBool(dialogMenuItem *self) +{ + if (self->data) { + *((int *)self->data) = !*((int *)self->data); + return DITEM_SUCCESS; + } + return DITEM_FAILURE; +} + +static int german_book, italian_book, slang_book; +static int spending; + +static int +check(dialogMenuItem *self) +{ + return ((int)(intptr_t)self->data == spending); +} + +static int +spend(dialogMenuItem *self) +{ + spending = (int)(intptr_t)self->data; + return DITEM_SUCCESS | DITEM_REDRAW; +} + +/* menu4 - Show off a simulated compound menu (group at top is checklist, group at bottom radio) */ +/* prompt title checked fire sel, data lbra mark rbra */ +static dialogMenuItem menu4[] = { + { "German", "Buy books on learning German", getBool, setBool, NULL, &german_book }, + { "Italian","Buy books on learning Italian", getBool, setBool, NULL, &italian_book }, + { "Slang", "Buy books on commonly used insults", getBool, setBool, NULL, &slang_book }, + { "-----", "----------------------------------", NULL, NULL, NULL, NULL, ' ', ' ', ' ' }, + { "1000", "Spend $1,000", check, spend, NULL, (void *)1000, '(', '*', ')' }, + { "500", "Spend $500", check, spend, NULL, (void *)500, '(', '*', ')' }, + { "100", "Spend $100", check, spend, NULL, (void *)100, '(', '*', ')' }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + + retval = dialog_checklist("this is dialog_checklist() in action, test #3", + "Now we show off some of the button 'styles' one can create.", + -1, -1, 7, -7, menu4, NULL); + dialog_clear(); + fprintf(stderr, "spent $%d on %s%s%s books\n", spending, german_book ? " german" : "", + italian_book ? " italian" : "", slang_book ? " slang" : ""); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/dselect.c b/gnu/lib/libodialog/TESTS/dselect.c new file mode 100644 index 0000000..1110379 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/dselect.c @@ -0,0 +1,41 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_dselect(".", "*"); + dialog_clear(); + fprintf(stderr, "returned value for dialog_dselect was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/fselect.c b/gnu/lib/libodialog/TESTS/fselect.c new file mode 100644 index 0000000..6cefaf3 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/fselect.c @@ -0,0 +1,44 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + char *retval; + + init_dialog(); + + retval = dialog_fselect(".", "*.[ch]"); + dialog_clear(); + if (retval) + fprintf(stderr, "returned value for dialog_fselect was %s\n", retval); + else + fprintf(stderr, "returned value for dialog_fselect was NULL\n"); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/ftree1.c b/gnu/lib/libodialog/TESTS/ftree1.c new file mode 100644 index 0000000..f21e0e5 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/ftree1.c @@ -0,0 +1,45 @@ +/* + * ftree1.c + * + * small test-driver for new dialog functionality + * + * Copyright (c) 1998, Anatoly A. Orehovsky + * + * file ./ftree1.test with xterm widget tree from + * direct editres(1) dump needed !!! + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <dialog.h> + +int +main(int argc, char **argv) +{ + int retval; + unsigned char *tresult; + + init_dialog(); + retval = dialog_ftree("ftree1.test", '\t', + "ftree dialog box example", + "xterm widget tree from direct editres(1) dump", + -1, -1, 15, + &tresult); + + dialog_update(); + + dialog_clear(); + + end_dialog(); + + if (!retval) + { + puts(tresult); + free(tresult); + } + + exit(retval); +} diff --git a/gnu/lib/libodialog/TESTS/ftree1.test b/gnu/lib/libodialog/TESTS/ftree1.test new file mode 100644 index 0000000..4a8f0fa --- /dev/null +++ b/gnu/lib/libodialog/TESTS/ftree1.test @@ -0,0 +1,73 @@ +XTerm login + VendorShellExt shellext + VT100 vt100 + SimpleMenu fontMenu + SmeBSB menuLabel + SmeBSB fontdefault + SmeBSB font1 + SmeBSB font2 + SmeBSB font3 + SmeBSB font4 + SmeBSB font5 + SmeBSB font6 + SmeBSB fontescape + SmeBSB fontsel + SimpleMenu mainMenu + SmeBSB menuLabel + SmeBSB securekbd + SmeBSB allowsends + SmeBSB redraw + SmeLine line1 + SmeBSB 8-bit control + SmeBSB sun function-keys + SmeLine line2 + SmeBSB suspend + SmeBSB continue + SmeBSB interrupt + SmeBSB hangup + SmeBSB terminate + SmeBSB kill + SmeLine line3 + SmeBSB quit + SimpleMenu vtMenu + SmeBSB menuLabel + SmeBSB scrollbar + SmeBSB jumpscroll + SmeBSB reversevideo + SmeBSB autowrap + SmeBSB reversewrap + SmeBSB autolinefeed + SmeBSB appcursor + SmeBSB appkeypad + SmeBSB scrollkey + SmeBSB scrollttyoutput + SmeBSB allow132 + SmeBSB cursesemul + SmeBSB visualbell + SmeBSB marginbell + SmeBSB altscreen + SmeLine line1 + SmeBSB softreset + SmeBSB hardreset + SmeBSB clearsavedlines + SmeLine line2 + SmeBSB tekshow + SmeBSB tekmode + SmeBSB vthide + TopLevelShell tektronix + VendorShellExt shellext + Tek4014 tek4014 + SimpleMenu tekMenu + SmeBSB menuLabel + SmeBSB tektextlarge + SmeBSB tektext2 + SmeBSB tektext3 + SmeBSB tektextsmall + SmeLine line1 + SmeBSB tekpage + SmeBSB tekreset + SmeBSB tekcopy + SmeLine line2 + SmeBSB vtshow + SmeBSB vtmode + SmeBSB tekhide diff --git a/gnu/lib/libodialog/TESTS/ftree2.c b/gnu/lib/libodialog/TESTS/ftree2.c new file mode 100644 index 0000000..aa4663a --- /dev/null +++ b/gnu/lib/libodialog/TESTS/ftree2.c @@ -0,0 +1,47 @@ +/* + * ftree2.c + * + * small test-driver for new dialog functionality + * + * Copyright (c) 1998, Anatoly A. Orehovsky + * + * file ./ftree2.test with xterm widget tree from + * preprocess editres(1) dump needed !!! + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <dialog.h> + +int +main(int argc, char **argv) +{ + int retval; + unsigned char *tresult; + + init_dialog(); + use_helpfile("ftree2.test"); + use_helpline("Press Arrows, Tab, Enter or F1"); + retval = dialog_ftree("ftree2.test", '\t', + "ftree dialog box example", + "xterm widget tree from preprocess editres(1) dump", + -1, -1, 15, + &tresult); + + dialog_update(); + + dialog_clear(); + + end_dialog(); + + if (!retval) + { + puts(tresult); + free(tresult); + } + + exit(retval); +} diff --git a/gnu/lib/libodialog/TESTS/ftree2.test b/gnu/lib/libodialog/TESTS/ftree2.test new file mode 100644 index 0000000..0850862 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/ftree2.test @@ -0,0 +1,73 @@ +XTerm login +XTerm login VendorShellExt shellext +XTerm login VT100 vt100 +XTerm login SimpleMenu fontMenu +XTerm login SimpleMenu fontMenu SmeBSB menuLabel +XTerm login SimpleMenu fontMenu SmeBSB fontdefault +XTerm login SimpleMenu fontMenu SmeBSB font1 +XTerm login SimpleMenu fontMenu SmeBSB font2 +XTerm login SimpleMenu fontMenu SmeBSB font3 +XTerm login SimpleMenu fontMenu SmeBSB font4 +XTerm login SimpleMenu fontMenu SmeBSB font5 +XTerm login SimpleMenu fontMenu SmeBSB font6 +XTerm login SimpleMenu fontMenu SmeBSB fontescape +XTerm login SimpleMenu fontMenu SmeBSB fontsel +XTerm login SimpleMenu mainMenu +XTerm login SimpleMenu mainMenu SmeBSB menuLabel +XTerm login SimpleMenu mainMenu SmeBSB securekbd +XTerm login SimpleMenu mainMenu SmeBSB allowsends +XTerm login SimpleMenu mainMenu SmeBSB redraw +XTerm login SimpleMenu mainMenu SmeLine line1 +XTerm login SimpleMenu mainMenu SmeBSB 8-bit control +XTerm login SimpleMenu mainMenu SmeBSB sun function-keys +XTerm login SimpleMenu mainMenu SmeLine line2 +XTerm login SimpleMenu mainMenu SmeBSB suspend +XTerm login SimpleMenu mainMenu SmeBSB continue +XTerm login SimpleMenu mainMenu SmeBSB interrupt +XTerm login SimpleMenu mainMenu SmeBSB hangup +XTerm login SimpleMenu mainMenu SmeBSB terminate +XTerm login SimpleMenu mainMenu SmeBSB kill +XTerm login SimpleMenu mainMenu SmeLine line3 +XTerm login SimpleMenu mainMenu SmeBSB quit +XTerm login SimpleMenu vtMenu +XTerm login SimpleMenu vtMenu SmeBSB menuLabel +XTerm login SimpleMenu vtMenu SmeBSB scrollbar +XTerm login SimpleMenu vtMenu SmeBSB jumpscroll +XTerm login SimpleMenu vtMenu SmeBSB reversevideo +XTerm login SimpleMenu vtMenu SmeBSB autowrap +XTerm login SimpleMenu vtMenu SmeBSB reversewrap +XTerm login SimpleMenu vtMenu SmeBSB autolinefeed +XTerm login SimpleMenu vtMenu SmeBSB appcursor +XTerm login SimpleMenu vtMenu SmeBSB appkeypad +XTerm login SimpleMenu vtMenu SmeBSB scrollkey +XTerm login SimpleMenu vtMenu SmeBSB scrollttyoutput +XTerm login SimpleMenu vtMenu SmeBSB allow132 +XTerm login SimpleMenu vtMenu SmeBSB cursesemul +XTerm login SimpleMenu vtMenu SmeBSB visualbell +XTerm login SimpleMenu vtMenu SmeBSB marginbell +XTerm login SimpleMenu vtMenu SmeBSB altscreen +XTerm login SimpleMenu vtMenu SmeLine line1 +XTerm login SimpleMenu vtMenu SmeBSB softreset +XTerm login SimpleMenu vtMenu SmeBSB hardreset +XTerm login SimpleMenu vtMenu SmeBSB clearsavedlines +XTerm login SimpleMenu vtMenu SmeLine line2 +XTerm login SimpleMenu vtMenu SmeBSB tekshow +XTerm login SimpleMenu vtMenu SmeBSB tekmode +XTerm login SimpleMenu vtMenu SmeBSB vthide +XTerm login TopLevelShell tektronix +XTerm login TopLevelShell tektronix VendorShellExt shellext +XTerm login TopLevelShell tektronix Tek4014 tek4014 +XTerm login SimpleMenu tekMenu +XTerm login SimpleMenu tekMenu SmeBSB menuLabel +XTerm login SimpleMenu tekMenu SmeBSB tektextlarge +XTerm login SimpleMenu tekMenu SmeBSB tektext2 +XTerm login SimpleMenu tekMenu SmeBSB tektext3 +XTerm login SimpleMenu tekMenu SmeBSB tektextsmall +XTerm login SimpleMenu tekMenu SmeLine line1 +XTerm login SimpleMenu tekMenu SmeBSB tekpage +XTerm login SimpleMenu tekMenu SmeBSB tekreset +XTerm login SimpleMenu tekMenu SmeBSB tekcopy +XTerm login SimpleMenu tekMenu SmeLine line2 +XTerm login SimpleMenu tekMenu SmeBSB vtshow +XTerm login SimpleMenu tekMenu SmeBSB vtmode +XTerm login SimpleMenu tekMenu SmeBSB tekhide diff --git a/gnu/lib/libodialog/TESTS/gauge.c b/gnu/lib/libodialog/TESTS/gauge.c new file mode 100644 index 0000000..bf0cfc9 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/gauge.c @@ -0,0 +1,41 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int i; + + init_dialog(); + + for (i = 0; i <= 100; i++) { + dialog_gauge("Gas tank", "When this gets 100% full, you'd better yank out the nozzle!", 10, 1, 7, 70, i); + usleep(30000); + } + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/input1.c b/gnu/lib/libodialog/TESTS/input1.c new file mode 100644 index 0000000..3751071 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/input1.c @@ -0,0 +1,45 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + unsigned char result[128]; + + init_dialog(); + + strcpy(result, "not this!"); + retval = dialog_inputbox("this is dialog_inputbox() in action, test #1", + "Enter something really profound below, please.", + -1, -1, result); + dialog_clear(); + fprintf(stderr, "returned value for dialog_inputbox was %d (%s)\n", retval, result); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/input2.c b/gnu/lib/libodialog/TESTS/input2.c new file mode 100644 index 0000000..827c052 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/input2.c @@ -0,0 +1,47 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + unsigned char result[128]; + + init_dialog(); + + result[0]='\0'; + DialogInputAttrs |= DITEM_NO_ECHO; + retval = dialog_inputbox("this is dialog_inputbox() in action, test #2 (no echo)", + "Enter something really secret below, please.", + -1, -1, result); + DialogInputAttrs &= DITEM_NO_ECHO; + dialog_clear(); + fprintf(stderr, "returned value for dialog_inputbox was %d (%s)\n", retval, result); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/menu1.c b/gnu/lib/libodialog/TESTS/menu1.c new file mode 100644 index 0000000..640157a --- /dev/null +++ b/gnu/lib/libodialog/TESTS/menu1.c @@ -0,0 +1,96 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Start of hook functions */ +static enum { nowhere, berlin, rome, ny } where; + +static int +_menu1_berlin_action(dialogMenuItem *self) +{ + if (where == berlin) { + dialog_mesgbox("excuse me?", "But you're already *in* Berlin!", -1, -1); + } + else { + where = berlin; + dialog_mesgbox("whoosh!", "Welcome to Berlin! Have a beer!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +static int +_menu1_rome_action(dialogMenuItem *self) +{ + if (where == rome) { + dialog_mesgbox("The wine must be getting to you..", "You're already in Rome!", -1, -1); + } + else { + where = rome; + dialog_mesgbox("whoosh!", "Welcome to Rome! Have a coffee!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +static int +_menu1_ny_action(dialogMenuItem *self) +{ + if (where == ny) { + dialog_mesgbox("Say what?", "You're already there!", -1, -1); + } + else { + where = ny; + dialog_mesgbox("whoosh!", "Welcome to New York! Now go someplace else!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +/* menu1 - show off the "fire" action hook */ +/* prompt title checked fire */ +static dialogMenuItem menu1[] = { + { "Berlin", "Go visit Germany's new capitol", NULL, _menu1_berlin_action }, + { "Rome", "Go visit the Roman ruins", NULL, _menu1_rome_action }, + { "New York", "Go visit the streets of New York", NULL, _menu1_ny_action }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_menu("this is dialog_menu() in action, test #1", + "this simple menu shows off some of the straight-forward features\n" + "of the new menu system's action dispatch hooks. Select Cancel to leave", + -1, -1, 3, -3, menu1, NULL, NULL, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_menu was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/menu2.c b/gnu/lib/libodialog/TESTS/menu2.c new file mode 100644 index 0000000..1545cd8 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/menu2.c @@ -0,0 +1,96 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Start of hook functions */ +static enum { nowhere, berlin, rome, ny } where; + +static int +_menu1_berlin_action(dialogMenuItem *self) +{ + if (where == berlin) + dialog_mesgbox("excuse me?", "But you're already *in* Berlin!", -1, -1); + else { + where = berlin; + dialog_mesgbox("whoosh!", "Welcome to Berlin! Have a beer!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +static int +_menu1_rome_action(dialogMenuItem *self) +{ + if (where == rome) + dialog_mesgbox("The wine must be getting to you..", "You're already in Rome!", -1, -1); + else { + where = rome; + dialog_mesgbox("whoosh!", "Welcome to Rome! Have a coffee!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +static int +_menu1_ny_action(dialogMenuItem *self) +{ + if (where == ny) + dialog_mesgbox("Say what?", "You're already there!", -1, -1); + else { + where = ny; + dialog_mesgbox("whoosh!", "Welcome to New York! Now go someplace else!", -1, -1); + } + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +/* menu1 - show off the "fire" action hook */ +/* prompt title checked fire */ +static dialogMenuItem menu1[] = { + { "Berlin", "Go visit Germany's new capitol", NULL, _menu1_berlin_action }, + { "Rome", "Go visit the Roman ruins", NULL, _menu1_rome_action }, + { "New York", "Go visit the streets of New York", NULL, _menu1_ny_action }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + use_helpfile("menu2.c"); + use_helpline("Type F1 to view the source for this demo"); + retval = dialog_menu("this is dialog_menu() in action, test #2", + "this simple menu shows off some of the straight-forward features\n" + "of the new menu system's action dispatch hooks as well as a helpline\n" + "and a helpfile. Select Cancel to leave", + -1, -1, 3, -3, menu1, NULL, NULL, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_menu was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/menu3.c b/gnu/lib/libodialog/TESTS/menu3.c new file mode 100644 index 0000000..3a7a137 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/menu3.c @@ -0,0 +1,107 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int +stop(dialogMenuItem *self) +{ + dialog_mesgbox("!", "I'm no idiot!", -1, -1); + return DITEM_SUCCESS; +} + +static int +maybe(dialogMenuItem *self) +{ + dialog_mesgbox("!", "I said don't rush me! I'm THINKING!", -1, -1); + return DITEM_SUCCESS | DITEM_RESTORE | DITEM_CONTINUE; +} + +/* Dummy menu just to show of the ability */ +static char *insurance[] = { + "1,000,000", "Mondo insurance policy", "Off", + "5,000,000", "Mega insurance policy", "Off", + "10,000,000", "Friend! Most Favored customer!", "On" +}; + +static void +preinsure(dialogMenuItem *self, int is_selected) +{ + if (is_selected) { + static WINDOW *w; + + /* This has to be here first if you want to see selection traverse properly in the invoking menu */ + refresh(); + + w = dupwin(newscr); + DialogX = 1; + DialogY = 13; + dialog_radiolist("How much insurance would you like to take out?", + "If you're really going to do this, we recommend some insurance\n" + "first! What kind of life insurance policy would you like?", + -1, -1, 3, 3, insurance, NULL); + touchwin(w); + wrefresh(w); + delwin(w); + } +} + +/* + * Show a simple menu that puts up a sub menu when a certain item is traversed to + */ + +/* prompt title checked fire sel */ +static dialogMenuItem doit[] = { + { "Rah!" }, + { "No way!" }, + { "Stop", "No, I'm not going to do that!", NULL, stop, NULL }, + { "Maybe", "I'm still thinking about it, don't rush me!", NULL, maybe, NULL, }, + { "Go", "Yes! Yes! I want to do it!", NULL, NULL, preinsure }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + + DialogX = 5; + DialogY = 1; + retval = dialog_menu("Do you have the GUTS?", + "C'mon, macho man! Do you have what it takes to do something REALLY\n" + "dangerous and stupid? WHAT ARE YOU WAITING FOR?!", + -1, -1, 3, -3, doit + 2, (char *)TRUE, NULL, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_menu was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/msg.c b/gnu/lib/libodialog/TESTS/msg.c new file mode 100644 index 0000000..c6f590f --- /dev/null +++ b/gnu/lib/libodialog/TESTS/msg.c @@ -0,0 +1,42 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_msgbox("This is dialog_msgbox() in action with pause on", "Hi there. Please press return now.", + -1, -1, 1); + dialog_clear(); + fprintf(stderr, "returned value for dialog_msgbox was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/prgbox.c b/gnu/lib/libodialog/TESTS/prgbox.c new file mode 100644 index 0000000..2da4611 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/prgbox.c @@ -0,0 +1,41 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_prgbox("This is dialog_prgbox() in action with cal(1)", "cal", 14, 50, TRUE, TRUE); + dialog_clear(); + fprintf(stderr, "returned value for dialog_prgbox was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/radio1.c b/gnu/lib/libodialog/TESTS/radio1.c new file mode 100644 index 0000000..c4eeffd --- /dev/null +++ b/gnu/lib/libodialog/TESTS/radio1.c @@ -0,0 +1,71 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int spending; + +static int +check(dialogMenuItem *self) +{ + return ((int)(intptr_t)self->data == spending); +} + +static int +spend(dialogMenuItem *self) +{ + spending = (int)(intptr_t)self->data; + return DITEM_SUCCESS | DITEM_REDRAW; +} + +/* menu5 - Show a simple radiolist menu that inherits the radio appearance by default */ +/* prompt title checked fire sel data */ +static dialogMenuItem menu5[] = { + { "1000", "Spend $1,000", check, spend, NULL, (void *)1000 }, + { "500", "Spend $500", check, spend, NULL, (void *)500 }, + { "100", "Spend $100", check, spend, NULL, (void *)100 }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + + retval = dialog_radiolist("this is dialog_radiolist() in action, test #1", + "this radio menu shows off some of the straight-forward features\n" + "of the new menu system's check & fire dispatch hooks", -1, -1, 3, -3, menu5, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_radiolist was %d (money set to %d)\n", retval, spending); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/radio2.c b/gnu/lib/libodialog/TESTS/radio2.c new file mode 100644 index 0000000..15e353c --- /dev/null +++ b/gnu/lib/libodialog/TESTS/radio2.c @@ -0,0 +1,89 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static char bachelor[10], bachelette[10]; + +static int +getBachelor(dialogMenuItem *self) +{ + return !strcmp(bachelor, self->prompt); +} + +static int +setBachelor(dialogMenuItem *self) +{ + strcpy(bachelor, self->prompt); + return DITEM_SUCCESS | DITEM_REDRAW; +} + +static int +getBachelette(dialogMenuItem *self) +{ + return !strcmp(bachelette, self->prompt); +} + +static int +setBachelette(dialogMenuItem *self) +{ + strcpy(bachelette, self->prompt); + return DITEM_SUCCESS | DITEM_REDRAW; +} + +/* menu6- More complex radiolist menu that creates two groups in a single menu */ +/* prompt title checked fire */ +static dialogMenuItem menu6[] = { + { "Tom", "Tom's a dynamic shoe salesman from Tulsa, OK!", getBachelor, setBachelor }, + { "Dick", "Dick's a retired engine inspector from McDonnell-Douglas!", getBachelor, setBachelor }, + { "Harry", "Harry's a professional female impersonator from Las Vegas!", getBachelor, setBachelor }, + { "-----", "----------------------------------", NULL, NULL, NULL, NULL, ' ', ' ', ' ' }, + { "Jane", "Jane's a twice-divorced housewife from Moose, Oregon!", getBachelette, setBachelette }, + { "Sally", "Sally's a shy Human Resources Manager for IBM!", getBachelette, setBachelette }, + { "Mary", "Mary's an energetic serial killer on the lam!", getBachelette, setBachelette }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_radiolist("this is dialog_radiolist() in action, test #2", + "Welcome to \"The Love Blender!\" - America's favorite game show\n" + "where YOU, the contestant, get to choose which of these two\n" + "fine specimens of humanity will go home together, whether they\n" + "like it or not!", -1, -1, 7, -7, menu6, NULL); + dialog_clear(); + fprintf(stderr, "I'm sure that %s and %s will be very happy together!\n", bachelor, bachelette); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/radio3.c b/gnu/lib/libodialog/TESTS/radio3.c new file mode 100644 index 0000000..2175358 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/radio3.c @@ -0,0 +1,98 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Hook functions */ + +static int spending; + +static int +check(dialogMenuItem *self) +{ + return ((int)(intptr_t)self->data == spending); +} + +static int +spend(dialogMenuItem *self) +{ + spending = (int)(intptr_t)self->data; + return DITEM_SUCCESS | DITEM_REDRAW; +} + +static void +ask(dialogMenuItem *self, int is_selected) +{ + if (is_selected) { + char *str; + + if (!strcmp(self->prompt, "1000")) + str = "You'd better ask both your parents first! "; + else if (!strcmp(self->prompt, "500")) + str = "You'd better at least ask your Dad! "; + else + str = "Yes, being frugal is probably a good idea!"; + DialogX = 15; + DialogY = 17; + dialog_msgbox("Free Advice", str, -1, -1, 0); + } +} + +/* + * menu5 - Show a simple radiolist menu that inherits the radio appearance by default and appears at + * a different location, leaving room for a msg box below it. This shows off the DialogX/DialogY extensions. + */ + +/* prompt title checked fire sel data */ +static dialogMenuItem menu5[] = { + { "1000", "Spend $1,000", check, spend, ask, (void *)1000 }, + { "500", "Spend $500", check, spend, ask, (void *)500 }, + { "100", "Spend $100", check, spend, ask, (void *)100 }, +}; + +/* End of hook functions */ + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + + DialogX = 5; + DialogY = 1; + retval = dialog_radiolist("this is dialog_radiolist() in action, test #3", + "This radio menu shows off the ability to put dialog menus and other\n" + "controls at different locations, as well as the `selected' hook which\n" + "lets you follow the traversal of the selection bar as well as what's\n" + "selected.", + -1, -1, 3, -3, menu5, NULL); + dialog_clear(); + fprintf(stderr, "returned value for dialog_radiolist was %d (money set to %d)\n", retval, spending); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/text.c b/gnu/lib/libodialog/TESTS/text.c new file mode 100644 index 0000000..e071937 --- /dev/null +++ b/gnu/lib/libodialog/TESTS/text.c @@ -0,0 +1,41 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int retval; + + init_dialog(); + + retval = dialog_textbox("This is dialog_textbox() in action with /etc/passwd", "/etc/passwd", 10, 60); + dialog_clear(); + fprintf(stderr, "returned value for dialog_textbox was %d\n", retval); + + end_dialog(); + return 0; +} diff --git a/gnu/lib/libodialog/TESTS/tree.c b/gnu/lib/libodialog/TESTS/tree.c new file mode 100644 index 0000000..c69b52e --- /dev/null +++ b/gnu/lib/libodialog/TESTS/tree.c @@ -0,0 +1,111 @@ +/* + * tree.c + * + * small test-driver for new dialog functionality + * + * Copyright (c) 1998, Anatoly A. Orehovsky + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <dialog.h> + +unsigned char *names[] = { + "/", + "/dev", + "/dev/fd", + "/tmp", + "/usr", + "/var", + "/home", + "/stand", + "/stand/etc", + "/stand/en_US.ISO8859-1", + "/stand/info", + "/stand/info/bin", + "/stand/info/des", + "/stand/info/games", + "/stand/info/manpages", + "/stand/info/proflibs", + "/stand/info/dict", + "/stand/info/info", + "/stand/info/src", + "/etc", + "/etc/gnats", + "/etc/kerberosIV", + "/etc/mtree", + "/etc/namedb", + "/etc/ppp", + "/etc/uucp", + "/etc/sliphome", + "/proc", + "/lkm", + "/mnt", + "/root", + "/sbin", + "/bin", + 0 +}; + +unsigned char *names1[] = { + "a", + "a:b", + "a:b:c", + "a:d" +}; + +int +main(int argc, char **argv) +{ + int retval; + unsigned char *tresult; + char comstr[BUFSIZ]; + + init_dialog(); + do { + use_helpline("Press OK for listing directory"); + retval = dialog_tree(names, + sizeof(names)/sizeof(unsigned char *) - 1, + '/', + "tree dialog box example", + "Typical find -x / -type d output", + -1, -1, 15, + &tresult); + + if (retval) + break; + + use_helpline(NULL); + (void)snprintf(comstr, sizeof(comstr), + "ls -CF %s", tresult); + + retval = dialog_prgbox( + comstr, + comstr, 20, 60, TRUE, TRUE); + + dialog_clear(); + + retval = dialog_tree(names1, + sizeof(names1)/sizeof(unsigned char *), + ':', + "tree dialog box example", + "Other tree", + -1, -1, 5, + &tresult); + if (!retval) + { + dialog_clear(); + } + } while (!retval); + + dialog_update(); + + dialog_clear(); + + end_dialog(); + + exit(retval); +} diff --git a/gnu/lib/libodialog/TESTS/yesno.c b/gnu/lib/libodialog/TESTS/yesno.c new file mode 100644 index 0000000..647b51c --- /dev/null +++ b/gnu/lib/libodialog/TESTS/yesno.c @@ -0,0 +1,45 @@ +/* + * small test-driver for new dialog functionality + * + * Copyright (c) 1995, Jordan Hubbard + * + * All rights reserved. + * + * This source code may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of the software nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <dialog.h> + +/* Kick it off, James! */ +int +main(int argc, char **argv) +{ + int rval1, rval2; + + init_dialog(); + + rval1 = dialog_yesno("This is dialog_yesno() in action", + "Have you stopped deliberately putting bugs into your code?", -1, -1); + dialog_clear(); + rval2 = dialog_noyes("This is dialog_noyes() in action", + "Have you stopped beating your wife?", -1, -1); + dialog_clear(); + end_dialog(); + fprintf(stderr, "returned value for dialog_yesno was %d\n", rval1); + fprintf(stderr, "returned value for dialog_noyes was %d\n", rval2); + return 0; +} diff --git a/gnu/lib/libodialog/TODO b/gnu/lib/libodialog/TODO new file mode 100644 index 0000000..132a4a4 --- /dev/null +++ b/gnu/lib/libodialog/TODO @@ -0,0 +1,36 @@ +- cut off names in the listbox that are to long +done 27Jan95 + The current behaviour may not be desirable. When browsing through + long names these, when highlighted, will be shown with the first + characters cut off, when not highlighted the last characters will + be cut off. + +- look at behaviour of TAB key when browsing through directories. +done 28Jan95 + +- make sure the full name of the directory is written to the + "Directory:"-box +done 28Jan95 + +- mark current selections in listbox when initializing the listobject +Idontknow +- test and use Notify() when checking for error conditions +ok +- test overall +- adapt color of buttons when changing focus to the button. +done 28Jan95 +- add shade to dialog_fselect()-window +done 29Jan95 +- add (nn%) indication to lists. +done 30Jan95 +- add use_helpfile() +done 13Feb95 +- add use_helpline() + +NOTE: apparently there is a bug (or a strange interaction between pkg_manage +and dialog_textbox) in dialog_textbox. When I use this routine to display +the helpfile in display_helpfile() the program gets mysterious segmentation +faults and bus errors. +I now use dialog_mesgbox, after I have read the file into a buffer. + + diff --git a/gnu/lib/libodialog/checklist.c b/gnu/lib/libodialog/checklist.c new file mode 100644 index 0000000..e349ca6 --- /dev/null +++ b/gnu/lib/libodialog/checklist.c @@ -0,0 +1,661 @@ +/* + * checklist.c -- implements the checklist box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * Substantial rennovation: 12/18/95, Jordan K. Hubbard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" + + +static void print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int status, int choice, int selected, dialogMenuItem *me, int list_width, int item_x, int check_x); + +#define DREF(di, item) ((di) ? &((di)[(item)]) : NULL) + +/* + * Display a dialog box with a list of options that can be turned on or off + */ +int +dialog_checklist(unsigned char *title, unsigned char *prompt, int height, int width, + int list_height, int cnt, void *it, unsigned char *result) +{ + int i, j, x, y, cur_x, cur_y, old_x, old_y, box_x, box_y, key = 0, button, + choice, l, k, scroll, max_choice, item_no = 0, *status; + int redraw_menu = FALSE, cursor_reset = FALSE; + int rval = 0, onlist = 1, ok_space, cancel_space; + char okButton, cancelButton; + WINDOW *dialog, *list; + unsigned char **items = NULL; + dialogMenuItem *ditems; + int list_width, check_x, item_x; + + /* Allocate space for storing item on/off status */ + if ((status = alloca(sizeof(int) * abs(cnt))) == NULL) { + endwin(); + fprintf(stderr, "\nCan't allocate memory in dialog_checklist().\n"); + exit(-1); + } + +draw: + choice = scroll = button = 0; + /* Previous calling syntax, e.g. just a list of strings? */ + if (cnt >= 0) { + items = it; + ditems = NULL; + item_no = cnt; + /* Initializes status */ + for (i = 0; i < item_no; i++) + status[i] = !strcasecmp(items[i*3 + 2], "on"); + } + /* It's the new specification format - fake the rest of the code out */ + else { + item_no = abs(cnt); + ditems = it; + if (!items) + items = (unsigned char **)alloca((item_no * 3) * sizeof(unsigned char *)); + + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE; + items[i*3] = ditems[i].prompt; + items[i*3 + 1] = ditems[i].title; + items[i*3 + 2] = status[i] ? "on" : "off"; + } + } + max_choice = MIN(list_height, item_no); + + check_x = 0; + item_x = 0; + /* Find length of longest item in order to center checklist */ + for (i = 0; i < item_no; i++) { + l = strlen(items[i*3]); + for (j = 0; j < item_no; j++) { + k = strlen(items[j*3 + 1]); + check_x = MAX(check_x, l + k + 6); + } + item_x = MAX(item_x, l); + } + if (height < 0) + height = strheight(prompt)+list_height+4+2; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j); + width = MAX(width,check_x+4)+4; + } + width = MAX(width,24); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = (COLS - width)/2; + y = (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width, y, x); + return -1; + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height - 1, width - 2, width, 1, 2, TRUE, FALSE); + + list_width = width - 6; + getyx(dialog, cur_y, cur_x); + box_y = cur_y + 1; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin(dialog, list_height, list_width, y + box_y + 1, x + box_x + 1); + if (list == NULL) { + delwin(dialog); + endwin(); + fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", list_height, list_width, + y + box_y + 1, x + box_x + 1); + return -1; + } + keypad(list, TRUE); + + /* draw a box around the list items */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, menubox_border_attr, menubox_attr); + + check_x = (list_width - check_x) / 2; + item_x = check_x + item_x + 6; + + /* Print the list */ + for (i = 0; i < max_choice; i++) + print_item(list, items[i * 3], items[i * 3 + 1], status[i], i, i == choice, DREF(ditems, i), list_width, item_x, check_x); + wnoutrefresh(list); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + + display_helpline(dialog, height - 1, width); + + x = width / 2 - 11; + y = height - 2; + /* Is this a fancy new style argument string where we get to override + * the buttons, or an old style one where they're fixed? + */ + if (ditems && result) { + cancelButton = toupper(ditems[CANCEL_BUTTON].prompt[0]); + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : FALSE); + okButton = toupper(ditems[OK_BUTTON].prompt[0]); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : TRUE); + } + else { + cancelButton = 'C'; + print_button(dialog, "Cancel", y, x + 14, FALSE); + okButton = 'O'; + print_button(dialog, " OK ", y, x, TRUE); + } + wnoutrefresh(dialog); + wmove(list, choice, check_x+1); + wrefresh(list); + + /* + * XXX Black magic voodoo that allows printing to the checklist + * window. For some reason, if this "refresh" code is not in + * place, printing to the window from the selected callback + * prints "behind" the checklist window. There is probably a + * better way to do this. + */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, menubox_border_attr, menubox_attr); + + for (i = 0; i < max_choice; i++) + print_item(list, items[i * 3], items[i * 3 + 1], status[i], i, i == choice, DREF(ditems, i), list_width, item_x, check_x); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + /* XXX Black magic XXX */ + + while (key != ESC) { + key = wgetch(dialog); + + /* Shortcut to OK? */ + if (toupper(key) == okButton) { + if (ditems) { + if (result && ditems[OK_BUTTON].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[OK_BUTTON].fire(&ditems[OK_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + } + else if (result) { + *result = '\0'; + for (i = 0; i < item_no; i++) { + if (status[i]) { + strcat(result, items[i*3]); + strcat(result, "\n"); + } + } + } + rval = 0; + key = ESC; /* Lemme out! */ + break; + } + + /* Shortcut to cancel? */ + if (toupper(key) == cancelButton) { + if (ditems && result && ditems[CANCEL_BUTTON].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[CANCEL_BUTTON].fire(&ditems[CANCEL_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + wmove(dialog, cur_y, cur_x); + } + delwin(save); + } + rval = 1; + key = ESC; /* I gotta go! */ + break; + } + + /* Check if key pressed matches first character of any item tag in list */ + for (i = 0; i < max_choice; i++) + if (key != ' ' && key < 0x100 && toupper(key) == toupper(items[(scroll+i)*3][0])) + break; + + if (i < max_choice || (key >= '1' && key <= MIN('9', '0'+max_choice)) || + KEY_IS_UP(key) || KEY_IS_DOWN(key) || ((key == ' ' || key == '\n' || + key == '\r') && onlist)) { + + /* if moving from buttons to the list, reset and redraw buttons */ + if (!onlist) { + onlist = 1; + button = 0; + + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + } + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + } + + if (key >= '1' && key <= MIN('9', '0'+max_choice)) + i = key - '1'; + + else if (KEY_IS_UP(key)) { + if (!choice) { + if (scroll) { + /* Scroll list down */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (list_height > 1) { + /* De-highlight current first item before scrolling down */ + print_item(list, items[scroll * 3], items[scroll * 3 + 1], status[scroll], 0, + FALSE, DREF(ditems, scroll), list_width, item_x, check_x); + scrollok(list, TRUE); + wscrl(list, -1); + scrollok(list, FALSE); + } + scroll--; + print_item(list, items[scroll*3], items[scroll*3 + 1], status[scroll], 0, + TRUE, DREF(ditems, scroll), list_width, item_x, check_x); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + else + i = choice - 1; + } + else if (KEY_IS_DOWN(key)) { + if (choice == max_choice - 1) { + if (scroll + choice < item_no - 1) { + /* Scroll list up */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item(list, items[(scroll + max_choice - 1) * 3], + items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], max_choice - 1, + FALSE, DREF(ditems, scroll + max_choice - 1), list_width, item_x, check_x); + scrollok(list, TRUE); + scroll(list); + scrollok(list, FALSE); + } + scroll++; + print_item(list, items[(scroll + max_choice - 1) * 3], + items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], max_choice - 1, TRUE, + DREF(ditems, scroll + max_choice - 1), list_width, item_x, check_x); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + else + i = choice + 1; + } + else if ((key == ' ' || key == '\n' || key == '\r') && onlist) { /* Toggle item status */ + char lbra = 0, rbra = 0, mark = 0; + + getyx(list, old_y, old_x); /* Save cursor position */ + + if (ditems) { + if (ditems[scroll + choice].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[scroll + choice].fire(&ditems[scroll + choice]); /* Call "fire" action */ + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + if (st & DITEM_REDRAW) { + wclear(list); + for (i = 0; i < item_no; i++) + status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE; + for (i = 0; i < max_choice; i++) { + print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], + status[scroll + i], i, i == choice, DREF(ditems, scroll + i), list_width, item_x, check_x); + } + wnoutrefresh(list); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, + cur_x, cur_y); + wrefresh(dialog); + } + if (st & DITEM_LEAVE_MENU) { + /* Allow a fire action to take us out of the menu */ + key = ESC; + rval = 0; + break; + } + else if (st & DITEM_RECREATE) { + delwin(list); + delwin(dialog); + dialog_clear(); + goto draw; + } + } + status[scroll + choice] = ditems[scroll + choice].checked ? + ditems[scroll + choice].checked(&ditems[scroll + choice]) : FALSE; + lbra = ditems[scroll + choice].lbra; + rbra = ditems[scroll + choice].rbra; + mark = ditems[scroll + choice].mark; + } + else + status[scroll + choice] = !status[scroll + choice]; + wmove(list, choice, check_x); + wattrset(list, check_selected_attr); + if (!lbra) + lbra = '['; + if (!rbra) + rbra = ']'; + if (!mark) + mark = 'X'; + wprintw(list, "%c%c%c", lbra, status[scroll + choice] ? mark : ' ', rbra); + wmove(list, old_y, old_x); /* Restore cursor to previous position */ + wrefresh(list); + continue; /* wait for another key press */ + } + + if (i != choice) { + /* De-highlight current item */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 + 1], + status[scroll + choice], choice, FALSE, DREF(ditems, scroll + choice), list_width, item_x, check_x); + + /* Highlight new item */ + choice = i; + print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 + 1], status[scroll + choice], choice, TRUE, DREF(ditems, scroll + choice), list_width, item_x, check_x); + wmove(list, choice, check_x+1); /* Restore cursor to previous position */ + wrefresh(list); + } + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_PPAGE: /* can we go up? */ + if (scroll > height - 4) + scroll -= (height-4); + else + scroll = 0; + redraw_menu = TRUE; + if (!onlist) { + onlist = 1; + button = 0; + } + break; + + case KEY_NPAGE: /* can we go down a full page? */ + if (scroll + list_height >= item_no-1 - list_height) { + scroll = item_no - list_height; + if (scroll < 0) + scroll = 0; + } + else + scroll += list_height; + redraw_menu = TRUE; + if (!onlist) { + onlist = 1; + button = 0; + } + break; + + case KEY_HOME: /* go to the top */ + scroll = 0; + choice = 0; + redraw_menu = TRUE; + cursor_reset = TRUE; + onlist = 1; + break; + + case KEY_END: /* Go to the bottom */ + scroll = item_no - list_height; + if (scroll < 0) + scroll = 0; + choice = max_choice - 1; + redraw_menu = TRUE; + cursor_reset = TRUE; + onlist = 1; + break; + + case TAB: + case KEY_BTAB: + /* move to next component */ + if (onlist) { /* on list, next is ok button */ + onlist = 0; + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + ok_space = 1; + cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6; + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + ok_space = 3; + cancel_space = 15; + } + if (button) + wmove(dialog, y, x + cancel_space); + else + wmove(dialog, y, x + ok_space); + wrefresh(dialog); + break; + } + else if (button) { /* on cancel button, next is list */ + button = 0; + onlist = 1; + redraw_menu = TRUE; + break; + } + /* on ok button, next is cancel button, same as left/right case */ + + case KEY_LEFT: + case KEY_RIGHT: + onlist = 0; + button = !button; + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + ok_space = 1; + cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6; + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + ok_space = 3; + cancel_space = 15; + } + if (button) + wmove(dialog, y, x + cancel_space); + else + wmove(dialog, y, x + ok_space); + wrefresh(dialog); + break; + + case ' ': + case '\n': + case '\r': + if (!onlist) { + if (ditems) { + if (result && ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire) { + int st; + WINDOW *save = dupwin(newscr); + + st = ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire(&ditems[button ? CANCEL_BUTTON : OK_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + if (st == DITEM_FAILURE) + continue; + } + } + else if (result) { + *result = '\0'; + for (i = 0; i < item_no; i++) { + if (status[i]) { + strcat(result, items[i*3]); + strcat(result, "\n"); + } + } + } + rval = button; + key = ESC; /* Bail out! */ + break; + } + + /* Let me outta here! */ + case ESC: + rval = -1; + break; + + /* Help! */ + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + + if (redraw_menu) { + getyx(list, old_y, old_x); + wclear(list); + + /* + * Re-draw a box around the list items. It is required + * if amount of list items is smaller than height of listbox. + * Otherwise un-redrawn field will be filled with default + * screen attributes instead of dialog attributes. + */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, menubox_border_attr, menubox_attr); + + for (i = 0; i < max_choice; i++) + print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], status[scroll + i], i, i == choice, DREF(ditems, scroll + i), list_width, item_x, check_x); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + + /* redraw buttons to fix highlighting */ + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + } + wnoutrefresh(dialog); + if (cursor_reset) { + wmove(list, choice, check_x+1); + cursor_reset = FALSE; + } + else { + wmove(list, old_y, old_x); + } + wrefresh(list); + redraw_menu = FALSE; + } + } + delwin(list); + delwin(dialog); + return rval; +} + + +/* + * Print list item + */ +static void +print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int status, int choice, int selected, dialogMenuItem *me, int list_width, int item_x, int check_x) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset(win, menubox_attr); + wmove(win, choice, 0); + for (i = 0; i < list_width; i++) + waddch(win, ' '); + wmove(win, choice, check_x); + wattrset(win, selected ? check_selected_attr : check_attr); + wprintw(win, "%c%c%c", me && me->lbra ? me->lbra : '[', + status ? me && me->mark ? me->mark : 'X' : ' ', + me && me->rbra ? me->rbra : ']'); + wattrset(win, menubox_attr); + waddch(win, ' '); + wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); + waddch(win, tag[0]); + wattrset(win, selected ? tag_selected_attr : tag_attr); + waddstr(win, tag + 1); + wmove(win, choice, item_x); + wattrset(win, selected ? item_selected_attr : item_attr); + waddstr(win, item); + /* If have a selection handler for this, call it */ + if (me && me->selected) { + wrefresh(win); + me->selected(me, selected); + } +} +/* End of print_item() */ diff --git a/gnu/lib/libodialog/colors.h b/gnu/lib/libodialog/colors.h new file mode 100644 index 0000000..7cea0a0 --- /dev/null +++ b/gnu/lib/libodialog/colors.h @@ -0,0 +1,219 @@ +/* + * colors.h -- color attribute definitions + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * Default color definitions + * + * *_FG = foreground + * *_BG = background + * *_HL = highlight? + */ +#define SCREEN_FG COLOR_CYAN +#define SCREEN_BG COLOR_BLUE +#define SCREEN_HL TRUE + +#define SHADOW_FG COLOR_BLACK +#define SHADOW_BG COLOR_BLACK +#define SHADOW_HL TRUE + +#define DIALOG_FG COLOR_BLACK +#define DIALOG_BG COLOR_WHITE +#define DIALOG_HL FALSE + +#define TITLE_FG COLOR_YELLOW +#define TITLE_BG COLOR_WHITE +#define TITLE_HL TRUE + +#define BORDER_FG COLOR_WHITE +#define BORDER_BG COLOR_WHITE +#define BORDER_HL TRUE + +#define BUTTON_ACTIVE_FG COLOR_WHITE +#define BUTTON_ACTIVE_BG COLOR_BLUE +#define BUTTON_ACTIVE_HL TRUE + +#define BUTTON_INACTIVE_FG COLOR_BLACK +#define BUTTON_INACTIVE_BG COLOR_WHITE +#define BUTTON_INACTIVE_HL FALSE + +#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE +#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE +#define BUTTON_KEY_ACTIVE_HL TRUE + +#define BUTTON_KEY_INACTIVE_FG COLOR_RED +#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE +#define BUTTON_KEY_INACTIVE_HL FALSE + +#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW +#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE +#define BUTTON_LABEL_ACTIVE_HL TRUE + +#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK +#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE +#define BUTTON_LABEL_INACTIVE_HL TRUE + +#define INPUTBOX_FG COLOR_BLACK +#define INPUTBOX_BG COLOR_WHITE +#define INPUTBOX_HL FALSE + +#define INPUTBOX_BORDER_FG COLOR_BLACK +#define INPUTBOX_BORDER_BG COLOR_WHITE +#define INPUTBOX_BORDER_HL FALSE + +#define SEARCHBOX_FG COLOR_BLACK +#define SEARCHBOX_BG COLOR_WHITE +#define SEARCHBOX_HL FALSE + +#define SEARCHBOX_TITLE_FG COLOR_YELLOW +#define SEARCHBOX_TITLE_BG COLOR_WHITE +#define SEARCHBOX_TITLE_HL TRUE + +#define SEARCHBOX_BORDER_FG COLOR_WHITE +#define SEARCHBOX_BORDER_BG COLOR_WHITE +#define SEARCHBOX_BORDER_HL TRUE + +#define POSITION_INDICATOR_FG COLOR_YELLOW +#define POSITION_INDICATOR_BG COLOR_WHITE +#define POSITION_INDICATOR_HL TRUE + +#define MENUBOX_FG COLOR_BLACK +#define MENUBOX_BG COLOR_WHITE +#define MENUBOX_HL FALSE + +#define MENUBOX_BORDER_FG COLOR_WHITE +#define MENUBOX_BORDER_BG COLOR_WHITE +#define MENUBOX_BORDER_HL TRUE + +#define ITEM_FG COLOR_BLACK +#define ITEM_BG COLOR_WHITE +#define ITEM_HL FALSE + +#define ITEM_SELECTED_FG COLOR_WHITE +#define ITEM_SELECTED_BG COLOR_BLUE +#define ITEM_SELECTED_HL TRUE + +#define TAG_FG COLOR_YELLOW +#define TAG_BG COLOR_WHITE +#define TAG_HL TRUE + +#define TAG_SELECTED_FG COLOR_YELLOW +#define TAG_SELECTED_BG COLOR_BLUE +#define TAG_SELECTED_HL TRUE + +#define TAG_KEY_FG COLOR_RED +#define TAG_KEY_BG COLOR_WHITE +#define TAG_KEY_HL TRUE + +#define TAG_KEY_SELECTED_FG COLOR_RED +#define TAG_KEY_SELECTED_BG COLOR_BLUE +#define TAG_KEY_SELECTED_HL TRUE + +#define CHECK_FG COLOR_BLACK +#define CHECK_BG COLOR_WHITE +#define CHECK_HL FALSE + +#define CHECK_SELECTED_FG COLOR_WHITE +#define CHECK_SELECTED_BG COLOR_BLUE +#define CHECK_SELECTED_HL TRUE + +#define UARROW_FG COLOR_GREEN +#define UARROW_BG COLOR_WHITE +#define UARROW_HL TRUE + +#define DARROW_FG COLOR_GREEN +#define DARROW_BG COLOR_WHITE +#define DARROW_HL TRUE + +/* End of default color definitions */ + +#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y))) +#define COLOR_NAME_LEN 10 +#define COLOR_COUNT 8 + + +/* + * Global variables + */ + +typedef struct { + unsigned char name[COLOR_NAME_LEN]; + int value; +} color_names_st; + + +#ifdef __DIALOG_MAIN__ + +/* + * For matching color names with color values + */ +color_names_st color_names[] = { + {"BLACK", COLOR_BLACK}, + {"RED", COLOR_RED}, + {"GREEN", COLOR_GREEN}, + {"YELLOW", COLOR_YELLOW}, + {"BLUE", COLOR_BLUE}, + {"MAGENTA", COLOR_MAGENTA}, + {"CYAN", COLOR_CYAN}, + {"WHITE", COLOR_WHITE}, +}; /* color names */ + + +/* + * Table of color values + */ +int color_table[][3] = { + {SCREEN_FG, SCREEN_BG, SCREEN_HL }, + {SHADOW_FG, SHADOW_BG, SHADOW_HL }, + {DIALOG_FG, DIALOG_BG, DIALOG_HL }, + {TITLE_FG, TITLE_BG, TITLE_HL }, + {BORDER_FG, BORDER_BG, BORDER_HL }, + {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL }, + {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL }, + {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL }, + {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL }, + {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL }, + {BUTTON_LABEL_INACTIVE_FG,BUTTON_LABEL_INACTIVE_BG,BUTTON_LABEL_INACTIVE_HL}, + {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL }, + {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL }, + {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL }, + {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL }, + {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL }, + {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL }, + {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL }, + {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL }, + {ITEM_FG, ITEM_BG, ITEM_HL }, + {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL }, + {TAG_FG, TAG_BG, TAG_HL }, + {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL }, + {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL }, + {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL }, + {CHECK_FG, CHECK_BG, CHECK_HL }, + {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL }, + {UARROW_FG, UARROW_BG, UARROW_HL }, + {DARROW_FG, DARROW_BG, DARROW_HL }, +}; /* color_table */ + +#else + +extern color_names_st color_names[]; +extern int color_table[][3]; + +#endif /* __DIALOG_MAIN__ */ diff --git a/gnu/lib/libodialog/dialog.3 b/gnu/lib/libodialog/dialog.3 new file mode 100644 index 0000000..2e4b8fb --- /dev/null +++ b/gnu/lib/libodialog/dialog.3 @@ -0,0 +1,842 @@ +.\" +.\" Copyright (c) 1995, Jordan Hubbard +.\" +.\" All rights reserved. +.\" +.\" This manual page may be used, modified, copied, distributed, and +.\" sold, in both source and binary form provided that the above +.\" copyright and these terms are retained, verbatim, as the first +.\" lines of this file. Under no circumstances is the author +.\" responsible for the proper functioning of the software described herein +.\" nor does the author assume any responsibility for damages incurred with +.\" its use. +.\" +.\" $FreeBSD$ +.\" +.Dd January 1, 2000 +.Dt DIALOG 3 +.Os +.Sh NAME +.Nm draw_shadow , +.Nm draw_box , +.Nm line_edit , +.Nm strheight , +.Nm strwidth , +.Nm dialog_create_rc , +.Nm dialog_yesno , +.Nm dialog_noyes , +.Nm dialog_prgbox , +.Nm dialog_msgbox , +.Nm dialog_textbox , +.Nm dialog_menu , +.Nm dialog_checklist , +.Nm dialog_radiolist , +.Nm dialog_inputbox , +.Nm dialog_clear_norefresh , +.Nm dialog_clear , +.Nm dialog_update , +.Nm dialog_fselect , +.Nm dialog_notify , +.Nm dialog_mesgbox , +.Nm dialog_gauge , +.Nm init_dialog , +.Nm end_dialog , +.Nm use_helpfile , +.Nm use_helpline , +.Nm get_helpline , +.Nm restore_helpline , +.Nm dialog_ftree , +.Nm dialog_tree +.Nd provide a simple ncurses-based GUI interface +.Sh SYNOPSIS +.In dialog.h +.Ft "void" +.Fn draw_shadow "WINDOW *win" "int y" "int x" "int height" "int width" +.Ft "void" +.Fn draw_box "WINDOW *win" "int y" "int x" "int height" "int width" "chtype box" "chtype border" +.Ft "int" +.Fo line_edit +.Fa "WINDOW *dialog" +.Fa "int box_y" +.Fa "int box_x" +.Fa "int flen" +.Fa "int box_width" +.Fa "chtype attr" +.Fa "int first" +.Fa "unsigned char *result" +.Fa "int attr_mask" +.Fc +.Ft "int" +.Fn strheight "const char *p" +.Ft "int" +.Fn strwidth "const char *p" +.Ft "void" +.Fn dialog_create_rc "unsigned char *filename" +.Ft "int" +.Fn dialog_yesno "unsigned char *title" "unsigned char *prompt" "int height" "int width" +.Ft "int" +.Fn dialog_noyes "unsigned char *title" "unsigned char *prompt" "int height" "int width" +.Ft "int" +.Fn dialog_prgbox "unsigned char *title" "const unsigned char *line" "int height" "int width" "int pause" "int use_shell" +.Ft "int" +.Fn dialog_textbox "unsigned char *title" "unsigned char *file" "int height" "int width" +.Ft "int" +.Fo dialog_menu +.Fa "unsigned char *title" +.Fa "unsigned char *prompt" +.Fa "int height" +.Fa "int width" +.Fa "int menu_height" +.Fa "int cnt" +.Fa "void *it" +.Fa "unsigned char *result" +.Fa "int *ch" +.Fa "int *sc" +.Fc +.Ft "int" +.Fn dialog_checklist "unsigned char *title" "unsigned char *prompt" "int height" "int width" "int list_height" "int cnt" "void *it" "unsigned char *result" +.Ft "int" +.Fn dialog_radiolist "unsigned char *title" "unsigned char *prompt" "int height" "int width" "int list_height" "int cnt" "void *it" "unsigned char *result" +.Ft "int" +.Fn dialog_inputbox "unsigned char *title" "unsigned char *prompt" "int height" "int width" "unsigned char *result" +.Ft "char *" +.Fn dialog_fselect "char *dir" "char *fmask" +.Ft "int" +.Fn dialog_dselect "char *dir" "char *fmask" +.Ft "void" +.Fn dialog_notify "char *msg" +.Ft "int" +.Fn dialog_mesgbox "unsigned char *title" "unsigned char *prompt" "int height" "int width" +.Ft "void" +.Fn dialog_gauge "char *title" "char *prompt" "int y" "int x" "int height" "int width" "int perc" +.Ft "void" +.Fn use_helpfile "char *hfile" +.Ft "void" +.Fn use_helpline "char *hline" +.Ft "char *" +.Fn get_helpline "void" +.Ft "void" +.Fn dialog_clear_norefresh "void" +.Ft "void" +.Fn dialog_clear "void" +.Ft "void" +.Fn dialog_update "void" +.Ft "void" +.Fn init_dialog "void" +.Ft "void" +.Fn end_dialog "void" +.Ft "int" +.Fn dialog_ftree "unsigned char *filename" "unsigned char FS" "unsigned char *title" "unsigned char *prompt" "int height" "int width" "int menu_height" "unsigned char **result" +.Ft "int" +.Fo dialog_tree +.Fa "unsigned char **names" +.Fa "int size" +.Fa "unsigned char FS" +.Fa "unsigned char *title" +.Fa "unsigned char *prompt" +.Fa "int height" +.Fa "int width" +.Fa "int menu_height" +.Fa "unsigned char **result" +.Fc +.Sh DESCRIPTION +The dialog library attempts to provide a fairly simplistic set of +fixed-presentation menus, input boxes, gauges, file requestors and +other general purpose GUI (a bit of a stretch, since it uses +ncurses) objects. +Since the library also had its roots in a +shell-script writer's utility (see the +.Xr dialog 1 +command), the +early API was somewhat primitively based on strings being passed in or +out and parsed. +This API was later extended to take either the +original arguments or arrays of +.Va dialogMenuItem +structures, +giving the user much more control over the internal behavior of each +control. +The +.Va dialogMenuItem +structure internals are public: +.Bd -literal -offset indent +typedef struct _dmenu_item { + char *prompt; + char *title; + int (*checked)(struct _dmenu_item *self); + int (*fire)(struct _dmenu_item *self); + int (*selected)(struct _dmenu_item *self, int is_selected); + void *data; + char lbra, mark, rbra; + long aux; +} dialogMenuItem; +.Ed +.Pp +The +.Dv prompt +and +.Dv title +strings are pretty much self-explanatory, +and the +.Va checked +and +.Va fire +function pointers provide optional +display and action hooks (the +.Dv data +variable being available for +the convenience of those hooks) when more tightly coupled feedback between +a menu object and user code is required. +The +.Va selected +hook also +allows you to verify whether or not a given item is selected (the cursor is +over it) for implementing pretty much any possible context-sensitive +behavior. +A number of clever tricks for simulating various kinds of item +types can also be done by adjusting the values of +.Va lbra +(default: '['), +.Va mark +(default: '*' for radio menus, 'X' for check menus) +and +.Va rbra +(default: ']') and declaring a reasonable +.Va checked +hook, +which should return TRUE for the +.Dq marked +state and FALSE for +.Dq unmarked . +The +.Va aux +field is not used internally, and is available for miscellaneous usage. +If an item has a +.Va fire +hook associated with it, it will also be called +whenever the item is "toggled" in some way and should return one of the +following codes: +.Bd -literal -offset 4n +#define DITEM_SUCCESS 0 /* Successful completion */ +#define DITEM_FAILURE 1 /* Failed to "fire" */ +.Ed +.Pp +The following flags are in the upper 16 bits of return status: +.Bd -literal -offset 4n +#define DITEM_LEAVE_MENU (1 << 16) +#define DITEM_REDRAW (1 << 17) +#define DITEM_RECREATE (1 << 18) +#define DITEM_RESTORE (1 << 19) +#define DITEM_CONTINUE (1 << 20) +.Ed +.Pp +Two special globals also exist for putting a dialog at any arbitrary +X,Y location (the early designers rather short-sightedly made no provisions +for this). +If set to zero, the default centering behavior will be in +effect. +.Pp +Below is a short description of the various functions: +.Pp +The +.Fn draw_shadow +function draws a shadow in curses window +.Va win +using the dimensions of +.Va x , y , width +and +.Va height . +.Pp +The +.Fn draw_box +function draws a bordered box using the dimensions of +.Va x , y , width +and +.Va height . +The attributes from +.Va box +and +.Va border +are used, if specified, while painting the box and border regions of the +object. +.Pp +The +.Fn line_edit +function invokes a simple line editor with an edit box of dimensions +.Va box_x , box_y +and +.Va box_width . +The field length is constrained by +.Va flen , +starting at the +.Va first +character specified and +optionally displayed with character attributes +.Va attr . +The string being edited is stored in +.Va result . +Returns 0 on success, 1 on Cancel, and -1 on failure or ESC. +.Pp +The +.Fn strheight +function returns the height of string in +.Va p , +counting newlines. +.Pp +The +.Fn strwidth +function returns the width of string in +.Va p , +counting newlines. +.Pp +The +.Fn dialog_create_rc +function dumps dialog library settings into +.Pa filename +for later retrieval as defaults. +Returns 0 on success, -1 on failure. +.Pp +The +.Fn dialog_yesno +function displays a text box using +.Va title +and +.Va prompt +strings of dimensions +.Va height +and +.Va width . +Also paint a pair of +.Em Yes +and +.Em \&No +buttons at the bottom. +The default selection is +.Em Yes . +If the +.Em Yes +button is chosen, return FALSE. +If +.Em \&No , +return TRUE. +.Pp +The +.Fn dialog_noyes +function is the same as +.Fn dialog_yesno , +except the default selection is +.Em \&No . +.Pp +The +.Fn dialog_prgbox +function displays a text box of dimensions +.Va height +and +.Va width +containing the output of command +.Va line . +If +.Va use_shell +is TRUE, +.Va line +is passed as an argument to +.Xr sh 1 , +otherwise it is simply passed to +.Xr exec 3 . +If +.Va pause +is TRUE, a final confirmation requestor will be put up when execution +terminates. +Returns 0 on success, -1 on failure. +.Pp +The +.Fn dialog_textbox +function displays a text box containing the contents of +.Va file +with dimensions of +.Va height +and +.Va width . +.Pp +The +.Fn dialog_menu +function displays a menu of dimensions +.Va height +and +.Va width +with an optional internal menu height of +.Va menu_height . +The +.Va cnt +and +.Va it +arguments are of particular importance since they, +together, determine which of the 2 available APIs to use. +To use the +older and traditional interface, +.Va cnt +should be a positive +integer representing the number of string pointer pairs to find in +.Va it +(which should be of type +.Ft char "**" ) , +the strings are +expected to be in prompt and title order for each item and the +.Va result +parameter is expected to point to an array where the +prompt string of the item selected will be copied. +To use the newer +interface, +.Va cnt +should be a +.Va negative +integer representing the number of +.Va dialogMenuItem +structures pointed to by +.Va it +(which should be of type +.Vt dialogMenuItem "*" ) , +one structure per item. +In the new interface, the +.Va result +variable is used as a simple boolean (not a pointer) and should be NULL if +.Va it +only points to menu items and the default OK and Cancel buttons are desired. +If +.Va result +is non-NULL, then +.Va it +is actually expected to point 2 locations +.Va past +the start of the menu item list. +.Va it +is then expected to +point to an item representing the Cancel button, from which the +.Va prompt +and +.Va fire +actions are used to override the default behavior, and +.Va it +to the same for the OK button. +.Pp +Using either API behavior, the +.Va ch +and +.Va sc +values may be passed in to preserve current +item selection and scroll position values across calls. +.Pp +The +.Fn dialog_checklist +function displays a menu of dimensions +.Va height +and +.Va width +with an +optional internal menu height of +.Va list_height . +The +.Va cnt +and +.Va it +arguments are of particular importance since they, +together, determine which of the 2 available APIs to use. +To use the +older and traditional interface, +.Va cnt +should be a positive +integer representing the number of string pointer tuples to find in +.Va it +(which should be of type +.Ft "char **" ) , +the strings are +expected to be in prompt, title and state ("on" or "off") order for +each item and the +.Va result +parameter is expected to point to an +array where the prompt string of the item(s) selected will be +copied. +To use the newer interface, +.Va cnt +should be a +.Em negative +integer representing the number of +.Ft dialogMenuItem +structures pointed to by +.Va it +(which should be of type +.Ft "dialogMenuItem *" ) , +one structure per item. +In the new interface, +the +.Va result +variable is used as a simple boolean (not a pointer) +and should be NULL if +.Va it +only points to menu items and the default OK and Cancel +buttons are desired. +If +.Va result +is non-NULL, then +.Va it +is actually expected to +point 2 locations +.Va past +the start of the menu item list. +.Va it +is then expected to point to an item representing the Cancel +button, from which the +.Va prompt +and +.Va fire +actions are used to override the default behavior, and +.Va it +to the same for the OK button. +.Pp +In the standard API model, the menu supports the selection of multiple items, +each of which is marked with an `X' character to denote selection. +When +the OK button is selected, the prompt values for all items selected are +concatenated into the +.Va result +string. +.Pp +In the new API model, it is not actually necessary to preserve +"checklist" semantics at all since practically everything about how +each item is displayed or marked as "selected" is fully configurable. +You could have a single checklist menu that actually contained a group +of items with "radio" behavior, "checklist" behavior and standard menu +item behavior. +The only reason to call +.Fn dialog_checklist +over +.Fn dialog_radiolist +in the new API model is to inherit the base +behavior, you are no longer constrained by it. +.Pp +Returns 0 on success, 1 on Cancel, and -1 on failure or ESC. +.Pp +The +.Fn dialog_radiolist +function displays a menu of dimensions +.Va height +and +.Va width +with an +optional internal menu height of +.Va list_height . +The +.Va cnt +and +.Va it +arguments are of particular importance since they, +together, determine which of the 2 available APIs to use. +To use the +older and traditional interface, +.Va cnt +should be a positive +integer representing the number of string pointer tuples to find in +.Va it +(which should be of type +.Ft "char **" ) , +the strings are +expected to be in prompt, title and state ("on" or "off") order for +each item and the +.Va result +parameter is expected to point to an +array where the prompt string of the item(s) selected will be +copied. +To use the newer interface, +.Va cnt +should be a +.Dv negative +integer representing the number of +.Ft dialogMenuItem +structures pointed to by +.Va it +(which should be of type +.Ft "dialogMenuItem *" , +one structure per item. +In the new interface, +the +.Va result +variable is used as a simple boolean (not a pointer) +and should be NULL if +.Va it +only points to menu items and the default OK and Cancel +buttons are desired. +If +.Va result +is non-NULL, then +.Va it +is actually expected to point 2 locations +.Va past +the start of the menu item list. +.Va it +is then expected to point to an item representing the Cancel +button, from which the +.Va prompt +and +.Va fire +actions are used to override the default behavior, and +.Va it +does the same for the traditional OK button. +.Pp +In the standard API model, the menu supports the selection of only one +of multiple items, the currently active item marked with an `*' +character to denote selection. +When the OK button is selected, the +prompt value for this item is copied into the +.Va result +string. +.Pp +In the new API model, it is not actually necessary to preserve +"radio button" semantics at all since practically everything about how +each item is displayed or marked as "selected" is fully configurable. +You could have a single radio menu that actually contained a group +of items with "checklist" behavior, "radio" behavior and standard menu +item behavior. +The only reason to call +.Fn dialog_radiolist +over +.Fn dialog_checklistlist +in the new API model is to inherit the base +behavior. +.Pp +Returns 0 on success, 1 on Cancel and -1 on failure or ESC. +.Pp +The +.Fn dialog_inputbox +function displays a single-line text input field in a box displaying +.Va title +and +.Va prompt +of dimensions +.Va height +and +.Va width . +The field entered is stored in +.Va result . +.Pp +Returns 0 on success, -1 on failure or ESC. +.Pp +The +.Fn dialog_fselect +function brings up a file selector dialog starting at +.Va dir +and showing only those file names +matching +.Va fmask . +.Pp +Returns filename selected or NULL. +.Pp +The +.Fn dialog_dselect +function brings up a directory selector dialog starting at +.Va dir +and showing only those directory names +matching +.Va fmask . +.Pp +Returns directory name selected or NULL. +.Pp +The +.Fn dialog_notify +function brings up a generic "hey, you!" notifier dialog containing +.Va msg . +.Pp +The +.Fn dialog_mesgbox +function displays a notifier dialog, but with more control over +.Va title , +.Va prompt , +.Va width +and +.Va height . +This object will also wait for user confirmation, unlike +.Fn dialog_notify . +.Pp +Returns 0 on success, -1 on failure. +.Pp +The +.Fn dialog_gauge +function displays a horizontal bar-graph style gauge. +A value of +.Em 100 +for +.Em perc +constitutes a full gauge, a value of +.Em 0 +an empty one. +.Pp +The +.Fn use_helpfile +function for any menu supporting context sensitive help, invokes the text box +object on this file whenever the +.Em F1 +key is pressed. +.Pp +The +.Fn use_helpline +function displays this line of helpful text below any menu being displayed. +.Pp +The +.Fn get_helpline +function gets the current value of the helpful text line. +.Pp +The +.Fn dialog_clear_norefresh +function clears the screen back to the dialog background color, but do not +refresh the contents just yet. +.Pp +The +.Fn dialog_clear +function clears the screen back to the dialog background color immediately. +.Pp +The +.Fn dialog_update +function does any pending screen refreshes now. +.Pp +The +.Fn init_dialog +function initializes the dialog library (call this routine before any other +dialog API calls). +.Pp +The +.Fn end_dialog +function shuts down the dialog library (call this if you need to get back to +sanity). +.Pp +The +.Fn dialog_ftree +function shows a tree described by the data from the file +.Pa filename . +The data in the file should look like +.Xr find 1 +output. +For the +.Xr find 1 +output, the field separator +.Va FS +will be +.Dq \&/ . +If +.Va height +and +.Va width +are positive numbers, they set the absolute +size of the whole +.Fn dialog_ftree +box. +If +.Va height +and +.Va width +are negative numbers, the size of the +.Fn dialog_ftree +box will be calculated automatically. +.Va menu_height +sets the height of the tree subwindow inside the +.Fn dialog_ftree +box and must be set. +.Va title +is shown centered on the upper border of the +.Fn dialog_ftree +box. +.Va prompt +is shown inside the +.Fn dialog_ftree +box above the tree subwindow and can contain +.Ql \e\&n +to split lines. +One can navigate in +the tree by pressing UP/DOWN or +.Sm off +.So \&+ Sc \&/ So \&- Sc , +.Sm on +PG_UP/PG_DOWN or +.Sm off +.So b Sc \&/SPACE +.Sm on +and +HOME/END or +.Sm off +.So g Sc \&/ So G Sc . +.Sm on +A leaf of the +tree is selected by pressing TAB or LEFT/RIGHT the OK +button and pressing ENTER. +filename may contain data like +.Xr find 1 +output, as well as like the output of +.Xr find 1 +with +.Fl d +option. +Some of the transient paths to the leaves of the tree may +be absent. +Such data is corrected when fed from filename. +.Pp +The function returns 0 and a pointer to the selected leaf (to the path to +the leaf from the root of the tree) into result, if the OK button was +selected. +The memory allocated for the building of the tree is freed on +exiting +.Fn dialog_ftree . +The memory for the result line should be freed +later manually, if necessary. +If the Cancel button was selected, the +function returns 1. +In case of exiting +.Fn dialog_ftree +on ESC, the function returns -1. +.Pp +The +.Fn dialog_tree +function returns the same results as +.Fn dialog_ftree . +If 0 is returned, result will contain a pointer from the array +.Va names . +.\" \fBdialog_tree\fR displays the tree very much like \fBdialog_ftree\fR does, +.\" with some exceptions. The source data for the building of the tree is an +.\" array \fBnames\fR of paths to the leaves (should be similar to \fBfind(1)\fR +.\" output) of the size \fBsize\fR. However, there is no correction of data like +.\" in \fBdialog_ftree\fR. Thus, to display a correct tree, the array must +.\" already contain correct data. Besides, in each session every unique use of +.\" \fBdialog_tree\fR is kept in memory, and later, when calling +.\" \fBdialog_tree\fR with the same \fBnames\fR, \fBsize\fR, \fBFS\fR, +.\" \fBheight\fR, \fBwidth\fR and \fBmenu_height\fR the position of the cursor +.\" in the tree subwindow is restored. +.Sh SEE ALSO +.Xr dialog 1 , +.Xr ncurses 3 +.Sh HISTORY +These functions appeared in +.Fx 2.0 +as the +.Xr dialog 1 +command and were soon split into a separate library +and command by +.An Andrey Chernov . +.An Marc van Kempen +implemented most of the extra controls and objects, +.An Jordan Hubbard +added the dialogMenuItem renovations and this man page and +.An Anatoly A. Orehovsky +implemented +.Fn dialog_ftree +and +.Fn dialog_tree . +.Sh AUTHORS +.An -nosplit +The primary author would appear to be +.An Savio Lam Aq lam836@cs.cuhk.hk +with contributions over the years by +.An Stuart Herbert Aq S.Herbert@sheffield.ac.uk , +.An Marc van Kempen Aq wmbfmk@urc.tue.nl , +.An Andrey Chernov Aq ache@FreeBSD.org , +.An Jordan Hubbard Aq jkh@FreeBSD.org +and +.An Anatoly A. Orehovsky Aq tolik@mpeks.tomsk.su . +.Sh BUGS +Sure! diff --git a/gnu/lib/libodialog/dialog.h b/gnu/lib/libodialog/dialog.h new file mode 100644 index 0000000..369b139 --- /dev/null +++ b/gnu/lib/libodialog/dialog.h @@ -0,0 +1,211 @@ +#ifndef _DIALOG_H_INCLUDE +#define _DIALOG_H_INCLUDE + +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * Substantial rennovation: 12/18/95, Jordan K. Hubbard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $FreeBSD$ + * + */ + +#define HAVE_NCURSES + +#ifdef HAVE_NCURSES +#include <ncurses.h> + +#else + +#ifdef ultrix +#include <cursesX.h> +#else +#include <curses.h> +#endif + +#endif + +/* special return codes for `fire' actions */ +#define DITEM_STATUS(flag) ((flag) & 0x0000FFFF) +#define DITEM_SUCCESS 0 +#define DITEM_FAILURE 1 + +/* Flags - returned in upper 16 bits of return status */ +#define DITEM_LEAVE_MENU (1 << 16) +#define DITEM_REDRAW (1 << 17) +#define DITEM_RECREATE (1 << 18) +#define DITEM_RESTORE (1 << 19) +#define DITEM_CONTINUE (1 << 20) + +/* Attributes as used by entry fields right now */ +#define DITEM_NO_ECHO 0x0001 + + +/* negative offsets for buttons in item lists, if specified */ +#define OK_BUTTON -2 +#define CANCEL_BUTTON -1 + +/* for use in describing more exotic behaviors */ +typedef struct _dmenu_item { + char *prompt; + char *title; + int (*checked)(struct _dmenu_item *self); + int (*fire)(struct _dmenu_item *self); + void (*selected)(struct _dmenu_item *self, int is_selected); + void *data; + char lbra, mark, rbra; + long aux; +} dialogMenuItem; + +#define VERSION "0.4" +#define MAX_LEN 4096 + +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef FALSE +#define FALSE (0) +#endif + +extern int DialogX, DialogY, DialogInputAttrs; + +/* + * Attribute names + */ +#define screen_attr attributes[0] +#define shadow_attr attributes[1] +#define dialog_attr attributes[2] +#define title_attr attributes[3] +#define border_attr attributes[4] +#define button_active_attr attributes[5] +#define button_inactive_attr attributes[6] +#define button_key_active_attr attributes[7] +#define button_key_inactive_attr attributes[8] +#define button_label_active_attr attributes[9] +#define button_label_inactive_attr attributes[10] +#define inputbox_attr attributes[11] +#define inputbox_border_attr attributes[12] +#define searchbox_attr attributes[13] +#define searchbox_title_attr attributes[14] +#define searchbox_border_attr attributes[15] +#define position_indicator_attr attributes[16] +#define menubox_attr attributes[17] +#define menubox_border_attr attributes[18] +#define item_attr attributes[19] +#define item_selected_attr attributes[20] +#define tag_attr attributes[21] +#define tag_selected_attr attributes[22] +#define tag_key_attr attributes[23] +#define tag_key_selected_attr attributes[24] +#define check_attr attributes[25] +#define check_selected_attr attributes[26] +#define uarrow_attr attributes[27] +#define darrow_attr attributes[28] + +/* number of attributes */ +#define ATTRIBUTE_COUNT 29 + +extern chtype attributes[]; + +#ifdef HAVE_NCURSES +extern bool use_shadow; +void draw_shadow(WINDOW *win, int y, int x, int height, int width); +#endif +void draw_box(WINDOW *win, int y, int x, int height, int width, chtype box, chtype border); +int line_edit(WINDOW *dialog, int box_y, int box_x, int flen, int box_width, chtype attrs, int first, unsigned char *result, int attr_mask); +int strheight(const char *p); +int strwidth(const char *p); + +void dialog_create_rc(unsigned char *filename); +int dialog_yesno(unsigned char *title, unsigned char *prompt, int height, int width); +int dialog_noyes(unsigned char *title, unsigned char *prompt, int height, int width); +int dialog_prgbox(unsigned char *title, const unsigned char *line, int height, int width, int pause, int use_shell); +int dialog_msgbox(unsigned char *title, unsigned char *prompt, int height, int width, int pause); +int dialog_textbox(unsigned char *title, unsigned char *file, int height, int width); +int dialog_menu(unsigned char *title, unsigned char *prompt, int height, int width, int menu_height, + int item_no, void *itptr, unsigned char *result, int *ch, int *sc); +int dialog_checklist(unsigned char *title, unsigned char *prompt, int height, int width, int list_height, + int item_no, void *itptr, unsigned char *result); +int dialog_radiolist(unsigned char *title, unsigned char *prompt, int height, int width, int list_height, + int item_no, void *itptr, unsigned char *result); +int dialog_inputbox(unsigned char *title, unsigned char *prompt, int height, int width, unsigned char *result); +void dialog_clear_norefresh(void); +void dialog_clear(void); +void dialog_update(void); +void init_dialog(void); +void end_dialog(void); + +/* Additions to libdialog */ +char *dialog_fselect(char *dir, char *fmask); +int dialog_dselect(char *dir, char *fmask); +void dialog_notify(char *msg); +int dialog_mesgbox(unsigned char *title, unsigned char *prompt, int height, int width); +void use_helpfile(char *helpfile); +void use_helpline(char *helpline); +char *get_helpline(void); +void restore_helpline(char *helpline); +void dialog_gauge(char *title, char *prompt, int y, int x, int height, int width, int perc); + +/* + * Display a tree menu from file + * + * filename - file with like find(1) output + * FS - fields separator + * title - title of dialog box + * prompt - prompt text into dialog box + * height - height of dialog box + * width - width of dialog box + * menu_height - height of menu box + * result - pointer to char array + * + * return values: + * -1 - ESC pressed + * 0 - Ok, result set (must be freed later) + * 1 - Cancel + */ +int dialog_ftree(unsigned char *filename, unsigned char FS, + unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + unsigned char **result); + +/* + * Display a tree menu from array + * + * names - array with like find(1) output + * size - size of array + * FS - fields separator + * title - title of dialog box + * prompt - prompt text into dialog box + * height - height of dialog box + * width - width of dialog box + * menu_height - height of menu box + * result - pointer to char array + * + * return values: + * -1 - ESC pressed + * 0 - Ok, result set + * 1 - Cancel + */ + +int dialog_tree(unsigned char **names, int size, unsigned char FS, + unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + unsigned char **result); + +#endif /* _DIALOG_H_INCLUDE */ diff --git a/gnu/lib/libodialog/dialog.priv.h b/gnu/lib/libodialog/dialog.priv.h new file mode 100644 index 0000000..06c01a5 --- /dev/null +++ b/gnu/lib/libodialog/dialog.priv.h @@ -0,0 +1,183 @@ +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> + +#if defined(LOCALE) +#include <locale.h> +#endif + + +/* + * Change these if you want + */ +#define USE_SHADOW TRUE +#define USE_COLORS TRUE + +#define ESC 27 +#define TAB 9 +#define BUF_SIZE (10*1024) + +#ifndef MIN +#define MIN(x,y) (x < y ? x : y) +#endif +#ifndef MAX +#define MAX(x,y) (x > y ? x : y) +#endif + +#ifndef ctrl +#define ctrl(a) ((a) - 'a' + 1) +#endif + +#ifndef HAVE_NCURSES +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif +#endif /* HAVE_NCURSES */ + +/* Travel key conventions */ +#define KEY_IS_UP(key) ((key) == KEY_UP || (key) == '-' || key == '\020' /* ^P */) +#define KEY_IS_DOWN(key) ((key) == KEY_DOWN || (key) == '+' || key == '\016' /* ^N */) + +/* + * Global variables + */ +#ifdef __DIALOG_MAIN__ + +#ifdef HAVE_NCURSES + +/* use colors by default? */ +bool use_colors = USE_COLORS; + +/* shadow dialog boxes by default? + Note that 'use_shadow' implies 'use_colors' */ +bool use_shadow = USE_SHADOW; + +#endif + + +/* + * Attribute values, default is for mono display + */ +chtype attributes[] = { + A_NORMAL, /* screen_attr */ + A_NORMAL, /* shadow_attr */ + A_REVERSE, /* dialog_attr */ + A_REVERSE, /* title_attr */ + A_REVERSE, /* border_attr */ + A_BOLD, /* button_active_attr */ + A_DIM, /* button_inactive_attr */ + A_UNDERLINE, /* button_key_active_attr */ + A_UNDERLINE, /* button_key_inactive_attr */ + A_NORMAL, /* button_label_active_attr */ + A_NORMAL, /* button_label_inactive_attr */ + A_REVERSE, /* inputbox_attr */ + A_REVERSE, /* inputbox_border_attr */ + A_REVERSE, /* searchbox_attr */ + A_REVERSE, /* searchbox_title_attr */ + A_REVERSE, /* searchbox_border_attr */ + A_REVERSE, /* position_indicator_attr */ + A_REVERSE, /* menubox_attr */ + A_REVERSE, /* menubox_border_attr */ + A_REVERSE, /* item_attr */ + A_NORMAL, /* item_selected_attr */ + A_REVERSE, /* tag_attr */ + A_REVERSE, /* tag_selected_attr */ + A_NORMAL, /* tag_key_attr */ + A_BOLD, /* tag_key_selected_attr */ + A_REVERSE, /* check_attr */ + A_REVERSE, /* check_selected_attr */ + A_REVERSE, /* uarrow_attr */ + A_REVERSE /* darrow_attr */ +}; + +#else + +#ifdef HAVE_NCURSES +extern bool use_colors; +#endif + +#endif /* __DIALOG_MAIN__ */ + + + +#ifdef HAVE_NCURSES + +/* + * Function prototypes + */ +#ifdef __DIALOG_MAIN__ + +extern int parse_rc(void); + +#endif /* __DIALOG_MAIN__ */ + +#endif + + +#ifdef HAVE_NCURSES +void color_setup(void); +#endif + +void attr_clear(WINDOW *win, int height, int width, chtype attr); +void print_autowrap(WINDOW *win, unsigned char *prompt, int height, int width, int maxwidth, + int y, int x, int center, int rawmode); +void print_button(WINDOW *win, unsigned char *label, int y, int x, int selected); +FILE *raw_popen(const char *program, char * const *argv, const char *type); +int raw_pclose(FILE *iop); +void display_helpfile(void); +void display_helpline(WINDOW *w, int y, int width); +void print_arrows(WINDOW *dialog, int scroll, int menu_height, int item_no, int box_x, + int box_y, int tag_x, int cur_x, int cur_y); + diff --git a/gnu/lib/libodialog/dir.c b/gnu/lib/libodialog/dir.c new file mode 100644 index 0000000..5348c2f --- /dev/null +++ b/gnu/lib/libodialog/dir.c @@ -0,0 +1,549 @@ +/**************************************************************************** + * + * Program: dir.c + * Author: Marc van Kempen + * desc: Directory routines, sorting and reading + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + ****************************************************************************/ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> /* XXX for _POSIX_VERSION ifdefs */ + +#if !defined sgi && !defined _POSIX_VERSION +#include <sys/dir.h> +#endif +#if defined __sun__ +#include <sys/dirent.h> +#endif +#if defined sgi || defined _POSIX_VERSION +#include <dirent.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fnmatch.h> +#include <sys/param.h> +#include "dir.h" + +/**************************************************************************** + * + * local prototypes + * + ****************************************************************************/ + +void toggle_dotfiles(void); +int show_dotfiles(void); +int dir_alphasort(const void *d1, const void *d2); +int dir_sizesort(const void *d1, const void *d2); +int dir_datesort(const void *d1, const void *d2); +int dir_extsort(const void *d1, const void *d2); + +/**************************************************************************** + * + * global variables + * + ****************************************************************************/ + + +/* This is user-selectable, I've set them fixed for now however */ + +void *_sort_func = dir_alphasort; +static int _showdotfiles = TRUE; + +/**************************************************************************** + * + * Functions + * + ****************************************************************************/ + +int +dir_select_nd( +#if defined __linux__ + const struct dirent *d +#else + struct dirent *d +#endif +) +/* + * desc: allways include a directory entry <d>, except + * for the current directory and other dot-files + * keep '..' however. + * pre: <d> points to a dirent + * post: returns TRUE if d->d_name != "." else FALSE + */ +{ + if (strcmp(d->d_name, ".")==0 || + (d->d_name[0] == '.' && strlen(d->d_name) > 1 && d->d_name[1] != '.')) { + return(FALSE); + } else { + return(TRUE); + } +}/* dir_select_nd() */ + + +int +dir_select( +#ifdef __linux__ + const struct dirent *d +#else + struct dirent *d +#endif +) +/* + * desc: allways include a directory entry <d>, except + * for the current directory + * pre: <d> points to a dirent + * post: returns TRUE if d->d_name != "." else FALSE + */ +{ + if (strcmp(d->d_name, ".")==0) { /* don't include the current directory */ + return(FALSE); + } else { + return(TRUE); + } +} /* dir_select() */ + +int +dir_select_root_nd( +#ifdef __linux__ + const struct dirent *d +#else + struct dirent *d +#endif +) +/* + * desc: allways include a directory entry <d>, except + * for the current directory and the parent directory. + * Also skip any other dot-files. + * pre: <d> points to a dirent + * post: returns TRUE if d->d_name[0] != "." else FALSE + */ +{ + if (d->d_name[0] == '.') { /* don't include the current directory */ + return(FALSE); /* nor the parent directory */ + } else { + return(TRUE); + } +} /* dir_select_root_nd() */ + + +int +dir_select_root( +#ifdef __linux__ + const struct dirent *d +#else + struct dirent *d +#endif +) +/* + * desc: allways include a directory entry <d>, except + * for the current directory and the parent directory + * pre: <d> points to a dirent + * post: returns TRUE if d->d_name[0] != "." else FALSE + */ +{ + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { + return(FALSE); + } else { + return(TRUE); + } +}/* dir_select_root() */ + + +#ifdef NO_ALPHA_SORT +int +alphasort(const void *d1, const void *d2) +/* + * desc: a replacement for what should be in the library + */ +{ + return(strcmp(((struct dirent *) d1)->d_name, + ((struct dirent *) d2)->d_name)); +} /* alphasort() */ +#endif + +int +dir_alphasort(const void *d1, const void *d2) +/* + * desc: compare d1 and d2, but put directories always first + * put '..' always on top + * + */ +{ + DirList *f1 = ((DirList *) d1), + *f2 = ((DirList *) d2); + struct stat *s1 = &(f1->filestatus); + struct stat *s2 = &(f2->filestatus); + + /* check for '..' */ + if (strcmp(((DirList *) d1)->filename, "..") == 0) { + return(-1); + } + if (strcmp(((DirList *) d2)->filename, "..") == 0) { + return(1); + } + + /* put directories first */ + if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { + return(strcmp(f1->filename, f2->filename)); + }; + if (s1->st_mode & S_IFDIR) { + return(-1); + } + if (s2->st_mode & S_IFDIR) { + return(1); + } + return(strcmp(f1->filename, f2->filename)); + +} /* dir_alphasort() */ + + +int +dir_sizesort(const void *d1, const void *d2) +/* + * desc: compare d1 and d2, but put directories always first + * + */ +{ + DirList *f1 = ((DirList *) d1), + *f2 = ((DirList *) d2); + struct stat *s1 = &(f1->filestatus); + struct stat *s2 = &(f2->filestatus); + + /* check for '..' */ + if (strcmp(((DirList *) d1)->filename, "..") == 0) { + return(-1); + } + if (strcmp(((DirList *) d2)->filename, "..") == 0) { + return(1); + } + + /* put directories first */ + if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { + return(s1->st_size < s2->st_size ? + -1 + : + s1->st_size >= s2->st_size); + }; + if (s1->st_mode & S_IFDIR) { + return(-1); + } + if (s2->st_mode & S_IFDIR) { + return(1); + } + return(s1->st_size < s2->st_size ? + -1 + : + s1->st_size >= s2->st_size); + +} /* dir_sizesort() */ + +int +dir_datesort(const void *d1, const void *d2) +/* + * desc: compare d1 and d2 on date, but put directories always first + */ +{ + DirList *f1 = ((DirList *) d1), + *f2 = ((DirList *) d2); + struct stat *s1 = &(f1->filestatus); + struct stat *s2 = &(f2->filestatus); + + + /* check for '..' */ + if (strcmp(((DirList *) d1)->filename, "..") == 0) { + return(-1); + } + if (strcmp(((DirList *) d2)->filename, "..") == 0) { + return(1); + } + + /* put directories first */ + if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { + return(s1->st_mtime < s2->st_mtime ? + -1 + : + s1->st_mtime >= s2->st_mtime); + }; + if (s1->st_mode & S_IFDIR) { + return(-1); + } + if (s2->st_mode & S_IFDIR) { + return(1); + } + return(s1->st_mtime < s2->st_mtime ? + -1 + : + s1->st_mtime >= s2->st_mtime); + +} /* dir_datesort() */ + + +int +null_strcmp(char *s1, char *s2) +/* + * desc: compare strings allowing NULL pointers + */ +{ + if ((s1 == NULL) && (s2 == NULL)) { + return(0); + } + if (s1 == NULL) { + return(-1); + } + if (s2 == NULL) { + return(1); + } + return(strcmp(s1, s2)); +} /* null_strcmp() */ + + +int +dir_extsort(const void *d1, const void *d2) +/* + * desc: compare d1 and d2 on extension, but put directories always first + * extension = "the characters after the last dot in the filename" + * pre: d1 and d2 are pointers to DirList type records + * post: see code + */ +{ + DirList *f1 = ((DirList *) d1), + *f2 = ((DirList *) d2); + struct stat *s1 = &(f1->filestatus); + struct stat *s2 = &(f2->filestatus); + char *ext1, *ext2; + int extf, ret; + + + /* check for '..' */ + if (strcmp(((DirList *) d1)->filename, "..") == 0) { + return(-1); + } + if (strcmp(((DirList *) d2)->filename, "..") == 0) { + return(1); + } + + + /* find the first extension */ + + ext1 = f1->filename + strlen(f1->filename); + extf = FALSE; + while (!extf && (ext1 > f1->filename)) { + extf = (*--ext1 == '.'); + } + if (!extf) { + ext1 = NULL; + } else { + ext1++; + } + /* ext1 == NULL if there's no "extension" else ext1 points */ + /* to the first character of the extension string */ + + /* find the second extension */ + + ext2 = f2->filename + strlen(f2->filename); + extf = FALSE; + while (!extf && (ext2 > f2->filename)) { + extf = (*--ext2 == '.'); + } + if (!extf) { + ext2 = NULL; + } else { + ext2++; + } + /* idem as for ext1 */ + + if ((s1->st_mode & S_IFDIR) && (s2->st_mode & S_IFDIR)) { + ret = null_strcmp(ext1, ext2); + if (ret == 0) { + return(strcmp(f1->filename, f2->filename)); + } else { + return(ret); + } + }; + if (s1->st_mode & S_IFDIR) { + return(-1); + } + if (s2->st_mode & S_IFDIR) { + return(1); + } + ret = null_strcmp(ext1, ext2); + if (ret == 0) { + return(strcmp(f1->filename, f2->filename)); + } else { + return(ret); + } + +} /* dir_extsort() */ + + +void +get_dir(char *dirname, char *fmask, DirList **dir, int *n) +/* + * desc: get the files in the current directory + * pre: <dir> == NULL + * post: <dir> contains <n> dir-entries + */ +{ + char cwd[MAXPATHLEN]; + char buf[256]; + struct dirent **dire; + struct stat status; + int i, j, nb; + long d; + + + getcwd(cwd, MAXPATHLEN); + if (strcmp(cwd, "/") == 0) { /* we are in the root directory */ + if (show_dotfiles()) { + *n = scandir(dirname, &dire, dir_select_root, alphasort); + } else { + *n = scandir(dirname, &dire, dir_select_root_nd, alphasort); + } + } else { + if (show_dotfiles()) { + *n = scandir(dirname, &dire, dir_select, alphasort); + } else { + *n = scandir(dirname, &dire, dir_select_nd, alphasort); + } + } + + /* There is the possibility that we have entered a directory */ + /* which we are not allowed to read, scandir thus returning */ + /* -1 for *n. */ + /* Actually I should also check for lack of memory, but I'll */ + /* let my application happily crash if this is the case */ + /* Solution: */ + /* manually insert the parent directory as the only */ + /* directory entry, and return. */ + + if (*n == -1) { + *n = 1; + *dir = (DirList *) malloc(sizeof(DirList)); + strcpy((*dir)[0].filename, ".."); + lstat("..", &status); + (*dir)[0].filestatus = status; + (*dir)[0].link = FALSE; + return; + } + + *dir = (DirList *) malloc( *n * sizeof(DirList) ); + d = 0; + i = 0; + j = 0; + while (j<*n) { + lstat(dire[j]->d_name, &status); + /* check if this file is to be included */ + /* always include directories, the rest is subject to fmask */ + if (S_ISDIR(status.st_mode) + || fnmatch(fmask, dire[j]->d_name, FNM_NOESCAPE) != FNM_NOMATCH) { + strcpy((*dir)[i].filename, dire[j]->d_name); + (*dir)[i].filestatus = status; + if ((S_IFMT & status.st_mode) == S_IFLNK) { /* handle links */ + (*dir)[i].link = TRUE; + stat(dire[j]->d_name, &status); + nb = readlink(dire[j]->d_name, buf, sizeof(buf) - 1); + if (nb == -1) { + printf("get_dir(): Error reading link: %s\n", dire[j]->d_name); + exit(-1); + } else { + (*dir)[i].linkname = malloc(sizeof(char) * nb + 1); + strncpy((*dir)[i].linkname, buf, nb); + (*dir)[i].linkname[nb] = 0; + } + (*dir)[i].filestatus = status; + } else { + (*dir)[i].link = FALSE; + (*dir)[i].linkname = NULL; + } + i++; + } else { + /* skip this entry */ + } + j++; + } + *n = i; + + /* sort the directory with the directory names on top */ + qsort((*dir), *n, sizeof(DirList), _sort_func); + + /* Free the allocated memory */ + for (i=0; i<*n; i++) { + free(dire[i]); + } + free(dire); + + return; +}/* get_dir() */ + + +void +FreeDir(DirList *d, int n) +/* + * desc: free the dirlist d + * pre: d != NULL + * post: memory allocated to d has been released + */ +{ + int i; + + if (d) { + for (i=0; i<n; i++) { + if (d[i].linkname) { + free(d[i].linkname); + } + } + free(d); + } else { + printf("dir.c:FreeDir(): d == NULL\n"); + exit(-1); + } + + return; +} /* FreeDir() */ + +void +toggle_dotfiles(void) +/* + * desc: toggle visibility of dot-files + */ +{ + _showdotfiles = !_showdotfiles; + + return; +} /* toggle_dotfiles() */ + +int +show_dotfiles(void) +/* + * desc: return the value of _showdotfiles + */ +{ + return(_showdotfiles); +} /* show_dotfiles() */ + +void +set_dotfiles(int b) +/* + * desc: set the value of _showdotfiles + */ +{ + _showdotfiles = b; + + return; +} /* set_dotfiles() */ diff --git a/gnu/lib/libodialog/dir.h b/gnu/lib/libodialog/dir.h new file mode 100644 index 0000000..eadc0c5 --- /dev/null +++ b/gnu/lib/libodialog/dir.h @@ -0,0 +1,38 @@ +/* + * include file for dir.c + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/dirent.h> + +typedef struct DirList { /* structure to hold the directory entries */ + char filename[MAXNAMLEN]; /* together with the stat-info per file */ + struct stat filestatus; /* filename, or the name to which it points */ + int link; /* is it a link ? */ + char *linkname; /* the name of the file the link points to */ +} DirList; + +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef FALSE +#define FALSE (0) +#endif + +void get_dir(char *dirname, char *fmask, DirList **dir, int *n); +void get_filenames(DirList *d, int n, char ***names, int *nf); +void FreeDir(DirList *d, int n); diff --git a/gnu/lib/libodialog/fselect.c b/gnu/lib/libodialog/fselect.c new file mode 100644 index 0000000..6669edc --- /dev/null +++ b/gnu/lib/libodialog/fselect.c @@ -0,0 +1,402 @@ +/* + * program: fselect.c + * author: Marc van Kempen (wmbfmk@urc.tue.nl) + * Desc: File selection routine + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> +#include <dialog.h> +#include "ui_objects.h" +#include "dir.h" +#include "dialog.priv.h" + +/* + * Local prototypes + */ + +char *dialog_dfselect(char *dir, char *fmask, int is_fselect); + +/* + * Functions + */ + +void +get_directories(DirList *d, int n, char ***names, int *nd) +/* + * Desc: return the directorienames in <dir> as an array in + * <names>, the # of entries in <nd>, memory allocated + * to *names should be freed when done with it. + */ +{ + int i; + + /* count the directories, which are in front */ + *nd = 0; + while ((*nd < n) && (S_ISDIR(d[*nd].filestatus.st_mode))) (*nd)++; + *names = (char **) malloc( *nd * sizeof(char *) ); + for (i=0; i<*nd; i++) { + (*names)[i] = (char *) malloc( strlen(d[i].filename) + 1); + strcpy((*names)[i], d[i].filename); + } + + return; +} /* get_directories() */ + +void +get_filenames(DirList *d, int n, char ***names, int *nf) +/* + * Desc: return the filenames in <dir> as an arry in + * <names>, the # of entries in <nf>, memory allocated + * to *names should be freed when done. + */ +{ + int nd, i; + + /* the # of regular files is the total # of files - # of directories */ + /* count the # of directories */ + nd = 0; + while ((nd < n) && (S_ISDIR(d[nd].filestatus.st_mode))) nd++; + + *names = (char **) malloc( (n-nd) * sizeof(char *) ); + *nf = n - nd; + for (i=0; i<*nf; i++) { + (*names)[i] = (char *) malloc( strlen(d[i+nd].filename) + 1); + strcpy((*names)[i], d[i+nd].filename); + } + + return; +} /* get_filenames() */ + +void +FreeNames(char **names, int n) +/* + * Desc: free the space occupied by names + */ +{ + int i; + + /* free the space occupied by names */ + for (i=0; i<n; i++) { + free(names[i]); + } + free(names); + + return; +} /* FreeNames() */ + +int +dialog_dselect_old(void) +/* + * Desc: starting from the current directory, + * choose a new current directory + */ +{ + DirList *d = NULL; + char **names, old_dir[MAXPATHLEN]; + WINDOW *ds_win; + ButtonObj *okbut, *cancelbut; + ListObj *dirs_obj; + StringObj *dir_obj; + char o_dir[MAXPATHLEN]; + struct ComposeObj *obj = NULL; + int n, nd, okbutton, cancelbutton, + quit, cancel, ret; + + ds_win = newwin(LINES-8, COLS-30, 4, 15); + if (ds_win == NULL) { + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", + LINES-8, COLS-30, 4, 15); + exit(1); + } + draw_box(ds_win, 0, 0, LINES-8, COLS-30, dialog_attr, border_attr); + wattrset(ds_win, dialog_attr); + mvwaddstr(ds_win, 0, (COLS-30)/2 - 9, " Directory Select "); + draw_shadow(stdscr, 4, 15, LINES-8, COLS-30); + display_helpline(ds_win, LINES-9, COLS-30); + + /* the Directory string input field */ + getcwd(o_dir, MAXPATHLEN); + dir_obj = NewStringObj(ds_win, "Directory:", o_dir, 1, 2, COLS-34, MAXPATHLEN-1); + AddObj(&obj, STRINGOBJ, (void *) dir_obj); + + /* the list of directories */ + get_dir(".", "*", &d, &n); + get_directories(d, n, &names, &nd); + dirs_obj = NewListObj(ds_win, "Directories:", names, o_dir, 5, 2, + LINES-15, COLS-48, nd); + AddObj(&obj, LISTOBJ, (void *) dirs_obj); + + /* the Ok-button */ + okbutton = FALSE; + okbut = NewButtonObj(ds_win, "Continue", &okbutton, 7, COLS-45); + AddObj(&obj, BUTTONOBJ, (void *) okbut); + + /* the Cancel-button */ + cancelbutton = FALSE; + cancelbut = NewButtonObj(ds_win, "Return", &cancelbutton, 11, COLS-44); + AddObj(&obj, BUTTONOBJ, (void *) cancelbut); + + quit = FALSE; + cancel = FALSE; + strcpy(old_dir, o_dir); + while (!quit) { + ret = PollObj(&obj); + switch(ret) { + case SEL_BUTTON: + if (okbutton) { + quit = TRUE; + } + if (cancelbutton) { + quit = TRUE; + cancel = TRUE; + } + break; + case SEL_CR: + if (strcmp(old_dir, o_dir)) { + /* the directory was changed, cd into it */ + if (chdir(o_dir)) { + dialog_notify("Could not change into directory"); + strcpy(o_dir, old_dir); + } else { + getcwd(o_dir, MAXPATHLEN); + strcpy(old_dir, o_dir); + } + RefreshStringObj(dir_obj); + } + get_dir(".", "*", &d, &n); + FreeNames(names, nd); + get_directories(d, n, &names, &nd); + UpdateListObj(dirs_obj, names, nd); + if (((obj->prev)->obj == (void *) dirs_obj)) { + obj=obj->prev; + } + break; + case SEL_ESC: + quit = TRUE; + cancel = TRUE; + break; + case KEY_F(1): + display_helpfile(); + break; + } + } + + FreeNames(names, nd); + DelObj(obj); + delwin(ds_win); + + return(cancel); + +} /* dialog_dselect() */ + +int +dialog_dselect(char *dir, char *fmask) +/* + * Desc: Choose a directory + */ +{ + if (dialog_dfselect(dir, fmask, FALSE)) { + return(FALSE); /* esc or cancel was pressed */ + } else { + return(TRUE); /* directory was selected */ + } +} /* dialog_dselect() */ + +char * +dialog_fselect(char *dir, char *fmask) +/* + * Desc: Choose a file from a directory + */ +{ + return(dialog_dfselect(dir, fmask, TRUE)); +} /* dialog_fselect() */ + +char * +dialog_dfselect(char *dir, char *fmask, int is_fselect) +/* + * Desc: choose a file from the directory <dir>, which + * initially display files with the mask <filemask> + * pre: <dir> is the initial directory + * only files corresponding to the mask <fmask> are displayed + * post: returns NULL if no file was selected + * else returns pointer to filename, space is allocated, should + * be freed after use. + */ +{ + DirList *d = NULL; + char msg[512]; + char **fnames, **dnames, *ret_name; + WINDOW *fs_win; + int n, nd, nf, ret; + StringObj *fm_obj, *dir_obj, *sel_obj; + char o_fm[255], o_dir[MAXPATHLEN], o_sel[MAXPATHLEN]; + char old_fmask[255], old_dir[MAXPATHLEN]; + ListObj *dirs_obj, *files_obj; + struct ComposeObj *obj = NULL, *o; + int quit, cancel; + ButtonObj *okbut_obj, *canbut_obj; + int ok_button, cancel_button; + + if (chdir(dir)) { + sprintf(msg, "Could not move into specified directory: %s", dir); + dialog_notify(msg); + return(NULL); + } + getcwd(o_dir, MAXPATHLEN); + + /* setup the fileselect-window and initialize its components */ + fs_win = newwin(LINES-2, COLS-20, 1, 10); + if (fs_win == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", + LINES-2, COLS-20, 2, 10); + exit(1); + } + draw_box(fs_win, 0, 0, LINES-2, COLS-20, dialog_attr, border_attr); + wattrset(fs_win, dialog_attr); + if (is_fselect) { + mvwaddstr(fs_win, 0, (COLS-20)/2 - 7, " File Select "); + } else { + mvwaddstr(fs_win, 0, (COLS-20)/2 - 9, " Directory Select "); + } + draw_shadow(stdscr, 1, 10, LINES-2, COLS-20); + display_helpline(fs_win, LINES-3, COLS-20); + + /* Filemask entry */ + strcpy(o_fm, fmask); + fm_obj = NewStringObj(fs_win, "Filemask:", o_fm, 1, 2, 19, 255); + AddObj(&obj, STRINGOBJ, (void *) fm_obj); + + /* Directory entry */ + dir_obj = NewStringObj(fs_win, "Directory:", o_dir, 1, 22, COLS-44, 255); + AddObj(&obj, STRINGOBJ, (void *) dir_obj); + + /* Directory list */ + get_dir(".", fmask, &d, &n); /* read the entire directory */ + get_directories(d, n, &dnames, &nd); /* extract the dir-entries */ + if (is_fselect) { + dirs_obj = NewListObj(fs_win, "Directories:", dnames, o_dir, 5, 2, + LINES-16, (COLS-20)/2-2, nd); + } else { + dirs_obj = NewListObj(fs_win, "Directories:", dnames, o_dir, 5, 2, + LINES-12, (COLS-20)/2-2, nd); + } + AddObj(&obj, LISTOBJ, (void *) dirs_obj); + + /* Filenames list */ + get_filenames(d, n, &fnames, &nf); /* extract the filenames */ + if (is_fselect) { + files_obj = NewListObj(fs_win, "Files:", fnames, o_sel, 5, (COLS-20)/2+1, + LINES-16, (COLS-20)/2-3, nf); + } else { + files_obj = NewListObj(fs_win, "Files:", fnames, o_sel, 5, (COLS-20)/2+1, + LINES-12, (COLS-20)/2-3, nf); + } + AddObj(&obj, LISTOBJ, (void *) files_obj); + + if (is_fselect) { + /* Selection entry */ + o_sel[0] = '\0'; + sel_obj = NewStringObj(fs_win, "Selection:", o_sel, LINES-10, 2, COLS-24, 255); + AddObj(&obj, STRINGOBJ, (void *) sel_obj); + } + + /* Ok button */ + ok_button = FALSE; + okbut_obj = NewButtonObj(fs_win, "Ok", &ok_button, LINES-6, 20); + AddObj(&obj, BUTTONOBJ, (void *) okbut_obj); + + /* Cancel button */ + cancel_button = FALSE; + canbut_obj = NewButtonObj(fs_win, "Cancel", &cancel_button, LINES-6, 30); + AddObj(&obj, BUTTONOBJ, (void *) canbut_obj); + + /* Make sure all objects on the window are drawn */ + wrefresh(fs_win); + keypad(fs_win, TRUE); + + /* Start the reading */ + o = obj; + strcpy(old_fmask, o_fm); + strcpy(old_dir, o_dir); + quit = FALSE; + cancel = FALSE; + while (!quit) { + ret = PollObj(&o); + switch(ret) { + case SEL_CR: + if (strcmp(old_fmask, o_fm) || strcmp(old_dir, o_dir)) { + /* reread directory and update the listobjects */ + if (strcmp(old_dir, o_dir)) { /* dir entry was changed */ + if (chdir(o_dir)) { + dialog_notify("Could not change into directory"); + strcpy(o_dir, old_dir); + } else { + getcwd(o_dir, MAXPATHLEN); + strcpy(old_dir, o_dir); + } + RefreshStringObj(dir_obj); + } else { /* fmask entry was changed */ + strcpy(old_fmask, o_fm); + } + get_dir(".", o_fm, &d, &n); + FreeNames(dnames, nd); + get_directories(d, n, &dnames, &nd); + UpdateListObj(dirs_obj, dnames, nd); + FreeNames(fnames, nf); + get_filenames(d, n, &fnames, &nf); + UpdateListObj(files_obj, fnames, nf); + if (((o->prev)->obj == (void *) dirs_obj)) { + o=o->prev; + } + } + break; + case SEL_BUTTON: + /* check which button was pressed */ + if (ok_button) { + quit = TRUE; + } + if (cancel_button) { + quit = TRUE; + cancel = TRUE; + } + break; + case SEL_ESC: + quit = TRUE; + cancel = TRUE; + break; + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + } + DelObj(obj); + FreeNames(dnames, nd); + FreeNames(fnames, nf); + delwin(fs_win); + + if (cancel || (strlen(o_sel) == 0)) { + return(NULL); + } else { + ret_name = (char *) malloc( strlen(o_sel) + 1 ); + strcpy(ret_name, o_sel); + return(ret_name); + } +} /* dialog_fselect() */ + diff --git a/gnu/lib/libodialog/gauge.c b/gnu/lib/libodialog/gauge.c new file mode 100644 index 0000000..d8138f6 --- /dev/null +++ b/gnu/lib/libodialog/gauge.c @@ -0,0 +1,79 @@ +/* + * gauge.c + * + * progress indicator for libdialog + * + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <string.h> + +#include "dialog.h" + +void +dialog_gauge(char *title, char *prompt, int y, int x, + int height, int width, int perc) +/* + * Desc: display a progress bar, progress indicated by <perc> + */ +{ + WINDOW *gw; + int glen, i; + char percs[5]; + + gw = newwin(height, width, y, x); + if (!gw) { + fprintf(stderr, "dialog_gauge: Error creating window (%d, %d, %d, %d)", + height, width, y, x); + exit(-1); + } + + draw_box(gw, 0, 0, height, width, dialog_attr, border_attr); + draw_shadow(stdscr, y, x, height, width); + + wattrset(gw, title_attr); + if (title) { + wmove(gw, 0, (width - strlen(title))/2 - 1); + waddstr(gw, "[ "); + waddstr(gw, title); + waddstr(gw, " ]"); + } + wattrset(gw, dialog_attr); + if (prompt) { + wmove(gw, 1, (width - strlen(prompt))/2 - 1); + waddstr(gw, prompt); + } + + draw_box(gw, 2, 2, 3, width-4, dialog_attr, border_attr); + glen = (int) ((float) perc/100 * (width-6)); + + wattrset(gw, dialog_attr); + sprintf(percs, "%3d%%", perc); + wmove(gw, 5, width/2 - 2); + waddstr(gw, percs); + + wattrset(gw, A_BOLD); + wmove(gw, 3, 3); + for (i=0; i<glen; i++) waddch(gw, ' '); + + wrefresh(gw); + delwin(gw); + + return; +} /* dialog_gauge() */ + diff --git a/gnu/lib/libodialog/help.c b/gnu/lib/libodialog/help.c new file mode 100644 index 0000000..de49c6a --- /dev/null +++ b/gnu/lib/libodialog/help.c @@ -0,0 +1,194 @@ +/*************************************************************** + * + * Program: help.c + * Author: Marc van Kempen + * Desc: get help + * + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + ***************************************************************/ + +#include <stdlib.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <dialog.h> + +static char _helpfilebuf[MAXPATHLEN]; +static char _helplinebuf[77]; /* limit the helpline to 76 characters */ +static char *_helpfile = NULL; +static char *_helpline = NULL; + +/****************************************************************** + * + * helpfile routines + * + ******************************************************************/ + +void +use_helpfile(char *hfile) +/* + * desc: set the helpfile to be opened on pressing F1 to <helpfile> + */ +{ + if (hfile != NULL) { + _helpfile = _helpfilebuf; + strcpy(_helpfile, hfile); + } else { + _helpfile = NULL; + } + + return; +} /* use_helpfile() */ + +void +display_helpfile(void) +/* + * desc: display the current helpfile in a window + */ +{ + WINDOW *w; + FILE *f; + struct stat sb; + char msg[80], *buf; + static int in_help = FALSE; + char *savehline = NULL; + + if (in_help) return; /* dont call help when you're in help */ + + if (_helpfile != NULL) { + if ((w = dupwin(newscr)) == NULL) { + dialog_notify("No memory to dup previous screen\n"); + return; + } + if ((f = fopen(_helpfile, "r")) == NULL) { + sprintf(msg, "Can't open helpfile : %s\n", _helpfile); + dialog_notify(msg); + return; + } + if (fstat(fileno(f), &sb)) { + sprintf(msg, "Can't stat helpfile : %s\n", _helpfile); + dialog_notify(msg); + return; + } + if ((buf = (char *) malloc( sb.st_size )) == NULL) { + sprintf(msg, "Could not malloc space for helpfile : %s\n", _helpfile); + dialog_notify(msg); + return; + } + if (fread(buf, 1, sb.st_size, f) != sb.st_size) { + sprintf(msg, "Could not read entire help file : %s", _helpfile); + dialog_notify(msg); + free(buf); + return; + } + buf[sb.st_size] = 0; + in_help = TRUE; + savehline = get_helpline(); + use_helpline("Use arrowkeys, PgUp, PgDn, Home and End to move through text"); + dialog_mesgbox("Online help", buf, LINES-4, COLS-4); + restore_helpline(savehline); + in_help = FALSE; + touchwin(w); + wrefresh(w); + delwin(w); + free(buf); + } else { + /* do nothing */ + } + + return; +} /* display_helpfile() */ + + +/****************************************************************** + * + * helpline routines + * + ******************************************************************/ + +void +use_helpline(char *hline) +/* + * desc: set the helpline to printed in dialogs + */ +{ + if (hline) { + _helpline = _helplinebuf; + if (strlen(hline) > 76) { + /* only display the first 76 characters in the helpline */ + strncpy(_helpline, hline, 76); + _helpline[76] = 0; + } else { + strcpy(_helpline, hline); + } + } else { + _helpline = NULL; + } + + return; +} /* use_helpline() */ + +void +display_helpline(WINDOW *w, int y, int width) +/* + * desc: display the helpline at the given coordinates <y, x> in the window <w> + */ +{ + if (_helpline != NULL) { + if (strlen(_helpline) > width - 6) { + _helpline[width - 6] = 0; + } + wmove(w, y, (int) (width - strlen(_helpline)- 4) / 2); + wattrset(w, title_attr); + waddstr(w, "[ "); + waddstr(w, _helpline); + waddstr(w, " ]"); + } else { + /* do nothing */ + } + + return; +} + +char * +get_helpline(void) +/* + * desc: allocate new space, copy the helpline to it and return a pointer to it + */ +{ + char *hlp; + + if (_helpline) { + hlp = (char *) malloc( strlen(_helpline) + 1 ); + strcpy(hlp, _helpline); + } else { + hlp = NULL; + } + + return(hlp); +} /* get_helpline() */ + +void +restore_helpline(char *helpline) +/* + * Desc: set the helpline to <helpline> and free the space allocated to it + */ +{ + use_helpline(helpline); + free(helpline); + + return; +} /* restore_helpline() */ diff --git a/gnu/lib/libodialog/inputbox.c b/gnu/lib/libodialog/inputbox.c new file mode 100644 index 0000000..1f61ed5 --- /dev/null +++ b/gnu/lib/libodialog/inputbox.c @@ -0,0 +1,190 @@ +/* + * inputbox.c -- implements the input box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <dialog.h> +#include "dialog.priv.h" + + +/* + * Display a dialog box for inputing a string + */ +int dialog_inputbox(unsigned char *title, unsigned char *prompt, int height, int width, unsigned char *result) +{ + int i, j, x, y, box_y, box_x, box_width, first, + key = 0, button = -1; + unsigned char instr[MAX_LEN+1]; + WINDOW *dialog; + + if (height < 0) + height = strheight(prompt)+2+4; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j) + 4; + } + width = MAX(width,24); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width)/2; + y = DialogY ? DialogY : (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE); + + /* Draw the input field box */ + box_width = width-6; + getyx(dialog, y, x); + box_y = y + 2; + box_x = (width - box_width)/2; + draw_box(dialog, y+1, box_x-1, 3, box_width+2, border_attr, dialog_attr); + + display_helpline(dialog, height-1, width); + + x = width/2-11; + y = height-2; + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + + first = 1; + strcpy(instr, result); + wattrset(dialog, dialog_attr); + + while (key != ESC) { + + if (button == -1) { /* Input box selected */ + key = line_edit(dialog, box_y, box_x, -1, box_width, inputbox_attr, first, instr, DialogInputAttrs); + first = 0; + } + else + key = wgetch(dialog); + + switch (key) { + case 'O': + case 'o': + delwin(dialog); + strcpy(result, instr); + return 0; + case 'C': + case 'c': + delwin(dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + case KEY_BTAB: + switch (button) { + case -1: + button = 1; /* Indicates "Cancel" button is selected */ + print_button(dialog, " OK ", y, x, FALSE); + print_button(dialog, "Cancel", y, x+14, TRUE); + wrefresh(dialog); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + wrefresh(dialog); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + wrefresh(dialog); + break; + case 0: + button = 1; /* Indicates "Cancel" button is selected */ + print_button(dialog, " OK ", y, x, FALSE); + print_button(dialog, "Cancel", y, x+14, TRUE); + wrefresh(dialog); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + break; + } + break; + case ' ': + case '\n': + case '\r': + delwin(dialog); + if (button < 1) + strcpy(result, instr); + return (button == -1 ? 0 : button); + case ESC: + break; + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + } + + delwin(dialog); + return -1; /* ESC pressed */ +} +/* End of dialog_inputbox() */ diff --git a/gnu/lib/libodialog/kernel.c b/gnu/lib/libodialog/kernel.c new file mode 100644 index 0000000..9ed5eac --- /dev/null +++ b/gnu/lib/libodialog/kernel.c @@ -0,0 +1,536 @@ +/* + * dialog - Display simple dialog boxes from shell scripts + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * HISTORY: + * + * 17/12/93 - Version 0.1 released. + * + * 19/12/93 - menu will now scroll if there are more items than can fit + * on the screen. + * - added 'checklist', a dialog box with a list of options that + * can be turned on or off. A list of options that are on is + * returned on exit. + * + * 20/12/93 - Version 0.15 released. + * + * 29/12/93 - Incorporated patch from Patrick J. Volkerding + * (volkerdi@mhd1.moorhead.msus.edu) that made these changes: + * - increased MAX_LEN to 2048 + * - added 'infobox', equivalent to a message box without pausing + * - added option '--clear' that will clear the screen + * - Explicit line breaking when printing prompt text can be + * invoked by real newline '\n' besides the string "\n" + * - an optional parameter '--title <string>' can be used to + * specify a title string for the dialog box + * + * 03/01/94 - added 'textbox', a dialog box for displaying text from a file. + * - Version 0.2 released. + * + * 04/01/94 - some fixes and improvements for 'textbox': + * - fixed a bug that will cause a segmentation violation when a + * line is longer than MAX_LEN characters. Lines will now be + * truncated if they are longer than MAX_LEN characters. + * - removed wrefresh() from print_line(). This will increase + * efficiency of print_page() which calls print_line(). + * - display current position in the form of percentage into file. + * - Version 0.21 released. + * + * 05/01/94 - some changes for faster screen update. + * + * 07/01/94 - much more flexible color settings. Can use all 16 colors + * (8 normal, 8 highlight) of the Linux console. + * + * 08/01/94 - added run-time configuration using configuration file. + * + * 09/01/94 - some minor bug fixes and cleanups for menubox, checklist and + * textbox. + * + * 11/01/94 - added a man page. + * + * 13/01/94 - some changes for easier porting to other Unix systems (tested + * on Ultrix, SunOS and HPUX) + * - Version 0.3 released. + * + * 08/06/94 - Patches by Stuart Herbert - S.Herbert@shef.ac.uk + * Fixed attr_clear and the textbox stuff to work with ncurses 1.8.5 + * Fixed the wordwrap routine - it'll actually wrap properly now + * Added a more 3D look to everything - having your own rc file could + * prove 'interesting' to say the least :-) + * Added radiolist option + * - Version 0.4 released. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define __DIALOG_MAIN__ + +#include <dialog.h> +#include <err.h> +#include "dialog.priv.h" +#ifdef HAVE_NCURSES +#include "colors.h" +#endif + +/* These are two "secret" globals that can be fiddled to make a dialog + * come up someplace other than a "centered" calculation for X,Y + */ +int DialogX, DialogY; + +/* This "secret" global allows you to change the behavior of an input field */ +int DialogInputAttrs; + +/* + * Do some initialization for dialog + */ +void init_dialog(void) +{ + + if (issetugid()) { + errx(1, "libdialog is unsafe to use in setugid applications"); + } + +#if defined(LOCALE) + (void) setlocale(LC_ALL, ""); +#endif + +#ifdef HAVE_NCURSES + if (parse_rc() == -1) /* Read the configuration file */ + exit(-1); +#endif + + if (initscr() == NULL) { /* Init curses */ + fprintf(stderr, "\nCurses initialization error.\n"); + exit(-1); + } + keypad(stdscr, TRUE); + cbreak(); + noecho(); + +#ifdef HAVE_NCURSES + if (use_colors || use_shadow) /* Set up colors */ + color_setup(); +#endif + + /* Set screen to screen attribute */ + dialog_clear_norefresh(); + DialogX = DialogY = 0; +} +/* End of init_dialog() */ + + +#ifdef HAVE_NCURSES +/* + * Setup for color display + */ +void color_setup(void) +{ + int i; + + if (has_colors()) { /* Terminal supports color? */ + start_color(); + + /* Initialize color pairs */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + init_pair(i+1, color_table[i][0], color_table[i][1]); + + /* Setup color attributes */ + for (i = 0; i < ATTRIBUTE_COUNT; i++) + attributes[i] = C_ATTR(color_table[i][2], i+1); + } +} +/* End of color_setup() */ +#endif + + +/* + * Set window to attribute 'attr' + */ +void attr_clear(WINDOW *win, int height, int width, chtype attr) +{ + int i, j; + + wattrset(win, attr); /* Set window to attribute 'attr' */ + for (i = 0; i < height; i++) { + wmove(win, i, 0); + for (j = 0; j < width; j++) + waddch(win, ' '); + } +} +/* End of attr_clear() */ + + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Note that the + * string may contain "\n" to represent a newline character or the real + * newline '\n', but in that case, auto wrap around will be disabled. + */ +void print_autowrap(WINDOW *win, unsigned char *prompt, int height, int width, int maxwidth, int y, int x, int center, int rawmode) +{ + int cur_x, cur_y, i; + unsigned char tempstr[MAX_LEN+1], *word, *tempptr, *tempptr1; + chtype ostuff[132], attrs = 0, init_bottom = 0; + + wsetscrreg(win, y, height); + getyx(win, cur_y, cur_x); + + strncpy(tempstr, prompt, MAX_LEN); + tempstr[MAX_LEN] = '\0'; + if ((!rawmode && strstr(tempstr, "\\n") != NULL) || + (strchr(tempstr, '\n') != NULL)) { /* Prompt contains "\n" or '\n' */ + word = tempstr; + while (1) { + tempptr = rawmode ? NULL : strstr(word, "\\n"); + tempptr1 = strchr(word, '\n'); + if (tempptr == NULL && tempptr1 == NULL) + break; + else if (tempptr == NULL) { /* No more "\n" */ + tempptr = tempptr1; + tempptr[0] = '\0'; + } + else if (tempptr1 == NULL) { /* No more '\n' */ + tempptr[0] = '\0'; + tempptr++; + } + else { /* Prompt contains both "\n" and '\n' */ + if (strlen(tempptr)-2 < strlen(tempptr1)-1) { + tempptr = tempptr1; + tempptr[0] = '\0'; + } + else { + tempptr[0] = '\0'; + tempptr++; + } + } + + waddstr(win, word); + word = tempptr + 1; + if (++cur_y > height) { + cur_y--; + if (!init_bottom) { + for (i = 0; i < x; i++) + ostuff[i] = mvwinch(win, cur_y, i); + for (i = width; i < maxwidth; i++) + ostuff[i] = mvwinch(win, cur_y, i); + attrs = getattrs(win); + init_bottom = 1; + } + scrollok(win, TRUE); + scroll(win); + scrollok(win, FALSE); + wmove(win, cur_y, 0); + for (i = 0; i < x; i++) { + wattrset(win, ostuff[i]&A_ATTRIBUTES); + waddch(win, ostuff[i]); + } + wattrset(win, attrs); + for ( ; i < width; i++) + waddch(win, ' '); + for ( ; i < maxwidth; i++) { + wattrset(win, ostuff[i]&A_ATTRIBUTES); + waddch(win, ostuff[i]); + } + wattrset(win, attrs); + wrefresh(win); + } + wmove(win, cur_y, cur_x = x); + } + waddstr(win, word); + } + else if (center && strlen(tempstr) <= width-x*2) { /* If prompt is short */ + wmove(win, cur_y, (width - strlen(tempstr)) / 2); + waddstr(win, tempstr); + } + else if (!center && strlen(tempstr) <= width-cur_x) { /* If prompt is short */ + waddstr(win, tempstr); + } + else { + char *p = tempstr; + + /* Print prompt word by word, wrap around if necessary */ + while ((word = strsep(&p, "\t\n ")) != NULL) { + int loop; + unsigned char sc; + + if (*word == '\0') + continue; + do { + loop = 0; + if (cur_x+strlen(word) >= width+1) { /* wrap around to next line */ + if (x+strlen(word) >= width+1) { + sc = word[width-cur_x-1]; + word[width-cur_x-1] = '\0'; + wmove(win, cur_y, cur_x); + waddstr(win, word); + word[width-cur_x-1] = sc; + word += width-cur_x-1; + getyx(win, cur_y, cur_x); + loop = 1; + } + cur_y++; + cur_x = x; + if (cur_y > height) { + cur_y--; + if (!init_bottom) { + for (i = 0; i < x; i++) + ostuff[i] = mvwinch(win, cur_y, i); + for (i = width; i < maxwidth; i++) + ostuff[i] = mvwinch(win, cur_y, i); + attrs = getattrs(win); + init_bottom = 1; + } + scrollok(win, TRUE); + scroll(win); + scrollok(win, FALSE); + wmove(win, cur_y, 0); + for (i = 0; i < x; i++) { + wattrset(win, ostuff[i]&A_ATTRIBUTES); + waddch(win, ostuff[i]); + } + wattrset(win, attrs); + for ( ; i < width; i++) + waddch(win, ' '); + for ( ; i < maxwidth; i++) { + wattrset(win, ostuff[i]&A_ATTRIBUTES); + waddch(win, ostuff[i]); + } + wattrset(win, attrs); + wrefresh(win); + } + } + } + while(loop); + wmove(win, cur_y, cur_x); + waddstr(win, word); + getyx(win, cur_y, cur_x); + cur_x++; + } + } +} +/* End of print_autowrap() */ + + +/* + * Print a button + */ +void print_button(WINDOW *win, unsigned char *label, int y, int x, int selected) +{ + int i, temp; + + wmove(win, y, x); + wattrset(win, selected ? button_active_attr : button_inactive_attr); + waddstr(win, selected ? "[" : " "); + temp = strspn(label, " "); + label += temp; + for (i = 0; i < temp; i++) + waddch(win, ' '); + wattrset(win, selected ? button_key_active_attr : button_key_inactive_attr); + waddch(win, label[0]); + wattrset(win, selected ? button_active_attr : button_inactive_attr); + waddstr(win, label+1); + waddstr(win, selected ? "]" : " "); + wmove(win, y, x+temp+1); +} +/* End of print_button() */ + + +/* + * Draw a rectangular box with line drawing characters + */ +void draw_box(WINDOW *win, int y, int x, int height, int width, chtype box, chtype border) +{ + int i, j; + + wattrset(win, 0); + for (i = 0; i < height; i++) { + wmove(win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch(win, border | ACS_ULCORNER); + else if (i == height-1 && !j) + waddch(win, border | ACS_LLCORNER); + else if (!i && j == width-1) + waddch(win, box | ACS_URCORNER); + else if (i == height-1 && j == width-1) + waddch(win, box | ACS_LRCORNER); + else if (!i) + waddch(win, border | ACS_HLINE); + else if (i == height-1) + waddch(win, box | ACS_HLINE); + else if (!j) + waddch(win, border | ACS_VLINE); + else if (j == width-1) + waddch(win, box | ACS_VLINE); + else + waddch(win, box | ' '); + } +} +/* End of draw_box() */ + + +#ifdef HAVE_NCURSES +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void draw_shadow(WINDOW *win, int y, int x, int height, int width) +{ + int i,sx,sy; + chtype attrs; + + if (has_colors()) { /* Whether terminal supports color? */ + getbegyx(win,sy,sx); + attrs = getattrs(win); + if (y+height < getmaxy(win)) { + /* small touch */ + wattrset(win, A_INVIS); + wmove(win, y + height, x + 2); + for (i = 0; i < width; i++) + if (i+x+2 < getmaxx(win)) + waddch(win, ' '); + /* end touch */ + wattrset(win, shadow_attr); + wmove(win, y + height, x + 2); + for (i = 0; i < width; i++) + if (i+x+2 < getmaxx(win)) + waddch(win, mvwinch(newscr, sy+y+height, sx+x+2+i) & A_CHARTEXT); + } + if (x+width < getmaxx(win)) { + for (i = y + 1; i < y + height + 1; i++) { + if (i < getmaxy(win)) { + /* small touch */ + wattrset(win, A_INVIS); + wmove(win, i, x + width); + waddch(win, ' '); + if (x+width+1 < getmaxx(win)) + waddch(win, ' '); + /* end touch */ + wattrset(win, shadow_attr); + wmove(win, i, x + width); + waddch(win, mvwinch(newscr, sy+i, sx+x+width) & A_CHARTEXT); + if (x+width+1 < getmaxx(win)) + waddch(win, mvwinch(newscr, sy+i, sx+x+width+1) & A_CHARTEXT); + } + } + } + wattrset(win, attrs); + wnoutrefresh(win); + } +} +/* End of draw_shadow() */ +#endif + +void dialog_clear_norefresh(void) +{ + attr_clear(stdscr, LINES, COLS, screen_attr); + touchwin(stdscr); + wnoutrefresh(stdscr); +} + +void dialog_clear(void) +{ + dialog_clear_norefresh(); + doupdate(); +} + +void dialog_update(void) +{ + refresh(); +} + +void end_dialog(void) +{ + endwin(); +} + +int strwidth(const char *p) +{ + int i = 0, len, incr; + const char *start, *s, *s1, *s2; + + for (start = s = p; ; start = (s += incr)) { + s1 = strchr(s, '\n'); + s2 = strstr(s, "\\n"); + if (s2 == NULL) + s = s1; + else if (s1 == NULL) + s = s2; + else + s = MIN(s1, s2); + if (s == NULL) + break; + incr = 1 + (s == s2); + len = s - start; + if (len > i) + i = len; + } + len = strlen(start); + if (len > i) + i = len; + return i; +} + +int strheight(const char *p) +{ + int i = 1, incr; + const char *s, *s1, *s2; + + for (s = p; ; s += incr) { + s1 = strchr(s, '\n'); + s2 = strstr(s, "\\n"); + if (s2 == NULL) + s = s1; + else if (s1 == NULL) + s = s2; + else + s = MIN(s1, s2); + if (s == NULL) + break; + incr = 1 + (s == s2); + i++; + } + return i; +} + +void print_arrows(WINDOW *dialog, int scroll, int menu_height, int item_no, + int box_x, int box_y, int tag_x, int cur_x, int cur_y) +{ + wmove(dialog, box_y, box_x + tag_x + 1); + wattrset(dialog, scroll ? uarrow_attr : menubox_attr); + waddch(dialog, scroll ? ACS_UARROW : ACS_HLINE); + wmove(dialog, box_y, box_x + tag_x + 2); + waddch(dialog, scroll ? '(' : ACS_HLINE); + wmove(dialog, box_y, box_x + tag_x + 3); + waddch(dialog, scroll ? '-' : ACS_HLINE); + wmove(dialog, box_y, box_x + tag_x + 4); + waddch(dialog, scroll ? ')' : ACS_HLINE); + wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 1); + wattrset(dialog, scroll+menu_height < item_no ? darrow_attr : menubox_border_attr); + waddch(dialog, scroll+menu_height < item_no ? ACS_DARROW : ACS_HLINE); + wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 2); + waddch(dialog, scroll+menu_height < item_no ? '(' : ACS_HLINE); + wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 3); + waddch(dialog, scroll+menu_height < item_no ? '+' : ACS_HLINE); + wmove(dialog, box_y + menu_height + 1, box_x + tag_x + 4); + waddch(dialog, scroll+menu_height < item_no ? ')' : ACS_HLINE); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ +} + diff --git a/gnu/lib/libodialog/lineedit.c b/gnu/lib/libodialog/lineedit.c new file mode 100644 index 0000000..7bfe0e0 --- /dev/null +++ b/gnu/lib/libodialog/lineedit.c @@ -0,0 +1,213 @@ +/* + * Changes Copyright (C) 1995 by Andrey A. Chernov, Moscow + * + * Original Copyright: + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <dialog.h> +#include "dialog.priv.h" + +static void redraw_field(WINDOW *dialog, int box_y, int box_x, int flen, int box_width, unsigned char instr[], int input_x, int scroll, chtype attr, chtype old_attr, int fexit, int attr_mask); + +/* + * Line editor + */ +int line_edit(WINDOW* dialog, int box_y, int box_x, int flen, int box_width, chtype attr, int first, unsigned char *result, int attr_mask) +{ + int i, key; + chtype old_attr; + static int input_x, scroll; + static unsigned char instr[MAX_LEN+1]; + unsigned char erase_char = erasechar(); + unsigned char kill_char = killchar(); +#ifdef notyet + unsignec char werase_char = cur_term->Ottyb.c_cc[VWERASE]; +#endif + + old_attr = getattrs(dialog); + keypad(dialog, TRUE); + + if (first) { + memset(instr, 0, sizeof(instr)); + strcpy(instr, result); + i = strlen(instr); +/* input_x = i % box_width;*/ + input_x = (i > box_width) ? box_width - 1 : i; +/* scroll = i - input_x;*/ + scroll = (i > box_width) ? i - box_width + 1: 0; + } + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + + for (;;) { + wattrset(dialog, attr); + wrefresh(dialog); + key = wgetch(dialog); + switch (key) { + case ctrl('q'): + goto ret; + break; + case KEY_F(1): + display_helpfile(); + break; + case TAB: + case KEY_BTAB: + case KEY_UP: + case KEY_DOWN: + case ESC: + case '\r': + case '\n': + for (i = strlen(instr) - 1; i >= scroll + input_x && instr[i] == ' '; i--) + instr[i] = '\0'; + if (key == '\r') + key = '\n'; + goto ret; + case '\025': + case '\030': + kill_it: + input_x = scroll = 0; + /* fall through */ + case '\013': + case KEY_EOL: + memset(instr + scroll + input_x, '\0', sizeof(instr) - scroll - input_x); + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + continue; + case '\001': + case KEY_HOME: + input_x = scroll = 0; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + continue; + case '\005': + case KEY_END: + for (i = strlen(instr) - 1; i >= scroll + input_x && instr[i] == ' '; i--) + instr[i] = '\0'; + i++; + input_x = i % box_width; + scroll = i - input_x; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + continue; + case '\002': + case KEY_LEFT: + if (input_x || scroll) { + if (!input_x) { + int oldscroll = scroll; + scroll = scroll < box_width-1 ? 0 : scroll-(box_width-1); + input_x = oldscroll - 1 - scroll; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + } else { + input_x--; + wmove(dialog, box_y, input_x + box_x); + } + } else + beep(); + continue; + case '\006': + case KEY_RIGHT: + if ( scroll+input_x < MAX_LEN + && (flen < 0 || scroll+input_x < flen) + ) { + if (!instr[scroll+input_x]) + instr[scroll+input_x] = ' '; + if (input_x == box_width-1) { + scroll++; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + } + else { + wmove(dialog, box_y, input_x + box_x); + waddch(dialog, instr[scroll+input_x]); + input_x++; + } + } else + beep(); /* Alarm user about overflow */ + continue; + case '\b': + case '\177': + case KEY_BACKSPACE: + erase_it: + if (input_x || scroll) { + i = strlen(instr); + memmove(instr+scroll+input_x-1, instr+scroll+input_x, i-(scroll+input_x)+1); + if (!input_x) { + int oldscroll = scroll; + scroll = scroll < box_width-1 ? 0 : scroll-(box_width-1); + input_x = oldscroll - 1 - scroll; + } else + input_x--; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + } else + beep(); + continue; + case '\004': + case KEY_DC: + for (i = strlen(instr) - 1; i >= scroll + input_x && instr[i] == ' '; i--) + instr[i] = '\0'; + i++; + if (i == 0) { + beep(); + continue; + } + memmove(instr+scroll+input_x, instr+scroll+input_x+1, i-(scroll+input_x)); + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + continue; + default: + if (CCEQ(key, erase_char)) + goto erase_it; + if (CCEQ(key, kill_char)) + goto kill_it; + if (key < 0x100 && isprint(key)) { + for (i = strlen(instr) - 1; i >= scroll + input_x && instr[i] == ' '; i--) + instr[i] = '\0'; + i++; + if (i < MAX_LEN && (flen < 0 || scroll+input_x < flen)) { + if (flen < 0 || i < flen) + memmove(instr+scroll+input_x+1, instr+scroll+input_x, i-(scroll+input_x)); + instr[scroll+input_x] = key; + if (input_x == box_width-1 && (flen < 0 || i < flen)) + scroll++; + else + input_x++; + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, FALSE, attr_mask); + } else + beep(); /* Alarm user about overflow */ + continue; + } + } + } +ret: + redraw_field(dialog, box_y, box_x, flen, box_width, instr, input_x, scroll, attr, old_attr, TRUE, attr_mask); + wrefresh(dialog); + strcpy(result, instr); + return key; +} + +static void +redraw_field(WINDOW *dialog, int box_y, int box_x, int flen, int box_width, unsigned char instr[], int input_x, int scroll, chtype attr, chtype old_attr, int fexit, int attr_mask) +{ + int i, fix_len; + + wattrset(dialog, fexit ? old_attr : attr); + wmove(dialog, box_y, box_x); + fix_len = flen >= 0 ? MIN(flen-scroll,box_width) : box_width; + for (i = 0; i < fix_len; i++) + waddch(dialog, instr[scroll+i] ? ((attr_mask & DITEM_NO_ECHO) ? '*' : instr[scroll+i]) : ' '); + wattrset(dialog, old_attr); + for ( ; i < box_width; i++) + waddch(dialog, instr[scroll+i] ? ((attr_mask & DITEM_NO_ECHO) ? '*' : instr[scroll+i]) : ' '); + wmove(dialog, box_y, input_x + box_x); +} diff --git a/gnu/lib/libodialog/menubox.c b/gnu/lib/libodialog/menubox.c new file mode 100644 index 0000000..a01acd5 --- /dev/null +++ b/gnu/lib/libodialog/menubox.c @@ -0,0 +1,469 @@ +/* + * menubox.c -- implements the menu box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * Substantial rennovation: 12/18/95, Jordan K. Hubbard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" +#include <err.h> +#include <ncurses.h> + +static void print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int choice, int selected, dialogMenuItem *me, int menu_width, int tag_x, int item_x); + +#define DREF(di, item) ((di) ? &((di)[(item)]) : NULL) + +/* + * Display a menu for choosing among a number of options + */ +int +dialog_menu(unsigned char *title, unsigned char *prompt, int height, int width, int menu_height, int cnt, void *it, unsigned char *result, int *ch, int *sc) +{ + int i, j, x, y, cur_x, cur_y, box_x, box_y, key = 0, button, choice, + l, k, scroll, max_choice, item_no, redraw_menu = FALSE; + char okButton, cancelButton; + int rval = 0, ok_space, cancel_space; + WINDOW *dialog, *menu; + unsigned char **items = NULL; + dialogMenuItem *ditems; + int menu_width, tag_x, item_x; + +draw: + choice = ch ? *ch : 0; + scroll = sc ? *sc : 0; + button = 0; + + /* If item_no is a positive integer, use old item specification format */ + if (cnt >= 0) { + items = it; + ditems = NULL; + item_no = cnt; + } + /* It's the new specification format - fake the rest of the code out */ + else { + item_no = abs(cnt); + ditems = it; + if (!items) + items = (unsigned char **)alloca((item_no * 2) * sizeof(unsigned char *)); + + /* Initializes status */ + for (i = 0; i < item_no; i++) { + items[i*2] = ditems[i].prompt; + items[i*2 + 1] = ditems[i].title; + } + } + max_choice = MIN(menu_height, item_no); + + tag_x = 0; + item_x = 0; + /* Find length of longest item in order to center menu */ + for (i = 0; i < item_no; i++) { + l = strlen(items[i * 2]); + for (j = 0; j < item_no; j++) { + k = strlen(items[j * 2 + 1]); + tag_x = MAX(tag_x, l + k + 2); + } + item_x = MAX(item_x, l); + } + if (height < 0) + height = strheight(prompt) + menu_height + 4 + 2; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i, j); + width = MAX(width, tag_x + 4) + 4; + } + width = MAX(width, 24); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width) / 2; + y = DialogY ? DialogY : (LINES - height) / 2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height, width, y, x); + return -1; + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height - 3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height - 2, 1); + for (i = 0; i < width - 2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title)) / 2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height - 1, width - 2, width, 1, 2, TRUE, FALSE); + + menu_width = width - 6; + getyx(dialog, cur_y, cur_x); + box_y = cur_y + 1; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1); + if (menu == NULL) { + delwin(dialog); + endwin(); + fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + return -1; + } + keypad(menu, TRUE); + + /* draw a box around the menu items */ + draw_box(dialog, box_y, box_x, menu_height+2, menu_width+2, menubox_border_attr, menubox_attr); + + tag_x = menu_width > tag_x + 1 ? (menu_width - tag_x) / 2 : 1; + item_x = menu_width > item_x + 4 ? tag_x + item_x + 2 : menu_width - 3; + + /* Print the menu */ + for (i = 0; i < max_choice; i++) + print_item(menu, items[(scroll + i) * 2], items[(scroll + i) * 2 + 1], i, i == choice, DREF(ditems, scroll + i), menu_width, tag_x, item_x); + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y); + + display_helpline(dialog, height - 1, width); + + x = width / 2 - 11; + y = height - 2; + + if (ditems && result) { + cancelButton = toupper(ditems[CANCEL_BUTTON].prompt[0]); + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : FALSE); + okButton = toupper(ditems[OK_BUTTON].prompt[0]); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : TRUE); + } + else { + cancelButton = 'C'; + print_button(dialog, "Cancel", y, x + 14, FALSE); + okButton = 'O'; + print_button(dialog, " OK ", y, x, TRUE); + } + + wrefresh(dialog); + while (key != ESC) { + key = wgetch(dialog); + + /* Shortcut to OK? */ + if (toupper(key) == okButton) { + if (ditems) { + if (result && ditems[OK_BUTTON].fire) { + int status; + WINDOW *save; + + save = dupwin(newscr); + status = ditems[OK_BUTTON].fire(&ditems[OK_BUTTON]); + if (status & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + } + else if (result) + strcpy(result, items[(scroll + choice) * 2]); + rval = 0; + key = ESC; /* Punt! */ + break; + } + + /* Shortcut to cancel? */ + if (toupper(key) == cancelButton) { + if (ditems && result && ditems[CANCEL_BUTTON].fire) { + int status; + WINDOW *save; + + save = dupwin(newscr); + status = ditems[CANCEL_BUTTON].fire(&ditems[CANCEL_BUTTON]); + if (status & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + rval = 1; + key = ESC; /* Run away! */ + break; + } + + /* Check if key pressed matches first character of any item tag in menu */ + for (i = 0; i < max_choice; i++) + if (key < 0x100 && key != ' ' && toupper(key) == toupper(items[(scroll + i) * 2][0])) + break; + + if (i < max_choice || (key >= '1' && key <= MIN('9', '0'+max_choice)) || KEY_IS_UP(key) || KEY_IS_DOWN(key)) { + if (key >= '1' && key <= MIN('9', '0'+max_choice)) + i = key - '1'; + else if (KEY_IS_UP(key)) { + if (!choice) { + if (scroll) { + /* Scroll menu down */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (menu_height > 1) { + /* De-highlight current first item before scrolling down */ + print_item(menu, items[scroll * 2], items[scroll * 2 + 1], 0, FALSE, DREF(ditems, scroll), menu_width, tag_x, item_x); + scrollok(menu, TRUE); + wscrl(menu, -1); + scrollok(menu, FALSE); + } + scroll--; + print_item(menu, items[scroll * 2], items[scroll * 2 + 1], 0, TRUE, DREF(ditems, scroll), menu_width, tag_x, item_x); + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y); + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + else + i = choice - 1; + } + else if (KEY_IS_DOWN(key)) { + if (choice == max_choice - 1) { + if (scroll + choice < item_no - 1) { + /* Scroll menu up */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (menu_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item(menu, items[(scroll + max_choice - 1) * 2], + items[(scroll + max_choice - 1) * 2 + 1], + max_choice-1, FALSE, DREF(ditems, scroll + max_choice - 1), menu_width, tag_x, item_x); + scrollok(menu, TRUE); + scroll(menu); + scrollok(menu, FALSE); + } + scroll++; + print_item(menu, items[(scroll + max_choice - 1) * 2], + items[(scroll + max_choice - 1) * 2 + 1], + max_choice - 1, TRUE, DREF(ditems, scroll + max_choice - 1), menu_width, tag_x, item_x); + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y); + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + else + i = choice + 1; + } + + if (i != choice) { + /* De-highlight current item */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + print_item(menu, items[(scroll + choice) * 2], items[(scroll + choice) * 2 + 1], choice, FALSE, DREF(ditems, scroll + choice), menu_width, tag_x, item_x); + + /* Highlight new item */ + choice = i; + print_item(menu, items[(scroll + choice) * 2], items[(scroll + choice) * 2 + 1], choice, TRUE, DREF(ditems, scroll + choice), menu_width, tag_x, item_x); + wnoutrefresh(menu); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_PPAGE: + if (scroll > height - 4) { /* can we go up? */ + scroll -= (height - 4); + } else { + scroll = 0; + } + redraw_menu = TRUE; + break; + + case KEY_NPAGE: + if (scroll + menu_height >= item_no-1 - menu_height) { /* can we go down a full page? */ + scroll = item_no - menu_height; + if (scroll < 0) + scroll = 0; + } else { + scroll += menu_height; + } + redraw_menu = TRUE; + break; + + case KEY_HOME: + scroll = 0; + choice = 0; + redraw_menu = TRUE; + break; + + case KEY_END: + scroll = item_no - menu_height; + if (scroll < 0) + scroll = 0; + choice = max_choice - 1; + redraw_menu = TRUE; + break; + + case KEY_BTAB: + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = !button; + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + ok_space = 1; + cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6; + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + ok_space = 3; + cancel_space = 15; + } + if (button) + wmove(dialog, y, x+cancel_space); + else + wmove(dialog, y, x+ok_space); + wrefresh(dialog); + break; + + case ' ': + case '\r': + case '\n': + if (!button) { + /* A fire routine can do just about anything to the screen, so be prepared + to accept some hints as to what to do in the aftermath. */ + if (ditems) { + if (ditems[scroll + choice].fire) { + int status; + WINDOW *save; + + save = dupwin(newscr); + status = ditems[scroll + choice].fire(&ditems[scroll + choice]); + if (status & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + if (status & DITEM_CONTINUE) + continue; + else if (status & DITEM_LEAVE_MENU) { + /* Allow a fire action to take us out of the menu */ + key = ESC; + break; + } + else if (status & DITEM_RECREATE) { + delwin(menu); + delwin(dialog); + dialog_clear(); + goto draw; + } + } + } + else if (result) + strcpy(result, items[(scroll+choice)*2]); + } + rval = button; + key = ESC; + break; + + case ESC: + rval = -1; + break; + + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + + /* save info about menu item position */ + if (ch) + *ch = choice; + if (sc) + *sc = scroll; + + if (redraw_menu) { + for (i = 0; i < max_choice; i++) { + print_item(menu, items[(scroll + i) * 2], items[(scroll + i) * 2 + 1], i, i == choice, DREF(ditems, scroll + i), menu_width, tag_x, item_x); + } + wnoutrefresh(menu); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, tag_x, cur_x, cur_y); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + redraw_menu = FALSE; + } + } + delwin(menu); + delwin(dialog); + return rval; +} + + +/* + * Print menu item + */ +static void +print_item(WINDOW *win, unsigned char *tag, unsigned char *item, int choice, int selected, dialogMenuItem *me, int menu_width, int tag_x, int item_x) +{ + int i; + + if (tag == NULL) + errx(1, "bad parameter to print_item()\n"); + + /* Clear 'residue' of last item */ + wattrset(win, menubox_attr); + wmove(win, choice, 0); + for (i = 0; i < menu_width; i++) + waddch(win, ' '); + wmove(win, choice, tag_x); + wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); + waddch(win, tag[0]); + wattrset(win, selected ? tag_selected_attr : tag_attr); + waddnstr(win, tag + 1, item_x - tag_x - 3); + wmove(win, choice, item_x); + wattrset(win, selected ? item_selected_attr : item_attr); + waddnstr(win, item, menu_width - item_x - 1); + /* If have a selection handler for this, call it */ + if (me && me->selected) { + wrefresh(win); + me->selected(me, selected); + } +} +/* End of print_item() */ diff --git a/gnu/lib/libodialog/msgbox.c b/gnu/lib/libodialog/msgbox.c new file mode 100644 index 0000000..41d78c8 --- /dev/null +++ b/gnu/lib/libodialog/msgbox.c @@ -0,0 +1,346 @@ +/* + * msgbox.c -- implements the message box and info box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" + + +/* local prototypes */ +static int getnlines(unsigned char *buf); +static void print_page(WINDOW *win, int height, int width, unsigned char *buf, int startline, int hscroll); +static void print_perc(WINDOW *win, int y, int x, float p); + + +/* + * Display a message box. Program will pause and display an "OK" button + * if the parameter 'pause' is non-zero. + */ +int dialog_msgbox(unsigned char *title, unsigned char *prompt, int height, int width, int pause) +{ + int i, j, x, y, key = 0; + WINDOW *dialog; + + if (height < 0) + height = strheight(prompt)+2+2*(!!pause); + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j)+4; + } + if (pause) + width = MAX(width,10); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width)/2; + y = DialogY ? DialogY : (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE); + + if (pause) { + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + display_helpline(dialog, height-1, width); + print_button(dialog, " OK ", height-2, width/2-6, TRUE); + wrefresh(dialog); + while (key != ESC && key != '\n' && key != ' ' && key != '\r') + key = wgetch(dialog); + if (key == '\r') + key = '\n'; + } + else { + key = '\n'; + wrefresh(dialog); + } + + delwin(dialog); + return (key == ESC ? -1 : 0); +} +/* End of dialog_msgbox() */ + +int +dialog_mesgbox(unsigned char *title, unsigned char *prompt, int height, int width) +/* + * Desc: basically the same as dialog_msgbox, but ... can use PGUP, PGDN and + * arrowkeys to move around the text and pause is always enabled + */ +{ + int i, j, x, y, key=0; + int theight, startline, hscroll, max_lines; + WINDOW *dialog; + + if (height < 0) + height = strheight(prompt)+2+2; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j)+4; + } + width = MAX(width,10); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = (COLS - width)/2; + y = (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + display_helpline(dialog, height-1, width); + print_button(dialog, " OK ", height-2, width/2-6, TRUE); + wattrset(dialog, dialog_attr); + + theight = height - 4; + startline = 0; + hscroll = 0; + max_lines = getnlines(prompt); + print_page(dialog, theight, width, prompt, startline, hscroll); + print_perc(dialog, height-3, width-9, (float) (startline+theight)/max_lines); + wmove(dialog, height-2, width/2-3); + wrefresh(dialog); + while ((key != ESC) && (key != '\n') && (key != '\r') && (key != ' ')) { + key = wgetch(dialog); + switch(key) { + case KEY_HOME: + startline=0; + hscroll=0; + break; + case KEY_END: + startline = max_lines - theight; + if (startline < 0) startline = 0; + break; + case '\020': /* ^P */ + case KEY_UP: + if (startline > 0) startline--; + break; + case '\016': /* ^N */ + case KEY_DOWN: + if (startline < max_lines - theight) startline++; + break; + case KEY_RIGHT: + hscroll+=5; + break; + case KEY_LEFT: + if (hscroll > 0) hscroll-=5; + if (hscroll < 0) hscroll =0; + break; + case KEY_PPAGE: + if (startline - height > 0) { + startline -= theight; + } else { + startline = 0; + } + break; + case KEY_NPAGE: + if (startline + theight < max_lines - theight) { + startline += theight; + } else { + startline = max_lines - theight; + if (startline < 0) startline = 0; + } + break; + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + print_page(dialog, theight, width, prompt, startline, hscroll); + print_perc(dialog, height-3, width-9, (float) (startline+theight)/max_lines); + wmove(dialog, height-2, width/2-3); + wrefresh(dialog); + } + + delwin(dialog); + return (key == ESC ? -1 : 0); + +} /* dialog_mesgbox() */ + +static void +print_perc(WINDOW *win, int y, int x, float p) +/* + * Desc: print p as a percentage at the coordinates (y,x) + */ +{ + char ps[10]; + + if (p>1.0) p=1.0; + sprintf(ps, "(%3d%%)", (int) (p*100)); + wmove(win, y, x); + waddstr(win, ps); + + return; +} /* print_perc() */ + +static int +getnlines(unsigned char *buf) +/* + * Desc: count the # of lines in <buf> + */ +{ + int i = 0; + + if (*buf) + i++; + while (*buf) { + if (*buf == '\n' || *buf == '\r') + i++; + buf++; + } + return(i); +} /* getlines() */ + + +unsigned char * +getline(unsigned char *buf, int n) +/* + * Desc: return a pointer to the n'th line in <buf> or NULL if its + * not there + */ +{ + int i; + + if (n<0) { + return(NULL); + } + + i=0; + while (*buf && i<n) { + if (*buf == '\n' || *buf == '\r') { + i++; + } + buf++; + } + if (i<n) { + return(NULL); + } else { + return(buf); + } +} /* getline() */ + +static void +print_page(WINDOW *win, int height, int width, unsigned char *buf, int startline, int hscroll) +/* + * Desc: Print a page of text in the current window, starting at line <startline> + * with a <horizontal> scroll of hscroll from buffer <buf> + */ +{ + int i, j; + unsigned char *b; + + b = getline(buf, startline); + for (i=0; i<height; i++) { + /* clear line */ + wmove(win, 1+i, 1); + for (j=0; j<width-2; j++) waddnstr(win, " ", 1); + wmove(win, 1+i, 1); + j = 0; + /* scroll to the right */ + while (*b && (*b != '\n') && (*b != '\r') && (j<hscroll)) { + b++; + j++; + } + /* print new line */ + j = 0; + while (*b && (*b != '\n') && (*b != '\r') && (j<width-2)) { + waddnstr(win, b, 1); + if (*b != '\t') { /* check for tabs */ + j++; + } else { + j = ((int) (j+1)/8 + 1) * 8 - 1; + } + b++; + } + while (*b && (*b != '\n') && (*b != '\r')) b++; + if (*b) b++; /* skip over '\n', if it exists */ + } +} /* print_page() */ + + + + diff --git a/gnu/lib/libodialog/notify.c b/gnu/lib/libodialog/notify.c new file mode 100644 index 0000000..7fc22eb --- /dev/null +++ b/gnu/lib/libodialog/notify.c @@ -0,0 +1,53 @@ +/* + * File: notify.c + * Author: Marc van Kempen + * Desc: display a notify box with a message + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include <stdio.h> +#include <stdlib.h> + +void +dialog_notify(char *msg) +/* + * Desc: display an error message + */ +{ + char *tmphlp; + WINDOW *w; + + w = dupwin(newscr); + if (w == NULL) { + endwin(); + fprintf(stderr, "\ndupwin(newscr) failed, malloc memory corrupted\n"); + exit(1); + } + tmphlp = get_helpline(); + use_helpline("Press enter or space"); + dialog_mesgbox("Message", msg, -1, -1); + restore_helpline(tmphlp); + touchwin(w); + wrefresh(w); + delwin(w); + + return; + +} /* dialog_notify() */ + diff --git a/gnu/lib/libodialog/prgbox.c b/gnu/lib/libodialog/prgbox.c new file mode 100644 index 0000000..bcafacf --- /dev/null +++ b/gnu/lib/libodialog/prgbox.c @@ -0,0 +1,152 @@ +/* + * prgbox.c -- implements the message box and info box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <dialog.h> +#include <errno.h> +#include <sys/wait.h> +#include "dialog.priv.h" + +/* + * Display a message box. Program will pause and display an "OK" button + * if the parameter 'pause' is non-zero. + */ +int dialog_prgbox(unsigned char *title, const unsigned char *line, int height, int width, int pause, int use_shell) +{ + int i, x, y, key = 0; + WINDOW *dialog; + FILE *f; + const unsigned char *name; + unsigned char *s, buf[MAX_LEN]; + int status; + + if (height < 0 || width < 0) { + endwin(); + fprintf(stderr, "\nAutosizing is impossible in dialog_prgbox().\n"); + exit(-1); + } + width = MAX(width,10); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width)/2; + y = DialogY ? DialogY : (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + + if (!use_shell) { + char cmdline[MAX_LEN]; + char *av[51], **ap = av, *val, *p; + + strcpy(cmdline, line); + p = cmdline; + while ((val = strsep(&p," \t")) != NULL) { + if (*val != '\0') + *ap++ = val; + } + *ap = NULL; + f = raw_popen(name = av[0], av, "r"); + } else + f = raw_popen(name = line, NULL, "r"); + + status = -1; + if (f == NULL) { + err: + sprintf(buf, "%s: %s\n", name, strerror(errno)); + prr: + print_autowrap(dialog, buf, height-(pause?3:1), width-2, width, 1, 2, FALSE, TRUE); + wrefresh(dialog); + } else { + while (fgets(buf, sizeof(buf), f) != NULL) { + i = strlen(buf); + if (buf[i-1] == '\n') + buf[i-1] = '\0'; + s = buf; + while ((s = strchr(s, '\t')) != NULL) + *s++ = ' '; + print_autowrap(dialog, buf, height-(pause?3:1), width-2, width, 1, 2, FALSE, TRUE); + print_autowrap(dialog, "\n", height-(pause?3:1), width-2, width, 1, 2, FALSE, FALSE); + wrefresh(dialog); + } + if ((status = raw_pclose(f)) == -1) + goto err; + if (WIFEXITED(status) && WEXITSTATUS(status) == 127) { + sprintf(buf, "%s: program not found\n", name); + goto prr; + } + } + + if (pause) { + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + display_helpline(dialog, height-1, width); + print_button(dialog, " OK ", height-2, width/2-6, TRUE); + wrefresh(dialog); + while (key != ESC && key != '\n' && key != ' ' && key != '\r') + key = wgetch(dialog); + if (key == '\r') + key = '\n'; + } + else { + key = '\n'; + wrefresh(dialog); + } + + delwin(dialog); + return (status); +} +/* End of dialog_msgbox() */ diff --git a/gnu/lib/libodialog/radiolist.c b/gnu/lib/libodialog/radiolist.c new file mode 100644 index 0000000..4b865a6 --- /dev/null +++ b/gnu/lib/libodialog/radiolist.c @@ -0,0 +1,628 @@ +/* + * radiolist.c -- implements the radiolist box + * + * AUTHOR: Stuart Herbert - S.Herbert@sheffield.ac.uk + * (from checklist.c by Savio Lam (lam836@cs.cuhk.hk)) + * + * Substantial rennovation: 12/18/95, Jordan K. Hubbard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" + + +static void print_item(WINDOW *win, char *tag, char *item, int status, int choice, int selected, dialogMenuItem *me); + +#define DREF(di, item) ((di) ? &((di)[(item)]) : NULL) + +static int list_width, check_x, item_x; + + +/* + * Display a dialog box with a list of options that can be turned on or off + */ +int +dialog_radiolist(unsigned char *title, unsigned char *prompt, int height, int width, int list_height, + int cnt, void *it, unsigned char *result) +{ + int i, j, x, y, cur_x, cur_y, old_x, old_y, box_x, box_y, key = 0, button, + choice, l, k, scroll, max_choice, *status, item_no = 0, was_on = 0; + int redraw_menu = FALSE, cursor_reset = FALSE; + int rval = 0, onlist = 1, ok_space, cancel_space; + char okButton, cancelButton; + WINDOW *dialog, *list; + unsigned char **items = NULL; + dialogMenuItem *ditems; + + /* Allocate space for storing item on/off status */ + if ((status = alloca(sizeof(int) * abs(cnt))) == NULL) { + endwin(); + fprintf(stderr, "\nCan't allocate memory in dialog_radiolist().\n"); + exit(-1); + } + +draw: + button = choice = scroll = 0; + /* Previous calling syntax, e.g. just a list of strings? */ + if (cnt >= 0) { + items = it; + ditems = NULL; + item_no = cnt; + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = !strcasecmp(items[i*3 + 2], "on"); + if (status[i]) { + if (was_on) + status[i] = FALSE; + else + was_on = 1; + } + } + } + /* It's the new specification format - fake the rest of the code out */ + else { + item_no = abs(cnt); + ditems = it; + if (!items) + items = (unsigned char **)alloca((item_no * 3) * sizeof(unsigned char *)); + /* Initializes status */ + for (i = 0; i < item_no; i++) { + status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE; + if (status[i]) { + if (was_on) + status[i] = FALSE; + else + was_on = 1; + } + items[i*3] = ditems[i].prompt; + items[i*3 + 1] = ditems[i].title; + items[i*3 + 2] = status[i] ? "on" : "off"; + } + } + max_choice = MIN(list_height, item_no); + + check_x = 0; + item_x = 0; + /* Find length of longest item in order to center radiolist */ + for (i = 0; i < item_no; i++) { + l = strlen(items[i * 3]); + for (j = 0; j < item_no; j++) { + k = strlen(items[j * 3 + 1]); + check_x = MAX(check_x, l + k + 6); + } + item_x = MAX(item_x, l); + } + if (height < 0) + height = strheight(prompt) + list_height + 4 + 2; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i, j); + width = MAX(width, check_x + 4) + 4; + } + width = MAX(width, 24); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width) / 2; + y = DialogY ? DialogY : (LINES - height) / 2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height, width, y, x); + return -1; + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height - 3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height - 2, 1); + for (i = 0; i < width - 2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title)) / 2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height - 1, width - 2, width, 1, 2, TRUE, FALSE); + + list_width = width - 6; + getyx(dialog, cur_y, cur_x); + box_y = cur_y + 1; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin(dialog, list_height, list_width, y + box_y + 1, x + box_x + 1); + if (list == NULL) { + delwin(dialog); + endwin(); + fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", list_height, list_width, + y + box_y + 1,x + box_x + 1); + return -1; + } + keypad(list, TRUE); + + /* draw a box around the list items */ + draw_box(dialog, box_y, box_x, list_height+2, list_width+2, menubox_border_attr, menubox_attr); + + check_x = (list_width - check_x) / 2; + item_x = check_x + item_x + 6; + + /* Print the list */ + for (i = 0; i < max_choice; i++) + print_item(list, items[i * 3], items[i * 3 + 1], status[i], i, i == choice, DREF(ditems, i)); + wnoutrefresh(list); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + + display_helpline(dialog, height-1, width); + + x = width/ 2 - 11; + y = height - 2; + if (ditems && result) { + cancelButton = toupper(ditems[CANCEL_BUTTON].prompt[0]); + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, + ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : FALSE); + okButton = toupper(ditems[OK_BUTTON].prompt[0]); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, + ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : TRUE); + } + else { + cancelButton = 'C'; + print_button(dialog, "Cancel", y, x + 14, FALSE); + okButton = 'O'; + print_button(dialog, " OK ", y, x, TRUE); + } + wnoutrefresh(dialog); + wmove(list, choice, check_x+1); + wrefresh(list); + + while (key != ESC) { + key = wgetch(dialog); + + /* See if its the short-cut to "OK" */ + if (toupper(key) == okButton) { + if (ditems) { + if (result && ditems[OK_BUTTON].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[OK_BUTTON].fire(&ditems[OK_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + } + else if (result) { + *result = '\0'; + for (i = 0; i < item_no; i++) { + if (status[i]) { + strcat(result, items[i*3]); + break; + } + } + } + rval = 0; + key = ESC; + break; + } + + /* Shortcut to cancel */ + if (toupper(key) == cancelButton) { + if (ditems && result && ditems[CANCEL_BUTTON].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[CANCEL_BUTTON].fire(&ditems[CANCEL_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + rval = 1; + key = ESC; + break; + } + + /* Check if key pressed matches first character of any item tag in list */ + for (i = 0; i < max_choice; i++) + if (key != ' ' && toupper(key) == toupper(items[(scroll + i) * 3][0])) + break; + + if (i < max_choice || (key >= '1' && key <= MIN('9', '0' + max_choice)) || + KEY_IS_UP(key) || KEY_IS_DOWN(key) || ((key == ' ' || key == '\r' || key == '\n') && onlist == 1)) { + + /* if moving from buttons to the list, reset and redraw buttons */ + if (!onlist) { + onlist = 1; + button = 0; + + if (ditems && result ) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, + ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, + ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + } + } + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + + if (key >= '1' && key <= MIN('9', '0' + max_choice)) + i = key - '1'; + else if (KEY_IS_UP(key)) { + if (!choice) { + if (scroll) { + /* Scroll list down */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (list_height > 1) { + /* De-highlight current first item before scrolling down */ + print_item(list, items[scroll*3], items[scroll*3 + 1], status[scroll], 0, + FALSE, DREF(ditems, scroll)); + scrollok(list, TRUE); + wscrl(list, -1); + scrollok(list, FALSE); + } + scroll--; + print_item(list, items[scroll*3], items[scroll*3 + 1], status[scroll], 0, + TRUE, DREF(ditems, scroll)); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + else + i = choice - 1; + } + else if (KEY_IS_DOWN(key)) { + if (choice == max_choice - 1) { + if (scroll + choice < item_no - 1) { + /* Scroll list up */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item(list, items[(scroll + max_choice - 1) * 3], + items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], max_choice - 1, + FALSE, DREF(ditems, scroll + max_choice - 1)); + scrollok(list, TRUE); + scroll(list); + scrollok(list, FALSE); + } + scroll++; + print_item(list, items[(scroll + max_choice - 1) * 3], + items[(scroll + max_choice - 1) * 3 + 1], + status[scroll + max_choice - 1], max_choice - 1, + TRUE, DREF(ditems, scroll + max_choice - 1)); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + wmove(list, choice, check_x+1); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + else + i = choice + 1; + } + else if ((key == ' ' || key == '\r' || key == '\n') && onlist) { /* Toggle item status */ + getyx(list, old_y, old_x); /* Save cursor position */ + if (status[scroll + choice]) + continue; + else if (ditems) { + if (ditems[scroll + choice].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[scroll + choice].fire(&ditems[scroll + choice]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + if (st & DITEM_REDRAW) { + wclear(list); + for (i = 0; i < item_no; i++) + status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE; + + for (i = 0; i < max_choice; i++) { + print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], + status[scroll + i], i, i == choice, + DREF(ditems, scroll + i)); + } +/* wmove(list, old_y, old_x);*/ /* Restore cursor to previous position */ +/* wrefresh(list); */ + } + if (st & DITEM_LEAVE_MENU) { + /* Allow a fire action to take us out of the menu */ + key = ESC; + break; + } + else if (st & DITEM_RECREATE) { + delwin(list); + delwin(dialog); + dialog_clear(); + goto draw; + } + } + for (i = 0; i < item_no; i++) + status[i] = ditems[i].checked ? ditems[i].checked(&ditems[i]) : FALSE; + } + else { + for (i = 0; i < item_no; i++) + status[i] = 0; + status[scroll + choice] = TRUE; + } + for (i = 0; i < max_choice; i++) + print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], + status[scroll + i], i, i == choice, DREF(ditems, scroll + i)); + wmove(list, choice, check_x+1); /* Restore cursor position */ + wrefresh(list); + continue; /* wait for another key press */ + } + + if (i != choice) { + /* De-highlight current item */ + print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 +1], + status[scroll + choice], choice, FALSE, DREF(ditems, scroll + choice)); + /* Highlight new item */ + choice = i; + print_item(list, items[(scroll + choice) * 3], items[(scroll + choice) * 3 + 1], + status[scroll + choice], choice, TRUE, DREF(ditems, scroll + choice)); + wmove(list, choice, check_x+1); /* Restore cursor position */ + wrefresh(list); + } + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_PPAGE: + if (scroll > height-4) /* can we go up? */ + scroll -= (height-4); + else + scroll = 0; + redraw_menu = TRUE; + if (!onlist) { + onlist = 1; + button = 0; + } + break; + + case KEY_NPAGE: + if (scroll + list_height >= item_no-1 - list_height) { /* can we go down a full page? */ + scroll = item_no - list_height; + if (scroll < 0) + scroll = 0; + } + else + scroll += list_height; + redraw_menu = TRUE; + if (!onlist) { + onlist = 1; + button = 0; + } + break; + + case KEY_HOME: + scroll = 0; + choice = 0; + redraw_menu = TRUE; + cursor_reset = TRUE; + onlist = 1; + break; + + case KEY_END: + scroll = item_no - list_height; + if (scroll < 0) + scroll = 0; + choice = max_choice - 1; + redraw_menu = TRUE; + cursor_reset = TRUE; + onlist = 1; + break; + + case TAB: + case KEY_BTAB: + /* move to next component */ + if (onlist) { /* on list, next is ok button */ + onlist = 0; + if (ditems && result) + ok_space = 1; + else + ok_space = 3; + wmove(dialog, y, x + ok_space); + wrefresh(dialog); + break; + } + else if (button) { /* on cancel button, next is list */ + button = 0; + onlist = 1; + redraw_menu = TRUE; + break; + } + /* on ok button, next is cancel button, same as left/right case */ + + case KEY_LEFT: + case KEY_RIGHT: + onlist = 0; + button = !button; + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, + ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, + ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + ok_space = 1; + cancel_space = strlen(ditems[OK_BUTTON].prompt) + 6; + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + ok_space = 3; + cancel_space = 15; + } + if (button) + wmove(dialog, y, x + cancel_space); + else + wmove(dialog, y, x + ok_space); + wrefresh(dialog); + break; + + case ' ': + case '\r': + case '\n': + if (!onlist) { + if (ditems) { + if (result && ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire) { + int st; + WINDOW *save; + + save = dupwin(newscr); + st = ditems[button ? CANCEL_BUTTON : OK_BUTTON].fire(&ditems[button ? CANCEL_BUTTON : OK_BUTTON]); + if (st & DITEM_RESTORE) { + touchwin(save); + wrefresh(save); + } + delwin(save); + } + } + else if (result) { + *result = '\0'; + for (i = 0; i < item_no; i++) { + if (status[i]) { + strcpy(result, items[i*3]); + break; + } + } + } + rval = button; + key = ESC; + break; + } + + case ESC: + rval = -1; + break; + + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + + if (redraw_menu) { + getyx(list, old_y, old_x); + wclear(list); + for (i = 0; i < max_choice; i++) + print_item(list, items[(scroll + i) * 3], items[(scroll + i) * 3 + 1], status[scroll + i], + i, i == choice, DREF(ditems, scroll + i)); + print_arrows(dialog, scroll, list_height, item_no, box_x, box_y, check_x + 4, cur_x, cur_y); + + /* redraw buttons to fix highlighting */ + if (ditems && result) { + print_button(dialog, ditems[CANCEL_BUTTON].prompt, y, x + strlen(ditems[OK_BUTTON].prompt) + 5, + ditems[CANCEL_BUTTON].checked ? ditems[CANCEL_BUTTON].checked(&ditems[CANCEL_BUTTON]) : button); + print_button(dialog, ditems[OK_BUTTON].prompt, y, x, + ditems[OK_BUTTON].checked ? ditems[OK_BUTTON].checked(&ditems[OK_BUTTON]) : !button); + } + else { + print_button(dialog, "Cancel", y, x + 14, button); + print_button(dialog, " OK ", y, x, !button); + } + wnoutrefresh(dialog); + if (cursor_reset) { + wmove(list, choice, check_x+1); + cursor_reset = FALSE; + } + else { + wmove(list, old_y, old_x); + } + wrefresh(list); + redraw_menu = FALSE; + } + } + + delwin(list); + delwin(dialog); + return rval; /* ESC pressed */ +} + +/* + * Print list item + */ +static void +print_item(WINDOW *win, char *tag, char *item, int status, int choice, int selected, dialogMenuItem *me) +{ + int i; + + /* Clear 'residue' of last item */ + wattrset(win, menubox_attr); + wmove(win, choice, 0); + for (i = 0; i < list_width; i++) + waddch(win, ' '); + wmove(win, choice, check_x); + wattrset(win, selected ? check_selected_attr : check_attr); + wprintw(win, "%c%c%c", me && me->lbra ? me->lbra : '(', + status ? me && me->mark ? me->mark : '*' : ' ', + me && me->rbra ? me->rbra : ')'); + wattrset(win, menubox_attr); + waddch(win, ' '); + wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); + waddch(win, tag[0]); + wattrset(win, selected ? tag_selected_attr : tag_attr); + waddstr(win, tag + 1); + wmove(win, choice, item_x); + wattrset(win, selected ? item_selected_attr : item_attr); + waddstr(win, item); + /* If have a selection handler for this, call it */ + if (me && me->selected) { + wrefresh(win); + me->selected(me, selected); + } +} +/* End of print_item() */ diff --git a/gnu/lib/libodialog/raw_popen.c b/gnu/lib/libodialog/raw_popen.c new file mode 100644 index 0000000..163c09a --- /dev/null +++ b/gnu/lib/libodialog/raw_popen.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software written by Ken Arnold and + * published in UNIX Review, Vol. 6, No. 8. + * + * 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[] = "@(#)popen.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/wait.h> + +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <paths.h> + +static struct pid { + struct pid *next; + FILE *fp; + pid_t pid; +} *pidlist; + +FILE * +raw_popen(const char *program, char * const *argv, const char *type) +{ + struct pid *cur; + FILE *iop; + int pdes[2], pid; + + if ((*type != 'r' && *type != 'w') || type[1]) + return (NULL); + + if ((cur = malloc(sizeof(struct pid))) == NULL) + return (NULL); + + if (pipe(pdes) < 0) { + (void)free(cur); + return (NULL); + } + + switch (pid = vfork()) { + case -1: /* Error. */ + (void)close(pdes[0]); + (void)close(pdes[1]); + (void)free(cur); + return (NULL); + /* NOTREACHED */ + case 0: /* Child. */ + if (*type == 'r') { + if (pdes[1] != STDOUT_FILENO) { + (void)dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + (void) close(pdes[0]); + } else { + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + } + if (argv == NULL) + execl(_PATH_BSHELL, "sh", "-c", program, (char *)NULL); + else + execvp(program, argv); + _exit(127); + /* NOTREACHED */ + } + + /* Parent; assume fdopen can't fail. */ + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + + /* Link into list of file descriptors. */ + cur->fp = iop; + cur->pid = pid; + cur->next = pidlist; + pidlist = cur; + + return (iop); +} + +/* + * pclose -- + * Pclose returns -1 if stream is not associated with a `popened' command, + * if already `pclosed', or waitpid returns an error. + */ +int +raw_pclose(FILE *iop) +{ + register struct pid *cur, *last; + int omask, pstat; + pid_t pid; + + (void)fclose(iop); + + /* Find the appropriate file pointer. */ + for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) + if (cur->fp == iop) + break; + if (cur == NULL) + return (-1); + + /* Get the status of the process. */ + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + do { + pid = waitpid(cur->pid, (int *) &pstat, 0); + } while (pid == -1 && errno == EINTR); + (void)sigsetmask(omask); + + /* Remove the entry from the linked list. */ + if (last == NULL) + pidlist = cur->next; + else + last->next = cur->next; + free(cur); + + return (pid == -1 ? -1 : pstat); +} diff --git a/gnu/lib/libodialog/rc.c b/gnu/lib/libodialog/rc.c new file mode 100644 index 0000000..36631a6 --- /dev/null +++ b/gnu/lib/libodialog/rc.c @@ -0,0 +1,375 @@ +/* + * rc.c -- routines for processing the configuration file + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <dialog.h> +#include "dialog.priv.h" +#include "colors.h" +#include "rc.h" + + +static unsigned char *attr_to_str(int fg, int bg, int hl); +static int str_to_attr(unsigned char *str, int *fg, int *bg, int *hl); +static int parse_line(unsigned char *line, unsigned char **var, unsigned char **value); + + +/* + * Create the configuration file + */ +void dialog_create_rc(unsigned char *filename) +{ + int i; + FILE *rc_file; + + if ((rc_file = fopen(filename, "wt")) == NULL) { + fprintf(stderr, "\nError opening file for writing in create_rc().\n"); + exit(-1); + } + + fprintf(rc_file, "#\ +\n# Run-time configuration file for dialog\ +\n#\ +\n# Automatically generated by \"dialog --create-rc <file>\"\ +\n#\ +\n#\ +\n# Types of values:\ +\n#\ +\n# Number - <number>\ +\n# String - \"string\"\ +\n# Boolean - <ON|OFF>\ +\n# Attribute - (foreground,background,highlight?)\ +\n#\n\n"); + + /* Print an entry for each configuration variable */ + for (i = 0; i < VAR_COUNT; i++) { + fprintf(rc_file, "\n# %s\n", vars[i].comment); /* print comment */ + switch (vars[i].type) { + case VAL_INT: + fprintf(rc_file, "%s = %d\n", vars[i].name, *((int *) vars[i].var)); + break; + case VAL_STR: + fprintf(rc_file, "%s = \"%s\"\n", vars[i].name, (unsigned char *) vars[i].var); + break; + case VAL_BOOL: + fprintf(rc_file, "%s = %s\n", vars[i].name, *((bool *) vars[i].var) ? "ON" : "OFF"); + break; + case VAL_ATTR: + fprintf(rc_file, "%s = %s\n", vars[i].name, attr_to_str(((int *) vars[i].var)[0], ((int *) vars[i].var)[1], ((int *) vars[i].var)[2])); + break; + } + } + + fclose(rc_file); +} +/* End of create_rc() */ + + +/* + * Parse the configuration file and set up variables + */ +int parse_rc(void) +{ + int i, l = 1, parse, fg, bg, hl; + unsigned char str[MAX_LEN+1], *var, *value, *tempptr; + FILE *rc_file; + + /* + * + * At start, 'dialog' determines the settings to use as follows: + * + * a) if environment variable DIALOGRC is set, it's value determines the + * name of the configuration file. + * + * b) if the file in (a) can't be found, use the file $HOME/.dialogrc + * as the configuration file. + * + * c) if the file in (b) can't be found, use compiled in defaults. + * + */ + + if ((tempptr = getenv("DIALOGRC")) != NULL) + rc_file = fopen(tempptr, "rt"); + + if (tempptr == NULL || rc_file == NULL) { /* step (a) failed? */ + /* try step (b) */ + if ((tempptr = getenv("HOME")) == NULL) + return 0; /* step (b) failed, use default values */ + + if (tempptr[0] == '\0' || lastch(tempptr) == '/') + sprintf(str, "%s%s", tempptr, DIALOGRC); + else + sprintf(str, "%s/%s", tempptr, DIALOGRC); + + if ((rc_file = fopen(str, "rt")) == NULL) + return 0; /* step (b) failed, use default values */ + } + + /* Scan each line and set variables */ + while (fgets(str, MAX_LEN, rc_file) != NULL) { + if (lastch(str) != '\n') { /* ignore rest of file if line too long */ + fprintf(stderr, "\nParse error: line %d of configuration file too long.\n", l); + fclose(rc_file); + return -1; /* parse aborted */ + } + else { + lastch(str) = '\0'; + parse = parse_line(str, &var, &value); /* parse current line */ + + switch (parse) { + case LINE_BLANK: /* ignore blank lines and comments */ + case LINE_COMMENT: + break; + case LINE_OK: + /* search table for matching config variable name */ + for (i = 0; i < VAR_COUNT && strcmp(vars[i].name, var); i++); + + if (i == VAR_COUNT) { /* no match */ + fprintf(stderr, "\nParse error: unknown variable at line %d of configuration file.\n", l); + return -1; /* parse aborted */ + } + else { /* variable found in table, set run time variables */ + switch (vars[i].type) { + case VAL_INT: + *((int *) vars[i].var) = atoi(value); + break; + case VAL_STR: + if (!isquote(value[0]) || !isquote(lastch(value)) || strlen(value) < 2) { + fprintf(stderr, "\nParse error: string value expected at line %d of configuration file.\n", l); + return -1; /* parse aborted */ + } + else { + /* remove the (") quotes */ + value++; + lastch(value) = '\0'; + strcpy((unsigned char *) vars[i].var, value); + } + break; + case VAL_BOOL: + if (!strcasecmp(value, "ON")) + *((bool *) vars[i].var) = TRUE; + else if (!strcasecmp(value, "OFF")) + *((bool *) vars[i].var) = FALSE; + else { + fprintf(stderr, "\nParse error: boolean value expected at line %d of configuration file.\n", l); + return -1; /* parse aborted */ + } + break; + case VAL_ATTR: + if (str_to_attr(value, &fg, &bg, &hl) == -1) { + fprintf(stderr, "\nParse error: attribute value expected at line %d of configuration file.\n", l); + return -1; /* parse aborted */ + } + ((int *) vars[i].var)[0] = fg; + ((int *) vars[i].var)[1] = bg; + ((int *) vars[i].var)[2] = hl; + break; + } + } + break; + case LINE_ERROR: + fprintf(stderr, "\nParse error: syntax error at line %d of configuration file.\n", l); + return -1; /* parse aborted */ + } + } + + l++; /* next line */ + } + + fclose(rc_file); + return 0; /* parse successful */ +} +/* End of parse_rc() */ + + +/* + * Convert an attribute to a string representation like this: + * + * "(foreground,background,highlight)" + */ +static unsigned char *attr_to_str(int fg, int bg, int hl) +{ + int i; + static unsigned char str[MAX_LEN+1]; + + strcpy(str, "("); + /* foreground */ + for (i = 0; fg != color_names[i].value; i++); + strcat(str, color_names[i].name); + strcat(str, ","); + + /* background */ + for (i = 0; bg != color_names[i].value; i++); + strcat(str, color_names[i].name); + + /* highlight */ + strcat(str, hl ? ",ON)" : ",OFF)"); + + return str; +} +/* End of attr_to_str() */ + + +/* + * Extract the foreground, background and highlight values from an attribute + * represented as a string in this form: + * + * "(foreground,background,highlight)" + */ +static int str_to_attr(unsigned char *str, int *fg, int *bg, int *hl) +{ + int i = 0, j, get_fg = 1; + unsigned char tempstr[MAX_LEN+1], *part; + + if (str[0] != '(' || lastch(str) != ')') + return -1; /* invalid representation */ + + /* remove the parenthesis */ + strcpy(tempstr, str + 1); + lastch(tempstr) = '\0'; + + + /* get foreground and background */ + + while (1) { + /* skip white space before fg/bg string */ + while (whitespace(tempstr[i]) && tempstr[i] != '\0') i++; + if (tempstr[i] == '\0') + return -1; /* invalid representation */ + part = tempstr + i; /* set 'part' to start of fg/bg string */ + + /* find end of fg/bg string */ + while(!whitespace(tempstr[i]) && tempstr[i] != ',' && tempstr[i] != '\0') i++; + + if (tempstr[i] == '\0') + return -1; /* invalid representation */ + else if (whitespace(tempstr[i])) { /* not yet ',' */ + tempstr[i++] = '\0'; + + /* skip white space before ',' */ + while(whitespace(tempstr[i]) && tempstr[i] != '\0') i++; + + if (tempstr[i] != ',') + return -1; /* invalid representation */ + } + + tempstr[i++] = '\0'; /* skip the ',' */ + for (j = 0; j < COLOR_COUNT && strcasecmp(part, color_names[j].name); j++); + if (j == COLOR_COUNT) /* invalid color name */ + return -1; + if (get_fg) { + *fg = color_names[j].value; + get_fg = 0; /* next we have to get the background */ + } + else { + *bg = color_names[j].value; + break; + } + } /* got foreground and background */ + + + /* get highlight */ + + /* skip white space before highlight string */ + while (whitespace(tempstr[i]) && tempstr[i] != '\0') i++; + if (tempstr[i] == '\0') + return -1; /* invalid representation */ + part = tempstr + i; /* set 'part' to start of highlight string */ + + /* trim trailing white space from highlight string */ + i = strlen(part) - 1; + while(whitespace(part[i])) i--; + part[i+1] = '\0'; + + if (!strcasecmp(part, "ON")) + *hl = TRUE; + else if (!strcasecmp(part, "OFF")) + *hl = FALSE; + else + return -1; /* invalid highlight value */ + + return 0; +} +/* End of str_to_attr() */ + + +/* + * Parse a line in the configuration file + * + * Each line is of the form: "variable = value". On exit, 'var' will contain + * the variable name, and 'value' will contain the value string. + * + * Return values: + * + * LINE_BLANK - line is blank + * LINE_COMMENT - line is comment + * LINE_OK - line is ok + * LINE_ERROR - syntax error in line + */ +static int parse_line(unsigned char *line, unsigned char **var, unsigned char **value) +{ + int i = 0; + + /* ignore white space at beginning of line */ + while(whitespace(line[i]) && line[i] != '\0') i++; + + if (line[i] == '\0') /* line is blank */ + return LINE_BLANK; + else if (line[i] == '#') /* line is comment */ + return LINE_COMMENT; + else if (line[i] == '=') /* variables names can't strart with a '=' */ + return LINE_ERROR; + + /* set 'var' to variable name */ + *var = line + i++; /* skip to next character */ + + /* find end of variable name */ + while(!whitespace(line[i]) && line[i] != '=' && line[i] != '\0') i++; + + if (line[i] == '\0') /* syntax error */ + return LINE_ERROR; + else if (line[i] == '=') + line[i++] = '\0'; + else { + line[i++] = '\0'; + + /* skip white space before '=' */ + while(whitespace(line[i]) && line[i] != '\0') i++; + + if (line[i] != '=') /* syntax error */ + return LINE_ERROR; + else + i++; /* skip the '=' */ + } + + /* skip white space after '=' */ + while(whitespace(line[i]) && line[i] != '\0') i++; + + if (line[i] == '\0') + return LINE_ERROR; + else + *value = line + i; /* set 'value' to value string */ + + /* trim trailing white space from 'value' */ + i = strlen(*value) - 1; + while(whitespace((*value)[i])) i--; + (*value)[i+1] = '\0'; + + return LINE_OK; /* no syntax error in line */ +} +/* End of parse_line() */ diff --git a/gnu/lib/libodialog/rc.h b/gnu/lib/libodialog/rc.h new file mode 100644 index 0000000..fc19d03 --- /dev/null +++ b/gnu/lib/libodialog/rc.h @@ -0,0 +1,222 @@ +/* + * rc.h -- declarations for configuration file processing + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#define DIALOGRC ".dialogrc" +#define VAR_LEN 30 +#define COMMENT_LEN 70 + +/* Types of values */ +#define VAL_INT 0 +#define VAL_STR 1 +#define VAL_BOOL 2 +#define VAL_ATTR 3 + +/* Type of line in configuration file */ +#define LINE_BLANK 2 +#define LINE_COMMENT 1 +#define LINE_OK 0 +#define LINE_ERROR -1 + +/* number of configuration variables */ +#define VAR_COUNT (sizeof(vars) / sizeof(vars_st)) + +/* check if character is white space */ +#define whitespace(c) (c == ' ' || c == '\t') + +/* check if character is string quoting characters */ +#define isquote(c) (c == '"' || c == '\'') + +/* get last character of string */ +#define lastch(str) str[strlen(str)-1] + +/* + * Configuration variables + */ +typedef struct { + unsigned char name[VAR_LEN]; /* name of configuration variable as in DIALOGRC */ + void *var; /* address of actually variable to change */ + int type; /* type of value */ + unsigned char comment[COMMENT_LEN]; /* comment to put in "rc" file */ +} vars_st; + +vars_st vars[] = { + { "use_shadow", + &use_shadow, + VAL_BOOL, + "Shadow dialog boxes? This also turns on color." }, + + { "use_colors", + &use_colors, + VAL_BOOL, + "Turn color support ON or OFF" }, + + { "screen_color", + color_table[0], + VAL_ATTR, + "Screen color" }, + + { "shadow_color", + color_table[1], + VAL_ATTR, + "Shadow color" }, + + { "dialog_color", + color_table[2], + VAL_ATTR, + "Dialog box color" }, + + { "title_color", + color_table[3], + VAL_ATTR, + "Dialog box title color" }, + + { "border_color", + color_table[4], + VAL_ATTR, + "Dialog box border color" }, + + { "button_active_color", + color_table[5], + VAL_ATTR, + "Active button color" }, + + { "button_inactive_color", + color_table[6], + VAL_ATTR, + "Inactive button color" }, + + { "button_key_active_color", + color_table[7], + VAL_ATTR, + "Active button key color" }, + + { "button_key_inactive_color", + color_table[8], + VAL_ATTR, + "Inactive button key color" }, + + { "button_label_active_color", + color_table[9], + VAL_ATTR, + "Active button label color" }, + + { "button_label_inactive_color", + color_table[10], + VAL_ATTR, + "Inactive button label color" }, + + { "inputbox_color", + color_table[11], + VAL_ATTR, + "Input box color" }, + + { "inputbox_border_color", + color_table[12], + VAL_ATTR, + "Input box border color" }, + + { "searchbox_color", + color_table[13], + VAL_ATTR, + "Search box color" }, + + { "searchbox_title_color", + color_table[14], + VAL_ATTR, + "Search box title color" }, + + { "searchbox_border_color", + color_table[15], + VAL_ATTR, + "Search box border color" }, + + { "position_indicator_color", + color_table[16], + VAL_ATTR, + "File position indicator color" }, + + { "menubox_color", + color_table[17], + VAL_ATTR, + "Menu box color" }, + + { "menubox_border_color", + color_table[18], + VAL_ATTR, + "Menu box border color" }, + + { "item_color", + color_table[19], + VAL_ATTR, + "Item color" }, + + { "item_selected_color", + color_table[20], + VAL_ATTR, + "Selected item color" }, + + { "tag_color", + color_table[21], + VAL_ATTR, + "Tag color" }, + + { "tag_selected_color", + color_table[22], + VAL_ATTR, + "Selected tag color" }, + + { "tag_key_color", + color_table[23], + VAL_ATTR, + "Tag key color" }, + + { "tag_key_selected_color", + color_table[24], + VAL_ATTR, + "Selected tag key color" }, + + { "check_color", + color_table[25], + VAL_ATTR, + "Check box color" }, + + { "check_selected_color", + color_table[26], + VAL_ATTR, + "Selected check box color" }, + + { "uarrow_color", + color_table[27], + VAL_ATTR, + "Up arrow color" }, + + { "darrow_color", + color_table[28], + VAL_ATTR, + "Down arrow color" } +}; /* vars */ + + + +/* + * Routines to process configuration file + */ +int parse_rc(void); diff --git a/gnu/lib/libodialog/textbox.c b/gnu/lib/libodialog/textbox.c new file mode 100644 index 0000000..19a13bb --- /dev/null +++ b/gnu/lib/libodialog/textbox.c @@ -0,0 +1,699 @@ +/* + * textbox.c -- implements the text box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" + + +static void back_lines(int n); +static void print_page(WINDOW *win, int height, int width); +static void print_line(WINDOW *win, int row, int width); +static unsigned char *get_line(void); +static int get_search_term(WINDOW *win, unsigned char *search_term, int height, int width); +static void print_position(WINDOW *win, int height, int width); + + +static int hscroll = 0, fd, file_size, bytes_read, begin_reached = 1, + end_reached = 0, page_length; +static unsigned char *buf, *page; + + +/* + * Display text from a file in a dialog box. + */ +int dialog_textbox(unsigned char *title, unsigned char *file, int height, int width) +{ + int i, x, y, cur_x, cur_y, fpos, key = 0, dir, temp, temp1; +#ifdef HAVE_NCURSES + int passed_end; +#endif + unsigned char search_term[MAX_LEN+1], *tempptr, *found; + WINDOW *dialog, *text; + + if (height < 0 || width < 0) { + fprintf(stderr, "\nAutosizing is impossible in dialog_textbox().\n"); + return(-1); + } + + search_term[0] = '\0'; /* no search term entered yet */ + + /* Open input file for reading */ + if ((fd = open(file, O_RDONLY)) == -1) { + fprintf(stderr, "\nCan't open input file <%s>in dialog_textbox().\n", file); + return(-1); + } + /* Get file size. Actually, 'file_size' is the real file size - 1, + since it's only the last byte offset from the beginning */ + if ((file_size = lseek(fd, 0, SEEK_END)) == -1) { + fprintf(stderr, "\nError getting file size in dialog_textbox().\n"); + return(-1); + } + /* Restore file pointer to beginning of file after getting file size */ + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + return(-1); + } + /* Allocate space for read buffer */ + if ((buf = malloc(BUF_SIZE+1)) == NULL) { + endwin(); + fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n"); + exit(-1); + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + fprintf(stderr, "\nError reading file in dialog_textbox().\n"); + return(-1); + } + buf[bytes_read] = '\0'; /* mark end of valid data */ + page = buf; /* page is pointer to start of page to be displayed */ + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width)/2; + y = DialogY ? DialogY : (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + /* Create window for text region, used for scrolling text */ +/* text = newwin(height-4, width-2, y+1, x+1); */ + text = subwin(dialog, height-4, width-2, y+1, x+1); + if (text == NULL) { + endwin(); + fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", height-4,width-2,y+1,x+1); + exit(1); + } + keypad(text, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + display_helpline(dialog, height-1, width); + + print_button(dialog, " OK ", height-2, width/2-6, TRUE); + wnoutrefresh(dialog); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear(text, height-4, width-2, dialog_attr); + print_page(text, height-4, width-2); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + + while ((key != ESC) && (key != '\n') && (key != '\r') && (key != ' ')) { + key = wgetch(dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + delwin(dialog); + free(buf); + close(fd); + return 0; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + /* First page not in buffer? */ + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + if (fpos > bytes_read) { /* Yes, we have to read it in */ + if (lseek(fd, 0, SEEK_SET) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in dialog_textbox().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + } + page = buf; + print_page(text, height-4, width-2); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'G': /* Last page */ +#ifdef HAVE_NCURSES + case KEY_END: +#endif + end_reached = 1; + /* Last page not in buffer? */ + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + if (fpos < file_size) { /* Yes, we have to read it in */ + if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in dialog_textbox().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + } + page = buf + bytes_read; + back_lines(height-4); + print_page(text, height-4, width-2); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + break; + case 'K': /* Previous line */ + case 'k': + case '\020': /* ^P */ + case KEY_UP: + if (!begin_reached) { + back_lines(page_length+1); +#ifdef HAVE_NCURSES + /* We don't call print_page() here but use scrolling to ensure + faster screen update. However, 'end_reached' and 'page_length' + should still be updated, and 'page' should point to start of + next page. This is done by calling get_line() in the following + 'for' loop. */ + scrollok(text, TRUE); + wscrl(text, -1); /* Scroll text region down one line */ + scrollok(text, FALSE); + page_length = 0; + passed_end = 0; + for (i = 0; i < height-4; i++) { + if (!i) { + print_line(text, 0, width-2); /* print first line of page */ + wnoutrefresh(text); + } + else + get_line(); /* Called to update 'end_reached' and 'page' */ + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } +#else + print_page(text, height-4, width-2); +#endif + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'B': /* Previous page */ + case 'b': + case KEY_PPAGE: + if (!begin_reached) { + back_lines(page_length + height-4); + print_page(text, height-4, width-2); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'J': /* Next line */ + case 'j': + case '\016': /* ^N */ + case KEY_DOWN: + if (!end_reached) { + begin_reached = 0; + scrollok(text, TRUE); + scroll(text); /* Scroll text region up one line */ + scrollok(text, FALSE); + print_line(text, height-5, width-2); +#ifndef HAVE_NCURSES + wmove(text, height-5, 0); + waddch(text, ' '); + wmove(text, height-5, width-3); + waddch(text, ' '); +#endif + wnoutrefresh(text); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'F': /* Next page */ + case 'f': + case KEY_NPAGE: + if (!end_reached) { + begin_reached = 0; + print_page(text, height-4, width-2); + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll > 0) { + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + print_page(text, height-4, width-2); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll < MAX_LEN) { + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + print_page(text, height-4, width-2); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + break; + case '/': /* Forward search */ + case 'n': /* Repeat forward search */ + case '?': /* Backward search */ + case 'N': /* Repeat backward search */ + /* set search direction */ + dir = (key == '/' || key == 'n') ? 1 : 0; + if (dir ? !end_reached : !begin_reached) { + if (key == 'n' || key == 'N') { + if (search_term[0] == '\0') { /* No search term yet */ + fprintf(stderr, "\a"); /* beep */ + break; + } + } + else /* Get search term from user */ + if (get_search_term(text, search_term, height-4, width-2) == -1) { + /* ESC pressed in get_search_term(). Reprint page to clear box */ + wattrset(text, dialog_attr); + back_lines(page_length); + print_page(text, height-4, width-2); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + break; + } + /* Save variables for restoring in case search term can't be found */ + tempptr = page; + temp = begin_reached; + temp1 = end_reached; + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + fpos -= bytes_read; + /* update 'page' to point to next (previous) line before + forward (backward) searching */ + back_lines(dir ? page_length-1 : page_length+1); + found = NULL; + if (dir) /* Forward search */ + while((found = strstr(get_line(), search_term)) == NULL) { + if (end_reached) + break; + } + else /* Backward search */ + while((found = strstr(get_line(), search_term)) == NULL) { + if (begin_reached) + break; + back_lines(2); + } + if (found == NULL) { /* not found */ + fprintf(stderr, "\a"); /* beep */ + /* Restore program state to that before searching */ + if (lseek(fd, fpos, SEEK_SET) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); + exit(-1); + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in dialog_textbox().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + page = tempptr; + begin_reached = temp; + end_reached = temp1; + /* move 'page' to point to start of current page in order to + re-print current page. Note that 'page' always points to + start of next page, so this is necessary */ + back_lines(page_length); + } + else /* Search term found */ + back_lines(1); + /* Reprint page */ + wattrset(text, dialog_attr); + print_page(text, height-4, width-2); + if (found != NULL) + print_position(dialog, height, width); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); + } + else /* no need to find */ + fprintf(stderr, "\a"); /* beep */ + break; + case ESC: + break; + case KEY_F(1): + display_helpfile(); + break; + } + } + + delwin(dialog); + free(buf); + close(fd); + return (key == ESC ? -1 : 0); +} +/* End of dialog_textbox() */ + + +/* + * Go back 'n' lines in text file. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void back_lines(int n) +{ + int i, fpos; + + begin_reached = 0; + /* We have to distinguish between end_reached and !end_reached since at end + of file, the line is not ended by a '\n'. The code inside 'if' basically + does a '--page' to move one character backward so as to skip '\n' of the + previous line */ + if (!end_reached) { + /* Either beginning of buffer or beginning of file reached? */ + if (page == buf) { + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + if (fpos > bytes_read) { /* Not beginning of file yet */ + /* We've reached beginning of buffer, but not beginning of file yet, + so read previous part of file into buffer. Note that we only + move backward for BUF_SIZE/2 bytes, but not BUF_SIZE bytes to + avoid re-reading again in print_page() later */ + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE/2 + bytes_read) { + /* No, move less then */ + if (lseek(fd, 0, SEEK_SET) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + page = buf + fpos - bytes_read; + } + else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek(fd, -(BUF_SIZE/2 + bytes_read), SEEK_CUR) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + page = buf + BUF_SIZE/2; + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in back_lines().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + } + else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + if (*(--page) != '\n') { /* '--page' here */ + /* Something's wrong... */ + endwin(); + fprintf(stderr, "\nInternal error in back_lines().\n"); + exit(-1); + } + } + + /* Go back 'n' lines */ + for (i = 0; i < n; i++) + do { + if (page == buf) { + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + if (fpos > bytes_read) { + /* Really possible to move backward BUF_SIZE/2 bytes? */ + if (fpos < BUF_SIZE/2 + bytes_read) { + /* No, move less then */ + if (lseek(fd, 0, SEEK_SET) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + page = buf + fpos - bytes_read; + } + else { /* Move backward BUF_SIZE/2 bytes */ + if (lseek(fd, -(BUF_SIZE/2 + bytes_read), SEEK_CUR) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in back_lines().\n"); + exit(-1); + } + page = buf + BUF_SIZE/2; + } + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in back_lines().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + } + else { /* Beginning of file reached */ + begin_reached = 1; + return; + } + } + } while (*(--page) != '\n'); + page++; +} +/* End of back_lines() */ + + +/* + * Print a new page of text. Called by dialog_textbox(). + */ +static void print_page(WINDOW *win, int height, int width) +{ + int i, passed_end = 0; + + page_length = 0; + for (i = 0; i < height; i++) { + print_line(win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh(win); +} +/* End of print_page() */ + + +/* + * Print a new line of text. Called by dialog_textbox() and print_page(). + */ +static void print_line(WINDOW *win, int row, int width) +{ + int i, y, x; + unsigned char *line; + + line = get_line(); + line += MIN(strlen(line),hscroll); /* Scroll horizontally */ + wmove(win, row, 0); /* move cursor to correct line */ + waddch(win,' '); +#ifdef HAVE_NCURSES + waddnstr(win, line, MIN(strlen(line),width-2)); +#else + line[MIN(strlen(line),width-2)] = '\0'; + waddstr(win, line); +#endif + + getyx(win, y, x); + /* Clear 'residue' of previous line */ + for (i = 0; i < width-x; i++) + waddch(win, ' '); +} +/* End of print_line() */ + + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static unsigned char *get_line(void) +{ + int i = 0, fpos; + static unsigned char line[MAX_LEN+1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { /* Either end of file or end of buffer reached */ + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in get_line().\n"); + exit(-1); + } + if (fpos < file_size) { /* Not end of file yet */ + /* We've reached end of buffer, but not end of file yet, so read next + part of file into buffer */ + if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { + endwin(); + fprintf(stderr, "\nError reading file in get_line().\n"); + exit(-1); + } + buf[bytes_read] = '\0'; + page = buf; + } + else { + if (!end_reached) + end_reached = 1; + break; + } + } + else + if (i < MAX_LEN) + line[i++] = *(page++); + else { + if (i == MAX_LEN) /* Truncate lines longer than MAX_LEN characters */ + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move pass '\n' */ + + return line; +} +/* End of get_line() */ + + +/* + * Display a dialog box and get the search term from user + */ +static int get_search_term(WINDOW *win, unsigned char *search_term, int height, int width) +{ + int x, y, key = 0, first, + box_height = 3, box_width = 30; + + x = (width - box_width)/2; + y = (height - box_height)/2; +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(win, y, x, box_height, box_width); +#endif + draw_box(win, y, x, box_height, box_width, dialog_attr, searchbox_border_attr); + wattrset(win, searchbox_title_attr); + wmove(win, y, x+box_width/2-4); + waddstr(win, " Search "); + wattrset(win, dialog_attr); + + search_term[0] = '\0'; + + first = 1; + while (key != ESC) { + key = line_edit(win, y+1, x+1, -1, box_width-2, searchbox_attr, first, search_term, 0); + first = 0; + switch (key) { + case '\n': + if (search_term[0] != '\0') + return 0; + break; + case ESC: + break; + } + } + + return -1; /* ESC pressed */ +} +/* End of get_search_term() */ + + +/* + * Print current position + */ +static void print_position(WINDOW *win, int height, int width) +{ + int fpos, percent; + + if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { + endwin(); + fprintf(stderr, "\nError moving file pointer in print_position().\n"); + exit(-1); + } + wattrset(win, position_indicator_attr); + percent = !file_size ? 100 : ((fpos-bytes_read+page-buf)*100)/file_size; + wmove(win, height-3, width-9); + wprintw(win, "(%3d%%)", percent); +} +/* End of print_position() */ diff --git a/gnu/lib/libodialog/tree.c b/gnu/lib/libodialog/tree.c new file mode 100644 index 0000000..ceacf05 --- /dev/null +++ b/gnu/lib/libodialog/tree.c @@ -0,0 +1,1133 @@ +/* + * tree.c -- implements the 'tree' interface element for libdialog + * + * Author: Anatoly A. Orehovsky (tolik@mpeks.tomsk.su) + * + * Copyright (c) 1997, Anatoly A. Orehovsky + * 09/28/98 - patched by Anatoly A. Orehovsky (smart_tree()) + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdlib.h> +#include <strings.h> +#include <stdio.h> +#include <dialog.h> +#include "dialog.priv.h" +#include <ncurses.h> + +/* static utils for make tree */ +struct leaf { + unsigned char *name; /* name of leaf */ + unsigned char *branches; /* branches that going by leaf */ + unsigned char slip; /* slip of leaf*/ + int shift; /* shift relative root of tree */ +}; + +static int mk_slip(struct leaf array[], int arr_size, + int number, int shift); + +/* make tree from file + * + * filename - name of file with like find(1) output + * p_names - pointer to array of strings + * p_size - pointer to size of array + * FS - fields separator + * p_array - pointer to array of leafs + * + * return values: + * 0 - ok and names by p_names, size by p_size, array by p_array set + * -1 - memory allocation error (errno set) + */ + +static int mk_ftree(char *filename, + unsigned char ***p_names, int *p_size, unsigned char FS, + struct leaf **p_array); + +/* make tree from array + * + * names - array of strings + * size - size of array + * FS - fields separator + * p_array - pointer to array of leafs + * + * return values: + * 0 - ok and array by p_array set + * -1 - memory allocation error (errno set) + */ + +static int mk_tree(unsigned char **names, int size, unsigned char FS, + struct leaf **p_array); + +/* free memory from tree (leafs) + * + * return values: + * nothing + */ + +static void free_leafs(struct leaf *array, int size); + +/* free memory from source data for tree (names) + * + * return values: + * if 0 <= choice <= size - pointer to name from names, + * and memory for name not released (must be freed later) + * else - NULL (recomended choice -1 for it) + */ + +static unsigned char *free_names(unsigned char **names, + int size, int choice); + +/* end of static utils for make tree */ + +/* static utils for ftree */ + +/* control struct for queue */ +struct queue { + int size; /* size of queue */ + struct m_queue *first; /* begin of queue */ + struct m_queue *last; /* end of queue */ +}; + +/* queue member */ +struct m_queue { + void *pointer; /* queue member */ + struct m_queue *next; /* next queue member */ +}; + +/* init struct queue by zeros */ +static void init_queue(struct queue *queue); + +/* add pointer to queue */ +/* return - pointer or NULL if error */ +static void *p2_queue(struct queue *queue, void *pointer); + +/* get first from queue */ +/* return - pointer or NULL if queue is empty */ +static void *first_queue(struct queue *queue); + +/* make zero terminated array from queue */ +/* return - pointer to array or NULL if error */ +static void **q2arr(struct queue *queue, int depth); + +/* smart_tree (for like find(1) with -d flag output compliance) */ +/* return - not NULL or NULL if malloc error */ +static unsigned char *smart_tree(struct queue *queue, unsigned char FS, + unsigned char *current, + unsigned char *prev); + +/* end of static utils for ftree */ + +/* static utils for saved_tree */ + +/* saved values for unique tree */ +struct saved_tree { + unsigned char **names; /* names + */ + int size; /* size + */ + unsigned char FS; /* FS + */ + int height; /* height + */ + int width; /* width + */ + int menu_height; /* menu_height - unique for treebox ? */ + int ch; /* saved ch - choice */ + int sc; /* saved sc - scroll */ +}; + +/* search saved tree within queue */ +/* return - struct saved_tree * or NULL if not found */ +static struct saved_tree *search_saved_tree(struct queue *queue, + unsigned char **names, + int size, + unsigned char FS, + int height, + int width, + int menu_height); + +/* end of static utils for saved_tree */ + +static void print_item(WINDOW *win, struct leaf item, int choice, int selected); + +static void print_position(WINDOW *win, int x, int y, + int cur_pos, int size); + +static int menu_width, item_x; + +static int dialog_treemenu(unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + int item_no, struct leaf items[], + int *result, + int *ch, int *sc); + +/* + * Display a menu for choosing among a number of options + */ +static +int dialog_treemenu(unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + int item_no, struct leaf items[], + int *result, + int *ch, int *sc) +{ + int i, j, x, y, cur_x, cur_y, box_x, box_y, key = 0, button = 0, choice = 0, + l, scroll = 0, max_choice, redraw_menu = FALSE; + WINDOW *dialog, *menu; + + if (ch) /* restore menu item info */ + choice = *ch; + if (sc) + scroll = *sc; + + max_choice = MIN(menu_height, item_no); + + item_x = 0; + /* Find length of longest item in order to center menu */ + for (i = 0; i < item_no; i++) { + l = strlen(items[i].name) + strlen(items[i].branches) * 4 + 4; + item_x = MAX(item_x, l); + } + + if (height < 0) + height = strheight(prompt)+menu_height+4+2; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j); + width = MAX(width,item_x+4)+4; + } + width = MAX(width,24); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = (COLS - width)/2; + y = (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE); + + menu_width = width-6; + getyx(dialog, cur_y, cur_x); + box_y = cur_y + 1; + box_x = (width - menu_width)/2 - 1; + + /* create new window for the menu */ + menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1); + if (menu == NULL) { + endwin(); + fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height,menu_width,y+box_y+1,x+box_x+1); + exit(1); + } + keypad(menu, TRUE); + + /* draw a box around the menu items */ + draw_box(dialog, box_y, box_x, menu_height+2, menu_width+2, menubox_border_attr, menubox_attr); + + item_x = 1; + + /* Print the menu */ + for (i = 0; i < max_choice; i++) + print_item(menu, items[(scroll+i)], i, i == choice); + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y); + print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no); + + display_helpline(dialog, height-1, width); + + x = width/2-11; + y = height-2; + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + + wrefresh(dialog); + + while (key != ESC) { + key = wgetch(dialog); + /* Check if key pressed matches first character of any item tag in menu */ + + if (key == KEY_UP || key == KEY_DOWN || key == '-' || key == '+') { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (scroll) { +#ifdef BROKEN_WSCRL + /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation + violation when scrolling windows of height = 4, so scrolling is not + used for now */ + scroll--; + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + /* Reprint menu to scroll down */ + for (i = 0; i < max_choice; i++) + print_item(menu, items[(scroll+i)], i, i == choice); + +#else + + /* Scroll menu down */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (menu_height > 1) { + /* De-highlight current first item before scrolling down */ + print_item(menu, items[scroll], 0, FALSE); + scrollok(menu, TRUE); + wscrl(menu, -1); + scrollok(menu, FALSE); + } + scroll--; + print_item(menu, items[scroll], 0, TRUE); +#endif + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y); + print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + else + i = choice - 1; + } + else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll+choice < item_no-1) { +#ifdef BROKEN_WSCRL + /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation + violation when scrolling windows of height = 4, so scrolling is not + used for now */ + scroll++; + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + /* Reprint menu to scroll up */ + for (i = 0; i < max_choice; i++) + print_item(menu, items[(scroll+i)], i, i == choice); + +#else + + /* Scroll menu up */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + if (menu_height > 1) { + /* De-highlight current last item before scrolling up */ + print_item(menu, items[(scroll+max_choice-1)], max_choice-1, FALSE); + scrollok(menu, TRUE); + scroll(menu); + scrollok(menu, FALSE); + } + scroll++; + print_item(menu, items[(scroll+max_choice-1)], max_choice-1, TRUE); +#endif + wnoutrefresh(menu); + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y); + print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + else + i = choice + 1; + } + + if (i != choice) { + /* De-highlight current item */ + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + print_item(menu, items[(scroll+choice)], choice, FALSE); + + /* Highlight new item */ + choice = i; + print_item(menu, items[(scroll+choice)], choice, TRUE); + wnoutrefresh(menu); + print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + } + continue; /* wait for another key press */ + } + + /* save info about menu item position */ + if (ch) + *ch = choice; + if (sc) + *sc = scroll; + + switch (key) { + case KEY_PPAGE: + case 'B' : + case 'b' : + if (scroll > menu_height) { /* can we go up? */ + scroll -= (menu_height); + } else { + scroll = 0; + } + redraw_menu = TRUE; + break; + case KEY_NPAGE: + case 'F' : + case 'f' : + if (scroll + menu_height >= item_no-1 - menu_height) { /* can we go down a full page? */ + scroll = item_no - menu_height; + if (scroll < 0) scroll = 0; + } else { + scroll += menu_height; + } + redraw_menu = TRUE; + break; + case KEY_HOME: + case 'g' : + scroll = 0; + choice = 0; + redraw_menu = TRUE; + break; + case KEY_END: + case 'G' : + scroll = item_no - menu_height; + if (scroll < 0) scroll = 0; + choice = max_choice - 1; + redraw_menu = TRUE; + break; + case 'O': + case 'o': + delwin(dialog); + *result = scroll+choice; + return 0; + case 'C': + case 'c': + delwin(dialog); + return 1; + case KEY_BTAB: + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + if (!button) { + button = 1; /* Indicates "Cancel" button is selected */ + print_button(dialog, " OK ", y, x, FALSE); + print_button(dialog, "Cancel", y, x+14, TRUE); + } + else { + button = 0; /* Indicates "OK" button is selected */ + print_button(dialog, "Cancel", y, x+14, FALSE); + print_button(dialog, " OK ", y, x, TRUE); + } + wrefresh(dialog); + break; + case ' ': + case '\r': + case '\n': + delwin(dialog); + if (!button) + *result = scroll+choice; + return button; + case ESC: + break; + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + if (redraw_menu) { + for (i = 0; i < max_choice; i++) { + print_item(menu, items[(scroll+i)], + i, i == choice); + } + wnoutrefresh(menu); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y); + print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no); + wmove(dialog, cur_y, cur_x); /* Restore cursor to previous position */ + wrefresh(dialog); + redraw_menu = FALSE; + } + } + + delwin(dialog); + return -1; /* ESC pressed */ +} +/* End of dialog_treemenu() */ + + +/* + * Print menu item + */ +static void print_item(WINDOW *win, struct leaf item, int choice, int selected) +{ + int i, j = menu_width - 2; + char *branches = item.branches; + + /* Clear 'residue' of last item */ + wattrset(win, menubox_attr); + wmove(win, choice, 0); + for (i = 0; i < menu_width; i++) + waddch(win, ' '); + wmove(win, choice, item_x); + + while(*branches && j) + { + switch (*branches++) { + case ' ' : waddch(win, ' '); + break; + case '|' : waddch(win, ACS_VLINE); + } + + j--; + i = 3; + while(i-- && j) + { + waddch(win, ' '); + j--; + } + } + + if (j) + { + switch (item.slip) { + case '+' : waddch(win, ACS_LTEE); + break; + case '`' : waddch(win, ACS_LLCORNER); + } + j--; + } + + i = 3; + while(i-- && j) + { + waddch(win, ACS_HLINE); + j--; + } + + wattrset(win, selected ? item_selected_attr : item_attr); + if (j) + waddnstr(win, item.name, j); +} +/* End of print_item() */ + +/* + * Print current position + */ +static void print_position(WINDOW *win, int x, int y, + int cur_pos, int size) +{ + int percent; + + wattrset(win, position_indicator_attr); + percent = cur_pos == size - 1 ? 100 : (cur_pos * 100)/(size - 1); + wmove(win, y + 1, x - 6); + wprintw(win, "(%3d%%)", percent); +} +/* End of print_position() */ + +/* + * Display a tree menu from file + * + * filename - file with like find(1) output + * FS - fields separator + * title - title of dialog box + * prompt - prompt text into dialog box + * height - height of dialog box + * width - width of dialog box + * menu_height - height of menu box + * result - pointer to char array + * + * return values: + * -1 - ESC pressed + * 0 - Ok, result set (must be freed later) + * 1 - Cancel + */ + +int dialog_ftree(unsigned char *filename, unsigned char FS, + unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + unsigned char **result) +{ + int retcode, choice, size; + struct leaf *items; + unsigned char **names; + + if (mk_ftree(filename, &names, &size, FS, &items)) + { + perror("dialog_ftree"); + end_dialog(); + exit(-1); + } + + if (!size) + { + fprintf(stderr, "\ndialog_ftree: file %s is empty\n", filename); + end_dialog(); + exit(-1); + } + + retcode = dialog_treemenu(title, prompt, height, width, menu_height, + size, items, &choice, NULL, NULL); + + free_leafs(items, size); + + if (!retcode) + *result = free_names(names, size, choice); + else + (void)free_names(names, size, -1); + + return retcode; +} +/* End of dialog_ftree() */ + +/* + * Display a tree menu from array + * + * names - array with like find(1) output + * size - size of array + * FS - fields separator + * title - title of dialog box + * prompt - prompt text into dialog box + * height - height of dialog box + * width - width of dialog box + * menu_height - height of menu box + * result - pointer to char array + * + * return values: + * -1 - ESC pressed + * 0 - Ok, result set + * 1 - Cancel + */ + +int dialog_tree(unsigned char **names, int size, unsigned char FS, + unsigned char *title, unsigned char *prompt, + int height, int width, int menu_height, + unsigned char **result) +{ + int retcode, choice; + struct leaf *items; + struct saved_tree *st; + static struct queue *q_saved_tree = NULL; + + if (!size) + { + fprintf(stderr, "\ndialog_tree: source array is empty\n"); + end_dialog(); + exit(-1); + } + + if (mk_tree(names, size, FS, &items)) + { + perror("dialog_tree"); + end_dialog(); + exit(-1); + } + +/* is tree saved ? */ + if (!(st = search_saved_tree(q_saved_tree, names, + size, FS, + height, width, menu_height))) { + if (!q_saved_tree) { + if (!(q_saved_tree = + calloc(sizeof (struct queue), 1))) { + perror("dialog_tree"); + end_dialog(); + exit(-1); + } + } + + if (!(st = calloc(sizeof (struct saved_tree), 1))) { + perror("dialog_tree"); + end_dialog(); + exit(-1); + } + + st->names = names; + st->size = size; + st->FS = FS; + st->height = height; + st->width = width; + st->menu_height = menu_height; + + if (!p2_queue(q_saved_tree, st)) { + perror("dialog_tree"); + end_dialog(); + exit(-1); + } + } + + retcode = dialog_treemenu(title, prompt, height, width, menu_height, + size, items, &choice, + &(st->ch), &(st->sc)); + + free_leafs(items, size); + + if (!retcode) + *result = names[choice]; + + return retcode; +} +/* End of dialog_tree() */ + +/* utils for ftree */ + +/* init struct queue by zeros */ +static void +init_queue(struct queue *queue) +{ + bzero((void *)queue, sizeof(struct queue)); +} + +/* add pointer to queue */ +/* return - pointer or NULL if error */ +static void * +p2_queue(struct queue *queue, void *pointer) +{ + if (!queue) + return NULL; + + if (!queue->first) + { + if (!(queue->first = queue->last = + calloc(1, sizeof(struct m_queue)))) + return NULL; + + } + else + { + if (!(queue->last->next = + calloc(1, sizeof(struct m_queue)))) + return NULL; + + queue->last = queue->last->next; + } + + queue->size++; + return queue->last->pointer = pointer; +} + +/* get first from queue */ +/* return - pointer or NULL if queue is empty */ +static void * +first_queue(struct queue *queue) +{ + void *retval; + struct m_queue *new_first; + + if (!queue || + !queue->first || + !queue->size) + return NULL; + + retval = queue->first->pointer; + new_first = queue->first->next; + free(queue->first); + queue->first = new_first; + queue->size--; + + return retval; + +} + +/* make zero terminated array from queue */ +/* return - pointer to array or NULL if error */ +static void ** +q2arr(struct queue *queue, int depth) +{ + void **mono, **end; + + if (!queue || + !queue->first || + !queue->size) + return NULL; + + /* memory allocation for array */ + if (!(mono = end = malloc(depth * sizeof(void *) + 1))) + return NULL; + + while(depth--) + { + if (!(*end++ = first_queue(queue))) + break; + } + + *end = NULL; + + return mono; + +} + +/* + * smart_tree (for like find(1) with -d flag output compliance) + * + * return values: + * NULL - malloc error + * not NULL - ok + * + */ +static +unsigned char * +smart_tree(struct queue *queue, + unsigned char FS, + unsigned char *current, + unsigned char *prev) { + unsigned char *pcurrent = current, *pprev = prev, *toqueue; + register char break_flag = 0; + + while(*pcurrent && *pprev) { + if (*pcurrent == *pprev) { + pcurrent++; + pprev++; + } + else { + break_flag = 1; + break; + } + } + + if (!*pprev || break_flag) { + if (*pcurrent == FS) { + pcurrent++; + + if ((!*prev) && (*pcurrent)) { + unsigned char tchar = *pcurrent; + + *pcurrent = '\0'; + if (!(toqueue = strdup(current))) { + *pcurrent = tchar; + return NULL; + } + if (!p2_queue(queue, toqueue)) { + *pcurrent = tchar; + return NULL; + } + *pcurrent = tchar; + } + } + + while(*pcurrent) { + if (*pcurrent == FS) { + *pcurrent = '\0'; + if (!(toqueue = strdup(current))) { + *pcurrent = FS; + return NULL; + } + if (!p2_queue(queue, toqueue)) { + *pcurrent = FS; + return NULL; + } + *pcurrent = FS; + } + pcurrent++; + } + if (!p2_queue(queue, current)) + return NULL; + } + return current; +} + +/* end of utils for ftree */ + +/* utils for make tree */ + +/* if error - return -1 */ +static +int +mk_slip(struct leaf array[], int arr_size, int number, int shift) +{ + int t_number; + int t_shift; + + if (number > arr_size - 1) + return number - 1; + + t_shift = shift; + + if (!(array[number].branches = calloc(1, t_shift + 1))) + return -1; + + (void)memset(array[number].branches, ' ', t_shift); + + t_number = number; + + while (array[number].shift < array[t_number + 1].shift) + { + t_number = mk_slip(array, arr_size, t_number + 1, t_shift + 1); + if (t_number < 0) + return -1; + if (t_number == arr_size - 1) + break; + } + + if (array[number].shift == array[t_number + 1].shift) + array[number].slip = '+'; + + if ((array[number].shift > array[t_number + 1].shift) || + t_number == arr_size - 1) + array[number].slip = '`'; + + return t_number; + +} /* mk_slip() */ + +/* make tree from file + * + * filename - name of file with like find(1) output + * p_names - pointer to array of strings + * p_size - pointer to size of array + * FS - fields separator + * p_array - pointer to array of leafs + * + * return values: + * 0 - ok and names by p_names, size by p_size, array by p_array set + * -1 - memory allocation error (errno set) + */ + +static +int +mk_ftree(char *filename, + unsigned char ***p_names, int *p_size, unsigned char FS, + struct leaf **p_array) +{ + int NR; /* number of input records */ + struct queue queue; + unsigned char *string, *sstring = ""; + unsigned char **names; + + FILE *input_file; + + if (!(input_file = fopen(filename, "r"))) + return -1; + + init_queue(&queue); + + if (!(string = malloc(BUFSIZ))) + return -1; + + /* read input file into queue */ + while(fgets(string, BUFSIZ, input_file)) + { + if (strchr(string, '\n')) + *strchr(string, '\n') = '\0'; + + if (!(string = realloc(string, strlen(string) + 1))) + return -1; + + if (!smart_tree(&queue, FS, string, sstring)) + return -1; + sstring = string; + + if (!(string = malloc(BUFSIZ))) + return -1; + } /* read input file into queue */ + + if (fclose(input_file) == EOF) + return -1; + + if (!(NR = queue.size)) + { + *p_size = 0; + return 0; + } + + /* make array from queue */ + if (!(names = (unsigned char **)q2arr(&queue, NR))) + return -1; + + *p_names = names; + *p_size = NR; + + /* make tree from array */ + return mk_tree(names, NR, FS, p_array); + +} /* mk_ftree */ + +/* make tree from array + * + * names - array of strings + * size - size of array + * FS - fields separator + * p_array - pointer to array of leafs + * + * return values: + * 0 - ok and array by p_array set + * -1 - memory allocation error (errno set) + */ + +static +int +mk_tree(unsigned char **names, int size, unsigned char FS, + struct leaf **p_array) +{ + int i; + struct leaf *array; + + /* make array of leafs */ + if (!(array = calloc(size, sizeof(struct leaf)))) + return -1; + + /* init leafs */ + for (i = 0; i < size; i++) + { + unsigned char *in_string, *name; + int shift = 0; + + in_string = name = names[i]; + while(*in_string) + { + if (*in_string == FS) { + if (!i && !*(in_string + 1)) + name = in_string; + else + { + shift++; + name = in_string + 1; + } + } + in_string++; + } + array[i].name = name; + array[i].shift = shift; + array[i].slip = '\0'; + array[i].branches = NULL; + } /* init leafs */ + + /* make slips */ + for (i = 0;i < size; i++) + { + i = mk_slip(array, size, i, 0); + if (i < 0) + return -1; + } /* make slips */ + + /* make branches */ + for (i = 1;i < size; i++) + { + unsigned char *src = array[i - 1].branches; + unsigned char *dst = array[i].branches; + + while(*src && *dst) + *dst++ = *src++; + + if (*dst) + switch (array[i - 1].slip) { + case '+' : *dst = '|'; + break; + case '`' : *dst = ' '; + } + } /* make branches */ + + *p_array = array; + return 0; + +} /* mk_tree() */ + +/* free memory from tree (leafs) + * + * return values: + * nothing + */ + +static +void +free_leafs(struct leaf *array, int size) +{ + struct leaf *p_array = array; + + while (size--) + free(array++->branches); + + free(p_array); +} /* free_leafs() */ + +/* free memory from source data for tree (names) + * + * return values: + * if 0 <= choice <= size - pointer to name from names, + * and memory for name not released (must be freed later) + * else - NULL (recomended choice -1 for it) + */ + +static +unsigned char * +free_names(unsigned char **names, int size, int choice) +{ + unsigned char *retval = NULL; + unsigned char **p_names = names; + + while (size--) + { + if (!choice--) + retval = *names++; + else + free(*names++); + } + free(p_names); + return retval; +} /* free_names() */ + +/* end of utils for make tree */ + +/* static utils for saved_tree */ + +/* search saved tree within queue */ +/* return - struct *saved_tree or NULL if not found */ +static +struct saved_tree * +search_saved_tree(struct queue *queue, unsigned char **names, int size, + unsigned char FS, + int height, int width, + int menu_height) +{ + struct m_queue *member; + struct saved_tree *retval; + + if (!queue || !names || !FS || + !height || !width || !menu_height) + return NULL; + + if (!(member = queue->first)) + return NULL; + + while (member->next) { + retval = member->pointer; + if ((names == retval->names) && + (size == retval->size) && + (FS == retval->FS) && + (height == retval->height) && + (width == retval->width) && + (menu_height == retval->menu_height)) + return retval; + member = member->next; + } + retval = member->pointer; + if ((names == retval->names) && + (size == retval->size) && + (FS == retval->FS) && + (height == retval->height) && + (width == retval->width) && + (menu_height == retval->menu_height)) + return retval; + return NULL; +} + +/* end of static utils for saved_tree */ diff --git a/gnu/lib/libodialog/ui_objects.c b/gnu/lib/libodialog/ui_objects.c new file mode 100644 index 0000000..dde1513 --- /dev/null +++ b/gnu/lib/libodialog/ui_objects.c @@ -0,0 +1,829 @@ +/* + * Program: objects.c + * Author: Marc van Kempen + * Desc: Implementation of UI-objects: + * - String input fields + * - List selection + * - Buttons + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include <stdlib.h> +#include <sys/param.h> +#include <ncurses.h> +#include <dialog.h> +#include "dialog.priv.h" +#include "ui_objects.h" + +#define ESC 27 + +/*********************************************************************** + * + * Obj routines + * + ***********************************************************************/ + +void +AddObj(ComposeObj **Obj, int objtype, void *obj) +/* + * Desc: Add the object <obj> to the list of objects <Obj> + */ +{ + if (*Obj == NULL) { + /* Create the root object */ + *Obj = (ComposeObj *) malloc( sizeof(ComposeObj) ); + if (!Obj) { + printf("AddObj: Error malloc'ing ComposeObj\n"); + exit(-1); + } + (*Obj)->objtype = objtype; + (*Obj)->obj = obj; + (*Obj)->next = NULL; + (*Obj)->prev = NULL; + } else { + ComposeObj *o = *Obj; + + /* create the next object */ + while (o->next) o = (ComposeObj *) o->next; + o->next = (struct ComposeObj *) malloc( sizeof(ComposeObj) ); + if (!o->next) { + printf("AddObj: Error malloc'ing o->next\n"); + exit(-1); + } + o->next->objtype = objtype; + o->next->obj = obj; + o->next->next = NULL; + o->next->prev = o; + } + + return; +} /* AddObj() */ + +void +FreeObj(ComposeObj *Obj) +/* + * Desc: free the memory occupied by *Obj + */ +{ + ComposeObj *o = Obj; + + o = Obj; + while (o) { + o = Obj->next; + free(Obj); + Obj = o; + } + + return; +} /* FreeObj() */ + + +int +ReadObj(ComposeObj *Obj) +/* + * Desc: navigate through the different objects calling their + * respective navigation routines as necessary + * Pre: Obj != NULL + */ +{ + ComposeObj *o; + ComposeObj *last; /* the last object in the list */ + int ret; /* the return value from the selection routine */ + + /* find the last object in the list */ + last = Obj; + while (last->next) last = last->next; + + ret = 0; + o = Obj; + while ((ret != SEL_BUTTON) && (ret != SEL_ESC)) { + switch(o->objtype) { + case STRINGOBJ: + ret = SelectStringObj((StringObj *) o->obj); + break; + case LISTOBJ: + ret = SelectListObj((ListObj *) o->obj); + break; + case BUTTONOBJ: + ret = SelectButtonObj((ButtonObj *) o->obj); + break; + } + switch(ret) { + case KEY_DOWN: + case SEL_CR: + case SEL_TAB: /* move to the next object in the list */ + if (o->next != NULL) { + o = o->next; /* next object */ + } else { + o = Obj; /* beginning of the list */ + } + break; + + case KEY_UP: + case SEL_BACKTAB: /* move to the previous object in the list */ + if (o->prev != NULL) { + o = o->prev; /* previous object */ + } else { + o = last; /* end of the list */ + } + break; + + case KEY_F(1): /* display help_file */ + case '?': + display_helpfile(); + break; + } + } + + return(ret); + +} /* ReadObj() */ + + +int +PollObj(ComposeObj **Obj) +{ + ComposeObj *last; /* the last object in the list */ + ComposeObj *first; /* the first object in the list */ + int ret; /* the return value from the selection routine */ + + /* find the last object in the list */ + last = *Obj; + while (last->next) last = last->next; + + /* find the first object in the list */ + first = *Obj; + while (first->prev) first = first->prev; + + ret = 0; + switch((*Obj)->objtype) { + case STRINGOBJ: + ret = SelectStringObj((StringObj *) (*Obj)->obj); + break; + case LISTOBJ: + ret = SelectListObj((ListObj *) (*Obj)->obj); + break; + case BUTTONOBJ: + ret = SelectButtonObj((ButtonObj *) (*Obj)->obj); + break; + } + switch(ret) { + case KEY_DOWN: + case SEL_CR: + case SEL_TAB: /* move to the next object in the list */ + if ((*Obj)->next != NULL) { + *Obj = (*Obj)->next; /* next object */ + } else { + *Obj = first; /* beginning of the list */ + } + break; + + case KEY_UP: + case SEL_BACKTAB: /* move to the previous object in the list */ + if ((*Obj)->prev != NULL) { + *Obj = (*Obj)->prev; /* previous object */ + } else { + *Obj = last; /* end of the list */ + } + break; + } + + return(ret); + +} /* PollObj() */ + + +void +DelObj(ComposeObj *Obj) +/* + * Desc: Free all objects + */ +{ + ComposeObj *o; + + o = Obj; + while (Obj != NULL) { + switch(Obj->objtype) { + case STRINGOBJ: + DelStringObj((StringObj *) Obj->obj); + break; + case LISTOBJ: + DelListObj((ListObj *) Obj->obj); + break; + case BUTTONOBJ: + DelButtonObj((ButtonObj *) Obj->obj); + break; + } + Obj = Obj->next; + } + + FreeObj(o); +} /* DelObj() */ + +/*********************************************************************** + * + * StringObj routines + * + ***********************************************************************/ + +static void +outstr(WINDOW *win, char *str, int attrs) +{ + if (attrs & DITEM_NO_ECHO) { + char *cpy; + int n = strlen(str); + + cpy = alloca(n + 1); + memset(cpy, '*', n); + cpy[n] = '\0'; + waddstr(win, cpy); + } + else + waddstr(win, str); +} + +void +RefreshStringObj(StringObj *so) +/* + * Desc: redraw the object + */ +{ + char tmp[512]; + + wmove(so->win, so->y, so->x+1); + wattrset(so->win, dialog_attr); + waddstr(so->win, so->title); + + draw_box(so->win, so->y+1, so->x, 3, so->w, dialog_attr, border_attr); + wattrset(so->win, item_attr); + wmove(so->win, so->y+2, so->x+1); + if (strlen(so->s) > so->w-2) { + strncpy(tmp, (char *) so->s + strlen(so->s) - so->w + 2, so->w - 1); + outstr(so->win, tmp, so->attr_mask); + } else { + outstr(so->win, so->s, so->attr_mask); + } + + return; +} /* RefreshStringObj() */ + +StringObj * +NewStringObj(WINDOW *win, char *title, char *s, int y, int x, int w, int len) +/* + * Desc: Initialize a new stringobj and return a pointer to it. + * Draw the object on the screen at the specified coordinates + */ +{ + StringObj *so; + + /* Initialize a new object */ + so = (StringObj *) malloc( sizeof(StringObj) ); + if (!so) { + printf("NewStringObj: Error malloc'ing StringObj\n"); + exit(-1); + } + so->title = (char *) malloc( strlen(title) + 1); + if (!so->title) { + printf("NewStringObj: Error malloc'ing so->title\n"); + exit(-1); + } + strcpy(so->title, title); + so->s = s; + strcpy(so->s, s); + so->x = x; + so->y = y; + so->w = w; + so->len = len; + so->win = win; + so->attr_mask = DialogInputAttrs; /* Grossly use a global to avoid changing API */ + + /* Draw it on the screen */ + RefreshStringObj(so); + + return(so); +} /* NewStringObj() */ + +int +SelectStringObj(StringObj *so) +/* + * Desc: get input using the info in <so> + */ +{ + int key; + char tmp[so->len+1]; + + strcpy(tmp, so->s); + key = line_edit(so->win, so->y+2, so->x+1, + so->len, so->w-2, inputbox_attr, TRUE, tmp, so->attr_mask); + if ((key == '\n') || (key == '\r') || (key == '\t') || key == (KEY_BTAB) ) { + strcpy(so->s, tmp); + } + RefreshStringObj(so); + if (key == ESC) { + return(SEL_ESC); + } + if (key == '\t') { + return(SEL_TAB); + } + if ( (key == KEY_BTAB) || (key == KEY_F(2)) ) { + return(SEL_BACKTAB); + } + if ((key == '\n') || (key == '\r')) { + return(SEL_CR); + } + return(key); +} /* SelectStringObj() */ + + +void +DelStringObj(StringObj *so) +/* + * Desc: Free the space occupied by <so> + */ +{ + free(so->title); + free(so); + + return; +} + +/*********************************************************************** + * + * ListObj routines + * + ***********************************************************************/ + +void +DrawNames(ListObj *lo) +/* + * Desc: Just refresh the names, not the surrounding box and title + */ +{ + int i, j, h, x, y; + char tmp[MAXPATHLEN]; + + x = lo->x + 1; + y = lo->y + 2; + h = lo->h - 2; + for (i=lo->scroll; i<lo->n && i<lo->scroll+h; i++) { + wmove(lo->win, y+i-lo->scroll, x); + if (lo->seld[i]) { + wattrset(lo->win, A_BOLD); + } else { + wattrset(lo->win, item_attr); + } + if (strlen(lo->name[i]) > lo->w-2) { + strncpy(tmp, lo->name[i], lo->w-2); + tmp[lo->w - 2] = 0; + waddstr(lo->win, tmp); + } else { + waddstr(lo->win, lo->name[i]); + for (j=strlen(lo->name[i]); j<lo->w-2; j++) waddstr(lo->win, " "); + } + } + wattrset(lo->win, item_attr); + while (i<lo->scroll+h) { + wmove(lo->win, y+i-lo->scroll, x); + for (j=0; j<lo->w-2; j++) waddstr(lo->win, " "); + i++; + } + + return; +} /* DrawNames() */ + +void +RefreshListObj(ListObj *lo) +/* + * Desc: redraw the list object + */ +{ + char perc[7]; + + /* setup the box */ + wmove(lo->win, lo->y, lo->x+1); + wattrset(lo->win, dialog_attr); + waddstr(lo->win, lo->title); + draw_box(lo->win, lo->y+1, lo->x, lo->h, lo->w, dialog_attr, border_attr); + + /* draw the names */ + DrawNames(lo); + + /* Draw % indication */ + sprintf(perc, "(%3d%%)", MIN(100, (int) (100 * (lo->sel+lo->h-2) / MAX(1, lo->n)))); + wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); + wattrset(lo->win, dialog_attr); + waddstr(lo->win, perc); + + + return; +} /* RefreshListObj() */ + +ListObj * +NewListObj(WINDOW *win, char *title, char **list, char *listelt, int y, int x, + int h, int w, int n) +/* + * Desc: create a listobj, draw it on the screen and return a pointer to it. + */ +{ + ListObj *lo; + int i; + + /* Initialize a new object */ + lo = (ListObj *) malloc( sizeof(ListObj) ); + if (!lo) { + fprintf(stderr, "NewListObj: Error malloc'ing ListObj\n"); + exit(-1); + } + lo->title = (char *) malloc( strlen(title) + 1); + if (!lo->title) { + fprintf(stderr, "NewListObj: Error malloc'ing lo->title\n"); + exit(-1); + } + strcpy(lo->title, title); + lo->name = list; + if (n>0) { + lo->seld = (int *) malloc( n * sizeof(int) ); + if (!lo->seld) { + fprintf(stderr, "NewListObj: Error malloc'ing lo->seld\n"); + exit(-1); + } + for (i=0; i<n; i++) { + lo->seld[i] = FALSE; + } + } else { + lo->seld = NULL; + } + lo->y = y; + lo->x = x; + lo->w = w; + lo->h = h; + lo->n = n; + lo->scroll = 0; + lo->sel = 0; + lo->elt = listelt; + lo->win = win; + + /* Draw the object on the screen */ + RefreshListObj(lo); + + return(lo); +} /* NewListObj() */ + +void +UpdateListObj(ListObj *lo, char **list, int n) +/* + * Desc: Update the list in the listobject with the provided list + * Pre: lo->name "has been freed" + * "(A i: 0<=i<lo->n: "lo->name[i] has been freed")" + */ +{ + int i; + + if (lo->seld) { + free(lo->seld); + } + + /* Rewrite the list in the object */ + lo->name = list; + if (n>0) { + lo->seld = (int *) malloc( n * sizeof(int) ); + if (!lo->seld) { + fprintf(stderr, "UpdateListObj: Error malloc'ing lo->seld\n"); + exit(-1); + } + for (i=0; i<n; i++) { + lo->seld[i] = FALSE; + } + } else { + lo->seld = NULL; + } + lo->n = n; + lo->scroll = 0; + lo->sel = 0; + + /* Draw the object on the screen */ + RefreshListObj(lo); + + return; +} /* UpdateListObj() */ + +int +SelectListObj(ListObj *lo) +/* + * Desc: get a listname (or listnames), TAB to move on, or ESC ESC to exit + * Pre: lo->n >= 1 + */ +{ + int key, sel_x, sel_y, quit; + char tmp[MAXPATHLEN]; + char perc[4]; + + sel_x = lo->x+1; + sel_y = lo->y + 2 + lo->sel - lo->scroll; + + if (lo->n == 0) return(SEL_TAB); + + keypad(lo->win, TRUE); + + /* Draw current selection in inverse video */ + wmove(lo->win, sel_y, sel_x); + wattrset(lo->win, item_selected_attr); + waddstr(lo->win, lo->name[lo->sel]); + + key = wgetch(lo->win); + quit = FALSE; + while ((key != '\t') && (key != '\n') && (key != '\r') + && (key != ESC) && (key != KEY_F(1)) && (key != '?') && !quit) { + /* first draw current item in normal video */ + wmove(lo->win, sel_y, sel_x); + if (lo->seld[lo->sel]) { + wattrset(lo->win, A_BOLD); + } else { + wattrset(lo->win, item_attr); + } + if (strlen(lo->name[lo->sel]) > lo->w - 2) { + strncpy(tmp, lo->name[lo->sel], lo->w - 2); + tmp[lo->w - 2] = 0; + waddstr(lo->win, tmp); + } else { + waddstr(lo->win, lo->name[lo->sel]); + } + + switch (key) { + case KEY_DOWN: + case ctrl('n'): + if (sel_y < lo->y + lo->h-1) { + if (lo->sel < lo->n-1) { + sel_y++; + lo->sel++; + } + } else { + if (lo->sel < lo->n-1) { + lo->sel++; + lo->scroll++; + DrawNames(lo); + wrefresh(lo->win); + } + } + break; + case KEY_UP: + case ctrl('p'): + if (sel_y > lo->y+2) { + if (lo->sel > 0) { + sel_y--; + lo->sel--; + } + } else { + if (lo->sel > 0) { + lo->sel--; + lo->scroll--; + DrawNames(lo); + wrefresh(lo->win); + } + } + break; + case KEY_HOME: + case ctrl('a'): + lo->sel = 0; + lo->scroll = 0; + sel_y = lo->y + 2; + DrawNames(lo); + wrefresh(lo->win); + break; + case KEY_END: + case ctrl('e'): + if (lo->n < lo->h - 3) { + lo->sel = lo->n-1; + lo->scroll = 0; + sel_y = lo->y + 2 + lo->sel - lo->scroll; + } else { + /* more than one page of list */ + lo->sel = lo->n-1; + lo->scroll = lo->n-1 - (lo->h-3); + sel_y = lo->y + 2 + lo->sel - lo->scroll; + DrawNames(lo); + wrefresh(lo->win); + } + break; + case KEY_NPAGE: + case ctrl('f'): + lo->sel += lo->h - 2; + if (lo->sel >= lo->n) lo->sel = lo->n - 1; + lo->scroll += lo->h - 2; + if (lo->scroll >= lo->n - 1) lo->scroll = lo->n - 1; + if (lo->scroll < 0) lo->scroll = 0; + sel_y = lo->y + 2 + lo->sel - lo->scroll; + DrawNames(lo); + wrefresh(lo->win); + break; + case KEY_PPAGE: + case ctrl('b'): + lo->sel -= lo->h - 2; + if (lo->sel < 0) lo->sel = 0; + lo->scroll -= lo->h - 2; + if (lo->scroll < 0) lo->scroll = 0; + sel_y = lo->y + 2 + lo->sel - lo->scroll; + DrawNames(lo); + wrefresh(lo->win); + break; + default: + quit = TRUE; + break; + } + /* Draw % indication */ + sprintf(perc, "(%3d%%)", MIN(100, (int) + (100 * (lo->sel+lo->h - 2) / MAX(1, lo->n)))); + wmove(lo->win, lo->y + lo->h, lo->x + lo->w - 8); + wattrset(lo->win, dialog_attr); + waddstr(lo->win, perc); + + /* draw current item in inverse */ + wmove(lo->win, sel_y, sel_x); + wattrset(lo->win, item_selected_attr); + if (strlen(lo->name[lo->sel]) > lo->w - 2) { + /* when printing in inverse video show the last characters in the */ + /* name that will fit in the window */ + strncpy(tmp, + lo->name[lo->sel] + strlen(lo->name[lo->sel]) - (lo->w - 2), + lo->w - 2); + tmp[lo->w - 2] = 0; + waddstr(lo->win, tmp); + } else { + waddstr(lo->win, lo->name[lo->sel]); + } + if (!quit) key = wgetch(lo->win); + } + + if (key == ESC) { + return(SEL_ESC); + } + if (key == '\t') { + return(SEL_TAB); + } + if ((key == KEY_BTAB) || (key == ctrl('b'))) { + return(SEL_BACKTAB); + } + if ((key == '\n') || (key == '\r')) { + strcpy(lo->elt, lo->name[lo->sel]); + return(SEL_CR); + } + return(key); +} /* SelectListObj() */ + +void +DelListObj(ListObj *lo) +/* + * Desc: Free the space occupied by the listobject + */ +{ + free(lo->title); + if (lo->seld != NULL) free(lo->seld); + free(lo); + + return; +} /* DelListObj() */ + +void +MarkCurrentListObj(ListObj *lo) +/* + * Desc: mark the current item for the selection list + */ +{ + lo->seld[lo->sel] = !(lo->seld[lo->sel]); + DrawNames(lo); + + return; +} /* MarkCurrentListObj() */ + +void +MarkAllListObj(ListObj *lo) +/* + * Desc: mark all items + */ +{ + int i; + + for (i=0; i<lo->n; i++) { + lo->seld[i] = TRUE; + } + DrawNames(lo); + + return; +} /* MarkAllListObj() */ + +void +UnMarkAllListObj(ListObj *lo) +/* + * Desc: unmark all items + */ +{ + int i; + + for (i=0; i<lo->n; i++) { + lo->seld[i] = FALSE; + } + DrawNames(lo); + + return; +} /* UnMarkAllListObj() */ + + +/*********************************************************************** + * + * ButtonObj routines + * + ***********************************************************************/ + + +void +RefreshButtonObj(ButtonObj *bo) +/* + * Desc: redraw the button + */ +{ + draw_box(bo->win, bo->y, bo->x, 3, bo->w, dialog_attr, border_attr); + print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); + + return; +} /* RefreshButtonObj() */ + +ButtonObj * +NewButtonObj(WINDOW *win, char *title, int *pushed, int y, int x) +/* + * Desc: Create a new button object + */ +{ + ButtonObj *bo; + + bo = (ButtonObj *) malloc( sizeof(ButtonObj) ); + + bo->win = win; + bo->title = (char *) malloc( strlen(title) + 1); + strcpy(bo->title, title); + bo->x = x; + bo->y = y; + bo->w = strlen(title) + 6; + bo->h = 3; + bo->pushed = pushed; + + RefreshButtonObj(bo); + + return(bo); +} /* NewButtonObj() */ + +int +SelectButtonObj(ButtonObj *bo) +/* + * Desc: Wait for buttonpresses or TAB's to move on, or ESC ESC + */ +{ + int key; + + print_button(bo->win, bo->title, bo->y+1, bo->x+2, TRUE); + wmove(bo->win, bo->y+1, bo->x+(bo->w/2)-1); + key = wgetch(bo->win); + print_button(bo->win, bo->title, bo->y+1, bo->x+2, FALSE); + switch(key) { + case '\t': + return(SEL_TAB); + break; + case KEY_BTAB: + case ctrl('b'): + return(SEL_BACKTAB); + case '\n': + case '\r': + *(bo->pushed) = TRUE; + return(SEL_BUTTON); + break; + case ESC: + return(SEL_ESC); + break; + default: + return(key); + break; + } +} /* SelectButtonObj() */ + +void +DelButtonObj(ButtonObj *bo) +/* + * Desc: Free the space occupied by <bo> + */ +{ + free(bo->title); + free(bo); + + return; +} /* DelButtonObj() */ diff --git a/gnu/lib/libodialog/ui_objects.h b/gnu/lib/libodialog/ui_objects.h new file mode 100644 index 0000000..b30feb8 --- /dev/null +++ b/gnu/lib/libodialog/ui_objects.h @@ -0,0 +1,114 @@ +/* + * Author: Marc van Kempen + * Desc: include file for UI-objects + * + * Copyright (c) 1995, Marc van Kempen + * + * All rights reserved. + * + * This software may be used, modified, copied, distributed, and + * sold, in both source and binary form provided that the above + * copyright and these terms are retained, verbatim, as the first + * lines of this file. Under no circumstances is the author + * responsible for the proper functioning of this software, nor does + * the author assume any responsibility for damages incurred with + * its use. + * + */ + +#include "dialog.h" +#include <ncurses.h> + +/*********************************************************************** + * + * Defines + * + ***********************************************************************/ + +#define ctrl(a) ((a) - 'a' + 1) + +/* the Object types */ +#define STRINGOBJ 1 +#define LISTOBJ 2 +#define BUTTONOBJ 3 + +/* the return signals from the selection routines */ +/* 1000 and higher should avoid conflicts with keys pressed */ +#define SEL_CR 1001 /* return was pressed */ +#define SEL_ESC 1002 /* ESC pressed */ +#define SEL_TAB 1003 /* TAB pressed */ +#define SEL_BACKTAB 1004 /* SHIFT-TAB pressed */ +#define SEL_BUTTON 1005 /* a button was pressed */ + +/*********************************************************************** + * + * Typedefs + * + ***********************************************************************/ + +typedef struct { + WINDOW *win; /* the window it's contained in */ + char *title; /* the prompt for the input field */ + char *s; /* initial value of the input field */ + int x, y, w, len; /* the (y, x) position of the upperleft */ + /* corner and the width <w> of the display */ + /* and length <len> of the field */ + int attr_mask; /* special attributes */ +} StringObj; + +typedef struct { + WINDOW *win; /* the windows it's contained in */ + char *title; /* the title of the list */ + char **name; /* the names of the list */ + int *seld; /* the currently selected names */ + char *elt; /* the current element in the list list[sel] */ + int x, y, w, h, n; /* dimensions of list and # of elements (n) */ + int scroll, sel; /* current position in the list */ +} ListObj; + +typedef struct { + WINDOW *win; /* the window it's contained in */ + char *title; /* title for the button */ + int x, y, w, h; /* its dimensions */ + int *pushed; /* boolean that determines wether button was pushed */ +} ButtonObj; + +typedef struct ComposeObj { + int objtype; + void *obj; + struct ComposeObj *next, *prev; +} ComposeObj; + +/********************************************************************** + * + * Prototypes + * + **********************************************************************/ + +void RefreshStringObj(StringObj *so); +StringObj *NewStringObj(WINDOW *win, char *title, char *s, + int y, int x, int w, int len); +int SelectStringObj(StringObj *so); +void DelStringObj(StringObj *so); + +void RefreshListObj(ListObj *lo); +ListObj *NewListObj(WINDOW *win, char *title, char **list, + char *listelt, int y, int x, int h, int w, int n); +void UpdateListObj(ListObj *lo, char **list, int n); +int SelectListObj(ListObj *lo); +void DelListObj(ListObj *obj); +void MarkCurrentListObj(ListObj *lo); +void MarkAllListObj(ListObj *lo); +void UnMarkAllListObj(ListObj *lo); + +void RefreshButtonObj(ButtonObj *bo); +ButtonObj *NewButtonObj(WINDOW *win, char *title, int *pushed, + int y, int x); +int SelectButtonObj(ButtonObj *bo); +void DelButtonObj(ButtonObj *bo); +void AddObj(ComposeObj **Obj, int objtype, void *obj); +void FreeObj(ComposeObj *Obj); +int ReadObj(ComposeObj *Obj); +int PollObj(ComposeObj **Obj); +void DelObj(ComposeObj *Obj); + diff --git a/gnu/lib/libodialog/yesno.c b/gnu/lib/libodialog/yesno.c new file mode 100644 index 0000000..24eb41a --- /dev/null +++ b/gnu/lib/libodialog/yesno.c @@ -0,0 +1,169 @@ +/* + * yesno.c -- implements the yes/no box + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <dialog.h> +#include "dialog.priv.h" + +/* Actual work function */ +static int dialog_yesno_proc(unsigned char *title, unsigned char *prompt, + int height, int width, int yesdefault); + +/* + * Display a dialog box with two buttons - Yes and No + */ +int +dialog_yesno(unsigned char *title, unsigned char *prompt, int height, int width) +{ + return dialog_yesno_proc(title, prompt, height, width, TRUE); +} + +/* + * Display a dialog box with two buttons - No and Yes + */ +int +dialog_noyes(unsigned char *title, unsigned char *prompt, int height, int width) +{ + return dialog_yesno_proc(title, prompt, height, width, FALSE); +} + +static int +dialog_yesno_proc(unsigned char *title, unsigned char *prompt, int height, int width, int yesdefault) +{ + int i, j, x, y, key, button; + WINDOW *dialog; + char *tmphlp; + + /* disable helpline */ + tmphlp = get_helpline(); + use_helpline(NULL); + + if (height < 0) + height = strheight(prompt)+4; + if (width < 0) { + i = strwidth(prompt); + j = ((title != NULL) ? strwidth(title) : 0); + width = MAX(i,j)+4; + } + width = MAX(width,23); + + if (width > COLS) + width = COLS; + if (height > LINES) + height = LINES; + /* center dialog box on screen */ + x = DialogX ? DialogX : (COLS - width)/2; + y = DialogY ? DialogY : (LINES - height)/2; + +#ifdef HAVE_NCURSES + if (use_shadow) + draw_shadow(stdscr, y, x, height, width); +#endif + dialog = newwin(height, width, y, x); + if (dialog == NULL) { + endwin(); + fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x); + exit(1); + } + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); + wattrset(dialog, border_attr); + wmove(dialog, height-3, 0); + waddch(dialog, ACS_LTEE); + for (i = 0; i < width-2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dialog_attr); + waddch(dialog, ACS_RTEE); + wmove(dialog, height-2, 1); + for (i = 0; i < width-2; i++) + waddch(dialog, ' '); + + if (title != NULL) { + wattrset(dialog, title_attr); + wmove(dialog, 0, (width - strlen(title))/2 - 1); + waddch(dialog, ' '); + waddstr(dialog, title); + waddch(dialog, ' '); + } + wattrset(dialog, dialog_attr); + wmove(dialog, 1, 2); + print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE); + + display_helpline(dialog, height-1, width); + + x = width/2-10; + y = height-2; + + /* preset button 0 or 1 for YES or NO as the default */ + key = 0; + button = !yesdefault; + while (key != ESC) { + print_button(dialog, " No ", y, x+13, button); + print_button(dialog, " Yes " , y, x, !button); + if (button) + wmove(dialog, y, x+16); + else + wmove(dialog, y, x+2); + wrefresh(dialog); + + key = wgetch(dialog); + switch (key) { + case 'Y': + case 'y': + delwin(dialog); + restore_helpline(tmphlp); + return 0; + case 'N': + case 'n': + delwin(dialog); + restore_helpline(tmphlp); + return 1; + case KEY_BTAB: + case TAB: + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + button = !button; + /* redrawn at the loop's entry */ + break; + case ' ': + case '\r': + case '\n': + delwin(dialog); + restore_helpline(tmphlp); + return button; + case ESC: + break; + case KEY_F(1): + case '?': + display_helpfile(); + break; + } + } + + delwin(dialog); + restore_helpline(tmphlp); + return -1; /* ESC pressed */ +} +/* End of dialog_yesno() */ diff --git a/gnu/lib/libreadline/Makefile b/gnu/lib/libreadline/Makefile new file mode 100644 index 0000000..637dcc8 --- /dev/null +++ b/gnu/lib/libreadline/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR = history readline + +.include <bsd.subdir.mk> diff --git a/gnu/lib/libreadline/Makefile.inc b/gnu/lib/libreadline/Makefile.inc new file mode 100644 index 0000000..992f929 --- /dev/null +++ b/gnu/lib/libreadline/Makefile.inc @@ -0,0 +1,14 @@ +# $FreeBSD$ + +SHLIB_MAJOR=8 +RL_LIBRARY_VERSION=5.2 + +SRCDIR= ${.CURDIR}/../../../../contrib/libreadline +.PATH: ${SRCDIR} ${.CURDIR}/.. + +LOCAL_CFLAGS= -DRL_LIBRARY_VERSION='"$(RL_LIBRARY_VERSION)"' +CFLAGS+=-I${.CURDIR}/.. -I${SRCDIR} -DHAVE_CONFIG_H ${LOCAL_CFLAGS} + +HISTSRC=history.c histexpand.c histfile.c histsearch.c shell.c mbutil.c + +.include "../Makefile.inc" diff --git a/gnu/lib/libreadline/config.h b/gnu/lib/libreadline/config.h new file mode 100644 index 0000000..c13b50c --- /dev/null +++ b/gnu/lib/libreadline/config.h @@ -0,0 +1,270 @@ +/* $FreeBSD$ */ +/* config.h. Generated by configure. */ +/* config.h.in. Maintained by hand. */ + +/* Define NO_MULTIBYTE_SUPPORT to not compile in support for multibyte + characters, even if the OS supports them. */ +/* #undef NO_MULTIBYTE_SUPPORT */ + +/* Define if on MINIX. */ +/* #undef _MINIX */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +#define VOID_SIGHANDLER 1 + +/* Characteristics of the compiler. */ +/* #undef const */ + +/* #undef size_t */ + +/* #undef ssize_t */ + +#define PROTOTYPES 1 + +/* #undef __CHAR_UNSIGNED__ */ + +/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define if you have the fcntl function. */ +#define HAVE_FCNTL 1 + +/* Define if you have the getpwent function. */ +#define HAVE_GETPWENT 1 + +/* Define if you have the getpwnam function. */ +#define HAVE_GETPWNAM 1 + +/* Define if you have the getpwuid function. */ +#define HAVE_GETPWUID 1 + +/* Define if you have the isascii function. */ +#define HAVE_ISASCII 1 + +/* Define if you have the iswctype function. */ +#define HAVE_ISWCTYPE 1 + +/* Define if you have the iswlower function. */ +#define HAVE_ISWLOWER 1 + +/* Define if you have the iswupper function. */ +#define HAVE_ISWUPPER 1 + +/* Define if you have the isxdigit function. */ +#define HAVE_ISXDIGIT 1 + +/* Define if you have the kill function. */ +#define HAVE_KILL 1 + +/* Define if you have the lstat function. */ +#define HAVE_LSTAT 1 + +/* Define if you have the mbrlen function. */ +#define HAVE_MBRLEN 1 + +/* Define if you have the mbrtowc function. */ +#define HAVE_MBRTOWC 1 + +/* Define if you have the mbsrtowcs function. */ +#define HAVE_MBSRTOWCS 1 + +/* Define if you have the memmove function. */ +#define HAVE_MEMMOVE 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define if you have the setenv function. */ +#define HAVE_SETENV 1 + +/* Define if you have the setlocale function. */ +#define HAVE_SETLOCALE 1 + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define if you have the strcoll function. */ +#define HAVE_STRCOLL 1 + +/* #undef STRCOLL_BROKEN */ + +/* Define if you have the strpbrk function. */ +#define HAVE_STRPBRK 1 + +/* Define if you have the tcgetattr function. */ +#define HAVE_TCGETATTR 1 + +/* Define if you have the towlower function. */ +#define HAVE_TOWLOWER 1 + +/* Define if you have the towupper function. */ +#define HAVE_TOWUPPER 1 + +/* Define if you have the vsnprintf function. */ +#define HAVE_VSNPRINTF 1 + +/* Define if you have the wcrtomb function. */ +#define HAVE_WCRTOMB 1 + +/* Define if you have the wcscoll function. */ +#define HAVE_WCSCOLL 1 + +/* Define if you have the wctype function. */ +#define HAVE_WCTYPE 1 + +/* Define if you have the wcwidth function. */ +#define HAVE_WCWIDTH 1 + +#define STDC_HEADERS 1 + +/* Define if you have the <dirent.h> header file. */ +#define HAVE_DIRENT_H 1 + +/* Define if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the <langinfo.h> header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the <ndir.h> header file. */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the <pwd.h> header file. */ +#define HAVE_PWD_H 1 + +/* Define if you have the <stdarg.h> header file. */ +#define HAVE_STDARG_H 1 + +/* Define if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define if you have the <sys/dir.h> header file. */ +/* #undef HAVE_SYS_DIR_H */ + +/* Define if you have the <sys/file.h> header file. */ +#define HAVE_SYS_FILE_H 1 + +/* Define if you have the <sys/ndir.h> header file. */ +/* #undef HAVE_SYS_NDIR_H */ + +/* Define if you have the <sys/pte.h> header file. */ +/* #undef HAVE_SYS_PTE_H */ + +/* Define if you have the <sys/ptem.h> header file. */ +/* #undef HAVE_SYS_PTEM_H */ + +/* Define if you have the <sys/select.h> header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define if you have the <sys/stream.h> header file. */ +/* #undef HAVE_SYS_STREAM_H */ + +/* Define if you have the <termcap.h> header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define if you have the <termio.h> header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define if you have the <termios.h> header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the <varargs.h> header file. */ +/* #undef HAVE_VARARGS_H */ + +/* Define if you have the <wchar.h> header file. */ +#define HAVE_WCHAR_H 1 + +/* Define if you have the <wctype.h> header file. */ +#define HAVE_WCTYPE_H 1 + +#define HAVE_MBSTATE_T 1 + +/* Define if you have wchar_t in <wctype.h>. */ +#define HAVE_WCHAR_T 1 + +/* Define if you have wctype_t in <wctype.h>. */ +#define HAVE_WCTYPE_T 1 + +/* Define if you have wint_t in <wctype.h>. */ +#define HAVE_WINT_T 1 + +/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Definitions pulled in from aclocal.m4. */ +#define VOID_SIGHANDLER 1 + +/* #undef GWINSZ_IN_SYS_IOCTL */ + +#define STRUCT_WINSIZE_IN_SYS_IOCTL 1 + +/* #undef STRUCT_WINSIZE_IN_TERMIOS */ + +#define TIOCSTAT_IN_SYS_IOCTL 1 + +#define FIONREAD_IN_SYS_IOCTL 1 + +/* #undef SPEED_T_IN_SYS_TYPES */ + +#define HAVE_GETPW_DECLS 1 + +/* #undef STRUCT_DIRENT_HAS_D_INO */ + +/* #undef STRUCT_DIRENT_HAS_D_FILENO */ + +/* #undef HAVE_BSD_SIGNALS */ + +#define HAVE_POSIX_SIGNALS 1 + +/* #undef HAVE_USG_SIGHOLD */ + +/* #undef MUST_REINSTALL_SIGHANDLERS */ + +#define HAVE_POSIX_SIGSETJMP 1 + +#define CTYPE_NON_ASCII 1 + +/* modify settings or make new ones based on what autoconf tells us. */ + +/* Ultrix botches type-ahead when switching from canonical to + non-canonical mode, at least through version 4.3 */ +#if !defined (HAVE_TERMIOS_H) || !defined (HAVE_TCGETATTR) || defined (ultrix) +# define TERMIOS_MISSING +#endif + +#if defined (STRCOLL_BROKEN) +# define HAVE_STRCOLL 1 +#endif + +#if defined (__STDC__) && defined (HAVE_STDARG_H) +# define PREFER_STDARG +# define USE_VARARGS +#else +# if defined (HAVE_VARARGS_H) +# define PREFER_VARARGS +# define USE_VARARGS +# endif +#endif diff --git a/gnu/lib/libreadline/history/Makefile b/gnu/lib/libreadline/history/Makefile new file mode 100644 index 0000000..1602872 --- /dev/null +++ b/gnu/lib/libreadline/history/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +SUBDIR= doc + +LIB= history +MAN= rlhistory.3 + +SRCS= $(HISTSRC) xmalloc.c + +rlhistory.3: doc/history.3 + cp -f ${.ALLSRC} ${.TARGET} + +CLEANFILES+= rlhistory.3 + +.include <bsd.lib.mk> diff --git a/gnu/lib/libreadline/history/doc/Makefile b/gnu/lib/libreadline/history/doc/Makefile new file mode 100644 index 0000000..4b050e7 --- /dev/null +++ b/gnu/lib/libreadline/history/doc/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +SRCDIR= ${.CURDIR}/../../../../../contrib/libreadline/doc + +INFO = history + +INFOSECTION= "Programming & development tools." +INFOENTRY_history= "* History: (history). The GNU History library." + +history.info: history.texi hstech.texi hsuser.texi version.texi fdl.texi + +.include <bsd.info.mk> diff --git a/gnu/lib/libreadline/readline/Makefile b/gnu/lib/libreadline/readline/Makefile new file mode 100644 index 0000000..fab4aa2 --- /dev/null +++ b/gnu/lib/libreadline/readline/Makefile @@ -0,0 +1,27 @@ +# $FreeBSD$ + +SUBDIR= doc + +LIB= readline +MAN= doc/readline.3 +SHLIBDIR?= /lib + +TILDESRC= tilde.c +SRCS= readline.c vi_mode.c funmap.c keymaps.c parens.c search.c \ + rltty.c complete.c bind.c isearch.c display.c signals.c \ + util.c kill.c undo.c macro.c input.c callback.c terminal.c \ + text.c nls.c misc.c compat.c xmalloc.c $(HISTSRC) $(TILDESRC) + +INSTALLED_HEADERS= readline.h chardefs.h keymaps.h history.h tilde.h \ + rlstdc.h rlconf.h rltypedefs.h + +DPADD= ${LIBTERMCAP} +LDADD= -ltermcap + +INCSDIR=${INCLUDEDIR}/readline + +.for hdr in ${INSTALLED_HEADERS} +INCS+= ${SRCDIR}/${hdr} +.endfor + +.include <bsd.lib.mk> diff --git a/gnu/lib/libreadline/readline/doc/Makefile b/gnu/lib/libreadline/readline/doc/Makefile new file mode 100644 index 0000000..7028971 --- /dev/null +++ b/gnu/lib/libreadline/readline/doc/Makefile @@ -0,0 +1,20 @@ +# $FreeBSD$ + +SRCDIR= ${.CURDIR}/../../../../../contrib/libreadline/doc + +INFO = readline rluserman + +INFOSECTION= "Programming & development tools." +INFOENTRY_readline= "* Readline: (readline). The GNU Readline library" +INFOENTRY_rluserman= "* Readline: (readline). The GNU readline library API" + +CLEANFILES += readline.texi + +readline.info: rlman.texi rluser.texi rltech.texi version.texi fdl.texi + +readline.texi: rlman.texi + cp -f ${SRCDIR}/rlman.texi ${.TARGET} + +rluserman.info: rluserman.texi version.texi rluser.texi fdl.texi + +.include <bsd.info.mk> diff --git a/gnu/lib/libregex/FREEBSD-upgrade b/gnu/lib/libregex/FREEBSD-upgrade new file mode 100644 index 0000000..d072aaf --- /dev/null +++ b/gnu/lib/libregex/FREEBSD-upgrade @@ -0,0 +1,18 @@ +$FreeBSD$ + +GNU regex (from glibc): + +Imported by: + +cvs -z 9 -d :pserver:anoncvs@sources.redhat.com:/cvs/glibc login +# enter "anoncvs" as the password +cvs -z 9 -d :pserver:anoncvs@sources.redhat.com:/cvs/glibc \ + co -r fedora-glibc-2_3_4-21 libc/posix libc/include +mkdir regex regex/posix +cd libc/posix +cp regcomp.c regex.c regex_internal.c regex_internal.h regexec.c $OLDPWD/regex/ +cp regex.h $OLDPWD/regex/posix/ +cd - +cp libc/include/regex.h regex/ +cd regex +cvs import src/gnu/lib/libregex FSF fedora-glibc-2_3_4-21 diff --git a/gnu/lib/libregex/Makefile b/gnu/lib/libregex/Makefile new file mode 100644 index 0000000..39692d4 --- /dev/null +++ b/gnu/lib/libregex/Makefile @@ -0,0 +1,28 @@ +# $FreeBSD$ + +SUBDIR= doc + +LIB= gnuregex +SHLIB_MAJOR= 5 + +SRCS= gnuregex.c +INCSGROUPS= INCS WRINCS PXINCS +INCS= regex.h.patched +INCSNAME= regex.h +INCSDIR= ${INCLUDEDIR}/gnu +WRINCS= gnuregex.h +PXINCS= posix/regex.h +PXINCSDIR= ${INCSDIR}/posix + +CFLAGS+=-DHAVE_CONFIG_H -I${.CURDIR} + +CLEANFILES= regex.h.patched gnuregex.c +regex.h.patched: regex.h + sed 's=<posix/regex\.h>=<gnu/posix/regex.h>=g' \ + < ${.ALLSRC} > ${.TARGET} + +gnuregex.c: regex.c + sed 's=<regex\.h>=<gnu/regex.h>=g' \ + < ${.ALLSRC} > ${.TARGET} + +.include <bsd.lib.mk> diff --git a/gnu/lib/libregex/config.h b/gnu/lib/libregex/config.h new file mode 100644 index 0000000..bf7f0a0 --- /dev/null +++ b/gnu/lib/libregex/config.h @@ -0,0 +1,12 @@ +/* $FreeBSD$ */ + +#define _REGEX_RE_COMP 1 +#define HAVE_LANGINFO_H 1 +#define HAVE_LANGINFO_CODESET 1 +#define HAVE_LOCALE_H 1 +#define HAVE_WCHAR_H 1 +#define HAVE_WCTYPE_H 1 +#define HAVE_ISBLANK 1 +#define HAVE_WCRTOMB 1 +#define HAVE_MBRTOWC 1 +#define HAVE_WCSCOLL 1 diff --git a/gnu/lib/libregex/doc/Makefile b/gnu/lib/libregex/doc/Makefile new file mode 100644 index 0000000..b812f44 --- /dev/null +++ b/gnu/lib/libregex/doc/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +INFO = regex +INFOSECTION= "Programming & development tools." +INFOENTRY_regex= "* Regex: (regex). Regular expression library." + +CLEANFILES += regex.texi + +regex.texi: xregex.texi ../regex.h + awk -f ${.CURDIR}/include.awk -v source=${.CURDIR}/../regex.h \ + < ${.CURDIR}/xregex.texi \ + | expand >$@ + +.include <bsd.info.mk> diff --git a/gnu/lib/libregex/doc/include.awk b/gnu/lib/libregex/doc/include.awk new file mode 100644 index 0000000..a1df3f8 --- /dev/null +++ b/gnu/lib/libregex/doc/include.awk @@ -0,0 +1,19 @@ +# Assume `source' is set with -vsource=filename on the command line. +# +/^\[\[\[/ { inclusion = $2; # name of the thing to include. + printing = 0; + while ((getline line < source) > 0) + { + if (match (line, "\\[\\[\\[end " inclusion "\\]\\]\\]")) + printing = 0; + + if (printing) + print line; + + if (match (line,"\\[\\[\\[begin " inclusion "\\]\\]\\]")) + printing = 1; + } + close (source); + next; + } + { print } diff --git a/gnu/lib/libregex/doc/xregex.texi b/gnu/lib/libregex/doc/xregex.texi new file mode 100644 index 0000000..fe01e4e --- /dev/null +++ b/gnu/lib/libregex/doc/xregex.texi @@ -0,0 +1,3021 @@ +\input texinfo +@c %**start of header +@setfilename regex.info +@settitle Regex +@c %**end of header + +@c \\{fill-paragraph} works better (for me, anyway) if the text in the +@c source file isn't indented. +@paragraphindent 2 + +@c Define a new index for our magic constants. +@defcodeindex cn + +@c Put everything in one index (arbitrarily chosen to be the concept index). +@syncodeindex cn cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp +@syncodeindex vr cp + +@c Here is what we use in the Info `dir' file: +@c * Regex: (regex). Regular expression library. + + +@ifinfo +This file documents the GNU regular expression library. + +Copyright (C) 1992, 1993 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. +@end ifinfo + + +@titlepage + +@title Regex +@subtitle edition 0.12a +@subtitle 19 September 1992 +@author Kathryn A. Hargreaves +@author Karl Berry + +@page + +@vskip 0pt plus 1filll +Copyright @copyright{} 1992 Free Software Foundation. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +section entitled ``GNU General Public License'' is included exactly as +in the original, and provided that the entire resulting derived work is +distributed under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that the section entitled ``GNU General Public License'' may be +included in a translation approved by the Free Software Foundation +instead of in the original English. + +@end titlepage + + +@ifinfo +@node Top, Overview, (dir), (dir) +@top Regular Expression Library + +This manual documents how to program with the GNU regular expression +library. This is edition 0.12a of the manual, 19 September 1992. + +The first part of this master menu lists the major nodes in this Info +document, including the index. The rest of the menu lists all the +lower level nodes in the document. + +@menu +* Overview:: +* Regular Expression Syntax:: +* Common Operators:: +* GNU Operators:: +* GNU Emacs Operators:: +* What Gets Matched?:: +* Programming with Regex:: +* Copying:: Copying and sharing Regex. +* Index:: General index. + --- The Detailed Node Listing --- + +Regular Expression Syntax + +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: + +Common Operators + +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ + +Repetition Operators + +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} + +List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +* Character Class Operators:: [:class:] +* Range Operator:: start-end + +Anchoring Operators + +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ + +GNU Operators + +* Word Operators:: +* Buffer Operators:: + +Word Operators + +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W + +Buffer Operators + +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' + +GNU Emacs Operators + +* Syntactic Class Operators:: + +Syntactic Class Operators + +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS + +Programming with Regex + +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: + +GNU Regex Functions + +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () + +POSIX Regex Functions + +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () + +BSD Regex Functions + +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu +@end ifinfo +@node Overview, Regular Expression Syntax, Top, Top +@chapter Overview + +A @dfn{regular expression} (or @dfn{regexp}, or @dfn{pattern}) is a text +string that describes some (mathematical) set of strings. A regexp +@var{r} @dfn{matches} a string @var{s} if @var{s} is in the set of +strings described by @var{r}. + +Using the Regex library, you can: + +@itemize @bullet + +@item +see if a string matches a specified pattern as a whole, and + +@item +search within a string for a substring matching a specified pattern. + +@end itemize + +Some regular expressions match only one string, i.e., the set they +describe has only one member. For example, the regular expression +@samp{foo} matches the string @samp{foo} and no others. Other regular +expressions match more than one string, i.e., the set they describe has +more than one member. For example, the regular expression @samp{f*} +matches the set of strings made up of any number (including zero) of +@samp{f}s. As you can see, some characters in regular expressions match +themselves (such as @samp{f}) and some don't (such as @samp{*}); the +ones that don't match themselves instead let you specify patterns that +describe many different strings. + +To either match or search for a regular expression with the Regex +library functions, you must first compile it with a Regex pattern +compiling function. A @dfn{compiled pattern} is a regular expression +converted to the internal format used by the library functions. Once +you've compiled a pattern, you can use it for matching or searching any +number of times. + +The Regex library consists of two source files: @file{regex.h} and +@file{regex.c}. +@pindex regex.h +@pindex regex.c +Regex provides three groups of functions with which you can operate on +regular expressions. One group---the @sc{gnu} group---is more powerful +but not completely compatible with the other two, namely the @sc{posix} +and Berkeley @sc{unix} groups; its interface was designed specifically +for @sc{gnu}. The other groups have the same interfaces as do the +regular expression functions in @sc{posix} and Berkeley +@sc{unix}. + +We wrote this chapter with programmers in mind, not users of +programs---such as Emacs---that use Regex. We describe the Regex +library in its entirety, not how to write regular expressions that a +particular program understands. + + +@node Regular Expression Syntax, Common Operators, Overview, Top +@chapter Regular Expression Syntax + +@cindex regular expressions, syntax of +@cindex syntax of regular expressions + +@dfn{Characters} are things you can type. @dfn{Operators} are things in +a regular expression that match one or more characters. You compose +regular expressions from operators, which in turn you specify using one +or more characters. + +Most characters represent what we call the match-self operator, i.e., +they match themselves; we call these characters @dfn{ordinary}. Other +characters represent either all or parts of fancier operators; e.g., +@samp{.} represents what we call the match-any-character operator +(which, no surprise, matches (almost) any character); we call these +characters @dfn{special}. Two different things determine what +characters represent what operators: + +@enumerate +@item +the regular expression syntax your program has told the Regex library to +recognize, and + +@item +the context of the character in the regular expression. +@end enumerate + +In the following sections, we describe these things in more detail. + +@menu +* Syntax Bits:: +* Predefined Syntaxes:: +* Collating Elements vs. Characters:: +* The Backslash Character:: +@end menu + + +@node Syntax Bits, Predefined Syntaxes, , Regular Expression Syntax +@section Syntax Bits + +@cindex syntax bits + +In any particular syntax for regular expressions, some characters are +always special, others are sometimes special, and others are never +special. The particular syntax that Regex recognizes for a given +regular expression depends on the value in the @code{syntax} field of +the pattern buffer of that regular expression. + +You get a pattern buffer by compiling a regular expression. @xref{GNU +Pattern Buffers}, and @ref{POSIX Pattern Buffers}, for more information +on pattern buffers. @xref{GNU Regular Expression Compiling}, @ref{POSIX +Regular Expression Compiling}, and @ref{BSD Regular Expression +Compiling}, for more information on compiling. + +Regex considers the value of the @code{syntax} field to be a collection +of bits; we refer to these bits as @dfn{syntax bits}. In most cases, +they affect what characters represent what operators. We describe the +meanings of the operators to which we refer in @ref{Common Operators}, +@ref{GNU Operators}, and @ref{GNU Emacs Operators}. + +For reference, here is the complete list of syntax bits, in alphabetical +order: + +@table @code + +@cnindex RE_BACKSLASH_ESCAPE_IN_LIST +@item RE_BACKSLASH_ESCAPE_IN_LISTS +If this bit is set, then @samp{\} inside a list (@pxref{List Operators} +quotes (makes ordinary, if it's special) the following character; if +this bit isn't set, then @samp{\} is an ordinary character inside lists. +(@xref{The Backslash Character}, for what `\' does outside of lists.) + +@cnindex RE_BK_PLUS_QM +@item RE_BK_PLUS_QM +If this bit is set, then @samp{\+} represents the match-one-or-more +operator and @samp{\?} represents the match-zero-or-more operator; if +this bit isn't set, then @samp{+} represents the match-one-or-more +operator and @samp{?} represents the match-zero-or-one operator. This +bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_CHAR_CLASSES +@item RE_CHAR_CLASSES +If this bit is set, then you can use character classes in lists; if this +bit isn't set, then you can't. + +@cnindex RE_CONTEXT_INDEP_ANCHORS +@item RE_CONTEXT_INDEP_ANCHORS +If this bit is set, then @samp{^} and @samp{$} are special anywhere outside +a list; if this bit isn't set, then these characters are special only in +certain contexts. @xref{Match-beginning-of-line Operator}, and +@ref{Match-end-of-line Operator}. + +@cnindex RE_CONTEXT_INDEP_OPS +@item RE_CONTEXT_INDEP_OPS +If this bit is set, then certain characters are special anywhere outside +a list; if this bit isn't set, then those characters are special only in +some contexts and are ordinary elsewhere. Specifically, if this bit +isn't set then @samp{*}, and (if the syntax bit @code{RE_LIMITED_OPS} +isn't set) @samp{+} and @samp{?} (or @samp{\+} and @samp{\?}, depending +on the syntax bit @code{RE_BK_PLUS_QM}) represent repetition operators +only if they're not first in a regular expression or just after an +open-group or alternation operator. The same holds for @samp{@{} (or +@samp{\@{}, depending on the syntax bit @code{RE_NO_BK_BRACES}) if +it is the beginning of a valid interval and the syntax bit +@code{RE_INTERVALS} is set. + +@cnindex RE_CONTEXT_INVALID_OPS +@item RE_CONTEXT_INVALID_OPS +If this bit is set, then repetition and alternation operators can't be +in certain positions within a regular expression. Specifically, the +regular expression is invalid if it has: + +@itemize @bullet + +@item +a repetition operator first in the regular expression or just after a +match-beginning-of-line, open-group, or alternation operator; or + +@item +an alternation operator first or last in the regular expression, just +before a match-end-of-line operator, or just after an alternation or +open-group operator. + +@end itemize + +If this bit isn't set, then you can put the characters representing the +repetition and alternation characters anywhere in a regular expression. +Whether or not they will in fact be operators in certain positions +depends on other syntax bits. + +@cnindex RE_DOT_NEWLINE +@item RE_DOT_NEWLINE +If this bit is set, then the match-any-character operator matches +a newline; if this bit isn't set, then it doesn't. + +@cnindex RE_DOT_NOT_NULL +@item RE_DOT_NOT_NULL +If this bit is set, then the match-any-character operator doesn't match +a null character; if this bit isn't set, then it does. + +@cnindex RE_INTERVALS +@item RE_INTERVALS +If this bit is set, then Regex recognizes interval operators; if this bit +isn't set, then it doesn't. + +@cnindex RE_LIMITED_OPS +@item RE_LIMITED_OPS +If this bit is set, then Regex doesn't recognize the match-one-or-more, +match-zero-or-one or alternation operators; if this bit isn't set, then +it does. + +@cnindex RE_NEWLINE_ALT +@item RE_NEWLINE_ALT +If this bit is set, then newline represents the alternation operator; if +this bit isn't set, then newline is ordinary. + +@cnindex RE_NO_BK_BRACES +@item RE_NO_BK_BRACES +If this bit is set, then @samp{@{} represents the open-interval operator +and @samp{@}} represents the close-interval operator; if this bit isn't +set, then @samp{\@{} represents the open-interval operator and +@samp{\@}} represents the close-interval operator. This bit is relevant +only if @code{RE_INTERVALS} is set. + +@cnindex RE_NO_BK_PARENS +@item RE_NO_BK_PARENS +If this bit is set, then @samp{(} represents the open-group operator and +@samp{)} represents the close-group operator; if this bit isn't set, then +@samp{\(} represents the open-group operator and @samp{\)} represents +the close-group operator. + +@cnindex RE_NO_BK_REFS +@item RE_NO_BK_REFS +If this bit is set, then Regex doesn't recognize @samp{\}@var{digit} as +the back reference operator; if this bit isn't set, then it does. + +@cnindex RE_NO_BK_VBAR +@item RE_NO_BK_VBAR +If this bit is set, then @samp{|} represents the alternation operator; +if this bit isn't set, then @samp{\|} represents the alternation +operator. This bit is irrelevant if @code{RE_LIMITED_OPS} is set. + +@cnindex RE_NO_EMPTY_RANGES +@item RE_NO_EMPTY_RANGES +If this bit is set, then a regular expression with a range whose ending +point collates lower than its starting point is invalid; if this bit +isn't set, then Regex considers such a range to be empty. + +@cnindex RE_UNMATCHED_RIGHT_PAREN_ORD +@item RE_UNMATCHED_RIGHT_PAREN_ORD +If this bit is set and the regular expression has no matching open-group +operator, then Regex considers what would otherwise be a close-group +operator (based on how @code{RE_NO_BK_PARENS} is set) to match @samp{)}. + +@end table + + +@node Predefined Syntaxes, Collating Elements vs. Characters, Syntax Bits, Regular Expression Syntax +@section Predefined Syntaxes + +If you're programming with Regex, you can set a pattern buffer's +(@pxref{GNU Pattern Buffers}, and @ref{POSIX Pattern Buffers}) +@code{syntax} field either to an arbitrary combination of syntax bits +(@pxref{Syntax Bits}) or else to the configurations defined by Regex. +These configurations define the syntaxes used by certain +programs---@sc{gnu} Emacs, +@cindex Emacs +@sc{posix} Awk, +@cindex POSIX Awk +traditional Awk, +@cindex Awk +Grep, +@cindex Grep +@cindex Egrep +Egrep---in addition to syntaxes for @sc{posix} basic and extended +regular expressions. + +The predefined syntaxes--taken directly from @file{regex.h}---are: + +@example +[[[ syntaxes ]]] +@end example + +@node Collating Elements vs. Characters, The Backslash Character, Predefined Syntaxes, Regular Expression Syntax +@section Collating Elements vs.@: Characters + +@sc{posix} generalizes the notion of a character to that of a +collating element. It defines a @dfn{collating element} to be ``a +sequence of one or more bytes defined in the current collating sequence +as a unit of collation.'' + +This generalizes the notion of a character in +two ways. First, a single character can map into two or more collating +elements. For example, the German +@tex +`\ss' +@end tex +@ifinfo +``es-zet'' +@end ifinfo +collates as the collating element @samp{s} followed by another collating +element @samp{s}. Second, two or more characters can map into one +collating element. For example, the Spanish @samp{ll} collates after +@samp{l} and before @samp{m}. + +Since @sc{posix}'s ``collating element'' preserves the essential idea of +a ``character,'' we use the latter, more familiar, term in this document. + +@node The Backslash Character, , Collating Elements vs. Characters, Regular Expression Syntax +@section The Backslash Character + +@cindex \ +The @samp{\} character has one of four different meanings, depending on +the context in which you use it and what syntax bits are set +(@pxref{Syntax Bits}). It can: 1) stand for itself, 2) quote the next +character, 3) introduce an operator, or 4) do nothing. + +@enumerate +@item +It stands for itself inside a list +(@pxref{List Operators}) if the syntax bit +@code{RE_BACKSLASH_ESCAPE_IN_LISTS} is not set. For example, @samp{[\]} +would match @samp{\}. + +@item +It quotes (makes ordinary, if it's special) the next character when you +use it either: + +@itemize @bullet +@item +outside a list,@footnote{Sometimes +you don't have to explicitly quote special characters to make +them ordinary. For instance, most characters lose any special meaning +inside a list (@pxref{List Operators}). In addition, if the syntax bits +@code{RE_CONTEXT_INVALID_OPS} and @code{RE_CONTEXT_INDEP_OPS} +aren't set, then (for historical reasons) the matcher considers special +characters ordinary if they are in contexts where the operations they +represent make no sense; for example, then the match-zero-or-more +operator (represented by @samp{*}) matches itself in the regular +expression @samp{*foo} because there is no preceding expression on which +it can operate. It is poor practice, however, to depend on this +behavior; if you want a special character to be ordinary outside a list, +it's better to always quote it, regardless.} or + +@item +inside a list and the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is set. + +@end itemize + +@item +It introduces an operator when followed by certain ordinary +characters---sometimes only when certain syntax bits are set. See the +cases @code{RE_BK_PLUS_QM}, @code{RE_NO_BK_BRACES}, @code{RE_NO_BK_VAR}, +@code{RE_NO_BK_PARENS}, @code{RE_NO_BK_REF} in @ref{Syntax Bits}. Also: + +@itemize @bullet +@item +@samp{\b} represents the match-word-boundary operator +(@pxref{Match-word-boundary Operator}). + +@item +@samp{\B} represents the match-within-word operator +(@pxref{Match-within-word Operator}). + +@item +@samp{\<} represents the match-beginning-of-word operator @* +(@pxref{Match-beginning-of-word Operator}). + +@item +@samp{\>} represents the match-end-of-word operator +(@pxref{Match-end-of-word Operator}). + +@item +@samp{\w} represents the match-word-constituent operator +(@pxref{Match-word-constituent Operator}). + +@item +@samp{\W} represents the match-non-word-constituent operator +(@pxref{Match-non-word-constituent Operator}). + +@item +@samp{\`} represents the match-beginning-of-buffer +operator and @samp{\'} represents the match-end-of-buffer operator +(@pxref{Buffer Operators}). + +@item +If Regex was compiled with the C preprocessor symbol @code{emacs} +defined, then @samp{\s@var{class}} represents the match-syntactic-class +operator and @samp{\S@var{class}} represents the +match-not-syntactic-class operator (@pxref{Syntactic Class Operators}). + +@end itemize + +@item +In all other cases, Regex ignores @samp{\}. For example, +@samp{\n} matches @samp{n}. + +@end enumerate + +@node Common Operators, GNU Operators, Regular Expression Syntax, Top +@chapter Common Operators + +You compose regular expressions from operators. In the following +sections, we describe the regular expression operators specified by +@sc{posix}; @sc{gnu} also uses these. Most operators have more than one +representation as characters. @xref{Regular Expression Syntax}, for +what characters represent what operators under what circumstances. + +For most operators that can be represented in two ways, one +representation is a single character and the other is that character +preceded by @samp{\}. For example, either @samp{(} or @samp{\(} +represents the open-group operator. Which one does depends on the +setting of a syntax bit, in this case @code{RE_NO_BK_PARENS}. Why is +this so? Historical reasons dictate some of the varying +representations, while @sc{posix} dictates others. + +Finally, almost all characters lose any special meaning inside a list +(@pxref{List Operators}). + +@menu +* Match-self Operator:: Ordinary characters. +* Match-any-character Operator:: . +* Concatenation Operator:: Juxtaposition. +* Repetition Operators:: * + ? @{@} +* Alternation Operator:: | +* List Operators:: [...] [^...] +* Grouping Operators:: (...) +* Back-reference Operator:: \digit +* Anchoring Operators:: ^ $ +@end menu + +@node Match-self Operator, Match-any-character Operator, , Common Operators +@section The Match-self Operator (@var{ordinary character}) + +This operator matches the character itself. All ordinary characters +(@pxref{Regular Expression Syntax}) represent this operator. For +example, @samp{f} is always an ordinary character, so the regular +expression @samp{f} matches only the string @samp{f}. In +particular, it does @emph{not} match the string @samp{ff}. + +@node Match-any-character Operator, Concatenation Operator, Match-self Operator, Common Operators +@section The Match-any-character Operator (@code{.}) + +@cindex @samp{.} + +This operator matches any single printing or nonprinting character +except it won't match a: + +@table @asis +@item newline +if the syntax bit @code{RE_DOT_NEWLINE} isn't set. + +@item null +if the syntax bit @code{RE_DOT_NOT_NULL} is set. + +@end table + +The @samp{.} (period) character represents this operator. For example, +@samp{a.b} matches any three-character string beginning with @samp{a} +and ending with @samp{b}. + +@node Concatenation Operator, Repetition Operators, Match-any-character Operator, Common Operators +@section The Concatenation Operator + +This operator concatenates two regular expressions @var{a} and @var{b}. +No character represents this operator; you simply put @var{b} after +@var{a}. The result is a regular expression that will match a string if +@var{a} matches its first part and @var{b} matches the rest. For +example, @samp{xy} (two match-self operators) matches @samp{xy}. + +@node Repetition Operators, Alternation Operator, Concatenation Operator, Common Operators +@section Repetition Operators + +Repetition operators repeat the preceding regular expression a specified +number of times. + +@menu +* Match-zero-or-more Operator:: * +* Match-one-or-more Operator:: + +* Match-zero-or-one Operator:: ? +* Interval Operators:: @{@} +@end menu + +@node Match-zero-or-more Operator, Match-one-or-more Operator, , Repetition Operators +@subsection The Match-zero-or-more Operator (@code{*}) + +@cindex @samp{*} + +This operator repeats the smallest possible preceding regular expression +as many times as necessary (including zero) to match the pattern. +@samp{*} represents this operator. For example, @samp{o*} +matches any string made up of zero or more @samp{o}s. Since this +operator operates on the smallest preceding regular expression, +@samp{fo*} has a repeating @samp{o}, not a repeating @samp{fo}. So, +@samp{fo*} matches @samp{f}, @samp{fo}, @samp{foo}, and so on. + +Since the match-zero-or-more operator is a suffix operator, it may be +useless as such when no regular expression precedes it. This is the +case when it: + +@itemize @bullet +@item +is first in a regular expression, or + +@item +follows a match-beginning-of-line, open-group, or alternation +operator. + +@end itemize + +@noindent +Three different things can happen in these cases: + +@enumerate +@item +If the syntax bit @code{RE_CONTEXT_INVALID_OPS} is set, then the +regular expression is invalid. + +@item +If @code{RE_CONTEXT_INVALID_OPS} isn't set, but +@code{RE_CONTEXT_INDEP_OPS} is, then @samp{*} represents the +match-zero-or-more operator (which then operates on the empty string). + +@item +Otherwise, @samp{*} is ordinary. + +@end enumerate + +@cindex backtracking +The matcher processes a match-zero-or-more operator by first matching as +many repetitions of the smallest preceding regular expression as it can. +Then it continues to match the rest of the pattern. + +If it can't match the rest of the pattern, it backtracks (as many times +as necessary), each time discarding one of the matches until it can +either match the entire pattern or be certain that it cannot get a +match. For example, when matching @samp{ca*ar} against @samp{caaar}, +the matcher first matches all three @samp{a}s of the string with the +@samp{a*} of the regular expression. However, it cannot then match the +final @samp{ar} of the regular expression against the final @samp{r} of +the string. So it backtracks, discarding the match of the last @samp{a} +in the string. It can then match the remaining @samp{ar}. + + +@node Match-one-or-more Operator, Match-zero-or-one Operator, Match-zero-or-more Operator, Repetition Operators +@subsection The Match-one-or-more Operator (@code{+} or @code{\+}) + +@cindex @samp{+} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't recognize +this operator. Otherwise, if the syntax bit @code{RE_BK_PLUS_QM} isn't +set, then @samp{+} represents this operator; if it is, then @samp{\+} +does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression at least once; +@pxref{Match-zero-or-more Operator}, for what it operates on, how some +syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{+} represents the match-one-or-more +operator; then @samp{ca+r} matches, e.g., @samp{car} and +@samp{caaaar}, but not @samp{cr}. + +@node Match-zero-or-one Operator, Interval Operators, Match-one-or-more Operator, Repetition Operators +@subsection The Match-zero-or-one Operator (@code{?} or @code{\?}) +@cindex @samp{?} + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_BK_PLUS_QM} isn't set, then @samp{?} represents this operator; +if it is, then @samp{\?} does. + +This operator is similar to the match-zero-or-more operator except that +it repeats the preceding regular expression once or not at all; +@pxref{Match-zero-or-more Operator}, to see what it operates on, how +some syntax bits affect it, and how Regex backtracks to match it. + +For example, supposing that @samp{?} represents the match-zero-or-one +operator; then @samp{ca?r} matches both @samp{car} and @samp{cr}, but +nothing else. + +@node Interval Operators, , Match-zero-or-one Operator, Repetition Operators +@subsection Interval Operators (@code{@{} @dots{} @code{@}} or @code{\@{} @dots{} @code{\@}}) + +@cindex interval expression +@cindex @samp{@{} +@cindex @samp{@}} +@cindex @samp{\@{} +@cindex @samp{\@}} + +If the syntax bit @code{RE_INTERVALS} is set, then Regex recognizes +@dfn{interval expressions}. They repeat the smallest possible preceding +regular expression a specified number of times. + +If the syntax bit @code{RE_NO_BK_BRACES} is set, @samp{@{} represents +the @dfn{open-interval operator} and @samp{@}} represents the +@dfn{close-interval operator} ; otherwise, @samp{\@{} and @samp{\@}} do. + +Specifically, supposing that @samp{@{} and @samp{@}} represent the +open-interval and close-interval operators; then: + +@table @code +@item @{@var{count}@} +matches exactly @var{count} occurrences of the preceding regular +expression. + +@item @{@var{min,}@} +matches @var{min} or more occurrences of the preceding regular +expression. + +@item @{@var{min, max}@} +matches at least @var{min} but no more than @var{max} occurrences of +the preceding regular expression. + +@end table + +The interval expression (but not necessarily the regular expression that +contains it) is invalid if: + +@itemize @bullet +@item +@var{min} is greater than @var{max}, or + +@item +any of @var{count}, @var{min}, or @var{max} are outside the range +zero to @code{RE_DUP_MAX} (which symbol @file{regex.h} +defines). + +@end itemize + +If the interval expression is invalid and the syntax bit +@code{RE_NO_BK_BRACES} is set, then Regex considers all the +characters in the would-be interval to be ordinary. If that bit +isn't set, then the regular expression is invalid. + +If the interval expression is valid but there is no preceding regular +expression on which to operate, then if the syntax bit +@code{RE_CONTEXT_INVALID_OPS} is set, the regular expression is invalid. +If that bit isn't set, then Regex considers all the characters---other +than backslashes, which it ignores---in the would-be interval to be +ordinary. + + +@node Alternation Operator, List Operators, Repetition Operators, Common Operators +@section The Alternation Operator (@code{|} or @code{\|}) + +@kindex | +@kindex \| +@cindex alternation operator +@cindex or operator + +If the syntax bit @code{RE_LIMITED_OPS} is set, then Regex doesn't +recognize this operator. Otherwise, if the syntax bit +@code{RE_NO_BK_VBAR} is set, then @samp{|} represents this operator; +otherwise, @samp{\|} does. + +Alternatives match one of a choice of regular expressions: +if you put the character(s) representing the alternation operator between +any two regular expressions @var{a} and @var{b}, the result matches +the union of the strings that @var{a} and @var{b} match. For +example, supposing that @samp{|} is the alternation operator, then +@samp{foo|bar|quux} would match any of @samp{foo}, @samp{bar} or +@samp{quux}. + +@ignore +@c Nobody needs to disallow empty alternatives any more. +If the syntax bit @code{RE_NO_EMPTY_ALTS} is set, then if either of the regular +expressions @var{a} or @var{b} is empty, the +regular expression is invalid. More precisely, if this syntax bit is +set, then the alternation operator can't: + +@itemize @bullet +@item +be first or last in a regular expression; + +@item +follow either another alternation operator or an open-group operator +(@pxref{Grouping Operators}); or + +@item +precede a close-group operator. + +@end itemize + +@noindent +For example, supposing @samp{(} and @samp{)} represent the open and +close-group operators, then @samp{|foo}, @samp{foo|}, @samp{foo||bar}, +@samp{foo(|bar)}, and @samp{(foo|)bar} would all be invalid. +@end ignore + +The alternation operator operates on the @emph{largest} possible +surrounding regular expressions. (Put another way, it has the lowest +precedence of any regular expression operator.) +Thus, the only way you can +delimit its arguments is to use grouping. For example, if @samp{(} and +@samp{)} are the open and close-group operators, then @samp{fo(o|b)ar} +would match either @samp{fooar} or @samp{fobar}. (@samp{foo|bar} would +match @samp{foo} or @samp{bar}.) + +@cindex backtracking +The matcher usually tries all combinations of alternatives so as to +match the longest possible string. For example, when matching +@samp{(fooq|foo)*(qbarquux|bar)} against @samp{fooqbarquux}, it cannot +take, say, the first (``depth-first'') combination it could match, since +then it would be content to match just @samp{fooqbar}. + +@comment xx something about leftmost-longest + + +@node List Operators, Grouping Operators, Alternation Operator, Common Operators +@section List Operators (@code{[} @dots{} @code{]} and @code{[^} @dots{} @code{]}) + +@cindex matching list +@cindex @samp{[} +@cindex @samp{]} +@cindex @samp{^} +@cindex @samp{-} +@cindex @samp{\} +@cindex @samp{[^} +@cindex nonmatching list +@cindex matching newline +@cindex bracket expression + +@dfn{Lists}, also called @dfn{bracket expressions}, are a set of one or +more items. An @dfn{item} is a character, +@ignore +(These get added when they get implemented.) +a collating symbol, an equivalence class expression, +@end ignore +a character class expression, or a range expression. The syntax bits +affect which kinds of items you can put in a list. We explain the last +two items in subsections below. Empty lists are invalid. + +A @dfn{matching list} matches a single character represented by one of +the list items. You form a matching list by enclosing one or more items +within an @dfn{open-matching-list operator} (represented by @samp{[}) +and a @dfn{close-list operator} (represented by @samp{]}). + +For example, @samp{[ab]} matches either @samp{a} or @samp{b}. +@samp{[ad]*} matches the empty string and any string composed of just +@samp{a}s and @samp{d}s in any order. Regex considers invalid a regular +expression with a @samp{[} but no matching +@samp{]}. + +@dfn{Nonmatching lists} are similar to matching lists except that they +match a single character @emph{not} represented by one of the list +items. You use an @dfn{open-nonmatching-list operator} (represented by +@samp{[^}@footnote{Regex therefore doesn't consider the @samp{^} to be +the first character in the list. If you put a @samp{^} character first +in (what you think is) a matching list, you'll turn it into a +nonmatching list.}) instead of an open-matching-list operator to start a +nonmatching list. + +For example, @samp{[^ab]} matches any character except @samp{a} or +@samp{b}. + +If the @code{posix_newline} field in the pattern buffer (@pxref{GNU +Pattern Buffers} is set, then nonmatching lists do not match a newline. + +Most characters lose any special meaning inside a list. The special +characters inside a list follow. + +@table @samp +@item ] +ends the list if it's not the first list item. So, if you want to make +the @samp{]} character a list item, you must put it first. + +@item \ +quotes the next character if the syntax bit @code{RE_BACKSLASH_ESCAPE_IN_LISTS} is +set. + +@ignore +Put these in if they get implemented. + +@item [. +represents the open-collating-symbol operator (@pxref{Collating Symbol +Operators}). + +@item .] +represents the close-collating-symbol operator. + +@item [= +represents the open-equivalence-class operator (@pxref{Equivalence Class +Operators}). + +@item =] +represents the close-equivalence-class operator. + +@end ignore + +@item [: +represents the open-character-class operator (@pxref{Character Class +Operators}) if the syntax bit @code{RE_CHAR_CLASSES} is set and what +follows is a valid character class expression. + +@item :] +represents the close-character-class operator if the syntax bit +@code{RE_CHAR_CLASSES} is set and what precedes it is an +open-character-class operator followed by a valid character class name. + +@item - +represents the range operator (@pxref{Range Operator}) if it's +not first or last in a list or the ending point of a range. + +@end table + +@noindent +All other characters are ordinary. For example, @samp{[.*]} matches +@samp{.} and @samp{*}. + +@menu +* Character Class Operators:: [:class:] +* Range Operator:: start-end +@end menu + +@ignore +(If collating symbols and equivalence class expressions get implemented, +then add this.) + +node Collating Symbol Operators +subsubsection Collating Symbol Operators (@code{[.} @dots{} @code{.]}) + +If the syntax bit @code{XX} is set, then you can represent +collating symbols inside lists. You form a @dfn{collating symbol} by +putting a collating element between an @dfn{open-collating-symbol +operator} and an @dfn{close-collating-symbol operator}. @samp{[.} +represents the open-collating-symbol operator and @samp{.]} represents +the close-collating-symbol operator. For example, if @samp{ll} is a +collating element, then @samp{[[.ll.]]} would match @samp{ll}. + +node Equivalence Class Operators +subsubsection Equivalence Class Operators (@code{[=} @dots{} @code{=]}) +@cindex equivalence class expression in regex +@cindex @samp{[=} in regex +@cindex @samp{=]} in regex + +If the syntax bit @code{XX} is set, then Regex recognizes equivalence class +expressions inside lists. A @dfn{equivalence class expression} is a set +of collating elements which all belong to the same equivalence class. +You form an equivalence class expression by putting a collating +element between an @dfn{open-equivalence-class operator} and a +@dfn{close-equivalence-class operator}. @samp{[=} represents the +open-equivalence-class operator and @samp{=]} represents the +close-equivalence-class operator. For example, if @samp{a} and @samp{A} +were an equivalence class, then both @samp{[[=a=]]} and @samp{[[=A=]]} +would match both @samp{a} and @samp{A}. If the collating element in an +equivalence class expression isn't part of an equivalence class, then +the matcher considers the equivalence class expression to be a collating +symbol. + +@end ignore + +@node Character Class Operators, Range Operator, , List Operators +@subsection Character Class Operators (@code{[:} @dots{} @code{:]}) + +@cindex character classes +@cindex @samp{[:} in regex +@cindex @samp{:]} in regex + +If the syntax bit @code{RE_CHARACTER_CLASSES} is set, then Regex +recognizes character class expressions inside lists. A @dfn{character +class expression} matches one character from a given class. You form a +character class expression by putting a character class name between an +@dfn{open-character-class operator} (represented by @samp{[:}) and a +@dfn{close-character-class operator} (represented by @samp{:]}). The +character class names and their meanings are: + +@table @code + +@item alnum +letters and digits + +@item alpha +letters + +@item blank +system-dependent; for @sc{gnu}, a space or tab + +@item cntrl +control characters (in the @sc{ascii} encoding, code 0177 and codes +less than 040) + +@item digit +digits + +@item graph +same as @code{print} except omits space + +@item lower +lowercase letters + +@item print +printable characters (in the @sc{ascii} encoding, space +tilde---codes 040 through 0176) + +@item punct +neither control nor alphanumeric characters + +@item space +space, carriage return, newline, vertical tab, and form feed + +@item upper +uppercase letters + +@item xdigit +hexadecimal digits: @code{0}--@code{9}, @code{a}--@code{f}, @code{A}--@code{F} + +@end table + +@noindent +These correspond to the definitions in the C library's @file{<ctype.h>} +facility. For example, @samp{[:alpha:]} corresponds to the standard +facility @code{isalpha}. Regex recognizes character class expressions +only inside of lists; so @samp{[[:alpha:]]} matches any letter, but +@samp{[:alpha:]} outside of a bracket expression and not followed by a +repetition operator matches just itself. + +@node Range Operator, , Character Class Operators, List Operators +@subsection The Range Operator (@code{-}) + +Regex recognizes @dfn{range expressions} inside a list. They represent +those characters +that fall between two elements in the current collating sequence. You +form a range expression by putting a @dfn{range operator} between two +@ignore +(If these get implemented, then substitute this for ``characters.'') +of any of the following: characters, collating elements, collating symbols, +and equivalence class expressions. The starting point of the range and +the ending point of the range don't have to be the same kind of item, +e.g., the starting point could be a collating element and the ending +point could be an equivalence class expression. If a range's ending +point is an equivalence class, then all the collating elements in that +class will be in the range. +@end ignore +characters.@footnote{You can't use a character class for the starting +or ending point of a range, since a character class is not a single +character.} @samp{-} represents the range operator. For example, +@samp{a-f} within a list represents all the characters from @samp{a} +through @samp{f} +inclusively. + +If the syntax bit @code{RE_NO_EMPTY_RANGES} is set, then if the range's +ending point collates less than its starting point, the range (and the +regular expression containing it) is invalid. For example, the regular +expression @samp{[z-a]} would be invalid. If this bit isn't set, then +Regex considers such a range to be empty. + +Since @samp{-} represents the range operator, if you want to make a +@samp{-} character itself +a list item, you must do one of the following: + +@itemize @bullet +@item +Put the @samp{-} either first or last in the list. + +@item +Include a range whose starting point collates strictly lower than +@samp{-} and whose ending point collates equal or higher. Unless a +range is the first item in a list, a @samp{-} can't be its starting +point, but @emph{can} be its ending point. That is because Regex +considers @samp{-} to be the range operator unless it is preceded by +another @samp{-}. For example, in the @sc{ascii} encoding, @samp{)}, +@samp{*}, @samp{+}, @samp{,}, @samp{-}, @samp{.}, and @samp{/} are +contiguous characters in the collating sequence. You might think that +@samp{[)-+--/]} has two ranges: @samp{)-+} and @samp{--/}. Rather, it +has the ranges @samp{)-+} and @samp{+--}, plus the character @samp{/}, so +it matches, e.g., @samp{,}, not @samp{.}. + +@item +Put a range whose starting point is @samp{-} first in the list. + +@end itemize + +For example, @samp{[-a-z]} matches a lowercase letter or a hyphen (in +English, in @sc{ascii}). + + +@node Grouping Operators, Back-reference Operator, List Operators, Common Operators +@section Grouping Operators (@code{(} @dots{} @code{)} or @code{\(} @dots{} @code{\)}) + +@kindex ( +@kindex ) +@kindex \( +@kindex \) +@cindex grouping +@cindex subexpressions +@cindex parenthesizing + +A @dfn{group}, also known as a @dfn{subexpression}, consists of an +@dfn{open-group operator}, any number of other operators, and a +@dfn{close-group operator}. Regex treats this sequence as a unit, just +as mathematics and programming languages treat a parenthesized +expression as a unit. + +Therefore, using @dfn{groups}, you can: + +@itemize @bullet +@item +delimit the argument(s) to an alternation operator (@pxref{Alternation +Operator}) or a repetition operator (@pxref{Repetition +Operators}). + +@item +keep track of the indices of the substring that matched a given group. +@xref{Using Registers}, for a precise explanation. +This lets you: + +@itemize @bullet +@item +use the back-reference operator (@pxref{Back-reference Operator}). + +@item +use registers (@pxref{Using Registers}). + +@end itemize + +@end itemize + +If the syntax bit @code{RE_NO_BK_PARENS} is set, then @samp{(} represents +the open-group operator and @samp{)} represents the +close-group operator; otherwise, @samp{\(} and @samp{\)} do. + +If the syntax bit @code{RE_UNMATCHED_RIGHT_PAREN_ORD} is set and a +close-group operator has no matching open-group operator, then Regex +considers it to match @samp{)}. + + +@node Back-reference Operator, Anchoring Operators, Grouping Operators, Common Operators +@section The Back-reference Operator (@dfn{\}@var{digit}) + +@cindex back references + +If the syntax bit @code{RE_NO_BK_REF} isn't set, then Regex recognizes +back references. A back reference matches a specified preceding group. +The back reference operator is represented by @samp{\@var{digit}} +anywhere after the end of a regular expression's @w{@var{digit}-th} +group (@pxref{Grouping Operators}). + +@var{digit} must be between @samp{1} and @samp{9}. The matcher assigns +numbers 1 through 9 to the first nine groups it encounters. By using +one of @samp{\1} through @samp{\9} after the corresponding group's +close-group operator, you can match a substring identical to the +one that the group does. + +Back references match according to the following (in all examples below, +@samp{(} represents the open-group, @samp{)} the close-group, @samp{@{} +the open-interval and @samp{@}} the close-interval operator): + +@itemize @bullet +@item +If the group matches a substring, the back reference matches an +identical substring. For example, @samp{(a)\1} matches @samp{aa} and +@samp{(bana)na\1bo\1} matches @samp{bananabanabobana}. Likewise, +@samp{(.*)\1} matches any (newline-free if the syntax bit +@code{RE_DOT_NEWLINE} isn't set) string that is composed of two +identical halves; the @samp{(.*)} matches the first half and the +@samp{\1} matches the second half. + +@item +If the group matches more than once (as it might if followed +by, e.g., a repetition operator), then the back reference matches the +substring the group @emph{last} matched. For example, +@samp{((a*)b)*\1\2} matches @samp{aabababa}; first @w{group 1} (the +outer one) matches @samp{aab} and @w{group 2} (the inner one) matches +@samp{aa}. Then @w{group 1} matches @samp{ab} and @w{group 2} matches +@samp{a}. So, @samp{\1} matches @samp{ab} and @samp{\2} matches +@samp{a}. + +@item +If the group doesn't participate in a match, i.e., it is part of an +alternative not taken or a repetition operator allows zero repetitions +of it, then the back reference makes the whole match fail. For example, +@samp{(one()|two())-and-(three\2|four\3)} matches @samp{one-and-three} +and @samp{two-and-four}, but not @samp{one-and-four} or +@samp{two-and-three}. For example, if the pattern matches +@samp{one-and-}, then its @w{group 2} matches the empty string and its +@w{group 3} doesn't participate in the match. So, if it then matches +@samp{four}, then when it tries to back reference @w{group 3}---which it +will attempt to do because @samp{\3} follows the @samp{four}---the match +will fail because @w{group 3} didn't participate in the match. + +@end itemize + +You can use a back reference as an argument to a repetition operator. For +example, @samp{(a(b))\2*} matches @samp{a} followed by two or more +@samp{b}s. Similarly, @samp{(a(b))\2@{3@}} matches @samp{abbbb}. + +If there is no preceding @w{@var{digit}-th} subexpression, the regular +expression is invalid. + + +@node Anchoring Operators, , Back-reference Operator, Common Operators +@section Anchoring Operators + +@cindex anchoring +@cindex regexp anchoring + +These operators can constrain a pattern to match only at the beginning or +end of the entire string or at the beginning or end of a line. + +@menu +* Match-beginning-of-line Operator:: ^ +* Match-end-of-line Operator:: $ +@end menu + + +@node Match-beginning-of-line Operator, Match-end-of-line Operator, , Anchoring Operators +@subsection The Match-beginning-of-line Operator (@code{^}) + +@kindex ^ +@cindex beginning-of-line operator +@cindex anchors + +This operator can match the empty string either at the beginning of the +string or after a newline character. Thus, it is said to @dfn{anchor} +the pattern to the beginning of a line. + +In the cases following, @samp{^} represents this operator. (Otherwise, +@samp{^} is ordinary.) + +@itemize @bullet + +@item +It (the @samp{^}) is first in the pattern, as in @samp{^foo}. + +@cnindex RE_CONTEXT_INDEP_ANCHORS @r{(and @samp{^})} +@item +The syntax bit @code{RE_CONTEXT_INDEP_ANCHORS} is set, and it is outside +a bracket expression. + +@cindex open-group operator and @samp{^} +@cindex alternation operator and @samp{^} +@item +It follows an open-group or alternation operator, as in @samp{a\(^b\)} +and @samp{a\|^b}. @xref{Grouping Operators}, and @ref{Alternation +Operator}. + +@end itemize + +These rules imply that some valid patterns containing @samp{^} cannot be +matched; for example, @samp{foo^bar} if @code{RE_CONTEXT_INDEP_ANCHORS} +is set. + +@vindex not_bol @r{field in pattern buffer} +If the @code{not_bol} field is set in the pattern buffer (@pxref{GNU +Pattern Buffers}), then @samp{^} fails to match at the beginning of the +string. @xref{POSIX Matching}, for when you might find this useful. + +@vindex newline_anchor @r{field in pattern buffer} +If the @code{newline_anchor} field is set in the pattern buffer, then +@samp{^} fails to match after a newline. This is useful when you do not +regard the string to be matched as broken into lines. + + +@node Match-end-of-line Operator, , Match-beginning-of-line Operator, Anchoring Operators +@subsection The Match-end-of-line Operator (@code{$}) + +@kindex $ +@cindex end-of-line operator +@cindex anchors + +This operator can match the empty string either at the end of +the string or before a newline character in the string. Thus, it is +said to @dfn{anchor} the pattern to the end of a line. + +It is always represented by @samp{$}. For example, @samp{foo$} usually +matches, e.g., @samp{foo} and, e.g., the first three characters of +@samp{foo\nbar}. + +Its interaction with the syntax bits and pattern buffer fields is +exactly the dual of @samp{^}'s; see the previous section. (That is, +``beginning'' becomes ``end'', ``next'' becomes ``previous'', and +``after'' becomes ``before''.) + + +@node GNU Operators, GNU Emacs Operators, Common Operators, Top +@chapter GNU Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't). + +@menu +* Word Operators:: +* Buffer Operators:: +@end menu + +@node Word Operators, Buffer Operators, , GNU Operators +@section Word Operators + +The operators in this section require Regex to recognize parts of words. +Regex uses a syntax table to determine whether or not a character is +part of a word, i.e., whether or not it is @dfn{word-constituent}. + +@menu +* Non-Emacs Syntax Tables:: +* Match-word-boundary Operator:: \b +* Match-within-word Operator:: \B +* Match-beginning-of-word Operator:: \< +* Match-end-of-word Operator:: \> +* Match-word-constituent Operator:: \w +* Match-non-word-constituent Operator:: \W +@end menu + +@node Non-Emacs Syntax Tables, Match-word-boundary Operator, , Word Operators +@subsection Non-Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. Regex always uses a @code{char *} variable +@code{re_syntax_table} as its syntax table. In some cases, it +initializes this variable and in others it expects you to initialize it. + +@itemize @bullet +@item +If Regex is compiled with the preprocessor symbols @code{emacs} and +@code{SYNTAX_TABLE} both undefined, then Regex allocates +@code{re_syntax_table} and initializes an element @var{i} either to +@code{Sword} (which it defines) if @var{i} is a letter, number, or +@samp{_}, or to zero if it's not. + +@item +If Regex is compiled with @code{emacs} undefined but @code{SYNTAX_TABLE} +defined, then Regex expects you to define a @code{char *} variable +@code{re_syntax_table} to be a valid syntax table. + +@item +@xref{Emacs Syntax Tables}, for what happens when Regex is compiled with +the preprocessor symbol @code{emacs} defined. + +@end itemize + +@node Match-word-boundary Operator, Match-within-word Operator, Non-Emacs Syntax Tables, Word Operators +@subsection The Match-word-boundary Operator (@code{\b}) + +@cindex @samp{\b} +@cindex word boundaries, matching + +This operator (represented by @samp{\b}) matches the empty string at +either the beginning or the end of a word. For example, @samp{\brat\b} +matches the separate word @samp{rat}. + +@node Match-within-word Operator, Match-beginning-of-word Operator, Match-word-boundary Operator, Word Operators +@subsection The Match-within-word Operator (@code{\B}) + +@cindex @samp{\B} + +This operator (represented by @samp{\B}) matches the empty string within +a word. For example, @samp{c\Brat\Be} matches @samp{crate}, but +@samp{dirty \Brat} doesn't match @samp{dirty rat}. + +@node Match-beginning-of-word Operator, Match-end-of-word Operator, Match-within-word Operator, Word Operators +@subsection The Match-beginning-of-word Operator (@code{\<}) + +@cindex @samp{\<} + +This operator (represented by @samp{\<}) matches the empty string at the +beginning of a word. + +@node Match-end-of-word Operator, Match-word-constituent Operator, Match-beginning-of-word Operator, Word Operators +@subsection The Match-end-of-word Operator (@code{\>}) + +@cindex @samp{\>} + +This operator (represented by @samp{\>}) matches the empty string at the +end of a word. + +@node Match-word-constituent Operator, Match-non-word-constituent Operator, Match-end-of-word Operator, Word Operators +@subsection The Match-word-constituent Operator (@code{\w}) + +@cindex @samp{\w} + +This operator (represented by @samp{\w}) matches any word-constituent +character. + +@node Match-non-word-constituent Operator, , Match-word-constituent Operator, Word Operators +@subsection The Match-non-word-constituent Operator (@code{\W}) + +@cindex @samp{\W} + +This operator (represented by @samp{\W}) matches any character that is +not word-constituent. + + +@node Buffer Operators, , Word Operators, GNU Operators +@section Buffer Operators + +Following are operators which work on buffers. In Emacs, a @dfn{buffer} +is, naturally, an Emacs buffer. For other programs, Regex considers the +entire string to be matched as the buffer. + +@menu +* Match-beginning-of-buffer Operator:: \` +* Match-end-of-buffer Operator:: \' +@end menu + + +@node Match-beginning-of-buffer Operator, Match-end-of-buffer Operator, , Buffer Operators +@subsection The Match-beginning-of-buffer Operator (@code{\`}) + +@cindex @samp{\`} + +This operator (represented by @samp{\`}) matches the empty string at the +beginning of the buffer. + +@node Match-end-of-buffer Operator, , Match-beginning-of-buffer Operator, Buffer Operators +@subsection The Match-end-of-buffer Operator (@code{\'}) + +@cindex @samp{\'} + +This operator (represented by @samp{\'}) matches the empty string at the +end of the buffer. + + +@node GNU Emacs Operators, What Gets Matched?, GNU Operators, Top +@chapter GNU Emacs Operators + +Following are operators that @sc{gnu} defines (and @sc{posix} doesn't) +that you can use only when Regex is compiled with the preprocessor +symbol @code{emacs} defined. + +@menu +* Syntactic Class Operators:: +@end menu + + +@node Syntactic Class Operators, , , GNU Emacs Operators +@section Syntactic Class Operators + +The operators in this section require Regex to recognize the syntactic +classes of characters. Regex uses a syntax table to determine this. + +@menu +* Emacs Syntax Tables:: +* Match-syntactic-class Operator:: \sCLASS +* Match-not-syntactic-class Operator:: \SCLASS +@end menu + +@node Emacs Syntax Tables, Match-syntactic-class Operator, , Syntactic Class Operators +@subsection Emacs Syntax Tables + +A @dfn{syntax table} is an array indexed by the characters in your +character set. In the @sc{ascii} encoding, therefore, a syntax table +has 256 elements. + +If Regex is compiled with the preprocessor symbol @code{emacs} defined, +then Regex expects you to define and initialize the variable +@code{re_syntax_table} to be an Emacs syntax table. Emacs' syntax +tables are more complicated than Regex's own (@pxref{Non-Emacs Syntax +Tables}). @xref{Syntax, , Syntax, emacs, The GNU Emacs User's Manual}, +for a description of Emacs' syntax tables. + +@node Match-syntactic-class Operator, Match-not-syntactic-class Operator, Emacs Syntax Tables, Syntactic Class Operators +@subsection The Match-syntactic-class Operator (@code{\s}@var{class}) + +@cindex @samp{\s} + +This operator matches any character whose syntactic class is represented +by a specified character. @samp{\s@var{class}} represents this operator +where @var{class} is the character representing the syntactic class you +want. For example, @samp{w} represents the syntactic +class of word-constituent characters, so @samp{\sw} matches any +word-constituent character. + +@node Match-not-syntactic-class Operator, , Match-syntactic-class Operator, Syntactic Class Operators +@subsection The Match-not-syntactic-class Operator (@code{\S}@var{class}) + +@cindex @samp{\S} + +This operator is similar to the match-syntactic-class operator except +that it matches any character whose syntactic class is @emph{not} +represented by the specified character. @samp{\S@var{class}} represents +this operator. For example, @samp{w} represents the syntactic class of +word-constituent characters, so @samp{\Sw} matches any character that is +not word-constituent. + + +@node What Gets Matched?, Programming with Regex, GNU Emacs Operators, Top +@chapter What Gets Matched? + +Regex usually matches strings according to the ``leftmost longest'' +rule; that is, it chooses the longest of the leftmost matches. This +does not mean that for a regular expression containing subexpressions +that it simply chooses the longest match for each subexpression, left to +right; the overall match must also be the longest possible one. + +For example, @samp{(ac*)(c*d[ac]*)\1} matches @samp{acdacaaa}, not +@samp{acdac}, as it would if it were to choose the longest match for the +first subexpression. + + +@node Programming with Regex, Copying, What Gets Matched?, Top +@chapter Programming with Regex + +Here we describe how you use the Regex data structures and functions in +C programs. Regex has three interfaces: one designed for @sc{gnu}, one +compatible with @sc{posix} and one compatible with Berkeley @sc{unix}. + +@menu +* GNU Regex Functions:: +* POSIX Regex Functions:: +* BSD Regex Functions:: +@end menu + + +@node GNU Regex Functions, POSIX Regex Functions, , Programming with Regex +@section GNU Regex Functions + +If you're writing code that doesn't need to be compatible with either +@sc{posix} or Berkeley @sc{unix}, you can use these functions. They +provide more options than the other interfaces. + +@menu +* GNU Pattern Buffers:: The re_pattern_buffer type. +* GNU Regular Expression Compiling:: re_compile_pattern () +* GNU Matching:: re_match () +* GNU Searching:: re_search () +* Matching/Searching with Split Data:: re_match_2 (), re_search_2 () +* Searching with Fastmaps:: re_compile_fastmap () +* GNU Translate Tables:: The `translate' field. +* Using Registers:: The re_registers type and related fns. +* Freeing GNU Pattern Buffers:: regfree () +@end menu + + +@node GNU Pattern Buffers, GNU Regular Expression Compiling, , GNU Regex Functions +@subsection GNU Pattern Buffers + +@cindex pattern buffer, definition of +@tindex re_pattern_buffer @r{definition} +@tindex struct re_pattern_buffer @r{definition} + +To compile, match, or search for a given regular expression, you must +supply a pattern buffer. A @dfn{pattern buffer} holds one compiled +regular expression.@footnote{Regular expressions are also referred to as +``patterns,'' hence the name ``pattern buffer.''} + +You can have several different pattern buffers simultaneously, each +holding a compiled pattern for a different regular expression. + +@file{regex.h} defines the pattern buffer @code{struct} as follows: + +@example +[[[ pattern_buffer ]]] +@end example + + +@node GNU Regular Expression Compiling, GNU Matching, GNU Pattern Buffers, GNU Regex Functions +@subsection GNU Regular Expression Compiling + +In @sc{gnu}, you can both match and search for a given regular +expression. To do either, you must first compile it in a pattern buffer +(@pxref{GNU Pattern Buffers}). + +@cindex syntax initialization +@vindex re_syntax_options @r{initialization} +Regular expressions match according to the syntax with which they were +compiled; with @sc{gnu}, you indicate what syntax you want by setting +the variable @code{re_syntax_options} (declared in @file{regex.h} and +defined in @file{regex.c}) before calling the compiling function, +@code{re_compile_pattern} (see below). @xref{Syntax Bits}, and +@ref{Predefined Syntaxes}. + +You can change the value of @code{re_syntax_options} at any time. +Usually, however, you set its value once and then never change it. + +@cindex pattern buffer initialization +@code{re_compile_pattern} takes a pattern buffer as an argument. You +must initialize the following fields: + +@table @code + +@item translate @r{initialization} + +@item translate +@vindex translate @r{initialization} +Initialize this to point to a translate table if you want one, or to +zero if you don't. We explain translate tables in @ref{GNU Translate +Tables}. + +@item fastmap +@vindex fastmap @r{initialization} +Initialize this to nonzero if you want a fastmap, or to zero if you +don't. + +@item buffer +@itemx allocated +@vindex buffer @r{initialization} +@vindex allocated @r{initialization} +@findex malloc +If you want @code{re_compile_pattern} to allocate memory for the +compiled pattern, set both of these to zero. If you have an existing +block of memory (allocated with @code{malloc}) you want Regex to use, +set @code{buffer} to its address and @code{allocated} to its size (in +bytes). + +@code{re_compile_pattern} uses @code{realloc} to extend the space for +the compiled pattern as necessary. + +@end table + +To compile a pattern buffer, use: + +@findex re_compile_pattern +@example +char * +re_compile_pattern (const char *@var{regex}, const int @var{regex_size}, + struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{regex} is the regular expression's address, @var{regex_size} is its +length, and @var{pattern_buffer} is the pattern buffer's address. + +If @code{re_compile_pattern} successfully compiles the regular +expression, it returns zero and sets @code{*@var{pattern_buffer}} to the +compiled pattern. It sets the pattern buffer's fields as follows: + +@table @code +@item buffer +@vindex buffer @r{field, set by @code{re_compile_pattern}} +to the compiled pattern. + +@item used +@vindex used @r{field, set by @code{re_compile_pattern}} +to the number of bytes the compiled pattern in @code{buffer} occupies. + +@item syntax +@vindex syntax @r{field, set by @code{re_compile_pattern}} +to the current value of @code{re_syntax_options}. + +@item re_nsub +@vindex re_nsub @r{field, set by @code{re_compile_pattern}} +to the number of subexpressions in @var{regex}. + +@item fastmap_accurate +@vindex fastmap_accurate @r{field, set by @code{re_compile_pattern}} +to zero on the theory that the pattern you're compiling is different +than the one previously compiled into @code{buffer}; in that case (since +you can't make a fastmap without a compiled pattern), +@code{fastmap} would either contain an incompatible fastmap, or nothing +at all. + +@c xx what else? +@end table + +If @code{re_compile_pattern} can't compile @var{regex}, it returns an +error string corresponding to one of the errors listed in @ref{POSIX +Regular Expression Compiling}. + + +@node GNU Matching, GNU Searching, GNU Regular Expression Compiling, GNU Regex Functions +@subsection GNU Matching + +@cindex matching with GNU functions + +Matching the @sc{gnu} way means trying to match as much of a string as +possible starting at a position within it you specify. Once you've compiled +a pattern into a pattern buffer (@pxref{GNU Regular Expression +Compiling}), you can ask the matcher to match that pattern against a +string using: + +@findex re_match +@example +int +re_match (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, struct re_registers *@var{regs}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer containing a +compiled pattern. @var{string} is the string you want to match; it can +contain newline and null characters. @var{size} is the length of that +string. @var{start} is the string index at which you want to +begin matching; the first character of @var{string} is at index zero. +@xref{Using Registers}, for a explanation of @var{regs}; you can safely +pass zero. + +@code{re_match} matches the regular expression in @var{pattern_buffer} +against the string @var{string} according to the syntax in +@var{pattern_buffers}'s @code{syntax} field. (@xref{GNU Regular +Expression Compiling}, for how to set it.) The function returns +@math{-1} if the compiled pattern does not match any part of +@var{string} and @math{-2} if an internal error happens; otherwise, it +returns how many (possibly zero) characters of @var{string} the pattern +matched. + +An example: suppose @var{pattern_buffer} points to a pattern buffer +containing the compiled pattern for @samp{a*}, and @var{string} points +to @samp{aaaaab} (whereupon @var{size} should be 6). Then if @var{start} +is 2, @code{re_match} returns 3, i.e., @samp{a*} would have matched the +last three @samp{a}s in @var{string}. If @var{start} is 0, +@code{re_match} returns 5, i.e., @samp{a*} would have matched all the +@samp{a}s in @var{string}. If @var{start} is either 5 or 6, it returns +zero. + +If @var{start} is not between zero and @var{size}, then +@code{re_match} returns @math{-1}. + + +@node GNU Searching, Matching/Searching with Split Data, GNU Matching, GNU Regex Functions +@subsection GNU Searching + +@cindex searching with GNU functions + +@dfn{Searching} means trying to match starting at successive positions +within a string. The function @code{re_search} does this. + +Before calling @code{re_search}, you must compile your regular +expression. @xref{GNU Regular Expression Compiling}. + +Here is the function declaration: + +@findex re_search +@example +int +re_search (struct re_pattern_buffer *@var{pattern_buffer}, + const char *@var{string}, const int @var{size}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}) +@end example + +@noindent +@vindex start @r{argument to @code{re_search}} +@vindex range @r{argument to @code{re_search}} +whose arguments are the same as those to @code{re_match} (@pxref{GNU +Matching}) except that the two arguments @var{start} and @var{range} +replace @code{re_match}'s argument @var{start}. + +If @var{range} is positive, then @code{re_search} attempts a match +starting first at index @var{start}, then at @math{@var{start} + 1} if +that fails, and so on, up to @math{@var{start} + @var{range}}; if +@var{range} is negative, then it attempts a match starting first at +index @var{start}, then at @math{@var{start} -1} if that fails, and so +on. + +If @var{start} is not between zero and @var{size}, then @code{re_search} +returns @math{-1}. When @var{range} is positive, @code{re_search} +adjusts @var{range} so that @math{@var{start} + @var{range} - 1} is +between zero and @var{size}, if necessary; that way it won't search +outside of @var{string}. Similarly, when @var{range} is negative, +@code{re_search} adjusts @var{range} so that @math{@var{start} + +@var{range} + 1} is between zero and @var{size}, if necessary. + +If the @code{fastmap} field of @var{pattern_buffer} is zero, +@code{re_search} matches starting at consecutive positions; otherwise, +it uses @code{fastmap} to make the search more efficient. +@xref{Searching with Fastmaps}. + +If no match is found, @code{re_search} returns @math{-1}. If +a match is found, it returns the index where the match began. If an +internal error happens, it returns @math{-2}. + + +@node Matching/Searching with Split Data, Searching with Fastmaps, GNU Searching, GNU Regex Functions +@subsection Matching and Searching with Split Data + +Using the functions @code{re_match_2} and @code{re_search_2}, you can +match or search in data that is divided into two strings. + +The function: + +@findex re_match_2 +@example +int +re_match_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similar to @code{re_match} (@pxref{GNU Matching}) except that you +pass @emph{two} data strings and sizes, and an index @var{stop} beyond +which you don't want the matcher to try matching. As with +@code{re_match}, if it succeeds, @code{re_match_2} returns how many +characters of @var{string} it matched. Regard @var{string1} and +@var{string2} as concatenated when you set the arguments @var{start} and +@var{stop} and use the contents of @var{regs}; @code{re_match_2} never +returns a value larger than @math{@var{size1} + @var{size2}}. + +The function: + +@findex re_search_2 +@example +int +re_search_2 (struct re_pattern_buffer *@var{buffer}, + const char *@var{string1}, const int @var{size1}, + const char *@var{string2}, const int @var{size2}, + const int @var{start}, const int @var{range}, + struct re_registers *@var{regs}, + const int @var{stop}) +@end example + +@noindent +is similarly related to @code{re_search}. + + +@node Searching with Fastmaps, GNU Translate Tables, Matching/Searching with Split Data, GNU Regex Functions +@subsection Searching with Fastmaps + +@cindex fastmaps +If you're searching through a long string, you should use a fastmap. +Without one, the searcher tries to match at consecutive positions in the +string. Generally, most of the characters in the string could not start +a match. It takes much longer to try matching at a given position in the +string than it does to check in a table whether or not the character at +that position could start a match. A @dfn{fastmap} is such a table. + +More specifically, a fastmap is an array indexed by the characters in +your character set. Under the @sc{ascii} encoding, therefore, a fastmap +has 256 elements. If you want the searcher to use a fastmap with a +given pattern buffer, you must allocate the array and assign the array's +address to the pattern buffer's @code{fastmap} field. You either can +compile the fastmap yourself or have @code{re_search} do it for you; +when @code{fastmap} is nonzero, it automatically compiles a fastmap the +first time you search using a particular compiled pattern. + +To compile a fastmap yourself, use: + +@findex re_compile_fastmap +@example +int +re_compile_fastmap (struct re_pattern_buffer *@var{pattern_buffer}) +@end example + +@noindent +@var{pattern_buffer} is the address of a pattern buffer. If the +character @var{c} could start a match for the pattern, +@code{re_compile_fastmap} makes +@code{@var{pattern_buffer}->fastmap[@var{c}]} nonzero. It returns +@math{0} if it can compile a fastmap and @math{-2} if there is an +internal error. For example, if @samp{|} is the alternation operator +and @var{pattern_buffer} holds the compiled pattern for @samp{a|b}, then +@code{re_compile_fastmap} sets @code{fastmap['a']} and +@code{fastmap['b']} (and no others). + +@code{re_search} uses a fastmap as it moves along in the string: it +checks the string's characters until it finds one that's in the fastmap. +Then it tries matching at that character. If the match fails, it +repeats the process. So, by using a fastmap, @code{re_search} doesn't +waste time trying to match at positions in the string that couldn't +start a match. + +If you don't want @code{re_search} to use a fastmap, +store zero in the @code{fastmap} field of the pattern buffer before +calling @code{re_search}. + +Once you've initialized a pattern buffer's @code{fastmap} field, you +need never do so again---even if you compile a new pattern in +it---provided the way the field is set still reflects whether or not you +want a fastmap. @code{re_search} will still either do nothing if +@code{fastmap} is null or, if it isn't, compile a new fastmap for the +new pattern. + +@node GNU Translate Tables, Using Registers, Searching with Fastmaps, GNU Regex Functions +@subsection GNU Translate Tables + +If you set the @code{translate} field of a pattern buffer to a translate +table, then the @sc{gnu} Regex functions to which you've passed that +pattern buffer use it to apply a simple transformation +to all the regular expression and string characters at which they look. + +A @dfn{translate table} is an array indexed by the characters in your +character set. Under the @sc{ascii} encoding, therefore, a translate +table has 256 elements. The array's elements are also characters in +your character set. When the Regex functions see a character @var{c}, +they use @code{translate[@var{c}]} in its place, with one exception: the +character after a @samp{\} is not translated. (This ensures that, the +operators, e.g., @samp{\B} and @samp{\b}, are always distinguishable.) + +For example, a table that maps all lowercase letters to the +corresponding uppercase ones would cause the matcher to ignore +differences in case.@footnote{A table that maps all uppercase letters to +the corresponding lowercase ones would work just as well for this +purpose.} Such a table would map all characters except lowercase letters +to themselves, and lowercase letters to the corresponding uppercase +ones. Under the @sc{ascii} encoding, here's how you could initialize +such a table (we'll call it @code{case_fold}): + +@example +for (i = 0; i < 256; i++) + case_fold[i] = i; +for (i = 'a'; i <= 'z'; i++) + case_fold[i] = i - ('a' - 'A'); +@end example + +You tell Regex to use a translate table on a given pattern buffer by +assigning that table's address to the @code{translate} field of that +buffer. If you don't want Regex to do any translation, put zero into +this field. You'll get weird results if you change the table's contents +anytime between compiling the pattern buffer, compiling its fastmap, and +matching or searching with the pattern buffer. + +@node Using Registers, Freeing GNU Pattern Buffers, GNU Translate Tables, GNU Regex Functions +@subsection Using Registers + +A group in a regular expression can match a (posssibly empty) substring +of the string that regular expression as a whole matched. The matcher +remembers the beginning and end of the substring matched by +each group. + +To find out what they matched, pass a nonzero @var{regs} argument to a +@sc{gnu} matching or searching function (@pxref{GNU Matching} and +@ref{GNU Searching}), i.e., the address of a structure of this type, as +defined in @file{regex.h}: + +@c We don't bother to include this directly from regex.h, +@c since it changes so rarely. +@example +@tindex re_registers +@vindex num_regs @r{in @code{struct re_registers}} +@vindex start @r{in @code{struct re_registers}} +@vindex end @r{in @code{struct re_registers}} +struct re_registers +@{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +@}; +@end example + +Except for (possibly) the @var{num_regs}'th element (see below), the +@var{i}th element of the @code{start} and @code{end} arrays records +information about the @var{i}th group in the pattern. (They're declared +as C pointers, but this is only because not all C compilers accept +zero-length arrays; conceptually, it is simplest to think of them as +arrays.) + +The @code{start} and @code{end} arrays are allocated in various ways, +depending on the value of the @code{regs_allocated} +@vindex regs_allocated +field in the pattern buffer passed to the matcher. + +The simplest and perhaps most useful is to let the matcher (re)allocate +enough space to record information for all the groups in the regular +expression. If @code{regs_allocated} is @code{REGS_UNALLOCATED}, +@vindex REGS_UNALLOCATED +the matcher allocates @math{1 + @var{re_nsub}} (another field in the +pattern buffer; @pxref{GNU Pattern Buffers}). The extra element is set +to @math{-1}, and sets @code{regs_allocated} to @code{REGS_REALLOCATE}. +@vindex REGS_REALLOCATE +Then on subsequent calls with the same pattern buffer and @var{regs} +arguments, the matcher reallocates more space if necessary. + +It would perhaps be more logical to make the @code{regs_allocated} field +part of the @code{re_registers} structure, instead of part of the +pattern buffer. But in that case the caller would be forced to +initialize the structure before passing it. Much existing code doesn't +do this initialization, and it's arguably better to avoid it anyway. + +@code{re_compile_pattern} sets @code{regs_allocated} to +@code{REGS_UNALLOCATED}, +so if you use the GNU regular expression +functions, you get this behavior by default. + +xx document re_set_registers + +@sc{posix}, on the other hand, requires a different interface: the +caller is supposed to pass in a fixed-length array which the matcher +fills. Therefore, if @code{regs_allocated} is @code{REGS_FIXED} +@vindex REGS_FIXED +the matcher simply fills that array. + +The following examples illustrate the information recorded in the +@code{re_registers} structure. (In all of them, @samp{(} represents the +open-group and @samp{)} the close-group operator. The first character +in the string @var{string} is at index 0.) + +@c xx i'm not sure this is all true anymore. + +@itemize @bullet + +@item +If the regular expression has an @w{@var{i}-th} +group not contained within another group that matches a +substring of @var{string}, then the function sets +@code{@w{@var{regs}->}start[@var{i}]} to the index in @var{string} where +the substring matched by the @w{@var{i}-th} group begins, and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +substring's end. The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match @samp{((a)(b))} against @samp{ab}, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} + +@item +1 in @code{@w{@var{regs}->}start[3]} and 2 in @code{@w{@var{regs}->}end[3]} +@end itemize + +@item +If a group matches more than once (as it might if followed by, +e.g., a repetition operator), then the function reports the information +about what the group @emph{last} matched. + +For example, when you match the pattern @samp{(a)*} against the string +@samp{aa}, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 2 in @code{@w{@var{regs}->}end[0]} + +@item +1 in @code{@w{@var{regs}->}start[1]} and 2 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group does not participate in a +successful match, e.g., it is an alternative not taken or a +repetition operator allows zero repetitions of it, then the function +sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}. + +For example, when you match the pattern @samp{(a)*b} against +the string @samp{b}, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} +@end itemize + +@item +If the @w{@var{i}-th} group matches a zero-length string, then the +function sets @code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to the index just beyond that +zero-length string. + +For example, when you match the pattern @samp{(a*)b} against the string +@samp{b}, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize + +@ignore +The function sets @code{@w{@var{regs}->}start[0]} and +@code{@w{@var{regs}->}end[0]} to analogous information about the entire +pattern. + +For example, when you match the pattern @samp{(a*)} against the empty +string, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 0 in @code{@w{@var{regs}->}end[0]} + +@item +0 in @code{@w{@var{regs}->}start[1]} and 0 in @code{@w{@var{regs}->}end[1]} +@end itemize +@end ignore + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} and +the function reports a match of the @w{@var{i}-th} group, then it +records in @code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} the last match (if it matched) of +the @w{@var{j}-th} group. + +For example, when you match the pattern @samp{((a*)b)*} against the +string @samp{abb}, @w{group 2} last matches the empty string, so you +get what it previously matched: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +2 in @code{@w{@var{regs}->}start[2]} and 2 in @code{@w{@var{regs}->}end[2]} +@end itemize + +When you match the pattern @samp{((a)*b)*} against the string +@samp{abb}, @w{group 2} doesn't participate in the last match, so you +get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 3 in @code{@w{@var{regs}->}end[0]} + +@item +2 in @code{@w{@var{regs}->}start[1]} and 3 in @code{@w{@var{regs}->}end[1]} + +@item +0 in @code{@w{@var{regs}->}start[2]} and 1 in @code{@w{@var{regs}->}end[2]} +@end itemize + +@item +If an @w{@var{i}-th} group contains a @w{@var{j}-th} group +in turn not contained within any other group within group @var{i} +and the function sets +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{regs}->}end[@var{i}]} to @math{-1}, then it also sets +@code{@w{@var{regs}->}start[@var{j}]} and +@code{@w{@var{regs}->}end[@var{j}]} to @math{-1}. + +For example, when you match the pattern @samp{((a)*b)*c} against the +string @samp{c}, you get: + +@itemize @bullet +@item +0 in @code{@w{@var{regs}->}start[0]} and 1 in @code{@w{@var{regs}->}end[0]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[1]} and @math{-1} in @code{@w{@var{regs}->}end[1]} + +@item +@math{-1} in @code{@w{@var{regs}->}start[2]} and @math{-1} in @code{@w{@var{regs}->}end[2]} +@end itemize + +@end itemize + +@node Freeing GNU Pattern Buffers, , Using Registers, GNU Regex Functions +@subsection Freeing GNU Pattern Buffers + +To free any allocated fields of a pattern buffer, you can use the +@sc{posix} function described in @ref{Freeing POSIX Pattern Buffers}, +since the type @code{regex_t}---the type for @sc{posix} pattern +buffers---is equivalent to the type @code{re_pattern_buffer}. After +freeing a pattern buffer, you need to again compile a regular expression +in it (@pxref{GNU Regular Expression Compiling}) before passing it to +a matching or searching function. + + +@node POSIX Regex Functions, BSD Regex Functions, GNU Regex Functions, Programming with Regex +@section POSIX Regex Functions + +If you're writing code that has to be @sc{posix} compatible, you'll need +to use these functions. Their interfaces are as specified by @sc{posix}, +draft 1003.2/D11.2. + +@menu +* POSIX Pattern Buffers:: The regex_t type. +* POSIX Regular Expression Compiling:: regcomp () +* POSIX Matching:: regexec () +* Reporting Errors:: regerror () +* Using Byte Offsets:: The regmatch_t type. +* Freeing POSIX Pattern Buffers:: regfree () +@end menu + + +@node POSIX Pattern Buffers, POSIX Regular Expression Compiling, , POSIX Regex Functions +@subsection POSIX Pattern Buffers + +To compile or match a given regular expression the @sc{posix} way, you +must supply a pattern buffer exactly the way you do for @sc{gnu} +(@pxref{GNU Pattern Buffers}). @sc{posix} pattern buffers have type +@code{regex_t}, which is equivalent to the @sc{gnu} pattern buffer +type @code{re_pattern_buffer}. + + +@node POSIX Regular Expression Compiling, POSIX Matching, POSIX Pattern Buffers, POSIX Regex Functions +@subsection POSIX Regular Expression Compiling + +With @sc{posix}, you can only search for a given regular expression; you +can't match it. To do this, you must first compile it in a +pattern buffer, using @code{regcomp}. + +@ignore +Before calling @code{regcomp}, you must initialize this pattern buffer +as you do for @sc{gnu} (@pxref{GNU Regular Expression Compiling}). See +below, however, for how to choose a syntax with which to compile. +@end ignore + +To compile a pattern buffer, use: + +@findex regcomp +@example +int +regcomp (regex_t *@var{preg}, const char *@var{regex}, int @var{cflags}) +@end example + +@noindent +@var{preg} is the initialized pattern buffer's address, @var{regex} is +the regular expression's address, and @var{cflags} is the compilation +flags, which Regex considers as a collection of bits. Here are the +valid bits, as defined in @file{regex.h}: + +@table @code + +@item REG_EXTENDED +@vindex REG_EXTENDED +says to use @sc{posix} Extended Regular Expression syntax; if this isn't +set, then says to use @sc{posix} Basic Regular Expression syntax. +@code{regcomp} sets @var{preg}'s @code{syntax} field accordingly. + +@item REG_ICASE +@vindex REG_ICASE +@cindex ignoring case +says to ignore case; @code{regcomp} sets @var{preg}'s @code{translate} +field to a translate table which ignores case, replacing anything you've +put there before. + +@item REG_NOSUB +@vindex REG_NOSUB +says to set @var{preg}'s @code{no_sub} field; @pxref{POSIX Matching}, +for what this means. + +@item REG_NEWLINE +@vindex REG_NEWLINE +says that a: + +@itemize @bullet + +@item +match-any-character operator (@pxref{Match-any-character +Operator}) doesn't match a newline. + +@item +nonmatching list not containing a newline (@pxref{List +Operators}) matches a newline. + +@item +match-beginning-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately after a newline, +regardless of how @code{REG_NOTBOL} is set (@pxref{POSIX Matching}, for +an explanation of @code{REG_NOTBOL}). + +@item +match-end-of-line operator (@pxref{Match-beginning-of-line +Operator}) matches the empty string immediately before a newline, +regardless of how @code{REG_NOTEOL} is set (@pxref{POSIX Matching}, +for an explanation of @code{REG_NOTEOL}). + +@end itemize + +@end table + +If @code{regcomp} successfully compiles the regular expression, it +returns zero and sets @code{*@var{pattern_buffer}} to the compiled +pattern. Except for @code{syntax} (which it sets as explained above), it +also sets the same fields the same way as does the @sc{gnu} compiling +function (@pxref{GNU Regular Expression Compiling}). + +If @code{regcomp} can't compile the regular expression, it returns one +of the error codes listed here. (Except when noted differently, the +syntax of in all examples below is basic regular expression syntax.) + +@table @code + +@comment repetitions +@item REG_BADRPT +For example, the consecutive repetition operators @samp{**} in +@samp{a**} are invalid. As another example, if the syntax is extended +regular expression syntax, then the repetition operator @samp{*} with +nothing on which to operate in @samp{*} is invalid. + +@item REG_BADBR +For example, the @var{count} @samp{-1} in @samp{a\@{-1} is invalid. + +@item REG_EBRACE +For example, @samp{a\@{1} is missing a close-interval operator. + +@comment lists +@item REG_EBRACK +For example, @samp{[a} is missing a close-list operator. + +@item REG_ERANGE +For example, the range ending point @samp{z} that collates lower than +does its starting point @samp{a} in @samp{[z-a]} is invalid. Also, the +range with the character class @samp{[:alpha:]} as its starting point in +@samp{[[:alpha:]-|]}. + +@item REG_ECTYPE +For example, the character class name @samp{foo} in @samp{[[:foo:]} is +invalid. + +@comment groups +@item REG_EPAREN +For example, @samp{a\)} is missing an open-group operator and @samp{\(a} +is missing a close-group operator. + +@item REG_ESUBREG +For example, the back reference @samp{\2} that refers to a nonexistent +subexpression in @samp{\(a\)\2} is invalid. + +@comment unfinished business + +@item REG_EEND +Returned when a regular expression causes no other more specific error. + +@item REG_EESCAPE +For example, the trailing backslash @samp{\} in @samp{a\} is invalid, as is the +one in @samp{\}. + +@comment kitchen sink +@item REG_BADPAT +For example, in the extended regular expression syntax, the empty group +@samp{()} in @samp{a()b} is invalid. + +@comment internal +@item REG_ESIZE +Returned when a regular expression needs a pattern buffer larger than +65536 bytes. + +@item REG_ESPACE +Returned when a regular expression makes Regex to run out of memory. + +@end table + + +@node POSIX Matching, Reporting Errors, POSIX Regular Expression Compiling, POSIX Regex Functions +@subsection POSIX Matching + +Matching the @sc{posix} way means trying to match a null-terminated +string starting at its first character. Once you've compiled a pattern +into a pattern buffer (@pxref{POSIX Regular Expression Compiling}), you +can ask the matcher to match that pattern against a string using: + +@findex regexec +@example +int +regexec (const regex_t *@var{preg}, const char *@var{string}, + size_t @var{nmatch}, regmatch_t @var{pmatch}[], int @var{eflags}) +@end example + +@noindent +@var{preg} is the address of a pattern buffer for a compiled pattern. +@var{string} is the string you want to match. + +@xref{Using Byte Offsets}, for an explanation of @var{pmatch}. If you +pass zero for @var{nmatch} or you compiled @var{preg} with the +compilation flag @code{REG_NOSUB} set, then @code{regexec} will ignore +@var{pmatch}; otherwise, you must allocate it to have at least +@var{nmatch} elements. @code{regexec} will record @var{nmatch} byte +offsets in @var{pmatch}, and set to @math{-1} any unused elements up to +@math{@var{pmatch}@code{[@var{nmatch}]} - 1}. + +@var{eflags} specifies @dfn{execution flags}---namely, the two bits +@code{REG_NOTBOL} and @code{REG_NOTEOL} (defined in @file{regex.h}). If +you set @code{REG_NOTBOL}, then the match-beginning-of-line operator +(@pxref{Match-beginning-of-line Operator}) always fails to match. +This lets you match against pieces of a line, as you would need to if, +say, searching for repeated instances of a given pattern in a line; it +would work correctly for patterns both with and without +match-beginning-of-line operators. @code{REG_NOTEOL} works analogously +for the match-end-of-line operator (@pxref{Match-end-of-line +Operator}); it exists for symmetry. + +@code{regexec} tries to find a match for @var{preg} in @var{string} +according to the syntax in @var{preg}'s @code{syntax} field. +(@xref{POSIX Regular Expression Compiling}, for how to set it.) The +function returns zero if the compiled pattern matches @var{string} and +@code{REG_NOMATCH} (defined in @file{regex.h}) if it doesn't. + +@node Reporting Errors, Using Byte Offsets, POSIX Matching, POSIX Regex Functions +@subsection Reporting Errors + +If either @code{regcomp} or @code{regexec} fail, they return a nonzero +error code, the possibilities for which are defined in @file{regex.h}. +@xref{POSIX Regular Expression Compiling}, and @ref{POSIX Matching}, for +what these codes mean. To get an error string corresponding to these +codes, you can use: + +@findex regerror +@example +size_t +regerror (int @var{errcode}, + const regex_t *@var{preg}, + char *@var{errbuf}, + size_t @var{errbuf_size}) +@end example + +@noindent +@var{errcode} is an error code, @var{preg} is the address of the pattern +buffer which provoked the error, @var{errbuf} is the error buffer, and +@var{errbuf_size} is @var{errbuf}'s size. + +@code{regerror} returns the size in bytes of the error string +corresponding to @var{errcode} (including its terminating null). If +@var{errbuf} and @var{errbuf_size} are nonzero, it also returns in +@var{errbuf} the first @math{@var{errbuf_size} - 1} characters of the +error string, followed by a null. +@var{errbuf_size} must be a nonnegative number less than or equal to the +size in bytes of @var{errbuf}. + +You can call @code{regerror} with a null @var{errbuf} and a zero +@var{errbuf_size} to determine how large @var{errbuf} need be to +accommodate @code{regerror}'s error string. + +@node Using Byte Offsets, Freeing POSIX Pattern Buffers, Reporting Errors, POSIX Regex Functions +@subsection Using Byte Offsets + +In @sc{posix}, variables of type @code{regmatch_t} hold analogous +information, but are not identical to, @sc{gnu}'s registers (@pxref{Using +Registers}). To get information about registers in @sc{posix}, pass to +@code{regexec} a nonzero @var{pmatch} of type @code{regmatch_t}, i.e., +the address of a structure of this type, defined in +@file{regex.h}: + +@tindex regmatch_t +@example +typedef struct +@{ + regoff_t rm_so; + regoff_t rm_eo; +@} regmatch_t; +@end example + +When reading in @ref{Using Registers}, about how the matching function +stores the information into the registers, substitute @var{pmatch} for +@var{regs}, @code{@w{@var{pmatch}[@var{i}]->}rm_so} for +@code{@w{@var{regs}->}start[@var{i}]} and +@code{@w{@var{pmatch}[@var{i}]->}rm_eo} for +@code{@w{@var{regs}->}end[@var{i}]}. + +@node Freeing POSIX Pattern Buffers, , Using Byte Offsets, POSIX Regex Functions +@subsection Freeing POSIX Pattern Buffers + +To free any allocated fields of a pattern buffer, use: + +@findex regfree +@example +void +regfree (regex_t *@var{preg}) +@end example + +@noindent +@var{preg} is the pattern buffer whose allocated fields you want freed. +@code{regfree} also sets @var{preg}'s @code{allocated} and @code{used} +fields to zero. After freeing a pattern buffer, you need to again +compile a regular expression in it (@pxref{POSIX Regular Expression +Compiling}) before passing it to the matching function (@pxref{POSIX +Matching}). + + +@node BSD Regex Functions, , POSIX Regex Functions, Programming with Regex +@section BSD Regex Functions + +If you're writing code that has to be Berkeley @sc{unix} compatible, +you'll need to use these functions whose interfaces are the same as those +in Berkeley @sc{unix}. + +@menu +* BSD Regular Expression Compiling:: re_comp () +* BSD Searching:: re_exec () +@end menu + +@node BSD Regular Expression Compiling, BSD Searching, , BSD Regex Functions +@subsection BSD Regular Expression Compiling + +With Berkeley @sc{unix}, you can only search for a given regular +expression; you can't match one. To search for it, you must first +compile it. Before you compile it, you must indicate the regular +expression syntax you want it compiled according to by setting the +variable @code{re_syntax_options} (declared in @file{regex.h} to some +syntax (@pxref{Regular Expression Syntax}). + +To compile a regular expression use: + +@findex re_comp +@example +char * +re_comp (char *@var{regex}) +@end example + +@noindent +@var{regex} is the address of a null-terminated regular expression. +@code{re_comp} uses an internal pattern buffer, so you can use only the +most recently compiled pattern buffer. This means that if you want to +use a given regular expression that you've already compiled---but it +isn't the latest one you've compiled---you'll have to recompile it. If +you call @code{re_comp} with the null string (@emph{not} the empty +string) as the argument, it doesn't change the contents of the pattern +buffer. + +If @code{re_comp} successfully compiles the regular expression, it +returns zero. If it can't compile the regular expression, it returns +an error string. @code{re_comp}'s error messages are identical to those +of @code{re_compile_pattern} (@pxref{GNU Regular Expression +Compiling}). + +@node BSD Searching, , BSD Regular Expression Compiling, BSD Regex Functions +@subsection BSD Searching + +Searching the Berkeley @sc{unix} way means searching in a string +starting at its first character and trying successive positions within +it to find a match. Once you've compiled a pattern using @code{re_comp} +(@pxref{BSD Regular Expression Compiling}), you can ask Regex +to search for that pattern in a string using: + +@findex re_exec +@example +int +re_exec (char *@var{string}) +@end example + +@noindent +@var{string} is the address of the null-terminated string in which you +want to search. + +@code{re_exec} returns either 1 for success or 0 for failure. It +automatically uses a @sc{gnu} fastmap (@pxref{Searching with Fastmaps}). + + +@node Copying, Index, Programming with Regex, Top +@appendix GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +675 Mass Ave, Cambridge, MA 02139, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) 19@var{yy} @var{name of author} + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@example +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end example + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + + +@node Index, , Copying, Top +@unnumbered Index + +@printindex cp + +@contents + +@bye diff --git a/gnu/lib/libregex/gnuregex.h b/gnu/lib/libregex/gnuregex.h new file mode 100644 index 0000000..7356f9f --- /dev/null +++ b/gnu/lib/libregex/gnuregex.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2004 David E. O'Brien + * Copyright (c) 2004 Andrey A. Chernov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifdef __GNUC__ +#warning "Use -I/usr/include/gnu and <regex.h> instead of <gnuregex.h>" +#endif +#include <gnu/regex.h> diff --git a/gnu/lib/libregex/posix/regex.h b/gnu/lib/libregex/posix/regex.h new file mode 100644 index 0000000..b2d9a62 --- /dev/null +++ b/gnu/lib/libregex/posix/regex.h @@ -0,0 +1,593 @@ +/* Definitions for data structures and routines for the regular + expression library. + Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _REGEX_H +#define _REGEX_H 1 + +#include <sys/types.h> + +/* Allow the use in C++ code. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX says that <sys/types.h> must be included (by the caller) before + <regex.h>. */ + +#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS +/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it + should be there. */ +# include <stddef.h> +#endif + +/* The following two types have to be signed and unsigned integer type + wide enough to hold a value of a pointer. For most ANSI compilers + ptrdiff_t and size_t should be likely OK. Still size of these two + types is 2 for Microsoft C. Ugh... */ +typedef long int s_reg_t; +typedef unsigned long int active_reg_t; + +/* The following bits are used to determine the regexp syntax we + recognize. The set/not-set meanings are chosen so that Emacs syntax + remains the value 0. The bits are given in alphabetical order, and + the definitions shifted by one from the previous bit; thus, when we + add or remove a bit, only one other definition need change. */ +typedef unsigned long int reg_syntax_t; + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) + +/* If this bit is not set, then + and ? are operators, and \+ and \? are + literals. + If set, then \+ and \? are operators and + and ? are literals. */ +#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) + +/* If this bit is set, then character classes are supported. They are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) + +/* If this bit is set, then ^ and $ are always anchors (outside bracket + expressions, of course). + If this bit is not set, then it depends: + ^ is an anchor if it is at the beginning of a regular + expression or after an open-group or an alternation operator; + $ is an anchor if it is at the end of a regular expression, or + before a close-group or an alternation operator. + + This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because + POSIX draft 11.2 says that * etc. in leading positions is undefined. + We already implemented a previous draft which made those constructs + invalid, though, so we haven't changed the code back. */ +#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) + +/* If this bit is set, then special characters are always special + regardless of where they are in the pattern. + If this bit is not set, then special characters are special only in + some contexts; otherwise they are ordinary. Specifically, + * + ? and intervals are only special when not after the beginning, + open-group, or alternation operator. */ +#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) + +/* If this bit is set, then *, +, ?, and { cannot be first in an re or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) + +/* If this bit is set, then . matches newline. + If not set, then it doesn't. */ +#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) + +/* If this bit is set, then . doesn't match NUL. + If not set, then it does. */ +#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) + +/* If this bit is set, nonmatching lists [^...] do not match newline. + If not set, they do. */ +#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) + +/* If this bit is set, either \{...\} or {...} defines an + interval, depending on RE_NO_BK_BRACES. + If not set, \{, \}, {, and } are literals. */ +#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) + +/* If this bit is set, +, ? and | aren't recognized as operators. + If not set, they are. */ +#define RE_LIMITED_OPS (RE_INTERVALS << 1) + +/* If this bit is set, newline is an alternation operator. + If not set, newline is literal. */ +#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) + +/* If this bit is set, then `{...}' defines an interval, and \{ and \} + are literals. + If not set, then `\{...\}' defines an interval. */ +#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) + +/* If this bit is set, (...) defines a group, and \( and \) are literals. + If not set, \(...\) defines a group, and ( and ) are literals. */ +#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) + +/* If this bit is set, then \<digit> matches <digit>. + If not set, then \<digit> is a back-reference. */ +#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) + +/* If this bit is set, then | is an alternation operator, and \| is literal. + If not set, then \| is an alternation operator, and | is literal. */ +#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) + +/* If this bit is set, then an ending range point collating higher + than the starting range point, as in [z-a], is invalid. + If not set, then when ending range point collates higher than the + starting range point, the range is ignored. */ +#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) + +/* If this bit is set, then an unmatched ) is ordinary. + If not set, then an unmatched ) is invalid. */ +#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) + +/* If this bit is set, succeed as soon as we match the whole pattern, + without further backtracking. */ +#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) + +/* If this bit is set, do not process the GNU regex operators. + If not set, then the GNU regex operators are recognized. */ +#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) + +/* If this bit is set, turn on internal regex debugging. + If not set, and debugging was on, turn it off. + This only works if regex.c is compiled -DDEBUG. + We define this bit always, so that all that's needed to turn on + debugging is to recompile regex.c; the calling code can always have + this bit set, and it won't affect anything in the normal case. */ +#define RE_DEBUG (RE_NO_GNU_OPS << 1) + +/* If this bit is set, a syntactically invalid interval is treated as + a string of ordinary characters. For example, the ERE 'a{1' is + treated as 'a\{1'. */ +#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1) + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) + +/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only + for ^, because it is difficult to scan the regex backwards to find + whether ^ should be special. */ +#define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) + +/* If this bit is set, then \{ cannot be first in an bre or + immediately after an alternation or begin-group operator. */ +#define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) + +/* If this bit is set, then no_sub will be set to 1 during + re_compile_pattern. */ +#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) + +/* This global variable defines the particular regexp syntax to use (for + some interfaces). When a regexp is compiled, the syntax used is + stored in the pattern buffer, so changing this does not affect + already-compiled regexps. */ +extern reg_syntax_t re_syntax_options; + +/* Define combinations of the above bits for the standard possibilities. + (The [[[ comments delimit what gets put into the Texinfo file, so + don't delete them!) */ +/* [[[begin syntaxes]]] */ +#define RE_SYNTAX_EMACS 0 + +#define RE_SYNTAX_AWK \ + (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ + | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ + | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GNU_AWK \ + ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \ + & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \ + | RE_CONTEXT_INVALID_OPS )) + +#define RE_SYNTAX_POSIX_AWK \ + (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ + | RE_INTERVALS | RE_NO_GNU_OPS) + +#define RE_SYNTAX_GREP \ + (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ + | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ + | RE_NEWLINE_ALT) + +#define RE_SYNTAX_EGREP \ + (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ + | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ + | RE_NO_BK_VBAR) + +#define RE_SYNTAX_POSIX_EGREP \ + (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ + | RE_INVALID_INTERVAL_ORD) + +/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ +#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC + +#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC + +/* Syntax bits common to both basic and extended POSIX regex syntax. */ +#define _RE_SYNTAX_POSIX_COMMON \ + (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ + | RE_INTERVALS | RE_NO_EMPTY_RANGES) + +#define RE_SYNTAX_POSIX_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) + +/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes + RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this + isn't minimal, since other operators, such as \`, aren't disabled. */ +#define RE_SYNTAX_POSIX_MINIMAL_BASIC \ + (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) + +#define RE_SYNTAX_POSIX_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) + +/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is + removed and RE_NO_BK_REFS is added. */ +#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ + (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ + | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ + | RE_NO_BK_PARENS | RE_NO_BK_REFS \ + | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) +/* [[[end syntaxes]]] */ + +/* Maximum number of duplicates an interval can allow. Some systems + (erroneously) define this in other header files, but we want our + value, so remove any previous define. */ +#ifdef RE_DUP_MAX +# undef RE_DUP_MAX +#endif +/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ +#define RE_DUP_MAX (0x7fff) + + +/* POSIX `cflags' bits (i.e., information for `regcomp'). */ + +/* If this bit is set, then use extended regular expression syntax. + If not set, then use basic regular expression syntax. */ +#define REG_EXTENDED 1 + +/* If this bit is set, then ignore case when matching. + If not set, then case is significant. */ +#define REG_ICASE (REG_EXTENDED << 1) + +/* If this bit is set, then anchors do not match at newline + characters in the string. + If not set, then anchors do match at newlines. */ +#define REG_NEWLINE (REG_ICASE << 1) + +/* If this bit is set, then report only success or fail in regexec. + If not set, then returns differ between not matching and errors. */ +#define REG_NOSUB (REG_NEWLINE << 1) + + +/* POSIX `eflags' bits (i.e., information for regexec). */ + +/* If this bit is set, then the beginning-of-line operator doesn't match + the beginning of the string (presumably because it's not the + beginning of a line). + If not set, then the beginning-of-line operator does match the + beginning of the string. */ +#define REG_NOTBOL 1 + +/* Like REG_NOTBOL, except for the end-of-line. */ +#define REG_NOTEOL (1 << 1) + +/* Use PMATCH[0] to delimit the start and end of the search in the + buffer. */ +#define REG_STARTEND (1 << 2) + + +/* If any error codes are removed, changed, or added, update the + `re_error_msg' table in regex.c. */ +typedef enum +{ +#ifdef _XOPEN_SOURCE + REG_ENOSYS = -1, /* This will never happen for this implementation. */ +#endif + + REG_NOERROR = 0, /* Success. */ + REG_NOMATCH, /* Didn't find a match (for regexec). */ + + /* POSIX regcomp return error codes. (In the order listed in the + standard.) */ + REG_BADPAT, /* Invalid pattern. */ + REG_ECOLLATE, /* Inalid collating element. */ + REG_ECTYPE, /* Invalid character class name. */ + REG_EESCAPE, /* Trailing backslash. */ + REG_ESUBREG, /* Invalid back reference. */ + REG_EBRACK, /* Unmatched left bracket. */ + REG_EPAREN, /* Parenthesis imbalance. */ + REG_EBRACE, /* Unmatched \{. */ + REG_BADBR, /* Invalid contents of \{\}. */ + REG_ERANGE, /* Invalid range end. */ + REG_ESPACE, /* Ran out of memory. */ + REG_BADRPT, /* No preceding re for repetition op. */ + + /* Error codes we've added. */ + REG_EEND, /* Premature end. */ + REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ + REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ +} reg_errcode_t; + +/* This data structure represents a compiled pattern. Before calling + the pattern compiler, the fields `buffer', `allocated', `fastmap', + `translate', and `no_sub' can be set. After the pattern has been + compiled, the `re_nsub' field is available. All other fields are + private to the regex routines. */ + +#ifndef RE_TRANSLATE_TYPE +# define RE_TRANSLATE_TYPE char * +#endif + +struct re_pattern_buffer +{ +/* [[[begin pattern_buffer]]] */ + /* Space that holds the compiled pattern. It is declared as + `unsigned char *' because its elements are + sometimes used as array indexes. */ + unsigned char *buffer; + + /* Number of bytes to which `buffer' points. */ + unsigned long int allocated; + + /* Number of bytes actually used in `buffer'. */ + unsigned long int used; + + /* Syntax setting with which the pattern was compiled. */ + reg_syntax_t syntax; + + /* Pointer to a fastmap, if any, otherwise zero. re_search uses + the fastmap, if there is one, to skip over impossible + starting points for matches. */ + char *fastmap; + + /* Either a translate table to apply to all characters before + comparing them, or zero for no translation. The translation + is applied to a pattern when it is compiled and to a string + when it is matched. */ + RE_TRANSLATE_TYPE translate; + + /* Number of subexpressions found by the compiler. */ + size_t re_nsub; + + /* Zero if this pattern cannot match the empty string, one else. + Well, in truth it's used only in `re_search_2', to see + whether or not we should use the fastmap, so we don't set + this absolutely perfectly; see `re_compile_fastmap' (the + `duplicate' case). */ + unsigned can_be_null : 1; + + /* If REGS_UNALLOCATED, allocate space in the `regs' structure + for `max (RE_NREGS, re_nsub + 1)' groups. + If REGS_REALLOCATE, reallocate space if necessary. + If REGS_FIXED, use what's there. */ +#define REGS_UNALLOCATED 0 +#define REGS_REALLOCATE 1 +#define REGS_FIXED 2 + unsigned regs_allocated : 2; + + /* Set to zero when `regex_compile' compiles a pattern; set to one + by `re_compile_fastmap' if it updates the fastmap. */ + unsigned fastmap_accurate : 1; + + /* If set, `re_match_2' does not return information about + subexpressions. */ + unsigned no_sub : 1; + + /* If set, a beginning-of-line anchor doesn't match at the + beginning of the string. */ + unsigned not_bol : 1; + + /* Similarly for an end-of-line anchor. */ + unsigned not_eol : 1; + + /* If true, an anchor at a newline matches. */ + unsigned newline_anchor : 1; + +/* [[[end pattern_buffer]]] */ +}; + +typedef struct re_pattern_buffer regex_t; + +/* Type for byte offsets within the string. POSIX mandates this. */ +typedef int regoff_t; + + +/* This is the structure we store register match data in. See + regex.texinfo for a full description of what registers match. */ +struct re_registers +{ + unsigned num_regs; + regoff_t *start; + regoff_t *end; +}; + + +/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, + `re_match_2' returns information about at least this many registers + the first time a `regs' structure is passed. */ +#ifndef RE_NREGS +# define RE_NREGS 30 +#endif + + +/* POSIX specification for registers. Aside from the different names than + `re_registers', POSIX uses an array of structures, instead of a + structure of arrays. */ +typedef struct +{ + regoff_t rm_so; /* Byte offset from string's start to substring's start. */ + regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ +} regmatch_t; + +/* Declarations for routines. */ + +/* To avoid duplicating every routine declaration -- once with a + prototype (if we are ANSI), and once without (if we aren't) -- we + use the following macro to declare argument types. This + unfortunately clutters up the declarations a bit, but I think it's + worth it. */ + +#if __STDC__ + +# define _RE_ARGS(args) args + +#else /* not __STDC__ */ + +# define _RE_ARGS(args) () + +#endif /* not __STDC__ */ + +/* Sets the current default syntax to SYNTAX, and return the old syntax. + You can also simply assign to the `re_syntax_options' variable. */ +extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +/* Compile the regular expression PATTERN, with length LENGTH + and syntax given by the global `re_syntax_options', into the buffer + BUFFER. Return NULL if successful, and an error string if not. */ +extern const char *re_compile_pattern + _RE_ARGS ((const char *pattern, size_t length, + struct re_pattern_buffer *buffer)); + + +/* Compile a fastmap for the compiled pattern in BUFFER; used to + accelerate searches. Return 0 if successful and -2 if was an + internal error. */ +extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + + +/* Search in the string STRING (with length LENGTH) for the pattern + compiled into BUFFER. Start searching at position START, for RANGE + characters. Return the starting position of the match, -1 for no + match, or -2 for an internal error. Also return register + information in REGS (if REGS and BUFFER->no_sub are nonzero). */ +extern int re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + + +/* Like `re_search', but search in the concatenation of STRING1 and + STRING2. Also, stop searching at index START + STOP. */ +extern int re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + + +/* Like `re_search', but return how many characters in STRING the regexp + in BUFFER matched, starting at position START. */ +extern int re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + + +/* Relates to `re_match' as `re_search_2' relates to `re_search'. */ +extern int re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using BUFFER and REGS will use this memory + for recording register information. STARTS and ENDS must be + allocated with malloc, and must each be at least `NUM_REGS * sizeof + (regoff_t)' bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ +extern void re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +#if defined _REGEX_RE_COMP || defined _LIBC +# ifndef _CRAY +/* 4.2 bsd compatibility. */ +extern char *re_comp _RE_ARGS ((const char *)); +extern int re_exec _RE_ARGS ((const char *)); +# endif +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if defined restrict || 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif +/* gcc 3.1 and up support the [restrict] syntax. */ +#ifndef __restrict_arr +# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define __restrict_arr __restrict +# else +# define __restrict_arr +# endif +#endif + +/* POSIX compatibility. */ +extern int regcomp _RE_ARGS ((regex_t *__restrict __preg, + const char *__restrict __pattern, + int __cflags)); + +extern int regexec _RE_ARGS ((const regex_t *__restrict __preg, + const char *__restrict __string, size_t __nmatch, + regmatch_t __pmatch[__restrict_arr], + int __eflags)); + +extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size)); + +extern void regfree _RE_ARGS ((regex_t *__preg)); + + +#ifdef __cplusplus +} +#endif /* C++ */ + +#endif /* regex.h */ + +/* +Local variables: +make-backup-files: t +version-control: t +trim-versions-without-asking: nil +End: +*/ diff --git a/gnu/lib/libregex/regcomp.c b/gnu/lib/libregex/regcomp.c new file mode 100644 index 0000000..68e2bda --- /dev/null +++ b/gnu/lib/libregex/regcomp.c @@ -0,0 +1,3924 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, + int length, reg_syntax_t syntax); +static void re_compile_fastmap_iter (regex_t *bufp, + const re_dfastate_t *init_state, + char *fastmap); +static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len); +static void init_word_char (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void free_charset (re_charset_t *cset); +#endif /* RE_ENABLE_I18N */ +static void free_workarea_compile (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +#ifdef RE_ENABLE_I18N +static void optimize_utf8 (re_dfa_t *dfa); +#endif +static reg_errcode_t analyze (regex_t *preg); +static reg_errcode_t create_initial_state (re_dfa_t *dfa); +static reg_errcode_t preorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t postorder (bin_tree_t *root, + reg_errcode_t (fn (void *, bin_tree_t *)), + void *extra); +static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); +static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); +static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, + bin_tree_t *node); +static reg_errcode_t calc_first (void *extra, bin_tree_t *node); +static reg_errcode_t calc_next (void *extra, bin_tree_t *node); +static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); +static reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node, + int top_clone_node, int root_node, + unsigned int constraint); +static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx, + unsigned int constraint); +static int search_duplicated_node (re_dfa_t *dfa, int org_node, + unsigned int constraint); +static reg_errcode_t calc_eclosure (re_dfa_t *dfa); +static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, + int node, int root); +static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); +static int fetch_number (re_string_t *input, re_token_t *token, + reg_syntax_t syntax); +static void fetch_token (re_token_t *result, re_string_t *input, + reg_syntax_t syntax); +static int peek_token (re_token_t *token, re_string_t *input, + reg_syntax_t syntax); +static int peek_token_bracket (re_token_t *token, re_string_t *input, + reg_syntax_t syntax); +static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, + re_token_t *token, reg_syntax_t syntax, + int nest, reg_errcode_t *err); +static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, + re_dfa_t *dfa, re_token_t *token, + reg_syntax_t syntax, reg_errcode_t *err); +static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, + re_token_t *token, reg_syntax_t syntax, + reg_errcode_t *err); +static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token, int token_len, + re_dfa_t *dfa, + reg_syntax_t syntax, + int accept_hyphen); +static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, + re_string_t *regexp, + re_token_t *token); +#ifndef _LIBC +# ifdef RE_ENABLE_I18N +static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, int *range_alloc, + bracket_elem_t *start_elem, + bracket_elem_t *end_elem); +static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *coll_sym_alloc, + const unsigned char *name); +# else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset, + bracket_elem_t *start_elem, + bracket_elem_t *end_elem); +static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset, + const unsigned char *name); +# endif /* not RE_ENABLE_I18N */ +#endif /* not _LIBC */ +#ifdef RE_ENABLE_I18N +static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *equiv_class_alloc, + const unsigned char *name); +static reg_errcode_t build_charclass (unsigned RE_TRANSLATE_TYPE trans, + re_bitset_ptr_t sbcset, + re_charset_t *mbcset, + int *char_class_alloc, + const unsigned char *class_name, + reg_syntax_t syntax); +#else /* not RE_ENABLE_I18N */ +static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset, + const unsigned char *name); +static reg_errcode_t build_charclass (unsigned RE_TRANSLATE_TYPE trans, + re_bitset_ptr_t sbcset, + const unsigned char *class_name, + reg_syntax_t syntax); +#endif /* not RE_ENABLE_I18N */ +static bin_tree_t *build_charclass_op (re_dfa_t *dfa, + unsigned RE_TRANSLATE_TYPE trans, + const unsigned char *class_name, + const unsigned char *extra, + int non_match, reg_errcode_t *err); +static bin_tree_t *create_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + re_token_type_t type); +static bin_tree_t *create_token_tree (re_dfa_t *dfa, + bin_tree_t *left, bin_tree_t *right, + const re_token_t *token); +static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); +static void free_token (re_token_t *node); +static reg_errcode_t free_tree (void *extra, bin_tree_t *node); +static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); + +/* This table gives an error message for each of the error codes listed + in regex.h. Obviously the order here has to be same as there. + POSIX doesn't require that we do anything for REG_NOERROR, + but why not be nice? */ + +const char __re_error_msgid[] attribute_hidden = + { +#define REG_NOERROR_IDX 0 + gettext_noop ("Success") /* REG_NOERROR */ + "\0" +#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") + gettext_noop ("No match") /* REG_NOMATCH */ + "\0" +#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") + gettext_noop ("Invalid regular expression") /* REG_BADPAT */ + "\0" +#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") + gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ + "\0" +#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") + gettext_noop ("Invalid character class name") /* REG_ECTYPE */ + "\0" +#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") + gettext_noop ("Trailing backslash") /* REG_EESCAPE */ + "\0" +#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") + gettext_noop ("Invalid back reference") /* REG_ESUBREG */ + "\0" +#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") + gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ + "\0" +#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") + gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ + "\0" +#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") + gettext_noop ("Unmatched \\{") /* REG_EBRACE */ + "\0" +#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") + gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ + "\0" +#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") + gettext_noop ("Invalid range end") /* REG_ERANGE */ + "\0" +#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") + gettext_noop ("Memory exhausted") /* REG_ESPACE */ + "\0" +#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") + gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ + "\0" +#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") + gettext_noop ("Premature end of regular expression") /* REG_EEND */ + "\0" +#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") + gettext_noop ("Regular expression too big") /* REG_ESIZE */ + "\0" +#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") + gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ + }; + +const size_t __re_error_msgid_idx[] attribute_hidden = + { + REG_NOERROR_IDX, + REG_NOMATCH_IDX, + REG_BADPAT_IDX, + REG_ECOLLATE_IDX, + REG_ECTYPE_IDX, + REG_EESCAPE_IDX, + REG_ESUBREG_IDX, + REG_EBRACK_IDX, + REG_EPAREN_IDX, + REG_EBRACE_IDX, + REG_BADBR_IDX, + REG_ERANGE_IDX, + REG_ESPACE_IDX, + REG_BADRPT_IDX, + REG_EEND_IDX, + REG_ESIZE_IDX, + REG_ERPAREN_IDX + }; + +/* Entry points for GNU code. */ + +/* re_compile_pattern is the GNU regular expression compiler: it + compiles PATTERN (of length LENGTH) and puts the result in BUFP. + Returns 0 if the pattern was valid, otherwise an error string. + + Assumes the `allocated' (and perhaps `buffer') and `translate' fields + are set in BUFP on entry. */ + +const char * +re_compile_pattern (pattern, length, bufp) + const char *pattern; + size_t length; + struct re_pattern_buffer *bufp; +{ + reg_errcode_t ret; + + /* And GNU code determines whether or not to get register information + by passing null for the REGS argument to re_match, etc., not by + setting no_sub, unless RE_NO_SUB is set. */ + bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); + + /* Match anchors at newline. */ + bufp->newline_anchor = 1; + + ret = re_compile_internal (bufp, pattern, length, re_syntax_options); + + if (!ret) + return NULL; + return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} +#ifdef _LIBC +weak_alias (__re_compile_pattern, re_compile_pattern) +#endif + +/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can + also be assigned to arbitrarily: each pattern buffer stores its own + syntax, so it can be changed between regex compilations. */ +/* This has no initializer because initialized variables in Emacs + become read-only after dumping. */ +reg_syntax_t re_syntax_options; + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit mask comprised of the various bits + defined in regex.h. We return the old syntax. */ + +reg_syntax_t +re_set_syntax (syntax) + reg_syntax_t syntax; +{ + reg_syntax_t ret = re_syntax_options; + + re_syntax_options = syntax; + return ret; +} +#ifdef _LIBC +weak_alias (__re_set_syntax, re_set_syntax) +#endif + +int +re_compile_fastmap (bufp) + struct re_pattern_buffer *bufp; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + char *fastmap = bufp->fastmap; + + memset (fastmap, '\0', sizeof (char) * SBC_MAX); + re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); + if (dfa->init_state != dfa->init_state_word) + re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); + if (dfa->init_state != dfa->init_state_nl) + re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); + if (dfa->init_state != dfa->init_state_begbuf) + re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); + bufp->fastmap_accurate = 1; + return 0; +} +#ifdef _LIBC +weak_alias (__re_compile_fastmap, re_compile_fastmap) +#endif + +static inline void +__attribute ((always_inline)) +re_set_fastmap (char *fastmap, int icase, int ch) +{ + fastmap[ch] = 1; + if (icase) + fastmap[tolower (ch)] = 1; +} + +/* Helper function for re_compile_fastmap. + Compile fastmap for the initial_state INIT_STATE. */ + +static void +re_compile_fastmap_iter (bufp, init_state, fastmap) + regex_t *bufp; + const re_dfastate_t *init_state; + char *fastmap; +{ + re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; + int node_cnt; + int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); + for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) + { + int node = init_state->nodes.elems[node_cnt]; + re_token_type_t type = dfa->nodes[node].type; + + if (type == CHARACTER) + { + re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); +#ifdef RE_ENABLE_I18N + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + unsigned char *buf = alloca (dfa->mb_cur_max), *p; + wchar_t wc; + mbstate_t state; + + p = buf; + *p++ = dfa->nodes[node].opr.c; + while (++node < dfa->nodes_len + && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].mb_partial) + *p++ = dfa->nodes[node].opr.c; + memset (&state, 0, sizeof (state)); + if (mbrtowc (&wc, (const char *) buf, p - buf, + &state) == p - buf + && (__wcrtomb ((char *) buf, towlower (wc), &state) + != (size_t) -1)) + re_set_fastmap (fastmap, 0, buf[0]); + } +#endif + } + else if (type == SIMPLE_BRACKET) + { + int i, j, ch; + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (dfa->nodes[node].opr.sbcset[i] & (1 << j)) + re_set_fastmap (fastmap, icase, ch); + } +#ifdef RE_ENABLE_I18N + else if (type == COMPLEX_BRACKET) + { + int i; + re_charset_t *cset = dfa->nodes[node].opr.mbcset; + if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes + || cset->nranges || cset->nchar_classes) + { +# ifdef _LIBC + if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0) + { + /* In this case we want to catch the bytes which are + the first byte of any collation elements. + e.g. In da_DK, we want to catch 'a' since "aa" + is a valid collation element, and don't catch + 'b' since 'b' is the only collation element + which starts from 'b'. */ + int j, ch; + const int32_t *table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (table[ch] < 0) + re_set_fastmap (fastmap, icase, ch); + } +# else + if (dfa->mb_cur_max > 1) + for (i = 0; i < SBC_MAX; ++i) + if (__btowc (i) == WEOF) + re_set_fastmap (fastmap, icase, i); +# endif /* not _LIBC */ + } + for (i = 0; i < cset->nmbchars; ++i) + { + char buf[256]; + mbstate_t state; + memset (&state, '\0', sizeof (state)); + if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) + re_set_fastmap (fastmap, icase, *(unsigned char *) buf); + if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) + { + if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) + != (size_t) -1) + re_set_fastmap (fastmap, 0, *(unsigned char *) buf); + } + } + } +#endif /* RE_ENABLE_I18N */ + else if (type == OP_PERIOD +#ifdef RE_ENABLE_I18N + || type == OP_UTF8_PERIOD +#endif /* RE_ENABLE_I18N */ + || type == END_OF_RE) + { + memset (fastmap, '\1', sizeof (char) * SBC_MAX); + if (type == END_OF_RE) + bufp->can_be_null = 1; + return; + } + } +} + +/* Entry point for POSIX code. */ +/* regcomp takes a regular expression as a string and compiles it. + + PREG is a regex_t *. We do not expect any fields to be initialized, + since POSIX says we shouldn't. Thus, we set + + `buffer' to the compiled pattern; + `used' to the length of the compiled pattern; + `syntax' to RE_SYNTAX_POSIX_EXTENDED if the + REG_EXTENDED bit in CFLAGS is set; otherwise, to + RE_SYNTAX_POSIX_BASIC; + `newline_anchor' to REG_NEWLINE being set in CFLAGS; + `fastmap' to an allocated space for the fastmap; + `fastmap_accurate' to zero; + `re_nsub' to the number of subexpressions in PATTERN. + + PATTERN is the address of the pattern string. + + CFLAGS is a series of bits which affect compilation. + + If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we + use POSIX basic syntax. + + If REG_NEWLINE is set, then . and [^...] don't match newline. + Also, regexec will try a match beginning after every newline. + + If REG_ICASE is set, then we considers upper- and lowercase + versions of letters to be equivalent when matching. + + If REG_NOSUB is set, then when PREG is passed to regexec, that + routine will report only success or failure, and nothing about the + registers. + + It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for + the return codes and their meanings.) */ + +int +regcomp (preg, pattern, cflags) + regex_t *__restrict preg; + const char *__restrict pattern; + int cflags; +{ + reg_errcode_t ret; + reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED + : RE_SYNTAX_POSIX_BASIC); + + preg->buffer = NULL; + preg->allocated = 0; + preg->used = 0; + + /* Try to allocate space for the fastmap. */ + preg->fastmap = re_malloc (char, SBC_MAX); + if (BE (preg->fastmap == NULL, 0)) + return REG_ESPACE; + + syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; + + /* If REG_NEWLINE is set, newlines are treated differently. */ + if (cflags & REG_NEWLINE) + { /* REG_NEWLINE implies neither . nor [^...] match newline. */ + syntax &= ~RE_DOT_NEWLINE; + syntax |= RE_HAT_LISTS_NOT_NEWLINE; + /* It also changes the matching behavior. */ + preg->newline_anchor = 1; + } + else + preg->newline_anchor = 0; + preg->no_sub = !!(cflags & REG_NOSUB); + preg->translate = NULL; + + ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); + + /* POSIX doesn't distinguish between an unmatched open-group and an + unmatched close-group: both are REG_EPAREN. */ + if (ret == REG_ERPAREN) + ret = REG_EPAREN; + + /* We have already checked preg->fastmap != NULL. */ + if (BE (ret == REG_NOERROR, 1)) + /* Compute the fastmap now, since regexec cannot modify the pattern + buffer. This function never fails in this implementation. */ + (void) re_compile_fastmap (preg); + else + { + /* Some error occurred while compiling the expression. */ + re_free (preg->fastmap); + preg->fastmap = NULL; + } + + return (int) ret; +} +#ifdef _LIBC +weak_alias (__regcomp, regcomp) +#endif + +/* Returns a message corresponding to an error code, ERRCODE, returned + from either regcomp or regexec. We don't use PREG here. */ + +size_t +regerror (errcode, preg, errbuf, errbuf_size) + int errcode; + const regex_t *preg; + char *errbuf; + size_t errbuf_size; +{ + const char *msg; + size_t msg_size; + + if (BE (errcode < 0 + || errcode >= (int) (sizeof (__re_error_msgid_idx) + / sizeof (__re_error_msgid_idx[0])), 0)) + /* Only error codes returned by the rest of the code should be passed + to this routine. If we are given anything else, or if other regex + code generates an invalid error code, then the program has a bug. + Dump core so we can fix it. */ + abort (); + + msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); + + msg_size = strlen (msg) + 1; /* Includes the null. */ + + if (BE (errbuf_size != 0, 1)) + { + if (BE (msg_size > errbuf_size, 0)) + { +#if defined HAVE_MEMPCPY || defined _LIBC + *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0'; +#else + memcpy (errbuf, msg, errbuf_size - 1); + errbuf[errbuf_size - 1] = 0; +#endif + } + else + memcpy (errbuf, msg, msg_size); + } + + return msg_size; +} +#ifdef _LIBC +weak_alias (__regerror, regerror) +#endif + + +#ifdef RE_ENABLE_I18N +/* This static array is used for the map to single-byte characters when + UTF-8 is used. Otherwise we would allocate memory just to initialize + it the same all the time. UTF-8 is the preferred encoding so this is + a worthwhile optimization. */ +static const bitset utf8_sb_map = +{ + /* Set the first 128 bits. */ +# if UINT_MAX == 0xffffffff + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +# else +# error "Add case for new unsigned int size" +# endif +}; +#endif + + +static void +free_dfa_content (re_dfa_t *dfa) +{ + int i, j; + + if (dfa->nodes) + for (i = 0; i < dfa->nodes_len; ++i) + free_token (dfa->nodes + i); + re_free (dfa->nexts); + for (i = 0; i < dfa->nodes_len; ++i) + { + if (dfa->eclosures != NULL) + re_node_set_free (dfa->eclosures + i); + if (dfa->inveclosures != NULL) + re_node_set_free (dfa->inveclosures + i); + if (dfa->edests != NULL) + re_node_set_free (dfa->edests + i); + } + re_free (dfa->edests); + re_free (dfa->eclosures); + re_free (dfa->inveclosures); + re_free (dfa->nodes); + + if (dfa->state_table) + for (i = 0; i <= dfa->state_hash_mask; ++i) + { + struct re_state_table_entry *entry = dfa->state_table + i; + for (j = 0; j < entry->num; ++j) + { + re_dfastate_t *state = entry->array[j]; + free_state (state); + } + re_free (entry->array); + } + re_free (dfa->state_table); +#ifdef RE_ENABLE_I18N + if (dfa->sb_char != utf8_sb_map) + re_free (dfa->sb_char); +#endif + re_free (dfa->subexp_map); +#ifdef DEBUG + re_free (dfa->re_str); +#endif + + re_free (dfa); +} + + +/* Free dynamically allocated space used by PREG. */ + +void +regfree (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + if (BE (dfa != NULL, 1)) + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + + re_free (preg->fastmap); + preg->fastmap = NULL; + + re_free (preg->translate); + preg->translate = NULL; +} +#ifdef _LIBC +weak_alias (__regfree, regfree) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC + +/* BSD has one and only one pattern buffer. */ +static struct re_pattern_buffer re_comp_buf; + +char * +# ifdef _LIBC +/* Make these definitions weak in libc, so POSIX programs can redefine + these names if they don't use our functions, and still use + regcomp/regexec above without link errors. */ +weak_function +# endif +re_comp (s) + const char *s; +{ + reg_errcode_t ret; + char *fastmap; + + if (!s) + { + if (!re_comp_buf.buffer) + return gettext ("No previous regular expression"); + return 0; + } + + if (re_comp_buf.buffer) + { + fastmap = re_comp_buf.fastmap; + re_comp_buf.fastmap = NULL; + __regfree (&re_comp_buf); + memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); + re_comp_buf.fastmap = fastmap; + } + + if (re_comp_buf.fastmap == NULL) + { + re_comp_buf.fastmap = (char *) malloc (SBC_MAX); + if (re_comp_buf.fastmap == NULL) + return (char *) gettext (__re_error_msgid + + __re_error_msgid_idx[(int) REG_ESPACE]); + } + + /* Since `re_exec' always passes NULL for the `regs' argument, we + don't need to initialize the pattern buffer fields which affect it. */ + + /* Match anchors at newlines. */ + re_comp_buf.newline_anchor = 1; + + ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); + + if (!ret) + return NULL; + + /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ + return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); +} + +#ifdef _LIBC +libc_freeres_fn (free_mem) +{ + __regfree (&re_comp_buf); +} +#endif + +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. + Compile the regular expression PATTERN, whose length is LENGTH. + SYNTAX indicate regular expression's syntax. */ + +static reg_errcode_t +re_compile_internal (preg, pattern, length, syntax) + regex_t *preg; + const char * pattern; + int length; + reg_syntax_t syntax; +{ + reg_errcode_t err = REG_NOERROR; + re_dfa_t *dfa; + re_string_t regexp; + + /* Initialize the pattern buffer. */ + preg->fastmap_accurate = 0; + preg->syntax = syntax; + preg->not_bol = preg->not_eol = 0; + preg->used = 0; + preg->re_nsub = 0; + preg->can_be_null = 0; + preg->regs_allocated = REGS_UNALLOCATED; + + /* Initialize the dfa. */ + dfa = (re_dfa_t *) preg->buffer; + if (BE (preg->allocated < sizeof (re_dfa_t), 0)) + { + /* If zero allocated, but buffer is non-null, try to realloc + enough space. This loses if buffer's address is bogus, but + that is the user's responsibility. If ->buffer is NULL this + is a simple allocation. */ + dfa = re_realloc (preg->buffer, re_dfa_t, 1); + if (dfa == NULL) + return REG_ESPACE; + preg->allocated = sizeof (re_dfa_t); + preg->buffer = (unsigned char *) dfa; + } + preg->used = sizeof (re_dfa_t); + + err = init_dfa (dfa, length); + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } +#ifdef DEBUG + dfa->re_str = re_malloc (char, length + 1); + strncpy (dfa->re_str, pattern, length + 1); +#endif + + err = re_string_construct (®exp, pattern, length, preg->translate, + syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + { + re_compile_internal_free_return: + free_workarea_compile (preg); + re_string_destruct (®exp); + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + return err; + } + + /* Parse the regular expression, and build a structure tree. */ + preg->re_nsub = 0; + dfa->str_tree = parse (®exp, preg, syntax, &err); + if (BE (dfa->str_tree == NULL, 0)) + goto re_compile_internal_free_return; + + /* Analyze the tree and create the nfa. */ + err = analyze (preg); + if (BE (err != REG_NOERROR, 0)) + goto re_compile_internal_free_return; + +#ifdef RE_ENABLE_I18N + /* If possible, do searching in single byte encoding to speed things up. */ + if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) + optimize_utf8 (dfa); +#endif + + /* Then create the initial state of the dfa. */ + err = create_initial_state (dfa); + + /* Release work areas. */ + free_workarea_compile (preg); + re_string_destruct (®exp); + + if (BE (err != REG_NOERROR, 0)) + { + free_dfa_content (dfa); + preg->buffer = NULL; + preg->allocated = 0; + } + + return err; +} + +/* Initialize DFA. We use the length of the regular expression PAT_LEN + as the initial length of some arrays. */ + +static reg_errcode_t +init_dfa (dfa, pat_len) + re_dfa_t *dfa; + int pat_len; +{ + int table_size; +#ifndef _LIBC + char *codeset_name; +#endif + + memset (dfa, '\0', sizeof (re_dfa_t)); + + /* Force allocation of str_tree_storage the first time. */ + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + + dfa->nodes_alloc = pat_len + 1; + dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); + + dfa->states_alloc = pat_len + 1; + + /* table_size = 2 ^ ceil(log pat_len) */ + for (table_size = 1; table_size > 0; table_size <<= 1) + if (table_size > pat_len) + break; + + dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); + dfa->state_hash_mask = table_size - 1; + + dfa->mb_cur_max = MB_CUR_MAX; +#ifdef _LIBC + if (dfa->mb_cur_max == 6 + && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) + dfa->is_utf8 = 1; + dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) + != 0); +#else +# ifdef HAVE_LANGINFO_CODESET + codeset_name = nl_langinfo (CODESET); +# else + codeset_name = getenv ("LC_ALL"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LC_CTYPE"); + if (codeset_name == NULL || codeset_name[0] == '\0') + codeset_name = getenv ("LANG"); + if (codeset_name == NULL) + codeset_name = ""; + else if (strchr (codeset_name, '.') != NULL) + codeset_name = strchr (codeset_name, '.') + 1; +# endif + + if (strcasecmp (codeset_name, "UTF-8") == 0 + || strcasecmp (codeset_name, "UTF8") == 0) + dfa->is_utf8 = 1; + + /* We check exhaustively in the loop below if this charset is a + superset of ASCII. */ + dfa->map_notascii = 0; +#endif + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + if (dfa->is_utf8) + dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; + else + { + int i, j, ch; + + dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1); + if (BE (dfa->sb_char == NULL, 0)) + return REG_ESPACE; + + /* Clear all bits by, then set those corresponding to single + byte chars. */ + bitset_empty (dfa->sb_char); + + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + { + wchar_t wch = __btowc (ch); + if (wch != WEOF) + dfa->sb_char[i] |= 1 << j; +# ifndef _LIBC + if (isascii (ch) && wch != (wchar_t) ch) + dfa->map_notascii = 1; +# endif + } + } + } +#endif + + if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +/* Initialize WORD_CHAR table, which indicate which character is + "word". In this case "word" means that it is the word construction + character used by some operators like "\<", "\>", etc. */ + +static void +init_word_char (dfa) + re_dfa_t *dfa; +{ + int i, j, ch; + dfa->word_ops_used = 1; + for (i = 0, ch = 0; i < BITSET_UINTS; ++i) + for (j = 0; j < UINT_BITS; ++j, ++ch) + if (isalnum (ch) || ch == '_') + dfa->word_char[i] |= 1 << j; +} + +/* Free the work area which are only used while compiling. */ + +static void +free_workarea_compile (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_storage_t *storage, *next; + for (storage = dfa->str_tree_storage; storage; storage = next) + { + next = storage->next; + re_free (storage); + } + dfa->str_tree_storage = NULL; + dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; + dfa->str_tree = NULL; + re_free (dfa->org_indices); + dfa->org_indices = NULL; +} + +/* Create initial states for all contexts. */ + +static reg_errcode_t +create_initial_state (dfa) + re_dfa_t *dfa; +{ + int first, i; + reg_errcode_t err; + re_node_set init_nodes; + + /* Initial states have the epsilon closure of the node which is + the first node of the regular expression. */ + first = dfa->str_tree->first->node_idx; + dfa->init_node = first; + err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* The back-references which are in initial states can epsilon transit, + since in this case all of the subexpressions can be null. + Then we add epsilon closures of the nodes which are the next nodes of + the back-references. */ + if (dfa->nbackref > 0) + for (i = 0; i < init_nodes.nelem; ++i) + { + int node_idx = init_nodes.elems[i]; + re_token_type_t type = dfa->nodes[node_idx].type; + + int clexp_idx; + if (type != OP_BACK_REF) + continue; + for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) + { + re_token_t *clexp_node; + clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; + if (clexp_node->type == OP_CLOSE_SUBEXP + && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) + break; + } + if (clexp_idx == init_nodes.nelem) + continue; + + if (type == OP_BACK_REF) + { + int dest_idx = dfa->edests[node_idx].elems[0]; + if (!re_node_set_contains (&init_nodes, dest_idx)) + { + re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx); + i = 0; + } + } + } + + /* It must be the first time to invoke acquire_state. */ + dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); + /* We don't check ERR here, since the initial state must not be NULL. */ + if (BE (dfa->init_state == NULL, 0)) + return err; + if (dfa->init_state->has_constraint) + { + dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_WORD); + dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, + CONTEXT_NEWLINE); + dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, + &init_nodes, + CONTEXT_NEWLINE + | CONTEXT_BEGBUF); + if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return err; + } + else + dfa->init_state_word = dfa->init_state_nl + = dfa->init_state_begbuf = dfa->init_state; + + re_node_set_free (&init_nodes); + return REG_NOERROR; +} + +#ifdef RE_ENABLE_I18N +/* If it is possible to do searching in single byte encoding instead of UTF-8 + to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change + DFA nodes where needed. */ + +static void +optimize_utf8 (dfa) + re_dfa_t *dfa; +{ + int node, i, mb_chars = 0, has_period = 0; + + for (node = 0; node < dfa->nodes_len; ++node) + switch (dfa->nodes[node].type) + { + case CHARACTER: + if (dfa->nodes[node].opr.c >= 0x80) + mb_chars = 1; + break; + case ANCHOR: + switch (dfa->nodes[node].opr.idx) + { + case LINE_FIRST: + case LINE_LAST: + case BUF_FIRST: + case BUF_LAST: + break; + default: + /* Word anchors etc. cannot be handled. */ + return; + } + break; + case OP_PERIOD: + has_period = 1; + break; + case OP_BACK_REF: + case OP_ALT: + case END_OF_RE: + case OP_DUP_ASTERISK: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + break; + case COMPLEX_BRACKET: + return; + case SIMPLE_BRACKET: + /* Just double check. */ + for (i = 0x80 / UINT_BITS; i < BITSET_UINTS; ++i) + if (dfa->nodes[node].opr.sbcset[i]) + return; + break; + default: + abort (); + } + + if (mb_chars || has_period) + for (node = 0; node < dfa->nodes_len; ++node) + { + if (dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].opr.c >= 0x80) + dfa->nodes[node].mb_partial = 0; + else if (dfa->nodes[node].type == OP_PERIOD) + dfa->nodes[node].type = OP_UTF8_PERIOD; + } + + /* The search can be in single byte locale. */ + dfa->mb_cur_max = 1; + dfa->is_utf8 = 0; + dfa->has_mb_node = dfa->nbackref > 0 || has_period; +} +#endif + +/* Analyze the structure tree, and calculate "first", "next", "edest", + "eclosure", and "inveclosure". */ + +static reg_errcode_t +analyze (preg) + regex_t *preg; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + reg_errcode_t ret; + + /* Allocate arrays. */ + dfa->nexts = re_malloc (int, dfa->nodes_alloc); + dfa->org_indices = re_malloc (int, dfa->nodes_alloc); + dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); + dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); + if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL + || dfa->eclosures == NULL, 0)) + return REG_ESPACE; + + dfa->subexp_map = re_malloc (int, preg->re_nsub); + if (dfa->subexp_map != NULL) + { + int i; + for (i = 0; i < preg->re_nsub; i++) + dfa->subexp_map[i] = i; + preorder (dfa->str_tree, optimize_subexps, dfa); + for (i = 0; i < preg->re_nsub; i++) + if (dfa->subexp_map[i] != i) + break; + if (i == preg->re_nsub) + { + free (dfa->subexp_map); + dfa->subexp_map = NULL; + } + } + + ret = postorder (dfa->str_tree, lower_subexps, preg); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = postorder (dfa->str_tree, calc_first, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + preorder (dfa->str_tree, calc_next, dfa); + ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + ret = calc_eclosure (dfa); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + /* We only need this during the prune_impossible_nodes pass in regexec.c; + skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ + if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) + || dfa->nbackref) + { + dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); + if (BE (dfa->inveclosures == NULL, 0)) + return REG_ESPACE; + ret = calc_inveclosure (dfa); + } + + return ret; +} + +/* Our parse trees are very unbalanced, so we cannot use a stack to + implement parse tree visits. Instead, we use parent pointers and + some hairy code in these two functions. */ +static reg_errcode_t +postorder (root, fn, extra) + bin_tree_t *root; + reg_errcode_t (fn (void *, bin_tree_t *)); + void *extra; +{ + bin_tree_t *node, *prev; + + for (node = root; ; ) + { + /* Descend down the tree, preferably to the left (or to the right + if that's the only child). */ + while (node->left || node->right) + if (node->left) + node = node->left; + else + node = node->right; + + do + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + if (node->parent == NULL) + return REG_NOERROR; + prev = node; + node = node->parent; + } + /* Go up while we have a node that is reached from the right. */ + while (node->right == prev || node->right == NULL); + node = node->right; + } +} + +static reg_errcode_t +preorder (root, fn, extra) + bin_tree_t *root; + reg_errcode_t (fn (void *, bin_tree_t *)); + void *extra; +{ + bin_tree_t *node; + + for (node = root; ; ) + { + reg_errcode_t err = fn (extra, node); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Go to the left node, or up and to the right. */ + if (node->left) + node = node->left; + else + { + bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + if (!node) + return REG_NOERROR; + } + node = node->right; + } + } +} + +/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell + re_search_internal to map the inner one's opr.idx to this one's. Adjust + backreferences as well. Requires a preorder visit. */ +static reg_errcode_t +optimize_subexps (extra, node) + void *extra; + bin_tree_t *node; +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + + if (node->token.type == OP_BACK_REF && dfa->subexp_map) + { + int idx = node->token.opr.idx; + node->token.opr.idx = dfa->subexp_map[idx]; + dfa->used_bkref_map |= 1 << node->token.opr.idx; + } + + else if (node->token.type == SUBEXP + && node->left && node->left->token.type == SUBEXP) + { + int other_idx = node->left->token.opr.idx; + + node->left = node->left->left; + if (node->left) + node->left->parent = node; + + dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; + if (other_idx < 8 * sizeof (dfa->used_bkref_map)) + dfa->used_bkref_map &= ~(1 << other_idx); + } + + return REG_NOERROR; +} + +/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation + of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ +static reg_errcode_t +lower_subexps (extra, node) + void *extra; + bin_tree_t *node; +{ + regex_t *preg = (regex_t *) extra; + reg_errcode_t err = REG_NOERROR; + + if (node->left && node->left->token.type == SUBEXP) + { + node->left = lower_subexp (&err, preg, node->left); + if (node->left) + node->left->parent = node; + } + if (node->right && node->right->token.type == SUBEXP) + { + node->right = lower_subexp (&err, preg, node->right); + if (node->right) + node->right->parent = node; + } + + return err; +} + +static bin_tree_t * +lower_subexp (err, preg, node) + reg_errcode_t *err; + regex_t *preg; + bin_tree_t *node; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *body = node->left; + bin_tree_t *op, *cls, *tree1, *tree; + + if (preg->no_sub + /* We do not optimize empty subexpressions, because otherwise we may + have bad CONCAT nodes with NULL children. This is obviously not + very common, so we do not lose much. An example that triggers + this case is the sed "script" /\(\)/x. */ + && node->left != NULL + && (node->token.opr.idx >= 8 * sizeof (dfa->used_bkref_map) + || !(dfa->used_bkref_map & (1 << node->token.opr.idx)))) + return node->left; + + /* Convert the SUBEXP node to the concatenation of an + OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ + op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); + cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); + tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; + tree = create_tree (dfa, op, tree1, CONCAT); + if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + + op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; + op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; + return tree; +} + +/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton + nodes. Requires a postorder visit. */ +static reg_errcode_t +calc_first (extra, node) + void *extra; + bin_tree_t *node; +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + if (node->token.type == CONCAT) + { + node->first = node->left->first; + node->node_idx = node->left->node_idx; + } + else + { + node->first = node; + node->node_idx = re_dfa_add_node (dfa, node->token); + if (BE (node->node_idx == -1, 0)) + return REG_ESPACE; + } + return REG_NOERROR; +} + +/* Pass 2: compute NEXT on the tree. Preorder visit. */ +static reg_errcode_t +calc_next (extra, node) + void *extra; + bin_tree_t *node; +{ + switch (node->token.type) + { + case OP_DUP_ASTERISK: + node->left->next = node; + break; + case CONCAT: + node->left->next = node->right->first; + node->right->next = node->next; + break; + default: + if (node->left) + node->left->next = node->next; + if (node->right) + node->right->next = node->next; + break; + } + return REG_NOERROR; +} + +/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ +static reg_errcode_t +link_nfa_nodes (extra, node) + void *extra; + bin_tree_t *node; +{ + re_dfa_t *dfa = (re_dfa_t *) extra; + int idx = node->node_idx; + reg_errcode_t err = REG_NOERROR; + + switch (node->token.type) + { + case CONCAT: + break; + + case END_OF_RE: + assert (node->next == NULL); + break; + + case OP_DUP_ASTERISK: + case OP_ALT: + { + int left, right; + dfa->has_plural_match = 1; + if (node->left != NULL) + left = node->left->first->node_idx; + else + left = node->next->node_idx; + if (node->right != NULL) + right = node->right->first->node_idx; + else + right = node->next->node_idx; + assert (left > -1); + assert (right > -1); + err = re_node_set_init_2 (dfa->edests + idx, left, right); + } + break; + + case ANCHOR: + case OP_OPEN_SUBEXP: + case OP_CLOSE_SUBEXP: + err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); + break; + + case OP_BACK_REF: + dfa->nexts[idx] = node->next->node_idx; + if (node->token.type == OP_BACK_REF) + re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); + break; + + default: + assert (!IS_EPSILON_NODE (node->token.type)); + dfa->nexts[idx] = node->next->node_idx; + break; + } + + return err; +} + +/* Duplicate the epsilon closure of the node ROOT_NODE. + Note that duplicated nodes have constraint INIT_CONSTRAINT in addition + to their own constraint. */ + +static reg_errcode_t +duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node, + init_constraint) + re_dfa_t *dfa; + int top_org_node, top_clone_node, root_node; + unsigned int init_constraint; +{ + reg_errcode_t err; + int org_node, clone_node, ret; + unsigned int constraint = init_constraint; + for (org_node = top_org_node, clone_node = top_clone_node;;) + { + int org_dest, clone_dest; + if (dfa->nodes[org_node].type == OP_BACK_REF) + { + /* If the back reference epsilon-transit, its destination must + also have the constraint. Then duplicate the epsilon closure + of the destination of the back reference, and store it in + edests of the back reference. */ + org_dest = dfa->nexts[org_node]; + re_node_set_empty (dfa->edests + clone_node); + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + dfa->nexts[clone_node] = dfa->nexts[org_node]; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else if (dfa->edests[org_node].nelem == 0) + { + /* In case of the node can't epsilon-transit, don't duplicate the + destination and store the original destination as the + destination of the node. */ + dfa->nexts[clone_node] = dfa->nexts[org_node]; + break; + } + else if (dfa->edests[org_node].nelem == 1) + { + /* In case of the node can epsilon-transit, and it has only one + destination. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + if (dfa->nodes[org_node].type == ANCHOR) + { + /* In case of the node has another constraint, append it. */ + if (org_node == root_node && clone_node != org_node) + { + /* ...but if the node is root_node itself, it means the + epsilon closure have a loop, then tie it to the + destination of the root_node. */ + ret = re_node_set_insert (dfa->edests + clone_node, + org_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + break; + } + constraint |= dfa->nodes[org_node].opr.ctx_type; + } + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + else /* dfa->edests[org_node].nelem == 2 */ + { + /* In case of the node can epsilon-transit, and it has two + destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ + org_dest = dfa->edests[org_node].elems[0]; + re_node_set_empty (dfa->edests + clone_node); + /* Search for a duplicated node which satisfies the constraint. */ + clone_dest = search_duplicated_node (dfa, org_dest, constraint); + if (clone_dest == -1) + { + /* There are no such a duplicated node, create a new one. */ + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + err = duplicate_node_closure (dfa, org_dest, clone_dest, + root_node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + { + /* There are a duplicated node which satisfy the constraint, + use it to avoid infinite loop. */ + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + + org_dest = dfa->edests[org_node].elems[1]; + err = duplicate_node (&clone_dest, dfa, org_dest, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); + if (BE (ret < 0, 0)) + return REG_ESPACE; + } + org_node = org_dest; + clone_node = clone_dest; + } + return REG_NOERROR; +} + +/* Search for a node which is duplicated from the node ORG_NODE, and + satisfies the constraint CONSTRAINT. */ + +static int +search_duplicated_node (dfa, org_node, constraint) + re_dfa_t *dfa; + int org_node; + unsigned int constraint; +{ + int idx; + for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) + { + if (org_node == dfa->org_indices[idx] + && constraint == dfa->nodes[idx].constraint) + return idx; /* Found. */ + } + return -1; /* Not found. */ +} + +/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. + The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded, + otherwise return the error code. */ + +static reg_errcode_t +duplicate_node (new_idx, dfa, org_idx, constraint) + re_dfa_t *dfa; + int *new_idx, org_idx; + unsigned int constraint; +{ + int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); + if (BE (dup_idx == -1, 0)) + return REG_ESPACE; + dfa->nodes[dup_idx].constraint = constraint; + if (dfa->nodes[org_idx].type == ANCHOR) + dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type; + dfa->nodes[dup_idx].duplicated = 1; + + /* Store the index of the original node. */ + dfa->org_indices[dup_idx] = org_idx; + *new_idx = dup_idx; + return REG_NOERROR; +} + +static reg_errcode_t +calc_inveclosure (dfa) + re_dfa_t *dfa; +{ + int src, idx, ret; + for (idx = 0; idx < dfa->nodes_len; ++idx) + re_node_set_init_empty (dfa->inveclosures + idx); + + for (src = 0; src < dfa->nodes_len; ++src) + { + int *elems = dfa->eclosures[src].elems; + for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) + { + ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); + if (BE (ret == -1, 0)) + return REG_ESPACE; + } + } + + return REG_NOERROR; +} + +/* Calculate "eclosure" for all the node in DFA. */ + +static reg_errcode_t +calc_eclosure (dfa) + re_dfa_t *dfa; +{ + int node_idx, incomplete; +#ifdef DEBUG + assert (dfa->nodes_len > 0); +#endif + incomplete = 0; + /* For each nodes, calculate epsilon closure. */ + for (node_idx = 0; ; ++node_idx) + { + reg_errcode_t err; + re_node_set eclosure_elem; + if (node_idx == dfa->nodes_len) + { + if (!incomplete) + break; + incomplete = 0; + node_idx = 0; + } + +#ifdef DEBUG + assert (dfa->eclosures[node_idx].nelem != -1); +#endif + + /* If we have already calculated, skip it. */ + if (dfa->eclosures[node_idx].nelem != 0) + continue; + /* Calculate epsilon closure of `node_idx'. */ + err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (dfa->eclosures[node_idx].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + return REG_NOERROR; +} + +/* Calculate epsilon closure of NODE. */ + +static reg_errcode_t +calc_eclosure_iter (new_set, dfa, node, root) + re_node_set *new_set; + re_dfa_t *dfa; + int node, root; +{ + reg_errcode_t err; + unsigned int constraint; + int i, incomplete; + re_node_set eclosure; + incomplete = 0; + err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* This indicates that we are calculating this node now. + We reference this value to avoid infinite loop. */ + dfa->eclosures[node].nelem = -1; + + constraint = ((dfa->nodes[node].type == ANCHOR) + ? dfa->nodes[node].opr.ctx_type : 0); + /* If the current node has constraints, duplicate all nodes. + Since they must inherit the constraints. */ + if (constraint + && dfa->edests[node].nelem + && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) + { + int org_node, cur_node; + org_node = cur_node = node; + err = duplicate_node_closure (dfa, node, node, node, constraint); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Expand each epsilon destination nodes. */ + if (IS_EPSILON_NODE(dfa->nodes[node].type)) + for (i = 0; i < dfa->edests[node].nelem; ++i) + { + re_node_set eclosure_elem; + int edest = dfa->edests[node].elems[i]; + /* If calculating the epsilon closure of `edest' is in progress, + return intermediate result. */ + if (dfa->eclosures[edest].nelem == -1) + { + incomplete = 1; + continue; + } + /* If we haven't calculated the epsilon closure of `edest' yet, + calculate now. Otherwise use calculated epsilon closure. */ + if (dfa->eclosures[edest].nelem == 0) + { + err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + } + else + eclosure_elem = dfa->eclosures[edest]; + /* Merge the epsilon closure of `edest'. */ + re_node_set_merge (&eclosure, &eclosure_elem); + /* If the epsilon closure of `edest' is incomplete, + the epsilon closure of this node is also incomplete. */ + if (dfa->eclosures[edest].nelem == 0) + { + incomplete = 1; + re_node_set_free (&eclosure_elem); + } + } + + /* Epsilon closures include itself. */ + re_node_set_insert (&eclosure, node); + if (incomplete && !root) + dfa->eclosures[node].nelem = 0; + else + dfa->eclosures[node] = eclosure; + *new_set = eclosure; + return REG_NOERROR; +} + +/* Functions for token which are used in the parser. */ + +/* Fetch a token from INPUT. + We must not use this function inside bracket expressions. */ + +static void +fetch_token (result, input, syntax) + re_token_t *result; + re_string_t *input; + reg_syntax_t syntax; +{ + re_string_skip_bytes (input, peek_token (result, input, syntax)); +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function inside bracket expressions. */ + +static int +peek_token (token, input, syntax) + re_token_t *token; + re_string_t *input; + reg_syntax_t syntax; +{ + unsigned char c; + + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + + c = re_string_peek_byte (input, 0); + token->opr.c = c; + + token->word_char = 0; +#ifdef RE_ENABLE_I18N + token->mb_partial = 0; + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + token->mb_partial = 1; + return 1; + } +#endif + if (c == '\\') + { + unsigned char c2; + if (re_string_cur_idx (input) + 1 >= re_string_length (input)) + { + token->type = BACK_SLASH; + return 1; + } + + c2 = re_string_peek_byte_case (input, 1); + token->opr.c = c2; + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, + re_string_cur_idx (input) + 1); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (c2) != 0; + + switch (c2) + { + case '|': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + if (!(syntax & RE_NO_BK_REFS)) + { + token->type = OP_BACK_REF; + token->opr.idx = c2 - '1'; + } + break; + case '<': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_FIRST; + } + break; + case '>': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_LAST; + } + break; + case 'b': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = WORD_DELIM; + } + break; + case 'B': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = NOT_WORD_DELIM; + } + break; + case 'w': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_WORD; + break; + case 'W': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTWORD; + break; + case 's': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_SPACE; + break; + case 'S': + if (!(syntax & RE_NO_GNU_OPS)) + token->type = OP_NOTSPACE; + break; + case '`': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_FIRST; + } + break; + case '\'': + if (!(syntax & RE_NO_GNU_OPS)) + { + token->type = ANCHOR; + token->opr.ctx_type = BUF_LAST; + } + break; + case '(': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (!(syntax & RE_NO_BK_PARENS)) + token->type = OP_CLOSE_SUBEXP; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) + token->type = OP_CLOSE_DUP_NUM; + break; + default: + break; + } + return 2; + } + + token->type = CHARACTER; +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); + token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; + } + else +#endif + token->word_char = IS_WORD_CHAR (token->opr.c); + + switch (c) + { + case '\n': + if (syntax & RE_NEWLINE_ALT) + token->type = OP_ALT; + break; + case '|': + if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) + token->type = OP_ALT; + break; + case '*': + token->type = OP_DUP_ASTERISK; + break; + case '+': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_PLUS; + break; + case '?': + if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) + token->type = OP_DUP_QUESTION; + break; + case '{': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_OPEN_DUP_NUM; + break; + case '}': + if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) + token->type = OP_CLOSE_DUP_NUM; + break; + case '(': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_OPEN_SUBEXP; + break; + case ')': + if (syntax & RE_NO_BK_PARENS) + token->type = OP_CLOSE_SUBEXP; + break; + case '[': + token->type = OP_OPEN_BRACKET; + break; + case '.': + token->type = OP_PERIOD; + break; + case '^': + if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && + re_string_cur_idx (input) != 0) + { + char prev = re_string_peek_byte (input, -1); + if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_FIRST; + break; + case '$': + if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && + re_string_cur_idx (input) + 1 != re_string_length (input)) + { + re_token_t next; + re_string_skip_bytes (input, 1); + peek_token (&next, input, syntax); + re_string_skip_bytes (input, -1); + if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) + break; + } + token->type = ANCHOR; + token->opr.ctx_type = LINE_LAST; + break; + default: + break; + } + return 1; +} + +/* Peek a token from INPUT, and return the length of the token. + We must not use this function out of bracket expressions. */ + +static int +peek_token_bracket (token, input, syntax) + re_token_t *token; + re_string_t *input; + reg_syntax_t syntax; +{ + unsigned char c; + if (re_string_eoi (input)) + { + token->type = END_OF_RE; + return 0; + } + c = re_string_peek_byte (input, 0); + token->opr.c = c; + +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1 && + !re_string_first_byte (input, re_string_cur_idx (input))) + { + token->type = CHARACTER; + return 1; + } +#endif /* RE_ENABLE_I18N */ + + if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) + && re_string_cur_idx (input) + 1 < re_string_length (input)) + { + /* In this case, '\' escape a character. */ + unsigned char c2; + re_string_skip_bytes (input, 1); + c2 = re_string_peek_byte (input, 0); + token->opr.c = c2; + token->type = CHARACTER; + return 1; + } + if (c == '[') /* '[' is a special char in a bracket exps. */ + { + unsigned char c2; + int token_len; + if (re_string_cur_idx (input) + 1 < re_string_length (input)) + c2 = re_string_peek_byte (input, 1); + else + c2 = 0; + token->opr.c = c2; + token_len = 2; + switch (c2) + { + case '.': + token->type = OP_OPEN_COLL_ELEM; + break; + case '=': + token->type = OP_OPEN_EQUIV_CLASS; + break; + case ':': + if (syntax & RE_CHAR_CLASSES) + { + token->type = OP_OPEN_CHAR_CLASS; + break; + } + /* else fall through. */ + default: + token->type = CHARACTER; + token->opr.c = c; + token_len = 1; + break; + } + return token_len; + } + switch (c) + { + case '-': + token->type = OP_CHARSET_RANGE; + break; + case ']': + token->type = OP_CLOSE_BRACKET; + break; + case '^': + token->type = OP_NON_MATCH_LIST; + break; + default: + token->type = CHARACTER; + } + return 1; +} + +/* Functions for parser. */ + +/* Entry point of the parser. + Parse the regular expression REGEXP and return the structure tree. + If an error is occured, ERR is set by error code, and return NULL. + This function build the following tree, from regular expression <reg_exp>: + CAT + / \ + / \ + <reg_exp> EOR + + CAT means concatenation. + EOR means end of regular expression. */ + +static bin_tree_t * +parse (regexp, preg, syntax, err) + re_string_t *regexp; + regex_t *preg; + reg_syntax_t syntax; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *eor, *root; + re_token_t current_token; + dfa->syntax = syntax; + fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); + tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + eor = create_tree (dfa, NULL, NULL, END_OF_RE); + if (tree != NULL) + root = create_tree (dfa, tree, eor, CONCAT); + else + root = eor; + if (BE (eor == NULL || root == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + return root; +} + +/* This function build the following tree, from regular expression + <branch1>|<branch2>: + ALT + / \ + / \ + <branch1> <branch2> + + ALT means alternative, which represents the operator `|'. */ + +static bin_tree_t * +parse_reg_exp (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree, *branch = NULL; + tree = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type == OP_ALT) + { + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + if (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + branch = parse_branch (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && branch == NULL, 0)) + return NULL; + } + else + branch = NULL; + tree = create_tree (dfa, tree, branch, OP_ALT); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + return tree; +} + +/* This function build the following tree, from regular expression + <exp1><exp2>: + CAT + / \ + / \ + <exp1> <exp2> + + CAT means concatenation. */ + +static bin_tree_t * +parse_branch (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + bin_tree_t *tree, *exp; + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + tree = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + + while (token->type != OP_ALT && token->type != END_OF_RE + && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) + { + exp = parse_expression (regexp, preg, token, syntax, nest, err); + if (BE (*err != REG_NOERROR && exp == NULL, 0)) + { + return NULL; + } + if (tree != NULL && exp != NULL) + { + tree = create_tree (dfa, tree, exp, CONCAT); + if (tree == NULL) + { + *err = REG_ESPACE; + return NULL; + } + } + else if (tree == NULL) + tree = exp; + /* Otherwise exp == NULL, we don't need to create new tree. */ + } + return tree; +} + +/* This function build the following tree, from regular expression a*: + * + | + a +*/ + +static bin_tree_t * +parse_expression (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + switch (token->type) + { + case CHARACTER: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (!re_string_eoi (regexp) + && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) + { + bin_tree_t *mbc_remain; + fetch_token (token, regexp, syntax); + mbc_remain = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree, mbc_remain, CONCAT); + if (BE (mbc_remain == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + } +#endif + break; + case OP_OPEN_SUBEXP: + tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_OPEN_BRACKET: + tree = parse_bracket_exp (regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_BACK_REF: + if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) + { + *err = REG_ESUBREG; + return NULL; + } + dfa->used_bkref_map |= 1 << token->opr.idx; + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + ++dfa->nbackref; + dfa->has_mb_node = 1; + break; + case OP_OPEN_DUP_NUM: + if (syntax & RE_CONTEXT_INVALID_DUP) + { + *err = REG_BADRPT; + return NULL; + } + /* FALLTHROUGH */ + case OP_DUP_ASTERISK: + case OP_DUP_PLUS: + case OP_DUP_QUESTION: + if (syntax & RE_CONTEXT_INVALID_OPS) + { + *err = REG_BADRPT; + return NULL; + } + else if (syntax & RE_CONTEXT_INDEP_OPS) + { + fetch_token (token, regexp, syntax); + return parse_expression (regexp, preg, token, syntax, nest, err); + } + /* else fall through */ + case OP_CLOSE_SUBEXP: + if ((token->type == OP_CLOSE_SUBEXP) && + !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) + { + *err = REG_ERPAREN; + return NULL; + } + /* else fall through */ + case OP_CLOSE_DUP_NUM: + /* We treat it as a normal character. */ + + /* Then we can these characters as normal characters. */ + token->type = CHARACTER; + /* mb_partial and word_char bits should be initialized already + by peek_token. */ + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + break; + case ANCHOR: + if ((token->opr.ctx_type + & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) + && dfa->word_ops_used == 0) + init_word_char (dfa); + if (token->opr.ctx_type == WORD_DELIM + || token->opr.ctx_type == NOT_WORD_DELIM) + { + bin_tree_t *tree_first, *tree_last; + if (token->opr.ctx_type == WORD_DELIM) + { + token->opr.ctx_type = WORD_FIRST; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = WORD_LAST; + } + else + { + token->opr.ctx_type = INSIDE_WORD; + tree_first = create_token_tree (dfa, NULL, NULL, token); + token->opr.ctx_type = INSIDE_NOTWORD; + } + tree_last = create_token_tree (dfa, NULL, NULL, token); + tree = create_tree (dfa, tree_first, tree_last, OP_ALT); + if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + else + { + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + } + /* We must return here, since ANCHORs can't be followed + by repetition operators. + eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>", + it must not be "<ANCHOR(^)><REPEAT(*)>". */ + fetch_token (token, regexp, syntax); + return tree; + case OP_PERIOD: + tree = create_token_tree (dfa, NULL, NULL, token); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + if (dfa->mb_cur_max > 1) + dfa->has_mb_node = 1; + break; + case OP_WORD: + case OP_NOTWORD: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "alnum", + (const unsigned char *) "_", + token->type == OP_NOTWORD, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_SPACE: + case OP_NOTSPACE: + tree = build_charclass_op (dfa, regexp->trans, + (const unsigned char *) "space", + (const unsigned char *) "", + token->type == OP_NOTSPACE, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + break; + case OP_ALT: + case END_OF_RE: + return NULL; + case BACK_SLASH: + *err = REG_EESCAPE; + return NULL; + default: + /* Must not happen? */ +#ifdef DEBUG + assert (0); +#endif + return NULL; + } + fetch_token (token, regexp, syntax); + + while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS + || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) + { + tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); + if (BE (*err != REG_NOERROR && tree == NULL, 0)) + return NULL; + /* In BRE consecutive duplications are not allowed. */ + if ((syntax & RE_CONTEXT_INVALID_DUP) + && (token->type == OP_DUP_ASTERISK + || token->type == OP_OPEN_DUP_NUM)) + { + *err = REG_BADRPT; + return NULL; + } + } + + return tree; +} + +/* This function build the following tree, from regular expression + (<reg_exp>): + SUBEXP + | + <reg_exp> +*/ + +static bin_tree_t * +parse_sub_exp (regexp, preg, token, syntax, nest, err) + re_string_t *regexp; + regex_t *preg; + re_token_t *token; + reg_syntax_t syntax; + int nest; + reg_errcode_t *err; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + bin_tree_t *tree; + size_t cur_nsub; + cur_nsub = preg->re_nsub++; + + fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); + + /* The subexpression may be a null string. */ + if (token->type == OP_CLOSE_SUBEXP) + tree = NULL; + else + { + tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); + if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) + *err = REG_EPAREN; + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + dfa->completed_bkref_map |= 1 << cur_nsub; + + tree = create_tree (dfa, tree, NULL, SUBEXP); + if (BE (tree == NULL, 0)) + { + *err = REG_ESPACE; + return NULL; + } + tree->token.opr.idx = cur_nsub; + return tree; +} + +/* This function parse repetition operators like "*", "+", "{1,3}" etc. */ + +static bin_tree_t * +parse_dup_op (elem, regexp, dfa, token, syntax, err) + bin_tree_t *elem; + re_string_t *regexp; + re_dfa_t *dfa; + re_token_t *token; + reg_syntax_t syntax; + reg_errcode_t *err; +{ + bin_tree_t *tree = NULL, *old_tree = NULL; + int i, start, end, start_idx = re_string_cur_idx (regexp); + re_token_t start_token = *token; + + if (token->type == OP_OPEN_DUP_NUM) + { + end = 0; + start = fetch_number (regexp, token, syntax); + if (start == -1) + { + if (token->type == CHARACTER && token->opr.c == ',') + start = 0; /* We treat "{,m}" as "{0,m}". */ + else + { + *err = REG_BADBR; /* <re>{} is invalid. */ + return NULL; + } + } + if (BE (start != -2, 1)) + { + /* We treat "{n}" as "{n,n}". */ + end = ((token->type == OP_CLOSE_DUP_NUM) ? start + : ((token->type == CHARACTER && token->opr.c == ',') + ? fetch_number (regexp, token, syntax) : -2)); + } + if (BE (start == -2 || end == -2, 0)) + { + /* Invalid sequence. */ + if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) + { + if (token->type == END_OF_RE) + *err = REG_EBRACE; + else + *err = REG_BADBR; + + return NULL; + } + + /* If the syntax bit is set, rollback. */ + re_string_set_index (regexp, start_idx); + *token = start_token; + token->type = CHARACTER; + /* mb_partial and word_char bits should be already initialized by + peek_token. */ + return elem; + } + + if (BE (end != -1 && start > end, 0)) + { + /* First number greater than second. */ + *err = REG_BADBR; + return NULL; + } + } + else + { + start = (token->type == OP_DUP_PLUS) ? 1 : 0; + end = (token->type == OP_DUP_QUESTION) ? 1 : -1; + } + + fetch_token (token, regexp, syntax); + + if (BE (elem == NULL, 0)) + return NULL; + if (BE (start == 0 && end == 0, 0)) + { + postorder (elem, free_tree, NULL); + return NULL; + } + + /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */ + if (BE (start > 0, 0)) + { + tree = elem; + for (i = 2; i <= start; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (start == end) + return tree; + + /* Duplicate ELEM before it is marked optional. */ + elem = duplicate_tree (elem, dfa); + old_tree = tree; + } + else + old_tree = NULL; + + if (elem->token.type == SUBEXP) + postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx); + + tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT)); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + + /* This loop is actually executed only when end != -1, + to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have + already created the start+1-th copy. */ + for (i = start + 2; i <= end; ++i) + { + elem = duplicate_tree (elem, dfa); + tree = create_tree (dfa, tree, elem, CONCAT); + if (BE (elem == NULL || tree == NULL, 0)) + goto parse_dup_op_espace; + + tree = create_tree (dfa, tree, NULL, OP_ALT); + if (BE (tree == NULL, 0)) + goto parse_dup_op_espace; + } + + if (old_tree) + tree = create_tree (dfa, old_tree, tree, CONCAT); + + return tree; + + parse_dup_op_espace: + *err = REG_ESPACE; + return NULL; +} + +/* Size of the names for collating symbol/equivalence_class/character_class. + I'm not sure, but maybe enough. */ +#define BRACKET_NAME_BUF_SIZE 32 + +#ifndef _LIBC + /* Local function for parse_bracket_exp only used in case of NOT _LIBC. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + +static reg_errcode_t +# ifdef RE_ENABLE_I18N +build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + int *range_alloc; +# else /* not RE_ENABLE_I18N */ +build_range_exp (sbcset, start_elem, end_elem) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + bracket_elem_t *start_elem, *end_elem; +{ + unsigned int start_ch, end_ch; + /* Equivalence Classes and Character Classes can't be a range start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + /* We can handle no multi character collating elements without libc + support. */ + if (BE ((start_elem->type == COLL_SYM + && strlen ((char *) start_elem->opr.name) > 1) + || (end_elem->type == COLL_SYM + && strlen ((char *) end_elem->opr.name) > 1), 0)) + return REG_ECOLLATE; + +# ifdef RE_ENABLE_I18N + { + wchar_t wc, start_wc, end_wc; + wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + + start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) + ? __btowc (start_ch) : start_elem->opr.wch); + end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) + ? __btowc (end_ch) : end_elem->opr.wch); + if (start_wc == WEOF || end_wc == WEOF) + return REG_ECOLLATE; + cmp_buf[0] = start_wc; + cmp_buf[4] = end_wc; + if (wcscoll (cmp_buf, cmp_buf + 4) > 0) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, for !_LIBC we have no collation elements: if the + character set is single byte, the single byte character set + that we build below suffices. parse_bracket_exp passes + no MBCSET if dfa->mb_cur_max == 1. */ + if (mbcset) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + wchar_t *new_array_start, *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + /* Use realloc since mbcset->range_starts and mbcset->range_ends + are NULL if *range_alloc == 0. */ + new_array_start = re_realloc (mbcset->range_starts, wchar_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, wchar_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_wc; + mbcset->range_ends[mbcset->nranges++] = end_wc; + } + + /* Build the table for single byte characters. */ + for (wc = 0; wc < SBC_MAX; ++wc) + { + cmp_buf[2] = wc; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + bitset_set (sbcset, wc); + } + } +# else /* not RE_ENABLE_I18N */ + { + unsigned int ch; + start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch + : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] + : 0)); + end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch + : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] + : 0)); + if (start_ch > end_ch) + return REG_ERANGE; + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ++ch) + if (start_ch <= ch && ch <= end_ch) + bitset_set (sbcset, ch); + } +# endif /* not RE_ENABLE_I18N */ + return REG_NOERROR; +} +#endif /* not _LIBC */ + +#ifndef _LIBC +/* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument since we may update it. */ + +static reg_errcode_t +# ifdef RE_ENABLE_I18N +build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + int *coll_sym_alloc; +# else /* not RE_ENABLE_I18N */ +build_collating_symbol (sbcset, name) +# endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *name; +{ + size_t name_len = strlen ((const char *) name); + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } +} +#endif /* not _LIBC */ + +/* This function parse bracket expression like "[abc]", "[a-c]", + "[[.a-a.]]" etc. */ + +static bin_tree_t * +parse_bracket_exp (regexp, dfa, token, syntax, err) + re_string_t *regexp; + re_dfa_t *dfa; + re_token_t *token; + reg_syntax_t syntax; + reg_errcode_t *err; +{ +#ifdef _LIBC + const unsigned char *collseqmb; + const char *collseqwc; + uint32_t nrules; + int32_t table_size; + const int32_t *symb_table; + const unsigned char *extra; + + /* Local function for parse_bracket_exp used in _LIBC environement. + Seek the collating symbol entry correspondings to NAME. + Return the index of the symbol in the SYMB_TABLE. */ + + auto inline int32_t + __attribute ((always_inline)) + seek_collating_symbol_entry (name, name_len) + const unsigned char *name; + size_t name_len; + { + int32_t hash = elem_hash ((const char *) name, name_len); + int32_t elem = hash % table_size; + int32_t second = hash % (table_size - 2); + while (symb_table[2 * elem] != 0) + { + /* First compare the hashing value. */ + if (symb_table[2 * elem] == hash + /* Compare the length of the name. */ + && name_len == extra[symb_table[2 * elem + 1]] + /* Compare the name. */ + && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], + name_len) == 0) + { + /* Yep, this is the entry. */ + break; + } + + /* Next entry. */ + elem += second; + } + return elem; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Look up the collation sequence value of BR_ELEM. + Return the value if succeeded, UINT_MAX otherwise. */ + + auto inline unsigned int + __attribute ((always_inline)) + lookup_collation_sequence_value (br_elem) + bracket_elem_t *br_elem; + { + if (br_elem->type == SB_CHAR) + { + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + return collseqmb[br_elem->opr.ch]; + else + { + wint_t wc = __btowc (br_elem->opr.ch); + return __collseq_table_lookup (collseqwc, wc); + } + } + else if (br_elem->type == MB_CHAR) + { + return __collseq_table_lookup (collseqwc, br_elem->opr.wch); + } + else if (br_elem->type == COLL_SYM) + { + size_t sym_name_len = strlen ((char *) br_elem->opr.name); + if (nrules != 0) + { + int32_t elem, idx; + elem = seek_collating_symbol_entry (br_elem->opr.name, + sym_name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + /* Skip the byte sequence of the collating element. */ + idx += 1 + extra[idx]; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the multibyte collation sequence value. */ + idx += sizeof (unsigned int); + /* Skip the wide char sequence of the collating element. */ + idx += sizeof (unsigned int) * + (1 + *(unsigned int *) (extra + idx)); + /* Return the collation sequence value. */ + return *(unsigned int *) (extra + idx); + } + else if (symb_table[2 * elem] == 0 && sym_name_len == 1) + { + /* No valid character. Match it as a single byte + character. */ + return collseqmb[br_elem->opr.name[0]]; + } + } + else if (sym_name_len == 1) + return collseqmb[br_elem->opr.name[0]]; + } + return UINT_MAX; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the range expression which starts from START_ELEM, and ends + at END_ELEM. The result are written to MBCSET and SBCSET. + RANGE_ALLOC is the allocated size of mbcset->range_starts, and + mbcset->range_ends, is a pointer argument sinse we may + update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) + re_charset_t *mbcset; + int *range_alloc; + re_bitset_ptr_t sbcset; + bracket_elem_t *start_elem, *end_elem; + { + unsigned int ch; + uint32_t start_collseq; + uint32_t end_collseq; + + /* Equivalence Classes and Character Classes can't be a range + start/end. */ + if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS + || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, + 0)) + return REG_ERANGE; + + start_collseq = lookup_collation_sequence_value (start_elem); + end_collseq = lookup_collation_sequence_value (end_elem); + /* Check start/end collation sequence values. */ + if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) + return REG_ECOLLATE; + if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) + return REG_ERANGE; + + /* Got valid collation sequence values, add them as a new entry. + However, if we have no collation elements, and the character set + is single byte, the single byte character set that we + build below suffices. */ + if (nrules > 0 || dfa->mb_cur_max > 1) + { + /* Check the space of the arrays. */ + if (BE (*range_alloc == mbcset->nranges, 0)) + { + /* There is not enough space, need realloc. */ + uint32_t *new_array_start; + uint32_t *new_array_end; + int new_nranges; + + /* +1 in case of mbcset->nranges is 0. */ + new_nranges = 2 * mbcset->nranges + 1; + new_array_start = re_realloc (mbcset->range_starts, uint32_t, + new_nranges); + new_array_end = re_realloc (mbcset->range_ends, uint32_t, + new_nranges); + + if (BE (new_array_start == NULL || new_array_end == NULL, 0)) + return REG_ESPACE; + + mbcset->range_starts = new_array_start; + mbcset->range_ends = new_array_end; + *range_alloc = new_nranges; + } + + mbcset->range_starts[mbcset->nranges] = start_collseq; + mbcset->range_ends[mbcset->nranges++] = end_collseq; + } + + /* Build the table for single byte characters. */ + for (ch = 0; ch < SBC_MAX; ch++) + { + uint32_t ch_collseq; + /* + if (MB_CUR_MAX == 1) + */ + if (nrules == 0) + ch_collseq = collseqmb[ch]; + else + ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); + if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) + bitset_set (sbcset, ch); + } + return REG_NOERROR; + } + + /* Local function for parse_bracket_exp used in _LIBC environement. + Build the collating element which is represented by NAME. + The result are written to MBCSET and SBCSET. + COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a + pointer argument sinse we may update it. */ + + auto inline reg_errcode_t + __attribute ((always_inline)) + build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) + re_charset_t *mbcset; + int *coll_sym_alloc; + re_bitset_ptr_t sbcset; + const unsigned char *name; + { + int32_t elem, idx; + size_t name_len = strlen ((const char *) name); + if (nrules != 0) + { + elem = seek_collating_symbol_entry (name, name_len); + if (symb_table[2 * elem] != 0) + { + /* We found the entry. */ + idx = symb_table[2 * elem + 1]; + /* Skip the name of collating element name. */ + idx += 1 + extra[idx]; + } + else if (symb_table[2 * elem] == 0 && name_len == 1) + { + /* No valid character, treat it as a normal + character. */ + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + else + return REG_ECOLLATE; + + /* Got valid collation sequence, add it as a new entry. */ + /* Check the space of the arrays. */ + if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->ncoll_syms is 0. */ + int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; + /* Use realloc since mbcset->coll_syms is NULL + if *alloc == 0. */ + int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, + new_coll_sym_alloc); + if (BE (new_coll_syms == NULL, 0)) + return REG_ESPACE; + mbcset->coll_syms = new_coll_syms; + *coll_sym_alloc = new_coll_sym_alloc; + } + mbcset->coll_syms[mbcset->ncoll_syms++] = idx; + return REG_NOERROR; + } + else + { + if (BE (name_len != 1, 0)) + return REG_ECOLLATE; + else + { + bitset_set (sbcset, name[0]); + return REG_NOERROR; + } + } + } +#endif + + re_token_t br_token; + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; + int equiv_class_alloc = 0, char_class_alloc = 0; +#endif /* not RE_ENABLE_I18N */ + int non_match = 0; + bin_tree_t *work_tree; + int token_len; + int first_round = 1; +#ifdef _LIBC + collseqmb = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules) + { + /* + if (MB_CUR_MAX > 1) + */ + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); + symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_TABLEMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_SYMB_EXTRAMB); + } +#endif + sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else + if (BE (sbcset == NULL, 0)) +#endif /* RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_NON_MATCH_LIST) + { +#ifdef RE_ENABLE_I18N + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + non_match = 1; + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set (sbcset, '\0'); + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + token_len = peek_token_bracket (token, regexp, syntax); + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_BADPAT; + goto parse_bracket_exp_free_return; + } + } + + /* We treat the first ']' as a normal character. */ + if (token->type == OP_CLOSE_BRACKET) + token->type = CHARACTER; + + while (1) + { + bracket_elem_t start_elem, end_elem; + unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; + unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; + reg_errcode_t ret; + int token_len2 = 0, is_range_exp = 0; + re_token_t token2; + + start_elem.opr.name = start_name_buf; + ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, + syntax, first_round); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + first_round = 0; + + /* Get information about the next token. We need it in any case. */ + token_len = peek_token_bracket (token, regexp, syntax); + + /* Do not check for ranges if we know they are not allowed. */ + if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) + { + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CHARSET_RANGE) + { + re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ + token_len2 = peek_token_bracket (&token2, regexp, syntax); + if (BE (token2.type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token2.type == OP_CLOSE_BRACKET) + { + /* We treat the last '-' as a normal character. */ + re_string_skip_bytes (regexp, -token_len); + token->type = CHARACTER; + } + else + is_range_exp = 1; + } + } + + if (is_range_exp == 1) + { + end_elem.opr.name = end_name_buf; + ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, + dfa, syntax, 1); + if (BE (ret != REG_NOERROR, 0)) + { + *err = ret; + goto parse_bracket_exp_free_return; + } + + token_len = peek_token_bracket (token, regexp, syntax); + +#ifdef _LIBC + *err = build_range_exp (sbcset, mbcset, &range_alloc, + &start_elem, &end_elem); +#else +# ifdef RE_ENABLE_I18N + *err = build_range_exp (sbcset, + dfa->mb_cur_max > 1 ? mbcset : NULL, + &range_alloc, &start_elem, &end_elem); +# else + *err = build_range_exp (sbcset, &start_elem, &end_elem); +# endif +#endif /* RE_ENABLE_I18N */ + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + } + else + { + switch (start_elem.type) + { + case SB_CHAR: + bitset_set (sbcset, start_elem.opr.ch); + break; +#ifdef RE_ENABLE_I18N + case MB_CHAR: + /* Check whether the array has enough space. */ + if (BE (mbchar_alloc == mbcset->nmbchars, 0)) + { + wchar_t *new_mbchars; + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nmbchars is 0. */ + mbchar_alloc = 2 * mbcset->nmbchars + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + new_mbchars = re_realloc (mbcset->mbchars, wchar_t, + mbchar_alloc); + if (BE (new_mbchars == NULL, 0)) + goto parse_bracket_exp_espace; + mbcset->mbchars = new_mbchars; + } + mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; + break; +#endif /* RE_ENABLE_I18N */ + case EQUIV_CLASS: + *err = build_equiv_class (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &equiv_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case COLL_SYM: + *err = build_collating_symbol (sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &coll_sym_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + case CHAR_CLASS: + *err = build_charclass (regexp->trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &char_class_alloc, +#endif /* RE_ENABLE_I18N */ + start_elem.opr.name, syntax); + if (BE (*err != REG_NOERROR, 0)) + goto parse_bracket_exp_free_return; + break; + default: + assert (0); + break; + } + } + if (BE (token->type == END_OF_RE, 0)) + { + *err = REG_EBRACK; + goto parse_bracket_exp_free_return; + } + if (token->type == OP_CLOSE_BRACKET) + break; + } + + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); + + if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes + || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes + || mbcset->non_match))) + { + bin_tree_t *mbc_tree; + int sbc_idx; + /* Build a tree for complex bracket. */ + dfa->has_mb_node = 1; + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto parse_bracket_exp_espace; + for (sbc_idx = 0; sbc_idx < BITSET_UINTS; ++sbc_idx) + if (sbcset[sbc_idx]) + break; + /* If there are no bits set in sbcset, there is no point + of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ + if (sbc_idx < BITSET_UINTS) + { + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + + /* Then join them by ALT node. */ + work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + else + { + re_free (sbcset); + work_tree = mbc_tree; + } + } + else +#endif /* not RE_ENABLE_I18N */ + { +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + work_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (work_tree == NULL, 0)) + goto parse_bracket_exp_espace; + } + return work_tree; + + parse_bracket_exp_espace: + *err = REG_ESPACE; + parse_bracket_exp_free_return: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + return NULL; +} + +/* Parse an element in the bracket expression. */ + +static reg_errcode_t +parse_bracket_element (elem, regexp, token, token_len, dfa, syntax, + accept_hyphen) + bracket_elem_t *elem; + re_string_t *regexp; + re_token_t *token; + int token_len; + re_dfa_t *dfa; + reg_syntax_t syntax; + int accept_hyphen; +{ +#ifdef RE_ENABLE_I18N + int cur_char_size; + cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); + if (cur_char_size > 1) + { + elem->type = MB_CHAR; + elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); + re_string_skip_bytes (regexp, cur_char_size); + return REG_NOERROR; + } +#endif /* RE_ENABLE_I18N */ + re_string_skip_bytes (regexp, token_len); /* Skip a token. */ + if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS + || token->type == OP_OPEN_EQUIV_CLASS) + return parse_bracket_symbol (elem, regexp, token); + if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) + { + /* A '-' must only appear as anything but a range indicator before + the closing bracket. Everything else is an error. */ + re_token_t token2; + (void) peek_token_bracket (&token2, regexp, syntax); + if (token2.type != OP_CLOSE_BRACKET) + /* The actual error value is not standardized since this whole + case is undefined. But ERANGE makes good sense. */ + return REG_ERANGE; + } + elem->type = SB_CHAR; + elem->opr.ch = token->opr.c; + return REG_NOERROR; +} + +/* Parse a bracket symbol in the bracket expression. Bracket symbols are + such as [:<character_class>:], [.<collating_element>.], and + [=<equivalent_class>=]. */ + +static reg_errcode_t +parse_bracket_symbol (elem, regexp, token) + bracket_elem_t *elem; + re_string_t *regexp; + re_token_t *token; +{ + unsigned char ch, delim = token->opr.c; + int i = 0; + if (re_string_eoi(regexp)) + return REG_EBRACK; + for (;; ++i) + { + if (i >= BRACKET_NAME_BUF_SIZE) + return REG_EBRACK; + if (token->type == OP_OPEN_CHAR_CLASS) + ch = re_string_fetch_byte_case (regexp); + else + ch = re_string_fetch_byte (regexp); + if (re_string_eoi(regexp)) + return REG_EBRACK; + if (ch == delim && re_string_peek_byte (regexp, 0) == ']') + break; + elem->opr.name[i] = ch; + } + re_string_skip_bytes (regexp, 1); + elem->opr.name[i] = '\0'; + switch (token->type) + { + case OP_OPEN_COLL_ELEM: + elem->type = COLL_SYM; + break; + case OP_OPEN_EQUIV_CLASS: + elem->type = EQUIV_CLASS; + break; + case OP_OPEN_CHAR_CLASS: + elem->type = CHAR_CLASS; + break; + default: + break; + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the equivalence class which is represented by NAME. + The result are written to MBCSET and SBCSET. + EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_equiv_class (sbcset, mbcset, equiv_class_alloc, name) + re_charset_t *mbcset; + int *equiv_class_alloc; +#else /* not RE_ENABLE_I18N */ +build_equiv_class (sbcset, name) +#endif /* not RE_ENABLE_I18N */ + re_bitset_ptr_t sbcset; + const unsigned char *name; +{ +#if defined _LIBC + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + const int32_t *table, *indirect; + const unsigned char *weights, *extra, *cp; + unsigned char char_buf[2]; + int32_t idx1, idx2; + unsigned int ch; + size_t len; + /* This #include defines a local function! */ +# include <locale/weight.h> + /* Calculate the index for equivalence class. */ + cp = name; + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + idx1 = findidx (&cp); + if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) + /* This isn't a valid character. */ + return REG_ECOLLATE; + + /* Build single byte matcing table for this equivalence class. */ + char_buf[1] = (unsigned char) '\0'; + len = weights[idx1]; + for (ch = 0; ch < SBC_MAX; ++ch) + { + char_buf[0] = ch; + cp = char_buf; + idx2 = findidx (&cp); +/* + idx2 = table[ch]; +*/ + if (idx2 == 0) + /* This isn't a valid character. */ + continue; + if (len == weights[idx2]) + { + int cnt = 0; + while (cnt <= len && + weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt]) + ++cnt; + + if (cnt > len) + bitset_set (sbcset, ch); + } + } + /* Check whether the array has enough space. */ + if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nequiv_classes is 0. */ + int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; + /* Use realloc since the array is NULL if *alloc == 0. */ + int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, + int32_t, + new_equiv_class_alloc); + if (BE (new_equiv_classes == NULL, 0)) + return REG_ESPACE; + mbcset->equiv_classes = new_equiv_classes; + *equiv_class_alloc = new_equiv_class_alloc; + } + mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; + } + else +#endif /* _LIBC */ + { + if (BE (strlen ((const char *) name) != 1, 0)) + return REG_ECOLLATE; + bitset_set (sbcset, *name); + } + return REG_NOERROR; +} + + /* Helper function for parse_bracket_exp. + Build the character class which is represented by NAME. + The result are written to MBCSET and SBCSET. + CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, + is a pointer argument sinse we may update it. */ + +static reg_errcode_t +#ifdef RE_ENABLE_I18N +build_charclass (trans, sbcset, mbcset, char_class_alloc, class_name, syntax) + re_charset_t *mbcset; + int *char_class_alloc; +#else /* not RE_ENABLE_I18N */ +build_charclass (trans, sbcset, class_name, syntax) +#endif /* not RE_ENABLE_I18N */ + unsigned RE_TRANSLATE_TYPE trans; + re_bitset_ptr_t sbcset; + const unsigned char *class_name; + reg_syntax_t syntax; +{ + int i; + const char *name = (const char *) class_name; + + /* In case of REG_ICASE "upper" and "lower" match the both of + upper and lower cases. */ + if ((syntax & RE_ICASE) + && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0)) + name = "alpha"; + +#ifdef RE_ENABLE_I18N + /* Check the space of the arrays. */ + if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) + { + /* Not enough, realloc it. */ + /* +1 in case of mbcset->nchar_classes is 0. */ + int new_char_class_alloc = 2 * mbcset->nchar_classes + 1; + /* Use realloc since array is NULL if *alloc == 0. */ + wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, + new_char_class_alloc); + if (BE (new_char_classes == NULL, 0)) + return REG_ESPACE; + mbcset->char_classes = new_char_classes; + *char_class_alloc = new_char_class_alloc; + } + mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name); +#endif /* RE_ENABLE_I18N */ + +#define BUILD_CHARCLASS_LOOP(ctype_func) \ + for (i = 0; i < SBC_MAX; ++i) \ + { \ + if (ctype_func (i)) \ + { \ + int ch = trans ? trans[i] : i; \ + bitset_set (sbcset, ch); \ + } \ + } + + if (strcmp (name, "alnum") == 0) + BUILD_CHARCLASS_LOOP (isalnum) + else if (strcmp (name, "cntrl") == 0) + BUILD_CHARCLASS_LOOP (iscntrl) + else if (strcmp (name, "lower") == 0) + BUILD_CHARCLASS_LOOP (islower) + else if (strcmp (name, "space") == 0) + BUILD_CHARCLASS_LOOP (isspace) + else if (strcmp (name, "alpha") == 0) + BUILD_CHARCLASS_LOOP (isalpha) + else if (strcmp (name, "digit") == 0) + BUILD_CHARCLASS_LOOP (isdigit) + else if (strcmp (name, "print") == 0) + BUILD_CHARCLASS_LOOP (isprint) + else if (strcmp (name, "upper") == 0) + BUILD_CHARCLASS_LOOP (isupper) + else if (strcmp (name, "blank") == 0) + BUILD_CHARCLASS_LOOP (isblank) + else if (strcmp (name, "graph") == 0) + BUILD_CHARCLASS_LOOP (isgraph) + else if (strcmp (name, "punct") == 0) + BUILD_CHARCLASS_LOOP (ispunct) + else if (strcmp (name, "xdigit") == 0) + BUILD_CHARCLASS_LOOP (isxdigit) + else + return REG_ECTYPE; + + return REG_NOERROR; +} + +static bin_tree_t * +build_charclass_op (dfa, trans, class_name, extra, non_match, err) + re_dfa_t *dfa; + unsigned RE_TRANSLATE_TYPE trans; + const unsigned char *class_name; + const unsigned char *extra; + int non_match; + reg_errcode_t *err; +{ + re_bitset_ptr_t sbcset; +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; + int alloc = 0; +#endif /* not RE_ENABLE_I18N */ + reg_errcode_t ret; + re_token_t br_token; + bin_tree_t *tree; + + sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS); +#ifdef RE_ENABLE_I18N + mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); +#endif /* RE_ENABLE_I18N */ + +#ifdef RE_ENABLE_I18N + if (BE (sbcset == NULL || mbcset == NULL, 0)) +#else /* not RE_ENABLE_I18N */ + if (BE (sbcset == NULL, 0)) +#endif /* not RE_ENABLE_I18N */ + { + *err = REG_ESPACE; + return NULL; + } + + if (non_match) + { +#ifdef RE_ENABLE_I18N + /* + if (syntax & RE_HAT_LISTS_NOT_NEWLINE) + bitset_set(cset->sbcset, '\0'); + */ + mbcset->non_match = 1; +#endif /* not RE_ENABLE_I18N */ + } + + /* We don't care the syntax in this case. */ + ret = build_charclass (trans, sbcset, +#ifdef RE_ENABLE_I18N + mbcset, &alloc, +#endif /* RE_ENABLE_I18N */ + class_name, 0); + + if (BE (ret != REG_NOERROR, 0)) + { + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = ret; + return NULL; + } + /* \w match '_' also. */ + for (; *extra; extra++) + bitset_set (sbcset, *extra); + + /* If it is non-matching list. */ + if (non_match) + bitset_not (sbcset); + +#ifdef RE_ENABLE_I18N + /* Ensure only single byte characters are set. */ + if (dfa->mb_cur_max > 1) + bitset_mask (sbcset, dfa->sb_char); +#endif + + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; + br_token.opr.sbcset = sbcset; + tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (tree == NULL, 0)) + goto build_word_op_espace; + +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + bin_tree_t *mbc_tree; + /* Build a tree for complex bracket. */ + br_token.type = COMPLEX_BRACKET; + br_token.opr.mbcset = mbcset; + dfa->has_mb_node = 1; + mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); + if (BE (mbc_tree == NULL, 0)) + goto build_word_op_espace; + /* Then join them by ALT node. */ + tree = create_tree (dfa, tree, mbc_tree, OP_ALT); + if (BE (mbc_tree != NULL, 1)) + return tree; + } + else + { + free_charset (mbcset); + return tree; + } +#else /* not RE_ENABLE_I18N */ + return tree; +#endif /* not RE_ENABLE_I18N */ + + build_word_op_espace: + re_free (sbcset); +#ifdef RE_ENABLE_I18N + free_charset (mbcset); +#endif /* RE_ENABLE_I18N */ + *err = REG_ESPACE; + return NULL; +} + +/* This is intended for the expressions like "a{1,3}". + Fetch a number from `input', and return the number. + Return -1, if the number field is empty like "{,1}". + Return -2, If an error is occured. */ + +static int +fetch_number (input, token, syntax) + re_string_t *input; + re_token_t *token; + reg_syntax_t syntax; +{ + int num = -1; + unsigned char c; + while (1) + { + fetch_token (token, input, syntax); + c = token->opr.c; + if (BE (token->type == END_OF_RE, 0)) + return -2; + if (token->type == OP_CLOSE_DUP_NUM || c == ',') + break; + num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) + ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); + num = (num > RE_DUP_MAX) ? -2 : num; + } + return num; +} + +#ifdef RE_ENABLE_I18N +static void +free_charset (re_charset_t *cset) +{ + re_free (cset->mbchars); +# ifdef _LIBC + re_free (cset->coll_syms); + re_free (cset->equiv_classes); + re_free (cset->range_starts); + re_free (cset->range_ends); +# endif + re_free (cset->char_classes); + re_free (cset); +} +#endif /* RE_ENABLE_I18N */ + +/* Functions for binary tree operation. */ + +/* Create a tree node. */ + +static bin_tree_t * +create_tree (dfa, left, right, type) + re_dfa_t *dfa; + bin_tree_t *left; + bin_tree_t *right; + re_token_type_t type; +{ + re_token_t t; + t.type = type; + return create_token_tree (dfa, left, right, &t); +} + +static bin_tree_t * +create_token_tree (dfa, left, right, token) + re_dfa_t *dfa; + bin_tree_t *left; + bin_tree_t *right; + const re_token_t *token; +{ + bin_tree_t *tree; + if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) + { + bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); + + if (storage == NULL) + return NULL; + storage->next = dfa->str_tree_storage; + dfa->str_tree_storage = storage; + dfa->str_tree_storage_idx = 0; + } + tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; + + tree->parent = NULL; + tree->left = left; + tree->right = right; + tree->token = *token; + tree->token.duplicated = 0; + tree->token.opt_subexp = 0; + tree->first = NULL; + tree->next = NULL; + tree->node_idx = -1; + + if (left != NULL) + left->parent = tree; + if (right != NULL) + right->parent = tree; + return tree; +} + +/* Mark the tree SRC as an optional subexpression. + To be called from preorder or postorder. */ + +static reg_errcode_t +mark_opt_subexp (extra, node) + void *extra; + bin_tree_t *node; +{ + int idx = (int) (long) extra; + if (node->token.type == SUBEXP && node->token.opr.idx == idx) + node->token.opt_subexp = 1; + + return REG_NOERROR; +} + +/* Free the allocated memory inside NODE. */ + +static void +free_token (re_token_t *node) +{ +#ifdef RE_ENABLE_I18N + if (node->type == COMPLEX_BRACKET && node->duplicated == 0) + free_charset (node->opr.mbcset); + else +#endif /* RE_ENABLE_I18N */ + if (node->type == SIMPLE_BRACKET && node->duplicated == 0) + re_free (node->opr.sbcset); +} + +/* Worker function for tree walking. Free the allocated memory inside NODE + and its children. */ + +static reg_errcode_t +free_tree (void *extra, bin_tree_t *node) +{ + free_token (&node->token); + return REG_NOERROR; +} + + +/* Duplicate the node SRC, and return new node. This is a preorder + visit similar to the one implemented by the generic visitor, but + we need more infrastructure to maintain two parallel trees --- so, + it's easier to duplicate. */ + +static bin_tree_t * +duplicate_tree (root, dfa) + const bin_tree_t *root; + re_dfa_t *dfa; +{ + const bin_tree_t *node; + bin_tree_t *dup_root; + bin_tree_t **p_new = &dup_root, *dup_node = root->parent; + + for (node = root; ; ) + { + /* Create a new tree and link it back to the current parent. */ + *p_new = create_token_tree (dfa, NULL, NULL, &node->token); + if (*p_new == NULL) + return NULL; + (*p_new)->parent = dup_node; + (*p_new)->token.duplicated = 1; + dup_node = *p_new; + + /* Go to the left node, or up and to the right. */ + if (node->left) + { + node = node->left; + p_new = &dup_node->left; + } + else + { + const bin_tree_t *prev = NULL; + while (node->right == prev || node->right == NULL) + { + prev = node; + node = node->parent; + dup_node = dup_node->parent; + if (!node) + return dup_root; + } + node = node->right; + p_new = &dup_node->right; + } + } +} diff --git a/gnu/lib/libregex/regex.c b/gnu/lib/libregex/regex.c new file mode 100644 index 0000000..df7abe2 --- /dev/null +++ b/gnu/lib/libregex/regex.c @@ -0,0 +1,98 @@ +/* $FreeBSD$ */ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _AIX +#pragma alloca +#else +# ifndef allocax /* predefined by HP cc +Olibcalls */ +# ifdef __GNUC__ +# define alloca(size) __builtin_alloca (size) +# else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef __hpux + void *alloca (); +# else +# if !defined __OS2__ && !defined WIN32 + char *alloca (); +# else +# include <malloc.h> /* OS/2 defines alloca in here */ +# endif +# endif +# endif +# endif +# endif +#endif + +#ifdef _LIBC +/* We have to keep the namespace clean. */ +# define regfree(preg) __regfree (preg) +# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) +# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) +# define regerror(errcode, preg, errbuf, errbuf_size) \ + __regerror(errcode, preg, errbuf, errbuf_size) +# define re_set_registers(bu, re, nu, st, en) \ + __re_set_registers (bu, re, nu, st, en) +# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ + __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) +# define re_match(bufp, string, size, pos, regs) \ + __re_match (bufp, string, size, pos, regs) +# define re_search(bufp, string, size, startpos, range, regs) \ + __re_search (bufp, string, size, startpos, range, regs) +# define re_compile_pattern(pattern, length, bufp) \ + __re_compile_pattern (pattern, length, bufp) +# define re_set_syntax(syntax) __re_set_syntax (syntax) +# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ + __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) +# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) + +# include "../locale/localeinfo.h" +#endif + +/* POSIX says that <sys/types.h> must be included (by the caller) before + <regex.h>. */ +#include <sys/types.h> + +/* On some systems, limits.h sets RE_DUP_MAX to a lower value than + GNU regex allows. Include it before <regex.h>, which correctly + #undefs RE_DUP_MAX and sets it to the right value. */ +#include <limits.h> + +#include <regex.h> +#include "regex_internal.h" + +#include "regex_internal.c" +#include "regcomp.c" +#include "regexec.c" + +/* Binary backward compatibility. */ +#if _LIBC +# include <shlib-compat.h> +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) +link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") +int re_max_failures = 2000; +# endif +#endif diff --git a/gnu/lib/libregex/regex.h b/gnu/lib/libregex/regex.h new file mode 100644 index 0000000..364966d --- /dev/null +++ b/gnu/lib/libregex/regex.h @@ -0,0 +1,47 @@ +/* $FreeBSD$ */ +#ifndef _REGEX_H +#include <posix/regex.h> + +/* Document internal interfaces. */ +extern reg_syntax_t __re_set_syntax _RE_ARGS ((reg_syntax_t syntax)); + +extern const char *__re_compile_pattern + _RE_ARGS ((const char *pattern, size_t length, + struct re_pattern_buffer *buffer)); + +extern int __re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer)); + +extern int __re_search + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, int range, struct re_registers *regs)); + +extern int __re_search_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, int range, struct re_registers *regs, int stop)); + +extern int __re_match + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string, + int length, int start, struct re_registers *regs)); + +extern int __re_match_2 + _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1, + int length1, const char *string2, int length2, + int start, struct re_registers *regs, int stop)); + +extern void __re_set_registers + _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs, + unsigned num_regs, regoff_t *starts, regoff_t *ends)); + +extern int __regcomp _RE_ARGS ((regex_t *__preg, const char *__pattern, + int __cflags)); + +extern int __regexec _RE_ARGS ((const regex_t *__preg, + const char *__string, size_t __nmatch, + regmatch_t __pmatch[], int __eflags)); + +extern size_t __regerror _RE_ARGS ((int __errcode, const regex_t *__preg, + char *__errbuf, size_t __errbuf_size)); + +extern void __regfree _RE_ARGS ((regex_t *__preg)); +#endif diff --git a/gnu/lib/libregex/regex_internal.c b/gnu/lib/libregex/regex_internal.c new file mode 100644 index 0000000..b3d44c3 --- /dev/null +++ b/gnu/lib/libregex/regex_internal.c @@ -0,0 +1,1674 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +static void re_string_construct_common (const char *str, int len, + re_string_t *pstr, + RE_TRANSLATE_TYPE trans, int icase, + const re_dfa_t *dfa) internal_function; +#ifdef RE_ENABLE_I18N +static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx, + wint_t *last_wc) internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate, + unsigned int hash) internal_function; +static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int hash) internal_function; +static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context, + unsigned int hash) internal_function; +static unsigned int inline calc_state_hash (const re_node_set *nodes, + unsigned int context) internal_function; + +/* Functions for string operation. */ + +/* This function allocate the buffers. It is necessary to call + re_string_reconstruct before using the object. */ + +static reg_errcode_t +re_string_allocate (pstr, str, len, init_len, trans, icase, dfa) + re_string_t *pstr; + const char *str; + int len, init_len, icase; + RE_TRANSLATE_TYPE trans; + const re_dfa_t *dfa; +{ + reg_errcode_t ret; + int init_buf_len; + + /* Ensure at least one character fits into the buffers. */ + if (init_len < dfa->mb_cur_max) + init_len = dfa->mb_cur_max; + init_buf_len = (len + 1 < init_len) ? len + 1: init_len; + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + ret = re_string_realloc_buffers (pstr, init_buf_len); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + pstr->word_char = dfa->word_char; + pstr->word_ops_used = dfa->word_ops_used; + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; + pstr->valid_raw_len = pstr->valid_len; + return REG_NOERROR; +} + +/* This function allocate the buffers, and initialize them. */ + +static reg_errcode_t +re_string_construct (pstr, str, len, trans, icase, dfa) + re_string_t *pstr; + const char *str; + int len, icase; + RE_TRANSLATE_TYPE trans; + const re_dfa_t *dfa; +{ + reg_errcode_t ret; + memset (pstr, '\0', sizeof (re_string_t)); + re_string_construct_common (str, len, pstr, trans, icase, dfa); + + if (len > 0) + { + ret = re_string_realloc_buffers (pstr, len + 1); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; + + if (icase) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + { + while (1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + if (pstr->valid_raw_len >= len) + break; + if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) + break; + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (trans != NULL) + re_string_translate_buffer (pstr); + else + { + pstr->valid_len = pstr->bufs_len; + pstr->valid_raw_len = pstr->bufs_len; + } + } + } + + return REG_NOERROR; +} + +/* Helper functions for re_string_allocate, and re_string_construct. */ + +static reg_errcode_t +re_string_realloc_buffers (pstr, new_buf_len) + re_string_t *pstr; + int new_buf_len; +{ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->wcs = new_array; + if (pstr->offsets != NULL) + { + int *new_array = re_realloc (pstr->offsets, int, new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->offsets = new_array; + } + } +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + { + unsigned char *new_array = re_realloc (pstr->mbs, unsigned char, + new_buf_len); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + pstr->mbs = new_array; + } + pstr->bufs_len = new_buf_len; + return REG_NOERROR; +} + + +static void +re_string_construct_common (str, len, pstr, trans, icase, dfa) + const char *str; + int len; + re_string_t *pstr; + RE_TRANSLATE_TYPE trans; + int icase; + const re_dfa_t *dfa; +{ + pstr->raw_mbs = (const unsigned char *) str; + pstr->len = len; + pstr->raw_len = len; + pstr->trans = (unsigned RE_TRANSLATE_TYPE) trans; + pstr->icase = icase ? 1 : 0; + pstr->mbs_allocated = (trans != NULL || icase); + pstr->mb_cur_max = dfa->mb_cur_max; + pstr->is_utf8 = dfa->is_utf8; + pstr->map_notascii = dfa->map_notascii; + pstr->stop = pstr->len; + pstr->raw_stop = pstr->stop; +} + +#ifdef RE_ENABLE_I18N + +/* Build wide character buffer PSTR->WCS. + If the byte sequence of the string are: + <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3> + Then wide character buffer will be: + <wc1> , WEOF , <wc2> , WEOF , <wc3> + We use WEOF for padding, they indicate that the position isn't + a first byte of a multibyte character. + + Note that this function assumes PSTR->VALID_LEN elements are already + built and starts from PSTR->VALID_LEN. */ + +static void +build_wcs_buffer (pstr) + re_string_t *pstr; +{ +#ifdef _LIBC + unsigned char buf[MB_CUR_MAX]; + assert (MB_CUR_MAX >= pstr->mb_cur_max); +#else + unsigned char buf[64]; +#endif + mbstate_t prev_st; + int byte_idx, end_idx, remain_len; + size_t mbclen; + + /* Build the buffers from pstr->valid_len to either pstr->len or + pstr->bufs_len. */ + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + for (byte_idx = pstr->valid_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + /* Apply the translation if we need. */ + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; + buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2, 0)) + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + if (BE (pstr->trans != NULL, 0)) + wc = pstr->trans[wc]; + pstr->cur_state = prev_st; + } + + /* Write wide character and padding. */ + pstr->wcs[byte_idx++] = wc; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; +} + +/* Build wide character buffer PSTR->WCS like build_wcs_buffer, + but for REG_ICASE. */ + +static int +build_wcs_upper_buffer (pstr) + re_string_t *pstr; +{ + mbstate_t prev_st; + int src_idx, byte_idx, end_idx, remain_len; + size_t mbclen; +#ifdef _LIBC + char buf[MB_CUR_MAX]; + assert (MB_CUR_MAX >= pstr->mb_cur_max); +#else + char buf[64]; +#endif + + byte_idx = pstr->valid_len; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + /* The following optimization assumes that ASCII characters can be + mapped to wide characters with a simple cast. */ + if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) + { + while (byte_idx < end_idx) + { + wchar_t wc; + + if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) + && mbsinit (&pstr->cur_state)) + { + /* In case of a singlebyte character. */ + pstr->mbs[byte_idx] + = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); + /* The next step uses the assumption that wchar_t is encoded + ASCII-safe: all ASCII values can be converted like this. */ + pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; + ++byte_idx; + continue; + } + + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, + ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx + + byte_idx), remain_len, &pstr->cur_state); + if (BE (mbclen + 2 > 2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb (buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else + { + src_idx = byte_idx; + goto offsets_needed; + } + } + else + memcpy (pstr->mbs + byte_idx, + pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; + pstr->mbs[byte_idx] = ch; + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = byte_idx; + return REG_NOERROR; + } + else + for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) + { + wchar_t wc; + const char *p; + offsets_needed: + remain_len = end_idx - byte_idx; + prev_st = pstr->cur_state; + if (BE (pstr->trans != NULL, 0)) + { + int i, ch; + + for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) + { + ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; + buf[i] = pstr->trans[ch]; + } + p = (const char *) buf; + } + else + p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; + mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state); + if (BE (mbclen + 2 > 2, 1)) + { + wchar_t wcu = wc; + if (iswlower (wc)) + { + size_t mbcdlen; + + wcu = towupper (wc); + mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); + if (BE (mbclen == mbcdlen, 1)) + memcpy (pstr->mbs + byte_idx, buf, mbclen); + else if (mbcdlen != (size_t) -1) + { + size_t i; + + if (byte_idx + mbcdlen > pstr->bufs_len) + { + pstr->cur_state = prev_st; + break; + } + + if (pstr->offsets == NULL) + { + pstr->offsets = re_malloc (int, pstr->bufs_len); + + if (pstr->offsets == NULL) + return REG_ESPACE; + } + if (!pstr->offsets_needed) + { + for (i = 0; i < (size_t) byte_idx; ++i) + pstr->offsets[i] = i; + pstr->offsets_needed = 1; + } + + memcpy (pstr->mbs + byte_idx, buf, mbcdlen); + pstr->wcs[byte_idx] = wcu; + pstr->offsets[byte_idx] = src_idx; + for (i = 1; i < mbcdlen; ++i) + { + pstr->offsets[byte_idx + i] + = src_idx + (i < mbclen ? i : mbclen - 1); + pstr->wcs[byte_idx + i] = WEOF; + } + pstr->len += mbcdlen - mbclen; + if (pstr->raw_stop > src_idx) + pstr->stop += mbcdlen - mbclen; + end_idx = (pstr->bufs_len > pstr->len) + ? pstr->len : pstr->bufs_len; + byte_idx += mbcdlen; + src_idx += mbclen; + continue; + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + } + else + memcpy (pstr->mbs + byte_idx, p, mbclen); + + if (BE (pstr->offsets_needed != 0, 0)) + { + size_t i; + for (i = 0; i < mbclen; ++i) + pstr->offsets[byte_idx + i] = src_idx + i; + } + src_idx += mbclen; + + pstr->wcs[byte_idx++] = wcu; + /* Write paddings. */ + for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) + pstr->wcs[byte_idx++] = WEOF; + } + else if (mbclen == (size_t) -1 || mbclen == 0) + { + /* It is an invalid character or '\0'. Just use the byte. */ + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; + + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans [ch]; + pstr->mbs[byte_idx] = ch; + + if (BE (pstr->offsets_needed != 0, 0)) + pstr->offsets[byte_idx] = src_idx; + ++src_idx; + + /* And also cast it to wide char. */ + pstr->wcs[byte_idx++] = (wchar_t) ch; + if (BE (mbclen == (size_t) -1, 0)) + pstr->cur_state = prev_st; + } + else + { + /* The buffer doesn't have enough space, finish to build. */ + pstr->cur_state = prev_st; + break; + } + } + pstr->valid_len = byte_idx; + pstr->valid_raw_len = src_idx; + return REG_NOERROR; +} + +/* Skip characters until the index becomes greater than NEW_RAW_IDX. + Return the index. */ + +static int +re_string_skip_chars (pstr, new_raw_idx, last_wc) + re_string_t *pstr; + int new_raw_idx; + wint_t *last_wc; +{ + mbstate_t prev_st; + int rawbuf_idx; + size_t mbclen; + wchar_t wc = 0; + + /* Skip the characters which are not necessary to check. */ + for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; + rawbuf_idx < new_raw_idx;) + { + int remain_len; + remain_len = pstr->len - rawbuf_idx; + prev_st = pstr->cur_state; + mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx, + remain_len, &pstr->cur_state); + if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) + { + /* We treat these cases as a singlebyte character. */ + mbclen = 1; + pstr->cur_state = prev_st; + } + /* Then proceed the next character. */ + rawbuf_idx += mbclen; + } + *last_wc = (wint_t) wc; + return rawbuf_idx; +} +#endif /* RE_ENABLE_I18N */ + +/* Build the buffer PSTR->MBS, and apply the translation if we need. + This function is used in case of REG_ICASE. */ + +static void +build_upper_buffer (pstr) + re_string_t *pstr; +{ + int char_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; + if (BE (pstr->trans != NULL, 0)) + ch = pstr->trans[ch]; + if (islower (ch)) + pstr->mbs[char_idx] = toupper (ch); + else + pstr->mbs[char_idx] = ch; + } + pstr->valid_len = char_idx; + pstr->valid_raw_len = char_idx; +} + +/* Apply TRANS to the buffer in PSTR. */ + +static void +re_string_translate_buffer (pstr) + re_string_t *pstr; +{ + int buf_idx, end_idx; + end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; + + for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) + { + int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; + pstr->mbs[buf_idx] = pstr->trans[ch]; + } + + pstr->valid_len = buf_idx; + pstr->valid_raw_len = buf_idx; +} + +/* This function re-construct the buffers. + Concretely, convert to wide character in case of pstr->mb_cur_max > 1, + convert to upper case in case of REG_ICASE, apply translation. */ + +static reg_errcode_t +re_string_reconstruct (pstr, idx, eflags) + re_string_t *pstr; + int idx, eflags; +{ + int offset = idx - pstr->raw_mbs_idx; + if (BE (offset < 0, 0)) + { + /* Reset buffer. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); +#endif /* RE_ENABLE_I18N */ + pstr->len = pstr->raw_len; + pstr->stop = pstr->raw_stop; + pstr->valid_len = 0; + pstr->raw_mbs_idx = 0; + pstr->valid_raw_len = 0; + pstr->offsets_needed = 0; + pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF); + if (!pstr->mbs_allocated) + pstr->mbs = (unsigned char *) pstr->raw_mbs; + offset = idx; + } + + if (BE (offset != 0, 1)) + { + /* Are the characters which are already checked remain? */ + if (BE (offset < pstr->valid_raw_len, 1) +#ifdef RE_ENABLE_I18N + /* Handling this would enlarge the code too much. + Accept a slowdown in that case. */ + && pstr->offsets_needed == 0 +#endif + ) + { + /* Yes, move them to the front of the buffer. */ + pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags); +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + memmove (pstr->wcs, pstr->wcs + offset, + (pstr->valid_len - offset) * sizeof (wint_t)); +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + memmove (pstr->mbs, pstr->mbs + offset, + pstr->valid_len - offset); + pstr->valid_len -= offset; + pstr->valid_raw_len -= offset; +#if DEBUG + assert (pstr->valid_len > 0); +#endif + } + else + { + /* No, skip all characters until IDX. */ +#ifdef RE_ENABLE_I18N + if (BE (pstr->offsets_needed, 0)) + { + pstr->len = pstr->raw_len - idx + offset; + pstr->stop = pstr->raw_stop - idx + offset; + pstr->offsets_needed = 0; + } +#endif + pstr->valid_len = 0; + pstr->valid_raw_len = 0; +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + int wcs_idx; + wint_t wc = WEOF; + + if (pstr->is_utf8) + { + const unsigned char *raw, *p, *q, *end; + + /* Special case UTF-8. Multi-byte chars start with any + byte other than 0x80 - 0xbf. */ + raw = pstr->raw_mbs + pstr->raw_mbs_idx; + end = raw + (offset - pstr->mb_cur_max); + for (p = raw + offset - 1; p >= end; --p) + if ((*p & 0xc0) != 0x80) + { + mbstate_t cur_state; + wchar_t wc2; + int mlen = raw + pstr->len - p; + unsigned char buf[6]; + + q = p; + if (BE (pstr->trans != NULL, 0)) + { + int i = mlen < 6 ? mlen : 6; + while (--i >= 0) + buf[i] = pstr->trans[p[i]]; + q = buf; + } + /* XXX Don't use mbrtowc, we know which conversion + to use (UTF-8 -> UCS4). */ + memset (&cur_state, 0, sizeof (cur_state)); + mlen = (mbrtowc (&wc2, (const char *) p, mlen, + &cur_state) + - (raw + offset - p)); + if (mlen >= 0) + { + memset (&pstr->cur_state, '\0', + sizeof (mbstate_t)); + pstr->valid_len = mlen; + wc = wc2; + } + break; + } + } + + if (wc == WEOF) + pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; + if (BE (pstr->valid_len, 0)) + { + for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) + pstr->wcs[wcs_idx] = WEOF; + if (pstr->mbs_allocated) + memset (pstr->mbs, 255, pstr->valid_len); + } + pstr->valid_raw_len = pstr->valid_len; + pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) + && IS_WIDE_WORD_CHAR (wc)) + ? CONTEXT_WORD + : ((IS_WIDE_NEWLINE (wc) + && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + } + else +#endif /* RE_ENABLE_I18N */ + { + int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; + if (pstr->trans) + c = pstr->trans[c]; + pstr->tip_context = (bitset_contain (pstr->word_char, c) + ? CONTEXT_WORD + : ((IS_NEWLINE (c) && pstr->newline_anchor) + ? CONTEXT_NEWLINE : 0)); + } + } + if (!BE (pstr->mbs_allocated, 0)) + pstr->mbs += offset; + } + pstr->raw_mbs_idx = idx; + pstr->len -= offset; + pstr->stop -= offset; + + /* Then build the buffers. */ +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + if (pstr->icase) + { + int ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else + build_wcs_buffer (pstr); + } + else +#endif /* RE_ENABLE_I18N */ + if (BE (pstr->mbs_allocated, 0)) + { + if (pstr->icase) + build_upper_buffer (pstr); + else if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + else + pstr->valid_len = pstr->len; + + pstr->cur_idx = 0; + return REG_NOERROR; +} + +static unsigned char +re_string_peek_byte_case (pstr, idx) + const re_string_t *pstr; + int idx; +{ + int ch, off; + + /* Handle the common (easiest) cases first. */ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_peek_byte (pstr, idx); + +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1 + && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) + return re_string_peek_byte (pstr, idx); +#endif + + off = pstr->cur_idx + idx; +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + off = pstr->offsets[off]; +#endif + + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + +#ifdef RE_ENABLE_I18N + /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I + this function returns CAPITAL LETTER I instead of first byte of + DOTLESS SMALL LETTER I. The latter would confuse the parser, + since peek_byte_case doesn't advance cur_idx in any way. */ + if (pstr->offsets_needed && !isascii (ch)) + return re_string_peek_byte (pstr, idx); +#endif + + return ch; +} + +static unsigned char +re_string_fetch_byte_case (pstr) + re_string_t *pstr; +{ + if (BE (!pstr->mbs_allocated, 1)) + return re_string_fetch_byte (pstr); + +#ifdef RE_ENABLE_I18N + if (pstr->offsets_needed) + { + int off, ch; + + /* For tr_TR.UTF-8 [[:islower:]] there is + [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip + in that case the whole multi-byte character and return + the original letter. On the other side, with + [[: DOTLESS SMALL LETTER I return [[:I, as doing + anything else would complicate things too much. */ + + if (!re_string_first_byte (pstr, pstr->cur_idx)) + return re_string_fetch_byte (pstr); + + off = pstr->offsets[pstr->cur_idx]; + ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; + + if (! isascii (ch)) + return re_string_fetch_byte (pstr); + + re_string_skip_bytes (pstr, + re_string_char_size_at (pstr, pstr->cur_idx)); + return ch; + } +#endif + + return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; +} + +static void +re_string_destruct (pstr) + re_string_t *pstr; +{ +#ifdef RE_ENABLE_I18N + re_free (pstr->wcs); + re_free (pstr->offsets); +#endif /* RE_ENABLE_I18N */ + if (pstr->mbs_allocated) + re_free (pstr->mbs); +} + +/* Return the context at IDX in INPUT. */ + +static unsigned int +re_string_context_at (input, idx, eflags) + const re_string_t *input; + int idx, eflags; +{ + int c; + if (BE (idx < 0, 0)) + /* In this case, we use the value stored in input->tip_context, + since we can't know the character in input->mbs[-1] here. */ + return input->tip_context; + if (BE (idx == input->len, 0)) + return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF + : CONTEXT_NEWLINE | CONTEXT_ENDBUF); +#ifdef RE_ENABLE_I18N + if (input->mb_cur_max > 1) + { + wint_t wc; + int wc_idx = idx; + while(input->wcs[wc_idx] == WEOF) + { +#ifdef DEBUG + /* It must not happen. */ + assert (wc_idx >= 0); +#endif + --wc_idx; + if (wc_idx < 0) + return input->tip_context; + } + wc = input->wcs[wc_idx]; + if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) + return CONTEXT_WORD; + return (IS_WIDE_NEWLINE (wc) && input->newline_anchor + ? CONTEXT_NEWLINE : 0); + } + else +#endif + { + c = re_string_byte_at (input, idx); + if (bitset_contain (input->word_char, c)) + return CONTEXT_WORD; + return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; + } +} + +/* Functions for set operation. */ + +static reg_errcode_t +re_node_set_alloc (set, size) + re_node_set *set; + int size; +{ + set->alloc = size; + set->nelem = 0; + set->elems = re_malloc (int, size); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_1 (set, elem) + re_node_set *set; + int elem; +{ + set->alloc = 1; + set->nelem = 1; + set->elems = re_malloc (int, 1); + if (BE (set->elems == NULL, 0)) + { + set->alloc = set->nelem = 0; + return REG_ESPACE; + } + set->elems[0] = elem; + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_2 (set, elem1, elem2) + re_node_set *set; + int elem1, elem2; +{ + set->alloc = 2; + set->elems = re_malloc (int, 2); + if (BE (set->elems == NULL, 0)) + return REG_ESPACE; + if (elem1 == elem2) + { + set->nelem = 1; + set->elems[0] = elem1; + } + else + { + set->nelem = 2; + if (elem1 < elem2) + { + set->elems[0] = elem1; + set->elems[1] = elem2; + } + else + { + set->elems[0] = elem2; + set->elems[1] = elem1; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +re_node_set_init_copy (dest, src) + re_node_set *dest; + const re_node_set *src; +{ + dest->nelem = src->nelem; + if (src->nelem > 0) + { + dest->alloc = dest->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + { + dest->alloc = dest->nelem = 0; + return REG_ESPACE; + } + memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); + } + else + re_node_set_init_empty (dest); + return REG_NOERROR; +} + +/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. + Note: We assume dest->elems is NULL, when dest->alloc is 0. */ + +static reg_errcode_t +re_node_set_add_intersect (dest, src1, src2) + re_node_set *dest; + const re_node_set *src1, *src2; +{ + int i1, i2, is, id, delta, sbase; + if (src1->nelem == 0 || src2->nelem == 0) + return REG_NOERROR; + + /* We need dest->nelem + 2 * elems_in_intersection; this is a + conservative estimate. */ + if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) + { + int new_alloc = src1->nelem + src2->nelem + dest->alloc; + int *new_elems = re_realloc (dest->elems, int, new_alloc); + if (BE (new_elems == NULL, 0)) + return REG_ESPACE; + dest->elems = new_elems; + dest->alloc = new_alloc; + } + + /* Find the items in the intersection of SRC1 and SRC2, and copy + into the top of DEST those that are not already in DEST itself. */ + sbase = dest->nelem + src1->nelem + src2->nelem; + i1 = src1->nelem - 1; + i2 = src2->nelem - 1; + id = dest->nelem - 1; + for (;;) + { + if (src1->elems[i1] == src2->elems[i2]) + { + /* Try to find the item in DEST. Maybe we could binary search? */ + while (id >= 0 && dest->elems[id] > src1->elems[i1]) + --id; + + if (id < 0 || dest->elems[id] != src1->elems[i1]) + dest->elems[--sbase] = src1->elems[i1]; + + if (--i1 < 0 || --i2 < 0) + break; + } + + /* Lower the highest of the two items. */ + else if (src1->elems[i1] < src2->elems[i2]) + { + if (--i2 < 0) + break; + } + else + { + if (--i1 < 0) + break; + } + } + + id = dest->nelem - 1; + is = dest->nelem + src1->nelem + src2->nelem - 1; + delta = is - sbase + 1; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place; this is more or + less the same loop that is in re_node_set_merge. */ + dest->nelem += delta; + if (delta > 0 && id >= 0) + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (--id < 0) + break; + } + } + + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int)); + + return REG_NOERROR; +} + +/* Calculate the union set of the sets SRC1 and SRC2. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +re_node_set_init_union (dest, src1, src2) + re_node_set *dest; + const re_node_set *src1, *src2; +{ + int i1, i2, id; + if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) + { + dest->alloc = src1->nelem + src2->nelem; + dest->elems = re_malloc (int, dest->alloc); + if (BE (dest->elems == NULL, 0)) + return REG_ESPACE; + } + else + { + if (src1 != NULL && src1->nelem > 0) + return re_node_set_init_copy (dest, src1); + else if (src2 != NULL && src2->nelem > 0) + return re_node_set_init_copy (dest, src2); + else + re_node_set_init_empty (dest); + return REG_NOERROR; + } + for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) + { + if (src1->elems[i1] > src2->elems[i2]) + { + dest->elems[id++] = src2->elems[i2++]; + continue; + } + if (src1->elems[i1] == src2->elems[i2]) + ++i2; + dest->elems[id++] = src1->elems[i1++]; + } + if (i1 < src1->nelem) + { + memcpy (dest->elems + id, src1->elems + i1, + (src1->nelem - i1) * sizeof (int)); + id += src1->nelem - i1; + } + else if (i2 < src2->nelem) + { + memcpy (dest->elems + id, src2->elems + i2, + (src2->nelem - i2) * sizeof (int)); + id += src2->nelem - i2; + } + dest->nelem = id; + return REG_NOERROR; +} + +/* Calculate the union set of the sets DEST and SRC. And store it to + DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ + +static reg_errcode_t +re_node_set_merge (dest, src) + re_node_set *dest; + const re_node_set *src; +{ + int is, id, sbase, delta; + if (src == NULL || src->nelem == 0) + return REG_NOERROR; + if (dest->alloc < 2 * src->nelem + dest->nelem) + { + int new_alloc = 2 * (src->nelem + dest->alloc); + int *new_buffer = re_realloc (dest->elems, int, new_alloc); + if (BE (new_buffer == NULL, 0)) + return REG_ESPACE; + dest->elems = new_buffer; + dest->alloc = new_alloc; + } + + if (BE (dest->nelem == 0, 0)) + { + dest->nelem = src->nelem; + memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); + return REG_NOERROR; + } + + /* Copy into the top of DEST the items of SRC that are not + found in DEST. Maybe we could binary search in DEST? */ + for (sbase = dest->nelem + 2 * src->nelem, + is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; ) + { + if (dest->elems[id] == src->elems[is]) + is--, id--; + else if (dest->elems[id] < src->elems[is]) + dest->elems[--sbase] = src->elems[is--]; + else /* if (dest->elems[id] > src->elems[is]) */ + --id; + } + + if (is >= 0) + { + /* If DEST is exhausted, the remaining items of SRC must be unique. */ + sbase -= is + 1; + memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int)); + } + + id = dest->nelem - 1; + is = dest->nelem + 2 * src->nelem - 1; + delta = is - sbase + 1; + if (delta == 0) + return REG_NOERROR; + + /* Now copy. When DELTA becomes zero, the remaining + DEST elements are already in place. */ + dest->nelem += delta; + for (;;) + { + if (dest->elems[is] > dest->elems[id]) + { + /* Copy from the top. */ + dest->elems[id + delta--] = dest->elems[is--]; + if (delta == 0) + break; + } + else + { + /* Slide from the bottom. */ + dest->elems[id + delta] = dest->elems[id]; + if (--id < 0) + { + /* Copy remaining SRC elements. */ + memcpy (dest->elems, dest->elems + sbase, + delta * sizeof (int)); + break; + } + } + } + + return REG_NOERROR; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have ELEM. + return -1 if an error is occured, return 1 otherwise. */ + +static int +re_node_set_insert (set, elem) + re_node_set *set; + int elem; +{ + int idx; + /* In case the set is empty. */ + if (set->alloc == 0) + { + if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1)) + return 1; + else + return -1; + } + + if (BE (set->nelem, 0) == 0) + { + /* We already guaranteed above that set->alloc != 0. */ + set->elems[0] = elem; + ++set->nelem; + return 1; + } + + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + int *new_array; + set->alloc = set->alloc * 2; + new_array = re_realloc (set->elems, int, set->alloc); + if (BE (new_array == NULL, 0)) + return -1; + set->elems = new_array; + } + + /* Move the elements which follows the new element. Test the + first element separately to skip a check in the inner loop. */ + if (elem < set->elems[0]) + { + idx = 0; + for (idx = set->nelem; idx > 0; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + else + { + for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) + set->elems[idx] = set->elems[idx - 1]; + } + + /* Insert the new element. */ + set->elems[idx] = elem; + ++set->nelem; + return 1; +} + +/* Insert the new element ELEM to the re_node_set* SET. + SET should not already have any element greater than or equal to ELEM. + Return -1 if an error is occured, return 1 otherwise. */ + +static int +re_node_set_insert_last (set, elem) + re_node_set *set; + int elem; +{ + /* Realloc if we need. */ + if (set->alloc == set->nelem) + { + int *new_array; + set->alloc = (set->alloc + 1) * 2; + new_array = re_realloc (set->elems, int, set->alloc); + if (BE (new_array == NULL, 0)) + return -1; + set->elems = new_array; + } + + /* Insert the new element. */ + set->elems[set->nelem++] = elem; + return 1; +} + +/* Compare two node sets SET1 and SET2. + return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */ + +static int +re_node_set_compare (set1, set2) + const re_node_set *set1, *set2; +{ + int i; + if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) + return 0; + for (i = set1->nelem ; --i >= 0 ; ) + if (set1->elems[i] != set2->elems[i]) + return 0; + return 1; +} + +/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ + +static int +re_node_set_contains (set, elem) + const re_node_set *set; + int elem; +{ + unsigned int idx, right, mid; + if (set->nelem <= 0) + return 0; + + /* Binary search the element. */ + idx = 0; + right = set->nelem - 1; + while (idx < right) + { + mid = (idx + right) / 2; + if (set->elems[mid] < elem) + idx = mid + 1; + else + right = mid; + } + return set->elems[idx] == elem ? idx + 1 : 0; +} + +static void +re_node_set_remove_at (set, idx) + re_node_set *set; + int idx; +{ + if (idx < 0 || idx >= set->nelem) + return; + --set->nelem; + for (; idx < set->nelem; idx++) + set->elems[idx] = set->elems[idx + 1]; +} + + +/* Add the token TOKEN to dfa->nodes, and return the index of the token. + Or return -1, if an error will be occured. */ + +static int +re_dfa_add_node (dfa, token) + re_dfa_t *dfa; + re_token_t token; +{ + int type = token.type; + if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) + { + int new_nodes_alloc = dfa->nodes_alloc * 2; + int *new_nexts, *new_indices; + re_node_set *new_edests, *new_eclosures; + + re_token_t *new_array = re_realloc (dfa->nodes, re_token_t, + new_nodes_alloc); + if (BE (new_array == NULL, 0)) + return -1; + dfa->nodes = new_array; + new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc); + new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc); + new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); + new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); + if (BE (new_nexts == NULL || new_indices == NULL + || new_edests == NULL || new_eclosures == NULL, 0)) + return -1; + dfa->nexts = new_nexts; + dfa->org_indices = new_indices; + dfa->edests = new_edests; + dfa->eclosures = new_eclosures; + dfa->nodes_alloc = new_nodes_alloc; + } + dfa->nodes[dfa->nodes_len] = token; + dfa->nodes[dfa->nodes_len].constraint = 0; +#ifdef RE_ENABLE_I18N + dfa->nodes[dfa->nodes_len].accept_mb = + (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET; +#endif + dfa->nexts[dfa->nodes_len] = -1; + re_node_set_init_empty (dfa->edests + dfa->nodes_len); + re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); + return dfa->nodes_len++; +} + +static unsigned int inline +calc_state_hash (nodes, context) + const re_node_set *nodes; + unsigned int context; +{ + unsigned int hash = nodes->nelem + context; + int i; + for (i = 0 ; i < nodes->nelem ; i++) + hash += nodes->elems[i]; + return hash; +} + +/* Search for the state whose node_set is equivalent to NODES. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t* +re_acquire_state (err, dfa, nodes) + reg_errcode_t *err; + re_dfa_t *dfa; + const re_node_set *nodes; +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (BE (nodes->nelem == 0, 0)) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, 0); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (hash != state->hash) + continue; + if (re_node_set_compare (&state->nodes, nodes)) + return state; + } + + /* There are no appropriate state in the dfa, create the new one. */ + new_state = create_ci_newstate (dfa, nodes, hash); + if (BE (new_state != NULL, 1)) + return new_state; + else + { + *err = REG_ESPACE; + return NULL; + } +} + +/* Search for the state whose node_set is equivalent to NODES and + whose context is equivalent to CONTEXT. + Return the pointer to the state, if we found it in the DFA. + Otherwise create the new one and return it. In case of an error + return NULL and set the error code in ERR. + Note: - We assume NULL as the invalid state, then it is possible that + return value is NULL and ERR is REG_NOERROR. + - We never return non-NULL value in case of any errors, it is for + optimization. */ + +static re_dfastate_t* +re_acquire_state_context (err, dfa, nodes, context) + reg_errcode_t *err; + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int context; +{ + unsigned int hash; + re_dfastate_t *new_state; + struct re_state_table_entry *spot; + int i; + if (nodes->nelem == 0) + { + *err = REG_NOERROR; + return NULL; + } + hash = calc_state_hash (nodes, context); + spot = dfa->state_table + (hash & dfa->state_hash_mask); + + for (i = 0 ; i < spot->num ; i++) + { + re_dfastate_t *state = spot->array[i]; + if (state->hash == hash + && state->context == context + && re_node_set_compare (state->entrance_nodes, nodes)) + return state; + } + /* There are no appropriate state in `dfa', create the new one. */ + new_state = create_cd_newstate (dfa, nodes, context, hash); + if (BE (new_state != NULL, 1)) + return new_state; + else + { + *err = REG_ESPACE; + return NULL; + } +} + +/* Finish initialization of the new state NEWSTATE, and using its hash value + HASH put in the appropriate bucket of DFA's state table. Return value + indicates the error code if failed. */ + +static reg_errcode_t +register_state (dfa, newstate, hash) + re_dfa_t *dfa; + re_dfastate_t *newstate; + unsigned int hash; +{ + struct re_state_table_entry *spot; + reg_errcode_t err; + int i; + + newstate->hash = hash; + err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < newstate->nodes.nelem; i++) + { + int elem = newstate->nodes.elems[i]; + if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) + re_node_set_insert_last (&newstate->non_eps_nodes, elem); + } + + spot = dfa->state_table + (hash & dfa->state_hash_mask); + if (BE (spot->alloc <= spot->num, 0)) + { + int new_alloc = 2 * spot->num + 2; + re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, + new_alloc); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + spot->array = new_array; + spot->alloc = new_alloc; + } + spot->array[spot->num++] = newstate; + return REG_NOERROR; +} + +/* Create the new state which is independ of contexts. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +create_ci_newstate (dfa, nodes, hash) + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int hash; +{ + int i; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->entrance_nodes = &newstate->nodes; + for (i = 0 ; i < nodes->nelem ; i++) + { + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (type == CHARACTER && !node->constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR || node->constraint) + newstate->has_constraint = 1; + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +/* Create the new state which is depend on the context CONTEXT. + Return the new state if succeeded, otherwise return NULL. */ + +static re_dfastate_t * +create_cd_newstate (dfa, nodes, context, hash) + re_dfa_t *dfa; + const re_node_set *nodes; + unsigned int context, hash; +{ + int i, nctx_nodes = 0; + reg_errcode_t err; + re_dfastate_t *newstate; + + newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); + if (BE (newstate == NULL, 0)) + return NULL; + err = re_node_set_init_copy (&newstate->nodes, nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_free (newstate); + return NULL; + } + + newstate->context = context; + newstate->entrance_nodes = &newstate->nodes; + + for (i = 0 ; i < nodes->nelem ; i++) + { + unsigned int constraint = 0; + re_token_t *node = dfa->nodes + nodes->elems[i]; + re_token_type_t type = node->type; + if (node->constraint) + constraint = node->constraint; + + if (type == CHARACTER && !constraint) + continue; +#ifdef RE_ENABLE_I18N + newstate->accept_mb |= node->accept_mb; +#endif /* RE_ENABLE_I18N */ + + /* If the state has the halt node, the state is a halt state. */ + if (type == END_OF_RE) + newstate->halt = 1; + else if (type == OP_BACK_REF) + newstate->has_backref = 1; + else if (type == ANCHOR) + constraint = node->opr.ctx_type; + + if (constraint) + { + if (newstate->entrance_nodes == &newstate->nodes) + { + newstate->entrance_nodes = re_malloc (re_node_set, 1); + if (BE (newstate->entrance_nodes == NULL, 0)) + { + free_state (newstate); + return NULL; + } + re_node_set_init_copy (newstate->entrance_nodes, nodes); + nctx_nodes = 0; + newstate->has_constraint = 1; + } + + if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) + { + re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); + ++nctx_nodes; + } + } + } + err = register_state (dfa, newstate, hash); + if (BE (err != REG_NOERROR, 0)) + { + free_state (newstate); + newstate = NULL; + } + return newstate; +} + +static void +free_state (state) + re_dfastate_t *state; +{ + re_node_set_free (&state->non_eps_nodes); + re_node_set_free (&state->inveclosure); + if (state->entrance_nodes != &state->nodes) + { + re_node_set_free (state->entrance_nodes); + re_free (state->entrance_nodes); + } + re_node_set_free (&state->nodes); + re_free (state->word_trtable); + re_free (state->trtable); + re_free (state); +} diff --git a/gnu/lib/libregex/regex_internal.h b/gnu/lib/libregex/regex_internal.h new file mode 100644 index 0000000..58fa749 --- /dev/null +++ b/gnu/lib/libregex/regex_internal.h @@ -0,0 +1,798 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _REGEX_INTERNAL_H +#define _REGEX_INTERNAL_H 1 + +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC +# include <langinfo.h> +#endif +#if defined HAVE_LOCALE_H || defined _LIBC +# include <locale.h> +#endif +#if defined HAVE_WCHAR_H || defined _LIBC +# include <wchar.h> +#endif /* HAVE_WCHAR_H || _LIBC */ +#if defined HAVE_WCTYPE_H || defined _LIBC +# include <wctype.h> +#endif /* HAVE_WCTYPE_H || _LIBC */ + +/* In case that the system doesn't have isblank(). */ +#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank +# define isblank(ch) ((ch) == ' ' || (ch) == '\t') +#endif + +#ifdef _LIBC +# ifndef _RE_DEFINE_LOCALE_FUNCTIONS +# define _RE_DEFINE_LOCALE_FUNCTIONS 1 +# include <locale/localeinfo.h> +# include <locale/elem-hash.h> +# include <locale/coll-lookup.h> +# endif +#endif + +/* This is for other GNU distributions with internationalized messages. */ +#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include <libintl.h> +# ifdef _LIBC +# undef gettext +# define gettext(msgid) \ + INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) +# endif +#else +# define gettext(msgid) (msgid) +#endif + +#ifndef gettext_noop +/* This define is so xgettext can find the internationalizable + strings. */ +# define gettext_noop(String) String +#endif + +#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC +# define RE_ENABLE_I18N +#endif + +#if __GNUC__ >= 3 +# define BE(expr, val) __builtin_expect (expr, val) +#else +# define BE(expr, val) (expr) +# define inline +#endif + +/* Number of bits in a byte. */ +#define BYTE_BITS 8 +/* Number of single byte character. */ +#define SBC_MAX 256 + +#define COLL_ELEM_LEN_MAX 8 + +/* The character which represents newline. */ +#define NEWLINE_CHAR '\n' +#define WIDE_NEWLINE_CHAR L'\n' + +/* Rename to standard API for using out of glibc. */ +#ifndef _LIBC +# define __wctype wctype +# define __iswctype iswctype +# define __btowc btowc +# define __mempcpy mempcpy +# define __wcrtomb wcrtomb +# define __regfree regfree +# define attribute_hidden +#endif /* not _LIBC */ + +#ifdef __GNUC__ +# define __attribute(arg) __attribute__ (arg) +#else +# define __attribute(arg) +#endif + +extern const char __re_error_msgid[] attribute_hidden; +extern const size_t __re_error_msgid_idx[] attribute_hidden; + +/* Number of bits in an unsinged int. */ +#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS) +/* Number of unsigned int in an bit_set. */ +#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS) +typedef unsigned int bitset[BITSET_UINTS]; +typedef unsigned int *re_bitset_ptr_t; +typedef const unsigned int *re_const_bitset_ptr_t; + +#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS) +#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS)) +#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS)) +#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS) +#define bitset_set_all(set) \ + memset (set, 255, sizeof (unsigned int) * BITSET_UINTS) +#define bitset_copy(dest,src) \ + memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS) +static inline void bitset_not (bitset set); +static inline void bitset_merge (bitset dest, const bitset src); +static inline void bitset_not_merge (bitset dest, const bitset src); +static inline void bitset_mask (bitset dest, const bitset src); + +#define PREV_WORD_CONSTRAINT 0x0001 +#define PREV_NOTWORD_CONSTRAINT 0x0002 +#define NEXT_WORD_CONSTRAINT 0x0004 +#define NEXT_NOTWORD_CONSTRAINT 0x0008 +#define PREV_NEWLINE_CONSTRAINT 0x0010 +#define NEXT_NEWLINE_CONSTRAINT 0x0020 +#define PREV_BEGBUF_CONSTRAINT 0x0040 +#define NEXT_ENDBUF_CONSTRAINT 0x0080 +#define WORD_DELIM_CONSTRAINT 0x0100 +#define NOT_WORD_DELIM_CONSTRAINT 0x0200 + +typedef enum +{ + INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, + WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, + LINE_FIRST = PREV_NEWLINE_CONSTRAINT, + LINE_LAST = NEXT_NEWLINE_CONSTRAINT, + BUF_FIRST = PREV_BEGBUF_CONSTRAINT, + BUF_LAST = NEXT_ENDBUF_CONSTRAINT, + WORD_DELIM = WORD_DELIM_CONSTRAINT, + NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT +} re_context_type; + +typedef struct +{ + int alloc; + int nelem; + int *elems; +} re_node_set; + +typedef enum +{ + NON_TYPE = 0, + + /* Node type, These are used by token, node, tree. */ + CHARACTER = 1, + END_OF_RE = 2, + SIMPLE_BRACKET = 3, + OP_BACK_REF = 4, + OP_PERIOD = 5, +#ifdef RE_ENABLE_I18N + COMPLEX_BRACKET = 6, + OP_UTF8_PERIOD = 7, +#endif /* RE_ENABLE_I18N */ + + /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used + when the debugger shows values of this enum type. */ +#define EPSILON_BIT 8 + OP_OPEN_SUBEXP = EPSILON_BIT | 0, + OP_CLOSE_SUBEXP = EPSILON_BIT | 1, + OP_ALT = EPSILON_BIT | 2, + OP_DUP_ASTERISK = EPSILON_BIT | 3, + ANCHOR = EPSILON_BIT | 4, + + /* Tree type, these are used only by tree. */ + CONCAT = 16, + SUBEXP = 17, + + /* Token type, these are used only by token. */ + OP_DUP_PLUS = 18, + OP_DUP_QUESTION, + OP_OPEN_BRACKET, + OP_CLOSE_BRACKET, + OP_CHARSET_RANGE, + OP_OPEN_DUP_NUM, + OP_CLOSE_DUP_NUM, + OP_NON_MATCH_LIST, + OP_OPEN_COLL_ELEM, + OP_CLOSE_COLL_ELEM, + OP_OPEN_EQUIV_CLASS, + OP_CLOSE_EQUIV_CLASS, + OP_OPEN_CHAR_CLASS, + OP_CLOSE_CHAR_CLASS, + OP_WORD, + OP_NOTWORD, + OP_SPACE, + OP_NOTSPACE, + BACK_SLASH + +} re_token_type_t; + +#ifdef RE_ENABLE_I18N +typedef struct +{ + /* Multibyte characters. */ + wchar_t *mbchars; + + /* Collating symbols. */ +# ifdef _LIBC + int32_t *coll_syms; +# endif + + /* Equivalence classes. */ +# ifdef _LIBC + int32_t *equiv_classes; +# endif + + /* Range expressions. */ +# ifdef _LIBC + uint32_t *range_starts; + uint32_t *range_ends; +# else /* not _LIBC */ + wchar_t *range_starts; + wchar_t *range_ends; +# endif /* not _LIBC */ + + /* Character classes. */ + wctype_t *char_classes; + + /* If this character set is the non-matching list. */ + unsigned int non_match : 1; + + /* # of multibyte characters. */ + int nmbchars; + + /* # of collating symbols. */ + int ncoll_syms; + + /* # of equivalence classes. */ + int nequiv_classes; + + /* # of range expressions. */ + int nranges; + + /* # of character classes. */ + int nchar_classes; +} re_charset_t; +#endif /* RE_ENABLE_I18N */ + +typedef struct +{ + union + { + unsigned char c; /* for CHARACTER */ + re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ +#ifdef RE_ENABLE_I18N + re_charset_t *mbcset; /* for COMPLEX_BRACKET */ +#endif /* RE_ENABLE_I18N */ + int idx; /* for BACK_REF */ + re_context_type ctx_type; /* for ANCHOR */ + } opr; +#if __GNUC__ >= 2 + re_token_type_t type : 8; +#else + re_token_type_t type; +#endif + unsigned int constraint : 10; /* context constraint */ + unsigned int duplicated : 1; + unsigned int opt_subexp : 1; +#ifdef RE_ENABLE_I18N + unsigned int accept_mb : 1; + /* These 2 bits can be moved into the union if needed (e.g. if running out + of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ + unsigned int mb_partial : 1; +#endif + unsigned int word_char : 1; +} re_token_t; + +#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) + +struct re_string_t +{ + /* Indicate the raw buffer which is the original string passed as an + argument of regexec(), re_search(), etc.. */ + const unsigned char *raw_mbs; + /* Store the multibyte string. In case of "case insensitive mode" like + REG_ICASE, upper cases of the string are stored, otherwise MBS points + the same address that RAW_MBS points. */ + unsigned char *mbs; +#ifdef RE_ENABLE_I18N + /* Store the wide character string which is corresponding to MBS. */ + wint_t *wcs; + int *offsets; + mbstate_t cur_state; +#endif + /* Index in RAW_MBS. Each character mbs[i] corresponds to + raw_mbs[raw_mbs_idx + i]. */ + int raw_mbs_idx; + /* The length of the valid characters in the buffers. */ + int valid_len; + /* The corresponding number of bytes in raw_mbs array. */ + int valid_raw_len; + /* The length of the buffers MBS and WCS. */ + int bufs_len; + /* The index in MBS, which is updated by re_string_fetch_byte. */ + int cur_idx; + /* length of RAW_MBS array. */ + int raw_len; + /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ + int len; + /* End of the buffer may be shorter than its length in the cases such + as re_match_2, re_search_2. Then, we use STOP for end of the buffer + instead of LEN. */ + int raw_stop; + /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ + int stop; + + /* The context of mbs[0]. We store the context independently, since + the context of mbs[0] may be different from raw_mbs[0], which is + the beginning of the input string. */ + unsigned int tip_context; + /* The translation passed as a part of an argument of re_compile_pattern. */ + unsigned RE_TRANSLATE_TYPE trans; + /* Copy of re_dfa_t's word_char. */ + re_const_bitset_ptr_t word_char; + /* 1 if REG_ICASE. */ + unsigned char icase; + unsigned char is_utf8; + unsigned char map_notascii; + unsigned char mbs_allocated; + unsigned char offsets_needed; + unsigned char newline_anchor; + unsigned char word_ops_used; + int mb_cur_max; +}; +typedef struct re_string_t re_string_t; + + +struct re_dfa_t; +typedef struct re_dfa_t re_dfa_t; + +#ifndef _LIBC +# ifdef __i386__ +# define internal_function __attribute ((regparm (3), stdcall)) +# else +# define internal_function +# endif +#endif + +#ifndef RE_NO_INTERNAL_PROTOTYPES +static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str, + int len, int init_len, + RE_TRANSLATE_TYPE trans, int icase, + const re_dfa_t *dfa) + internal_function; +static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str, + int len, RE_TRANSLATE_TYPE trans, + int icase, const re_dfa_t *dfa) + internal_function; +static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx, + int eflags) internal_function; +static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, + int new_buf_len) + internal_function; +# ifdef RE_ENABLE_I18N +static void build_wcs_buffer (re_string_t *pstr) internal_function; +static int build_wcs_upper_buffer (re_string_t *pstr) internal_function; +# endif /* RE_ENABLE_I18N */ +static void build_upper_buffer (re_string_t *pstr) internal_function; +static void re_string_translate_buffer (re_string_t *pstr) internal_function; +static void re_string_destruct (re_string_t *pstr) internal_function; +# ifdef RE_ENABLE_I18N +static int re_string_elem_size_at (const re_string_t *pstr, int idx) + internal_function __attribute ((pure)); +static inline int re_string_char_size_at (const re_string_t *pstr, int idx) + internal_function __attribute ((pure)); +static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx) + internal_function __attribute ((pure)); +# endif /* RE_ENABLE_I18N */ +static unsigned int re_string_context_at (const re_string_t *input, int idx, + int eflags) + internal_function __attribute ((pure)); +static unsigned char re_string_peek_byte_case (const re_string_t *pstr, + int idx) + internal_function __attribute ((pure)); +static unsigned char re_string_fetch_byte_case (re_string_t *pstr) + internal_function __attribute ((pure)); +#endif +#define re_string_peek_byte(pstr, offset) \ + ((pstr)->mbs[(pstr)->cur_idx + offset]) +#define re_string_fetch_byte(pstr) \ + ((pstr)->mbs[(pstr)->cur_idx++]) +#define re_string_first_byte(pstr, idx) \ + ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) +#define re_string_is_single_byte_char(pstr, idx) \ + ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ + || (pstr)->wcs[(idx) + 1] != WEOF)) +#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) +#define re_string_cur_idx(pstr) ((pstr)->cur_idx) +#define re_string_get_buffer(pstr) ((pstr)->mbs) +#define re_string_length(pstr) ((pstr)->len) +#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) +#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) +#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) + +#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) +#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t))) +#define re_free(p) free (p) + +struct bin_tree_t +{ + struct bin_tree_t *parent; + struct bin_tree_t *left; + struct bin_tree_t *right; + struct bin_tree_t *first; + struct bin_tree_t *next; + + re_token_t token; + + /* `node_idx' is the index in dfa->nodes, if `type' == 0. + Otherwise `type' indicate the type of this node. */ + int node_idx; +}; +typedef struct bin_tree_t bin_tree_t; + +#define BIN_TREE_STORAGE_SIZE \ + ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) + +struct bin_tree_storage_t +{ + struct bin_tree_storage_t *next; + bin_tree_t data[BIN_TREE_STORAGE_SIZE]; +}; +typedef struct bin_tree_storage_t bin_tree_storage_t; + +#define CONTEXT_WORD 1 +#define CONTEXT_NEWLINE (CONTEXT_WORD << 1) +#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) +#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) + +#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) +#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) +#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) +#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) +#define IS_ORDINARY_CONTEXT(c) ((c) == 0) + +#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') +#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) +#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') +#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) + +#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ + ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ + || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) + +#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ + ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ + || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ + || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) + +struct re_dfastate_t +{ + unsigned int hash; + re_node_set nodes; + re_node_set non_eps_nodes; + re_node_set inveclosure; + re_node_set *entrance_nodes; + struct re_dfastate_t **trtable, **word_trtable; + unsigned int context : 4; + unsigned int halt : 1; + /* If this state can accept `multi byte'. + Note that we refer to multibyte characters, and multi character + collating elements as `multi byte'. */ + unsigned int accept_mb : 1; + /* If this state has backreference node(s). */ + unsigned int has_backref : 1; + unsigned int has_constraint : 1; +}; +typedef struct re_dfastate_t re_dfastate_t; + +struct re_state_table_entry +{ + int num; + int alloc; + re_dfastate_t **array; +}; + +/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ + +typedef struct +{ + int next_idx; + int alloc; + re_dfastate_t **array; +} state_array_t; + +/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ + +typedef struct +{ + int node; + int str_idx; /* The position NODE match at. */ + state_array_t path; +} re_sub_match_last_t; + +/* Store information about the node NODE whose type is OP_OPEN_SUBEXP. + And information about the node, whose type is OP_CLOSE_SUBEXP, + corresponding to NODE is stored in LASTS. */ + +typedef struct +{ + int str_idx; + int node; + int next_last_offset; + state_array_t *path; + int alasts; /* Allocation size of LASTS. */ + int nlasts; /* The number of LASTS. */ + re_sub_match_last_t **lasts; +} re_sub_match_top_t; + +struct re_backref_cache_entry +{ + int node; + int str_idx; + int subexp_from; + int subexp_to; + char more; + char unused; + unsigned short int eps_reachable_subexps_map; +}; + +typedef struct +{ + /* The string object corresponding to the input string. */ + re_string_t input; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + re_dfa_t *const dfa; +#else + re_dfa_t *dfa; +#endif + /* EFLAGS of the argument of regexec. */ + int eflags; + /* Where the matching ends. */ + int match_last; + int last_node; + /* The state log used by the matcher. */ + re_dfastate_t **state_log; + int state_log_top; + /* Back reference cache. */ + int nbkref_ents; + int abkref_ents; + struct re_backref_cache_entry *bkref_ents; + int max_mb_elem_len; + int nsub_tops; + int asub_tops; + re_sub_match_top_t **sub_tops; +} re_match_context_t; + +typedef struct +{ + re_dfastate_t **sifted_states; + re_dfastate_t **limited_states; + int last_node; + int last_str_idx; + re_node_set limits; +} re_sift_context_t; + +struct re_fail_stack_ent_t +{ + int idx; + int node; + regmatch_t *regs; + re_node_set eps_via_nodes; +}; + +struct re_fail_stack_t +{ + int num; + int alloc; + struct re_fail_stack_ent_t *stack; +}; + +struct re_dfa_t +{ + re_token_t *nodes; + int nodes_alloc; + int nodes_len; + int *nexts; + int *org_indices; + re_node_set *edests; + re_node_set *eclosures; + re_node_set *inveclosures; + struct re_state_table_entry *state_table; + re_dfastate_t *init_state; + re_dfastate_t *init_state_word; + re_dfastate_t *init_state_nl; + re_dfastate_t *init_state_begbuf; + bin_tree_t *str_tree; + bin_tree_storage_t *str_tree_storage; + re_bitset_ptr_t sb_char; + int str_tree_storage_idx; + + /* number of subexpressions `re_nsub' is in regex_t. */ + unsigned int state_hash_mask; + int states_alloc; + int init_node; + int nbackref; /* The number of backreference in this dfa. */ + + /* Bitmap expressing which backreference is used. */ + unsigned int used_bkref_map; + unsigned int completed_bkref_map; + + unsigned int has_plural_match : 1; + /* If this dfa has "multibyte node", which is a backreference or + a node which can accept multibyte character or multi character + collating element. */ + unsigned int has_mb_node : 1; + unsigned int is_utf8 : 1; + unsigned int map_notascii : 1; + unsigned int word_ops_used : 1; + int mb_cur_max; + bitset word_char; + reg_syntax_t syntax; + int *subexp_map; +#ifdef DEBUG + char* re_str; +#endif +}; + +#ifndef RE_NO_INTERNAL_PROTOTYPES +static reg_errcode_t re_node_set_alloc (re_node_set *set, int size) internal_function; +static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem) internal_function; +static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1, + int elem2) internal_function; +#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) +static reg_errcode_t re_node_set_init_copy (re_node_set *dest, + const re_node_set *src) internal_function; +static reg_errcode_t re_node_set_add_intersect (re_node_set *dest, + const re_node_set *src1, + const re_node_set *src2) internal_function; +static reg_errcode_t re_node_set_init_union (re_node_set *dest, + const re_node_set *src1, + const re_node_set *src2) internal_function; +static reg_errcode_t re_node_set_merge (re_node_set *dest, + const re_node_set *src) internal_function; +static int re_node_set_insert (re_node_set *set, int elem) internal_function; +static int re_node_set_insert_last (re_node_set *set, + int elem) internal_function; +static int re_node_set_compare (const re_node_set *set1, + const re_node_set *set2) + internal_function __attribute ((pure)); +static int re_node_set_contains (const re_node_set *set, int elem) + internal_function __attribute ((pure)); +static void re_node_set_remove_at (re_node_set *set, int idx) internal_function; +#define re_node_set_remove(set,id) \ + (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) +#define re_node_set_empty(p) ((p)->nelem = 0) +#define re_node_set_free(set) re_free ((set)->elems) +static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token) internal_function; +static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa, + const re_node_set *nodes) internal_function; +static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err, + re_dfa_t *dfa, + const re_node_set *nodes, + unsigned int context) internal_function; +static void free_state (re_dfastate_t *state) internal_function; +#endif + + +typedef enum +{ + SB_CHAR, + MB_CHAR, + EQUIV_CLASS, + COLL_SYM, + CHAR_CLASS +} bracket_elem_type; + +typedef struct +{ + bracket_elem_type type; + union + { + unsigned char ch; + unsigned char *name; + wchar_t wch; + } opr; +} bracket_elem_t; + + +/* Inline functions for bitset operation. */ +static inline void +bitset_not (bitset set) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i) + set[bitset_i] = ~set[bitset_i]; +} + +static inline void +bitset_merge (bitset dest, const bitset src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i) + dest[bitset_i] |= src[bitset_i]; +} + +static inline void +bitset_not_merge (bitset dest, const bitset src) +{ + int i; + for (i = 0; i < BITSET_UINTS; ++i) + dest[i] |= ~src[i]; +} + +static inline void +bitset_mask (bitset dest, const bitset src) +{ + int bitset_i; + for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i) + dest[bitset_i] &= src[bitset_i]; +} + +#if defined RE_ENABLE_I18N && !defined RE_NO_INTERNAL_PROTOTYPES +/* Inline functions for re_string. */ +static inline int +internal_function +re_string_char_size_at (const re_string_t *pstr, int idx) +{ + int byte_idx; + if (pstr->mb_cur_max == 1) + return 1; + for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) + if (pstr->wcs[idx + byte_idx] != WEOF) + break; + return byte_idx; +} + +static inline wint_t +internal_function +re_string_wchar_at (const re_string_t *pstr, int idx) +{ + if (pstr->mb_cur_max == 1) + return (wint_t) pstr->mbs[idx]; + return (wint_t) pstr->wcs[idx]; +} + +static int +internal_function +re_string_elem_size_at (const re_string_t *pstr, int idx) +{ +#ifdef _LIBC + const unsigned char *p, *extra; + const int32_t *table, *indirect; + int32_t tmp; +# include <locale/weight.h> + uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + + if (nrules != 0) + { + table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, + _NL_COLLATE_INDIRECTMB); + p = pstr->mbs + idx; + tmp = findidx (&p); + return p - pstr->mbs - idx; + } + else +#endif /* _LIBC */ + return 1; +} +#endif /* RE_ENABLE_I18N */ + +#endif /* _REGEX_INTERNAL_H */ diff --git a/gnu/lib/libregex/regexec.c b/gnu/lib/libregex/regexec.c new file mode 100644 index 0000000..3c226e3 --- /dev/null +++ b/gnu/lib/libregex/regexec.c @@ -0,0 +1,4327 @@ +/* Extended regular expression matching and search library. + Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, + int n) internal_function; +static void match_ctx_clean (re_match_context_t *mctx) internal_function; +static void match_ctx_free (re_match_context_t *cache) internal_function; +static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node, + int str_idx, int from, int to) + internal_function; +static int search_cur_bkref_entry (re_match_context_t *mctx, int str_idx) + internal_function; +static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node, + int str_idx) internal_function; +static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, + int node, int str_idx) + internal_function; +static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, + re_dfastate_t **limited_sts, int last_node, + int last_str_idx) + internal_function; +static reg_errcode_t re_search_internal (const regex_t *preg, + const char *string, int length, + int start, int range, int stop, + size_t nmatch, regmatch_t pmatch[], + int eflags) internal_function; +static int re_search_2_stub (struct re_pattern_buffer *bufp, + const char *string1, int length1, + const char *string2, int length2, + int start, int range, struct re_registers *regs, + int stop, int ret_len) internal_function; +static int re_search_stub (struct re_pattern_buffer *bufp, + const char *string, int length, int start, + int range, int stop, struct re_registers *regs, + int ret_len) internal_function; +static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, + int nregs, int regs_allocated) internal_function; +static inline re_dfastate_t *acquire_init_state_context + (reg_errcode_t *err, const re_match_context_t *mctx, int idx) + __attribute ((always_inline)) internal_function; +static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx) + internal_function; +static int check_matching (re_match_context_t *mctx, int fl_longest_match, + int *p_match_first) + internal_function; +static int check_halt_node_context (const re_dfa_t *dfa, int node, + unsigned int context) internal_function; +static int check_halt_state_context (const re_match_context_t *mctx, + const re_dfastate_t *state, int idx) + internal_function; +static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, + regmatch_t *prev_idx_match, int cur_node, + int cur_idx, int nmatch) internal_function; +static int proceed_next_node (const re_match_context_t *mctx, + int nregs, regmatch_t *regs, + int *pidx, int node, re_node_set *eps_via_nodes, + struct re_fail_stack_t *fs) internal_function; +static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, + int str_idx, int dest_node, int nregs, + regmatch_t *regs, + re_node_set *eps_via_nodes) internal_function; +static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs, + regmatch_t *regs, re_node_set *eps_via_nodes) internal_function; +static reg_errcode_t set_regs (const regex_t *preg, + const re_match_context_t *mctx, + size_t nmatch, regmatch_t *pmatch, + int fl_backtrack) internal_function; +static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) internal_function; + +#ifdef RE_ENABLE_I18N +static int sift_states_iter_mb (const re_match_context_t *mctx, + re_sift_context_t *sctx, + int node_idx, int str_idx, int max_str_idx) internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t sift_states_backward (re_match_context_t *mctx, + re_sift_context_t *sctx) internal_function; +static reg_errcode_t build_sifted_states (re_match_context_t *mctx, + re_sift_context_t *sctx, int str_idx, + re_node_set *cur_dest) internal_function; +static reg_errcode_t update_cur_sifted_state (re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, + re_node_set *dest_nodes) internal_function; +static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates) internal_function; +static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node, + re_node_set *dest_nodes, + const re_node_set *and_nodes) internal_function; +static int check_dst_limits (re_match_context_t *mctx, re_node_set *limits, + int dst_node, int dst_idx, int src_node, + int src_idx) internal_function; +static int check_dst_limits_calc_pos_1 (re_match_context_t *mctx, + int boundaries, int subexp_idx, + int from_node, int bkref_idx) internal_function; +static int check_dst_limits_calc_pos (re_match_context_t *mctx, + int limit, int subexp_idx, + int node, int str_idx, + int bkref_idx) internal_function; +static reg_errcode_t check_subexp_limits (re_dfa_t *dfa, + re_node_set *dest_nodes, + const re_node_set *candidates, + re_node_set *limits, + struct re_backref_cache_entry *bkref_ents, + int str_idx) internal_function; +static reg_errcode_t sift_states_bkref (re_match_context_t *mctx, + re_sift_context_t *sctx, + int str_idx, const re_node_set *candidates) internal_function; +static reg_errcode_t clean_state_log_if_needed (re_match_context_t *mctx, + int next_state_log_idx) internal_function; +static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst, + re_dfastate_t **src, int num) internal_function; +static re_dfastate_t *find_recover_state (reg_errcode_t *err, + re_match_context_t *mctx) internal_function; +static re_dfastate_t *transit_state (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *state) internal_function; +static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *next_state) internal_function; +static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, + re_node_set *cur_nodes, + int str_idx) internal_function; +#if 0 +static re_dfastate_t *transit_state_sb (reg_errcode_t *err, + re_match_context_t *mctx, + re_dfastate_t *pstate) internal_function; +#endif +#ifdef RE_ENABLE_I18N +static reg_errcode_t transit_state_mb (re_match_context_t *mctx, + re_dfastate_t *pstate) internal_function; +#endif /* RE_ENABLE_I18N */ +static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, + const re_node_set *nodes) internal_function; +static reg_errcode_t get_subexp (re_match_context_t *mctx, + int bkref_node, int bkref_str_idx) internal_function; +static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, + const re_sub_match_top_t *sub_top, + re_sub_match_last_t *sub_last, + int bkref_node, int bkref_str) internal_function; +static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, + int subexp_idx, int type) internal_function; +static reg_errcode_t check_arrival (re_match_context_t *mctx, + state_array_t *path, int top_node, + int top_str, int last_node, int last_str, + int type) internal_function; +static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, + int str_idx, + re_node_set *cur_nodes, + re_node_set *next_nodes) internal_function; +static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa, + re_node_set *cur_nodes, + int ex_subexp, int type) internal_function; +static reg_errcode_t check_arrival_expand_ecl_sub (re_dfa_t *dfa, + re_node_set *dst_nodes, + int target, int ex_subexp, + int type) internal_function; +static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, + re_node_set *cur_nodes, int cur_str, + int subexp_num, int type) internal_function; +static int build_trtable (re_dfa_t *dfa, + re_dfastate_t *state) internal_function; +#ifdef RE_ENABLE_I18N +static int check_node_accept_bytes (re_dfa_t *dfa, int node_idx, + const re_string_t *input, int idx) internal_function; +# ifdef _LIBC +static unsigned int find_collation_sequence_value (const unsigned char *mbs, + size_t name_len) internal_function; +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ +static int group_nodes_into_DFAstates (re_dfa_t *dfa, + const re_dfastate_t *state, + re_node_set *states_node, + bitset *states_ch) internal_function; +static int check_node_accept (const re_match_context_t *mctx, + const re_token_t *node, int idx) internal_function; +static reg_errcode_t extend_buffers (re_match_context_t *mctx) internal_function; + +/* Entry point for POSIX code. */ + +/* regexec searches for a given pattern, specified by PREG, in the + string STRING. + + If NMATCH is zero or REG_NOSUB was set in the cflags argument to + `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at + least NMATCH elements, and we set them to the offsets of the + corresponding matched substrings. + + EFLAGS specifies `execution flags' which affect matching: if + REG_NOTBOL is set, then ^ does not match at the beginning of the + string; if REG_NOTEOL is set, then $ does not match at the end. + + We return 0 if we find a match and REG_NOMATCH if not. */ + +int +regexec (preg, string, nmatch, pmatch, eflags) + const regex_t *__restrict preg; + const char *__restrict string; + size_t nmatch; + regmatch_t pmatch[]; + int eflags; +{ + reg_errcode_t err; + int start, length; + + if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) + return REG_BADPAT; + + if (eflags & REG_STARTEND) + { + start = pmatch[0].rm_so; + length = pmatch[0].rm_eo; + } + else + { + start = 0; + length = strlen (string); + } + if (preg->no_sub) + err = re_search_internal (preg, string, length, start, length - start, + length, 0, NULL, eflags); + else + err = re_search_internal (preg, string, length, start, length - start, + length, nmatch, pmatch, eflags); + return err != REG_NOERROR; +} + +#ifdef _LIBC +# include <shlib-compat.h> +versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); + +# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) +__typeof__ (__regexec) __compat_regexec; + +int +attribute_compat_text_section +__compat_regexec (const regex_t *__restrict preg, + const char *__restrict string, size_t nmatch, + regmatch_t pmatch[], int eflags) +{ + return regexec (preg, string, nmatch, pmatch, + eflags & (REG_NOTBOL | REG_NOTEOL)); +} +compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); +# endif +#endif + +/* Entry points for GNU code. */ + +/* re_match, re_search, re_match_2, re_search_2 + + The former two functions operate on STRING with length LENGTH, + while the later two operate on concatenation of STRING1 and STRING2 + with lengths LENGTH1 and LENGTH2, respectively. + + re_match() matches the compiled pattern in BUFP against the string, + starting at index START. + + re_search() first tries matching at index START, then it tries to match + starting from index START + 1, and so on. The last start position tried + is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same + way as re_match().) + + The parameter STOP of re_{match,search}_2 specifies that no match exceeding + the first STOP characters of the concatenation of the strings should be + concerned. + + If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match + and all groups is stroed in REGS. (For the "_2" variants, the offsets are + computed relative to the concatenation, not relative to the individual + strings.) + + On success, re_match* functions return the length of the match, re_search* + return the position of the start of the match. Return value -1 means no + match was found and -2 indicates an internal error. */ + +int +re_match (bufp, string, length, start, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, 0, length, regs, 1); +} +#ifdef _LIBC +weak_alias (__re_match, re_match) +#endif + +int +re_search (bufp, string, length, start, range, regs) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range; + struct re_registers *regs; +{ + return re_search_stub (bufp, string, length, start, range, length, regs, 0); +} +#ifdef _LIBC +weak_alias (__re_search, re_search) +#endif + +int +re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, 0, regs, stop, 1); +} +#ifdef _LIBC +weak_alias (__re_match_2, re_match_2) +#endif + +int +re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop; + struct re_registers *regs; +{ + return re_search_2_stub (bufp, string1, length1, string2, length2, + start, range, regs, stop, 0); +} +#ifdef _LIBC +weak_alias (__re_search_2, re_search_2) +#endif + +static int +re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs, + stop, ret_len) + struct re_pattern_buffer *bufp; + const char *string1, *string2; + int length1, length2, start, range, stop, ret_len; + struct re_registers *regs; +{ + const char *str; + int rval; + int len = length1 + length2; + int free_str = 0; + + if (BE (length1 < 0 || length2 < 0 || stop < 0, 0)) + return -2; + + /* Concatenate the strings. */ + if (length2 > 0) + if (length1 > 0) + { + char *s = re_malloc (char, len); + + if (BE (s == NULL, 0)) + return -2; + memcpy (s, string1, length1); + memcpy (s + length1, string2, length2); + str = s; + free_str = 1; + } + else + str = string2; + else + str = string1; + + rval = re_search_stub (bufp, str, len, start, range, stop, regs, + ret_len); + if (free_str) + re_free ((char *) str); + return rval; +} + +/* The parameters have the same meaning as those of re_search. + Additional parameters: + If RET_LEN is nonzero the length of the match is returned (re_match style); + otherwise the position of the match is returned. */ + +static int +re_search_stub (bufp, string, length, start, range, stop, regs, ret_len) + struct re_pattern_buffer *bufp; + const char *string; + int length, start, range, stop, ret_len; + struct re_registers *regs; +{ + reg_errcode_t result; + regmatch_t *pmatch; + int nregs, rval; + int eflags = 0; + + /* Check for out-of-range. */ + if (BE (start < 0 || start > length, 0)) + return -1; + if (BE (start + range > length, 0)) + range = length - start; + else if (BE (start + range < 0, 0)) + range = -start; + + eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; + eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; + + /* Compile fastmap if we haven't yet. */ + if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate) + re_compile_fastmap (bufp); + + if (BE (bufp->no_sub, 0)) + regs = NULL; + + /* We need at least 1 register. */ + if (regs == NULL) + nregs = 1; + else if (BE (bufp->regs_allocated == REGS_FIXED && + regs->num_regs < bufp->re_nsub + 1, 0)) + { + nregs = regs->num_regs; + if (BE (nregs < 1, 0)) + { + /* Nothing can be copied to regs. */ + regs = NULL; + nregs = 1; + } + } + else + nregs = bufp->re_nsub + 1; + pmatch = re_malloc (regmatch_t, nregs); + if (BE (pmatch == NULL, 0)) + return -2; + + result = re_search_internal (bufp, string, length, start, range, stop, + nregs, pmatch, eflags); + + rval = 0; + + /* I hope we needn't fill ther regs with -1's when no match was found. */ + if (result != REG_NOERROR) + rval = -1; + else if (regs != NULL) + { + /* If caller wants register contents data back, copy them. */ + bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, + bufp->regs_allocated); + if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) + rval = -2; + } + + if (BE (rval == 0, 1)) + { + if (ret_len) + { + assert (pmatch[0].rm_so == start); + rval = pmatch[0].rm_eo - start; + } + else + rval = pmatch[0].rm_so; + } + re_free (pmatch); + return rval; +} + +static unsigned +re_copy_regs (regs, pmatch, nregs, regs_allocated) + struct re_registers *regs; + regmatch_t *pmatch; + int nregs, regs_allocated; +{ + int rval = REGS_REALLOCATE; + int i; + int need_regs = nregs + 1; + /* We need one extra element beyond `num_regs' for the `-1' marker GNU code + uses. */ + + /* Have the register data arrays been allocated? */ + if (regs_allocated == REGS_UNALLOCATED) + { /* No. So allocate them with malloc. */ + regs->start = re_malloc (regoff_t, need_regs); + regs->end = re_malloc (regoff_t, need_regs); + if (BE (regs->start == NULL, 0) || BE (regs->end == NULL, 0)) + return REGS_UNALLOCATED; + regs->num_regs = need_regs; + } + else if (regs_allocated == REGS_REALLOCATE) + { /* Yes. If we need more elements than were already + allocated, reallocate them. If we need fewer, just + leave it alone. */ + if (BE (need_regs > regs->num_regs, 0)) + { + regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); + regoff_t *new_end = re_realloc (regs->end, regoff_t, need_regs); + if (BE (new_start == NULL, 0) || BE (new_end == NULL, 0)) + return REGS_UNALLOCATED; + regs->start = new_start; + regs->end = new_end; + regs->num_regs = need_regs; + } + } + else + { + assert (regs_allocated == REGS_FIXED); + /* This function may not be called with REGS_FIXED and nregs too big. */ + assert (regs->num_regs >= nregs); + rval = REGS_FIXED; + } + + /* Copy the regs. */ + for (i = 0; i < nregs; ++i) + { + regs->start[i] = pmatch[i].rm_so; + regs->end[i] = pmatch[i].rm_eo; + } + for ( ; i < regs->num_regs; ++i) + regs->start[i] = regs->end[i] = -1; + + return rval; +} + +/* Set REGS to hold NUM_REGS registers, storing them in STARTS and + ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use + this memory for recording register information. STARTS and ENDS + must be allocated using the malloc library routine, and must each + be at least NUM_REGS * sizeof (regoff_t) bytes long. + + If NUM_REGS == 0, then subsequent matches should allocate their own + register data. + + Unless this function is called, the first search or match using + PATTERN_BUFFER will allocate its own register data, without + freeing the old data. */ + +void +re_set_registers (bufp, regs, num_regs, starts, ends) + struct re_pattern_buffer *bufp; + struct re_registers *regs; + unsigned num_regs; + regoff_t *starts, *ends; +{ + if (num_regs) + { + bufp->regs_allocated = REGS_REALLOCATE; + regs->num_regs = num_regs; + regs->start = starts; + regs->end = ends; + } + else + { + bufp->regs_allocated = REGS_UNALLOCATED; + regs->num_regs = 0; + regs->start = regs->end = (regoff_t *) 0; + } +} +#ifdef _LIBC +weak_alias (__re_set_registers, re_set_registers) +#endif + +/* Entry points compatible with 4.2 BSD regex library. We don't define + them unless specifically requested. */ + +#if defined _REGEX_RE_COMP || defined _LIBC +int +# ifdef _LIBC +weak_function +# endif +re_exec (s) + const char *s; +{ + return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); +} +#endif /* _REGEX_RE_COMP */ + +/* Internal entry point. */ + +/* Searches for a compiled pattern PREG in the string STRING, whose + length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same + mingings with regexec. START, and RANGE have the same meanings + with re_search. + Return REG_NOERROR if we find a match, and REG_NOMATCH if not, + otherwise return the error code. + Note: We assume front end functions already check ranges. + (START + RANGE >= 0 && START + RANGE <= LENGTH) */ + +static reg_errcode_t +re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch, + eflags) + const regex_t *preg; + const char *string; + int length, start, range, stop, eflags; + size_t nmatch; + regmatch_t pmatch[]; +{ + reg_errcode_t err; + re_dfa_t *dfa = (re_dfa_t *)preg->buffer; + int left_lim, right_lim, incr; + int fl_longest_match, match_first, match_kind, match_last = -1; + int extra_nmatch; + int sb, ch; +#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) + re_match_context_t mctx = { .dfa = dfa }; +#else + re_match_context_t mctx; +#endif + char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate + && range && !preg->can_be_null) ? preg->fastmap : NULL; + unsigned RE_TRANSLATE_TYPE t = (unsigned RE_TRANSLATE_TYPE) preg->translate; + +#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) + memset (&mctx, '\0', sizeof (re_match_context_t)); + mctx.dfa = dfa; +#endif + + extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; + nmatch -= extra_nmatch; + + /* Check if the DFA haven't been compiled. */ + if (BE (preg->used == 0 || dfa->init_state == NULL + || dfa->init_state_word == NULL || dfa->init_state_nl == NULL + || dfa->init_state_begbuf == NULL, 0)) + return REG_NOMATCH; + +#ifdef DEBUG + /* We assume front-end functions already check them. */ + assert (start + range >= 0 && start + range <= length); +#endif + + /* If initial states with non-begbuf contexts have no elements, + the regex must be anchored. If preg->newline_anchor is set, + we'll never use init_state_nl, so do not check it. */ + if (dfa->init_state->nodes.nelem == 0 + && dfa->init_state_word->nodes.nelem == 0 + && (dfa->init_state_nl->nodes.nelem == 0 + || !preg->newline_anchor)) + { + if (start != 0 && start + range != 0) + return REG_NOMATCH; + start = range = 0; + } + + /* We must check the longest matching, if nmatch > 0. */ + fl_longest_match = (nmatch != 0 || dfa->nbackref); + + err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, + preg->translate, preg->syntax & RE_ICASE, dfa); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + mctx.input.stop = stop; + mctx.input.raw_stop = stop; + mctx.input.newline_anchor = preg->newline_anchor; + + err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* We will log all the DFA states through which the dfa pass, + if nmatch > 1, or this dfa has "multibyte node", which is a + back-reference or a node which can accept multibyte character or + multi character collating element. */ + if (nmatch > 1 || dfa->has_mb_node) + { + mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); + if (BE (mctx.state_log == NULL, 0)) + { + err = REG_ESPACE; + goto free_return; + } + } + else + mctx.state_log = NULL; + + match_first = start; + mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF + : CONTEXT_NEWLINE | CONTEXT_BEGBUF; + + /* Check incrementally whether of not the input string match. */ + incr = (range < 0) ? -1 : 1; + left_lim = (range < 0) ? start + range : start; + right_lim = (range < 0) ? start : start + range; + sb = dfa->mb_cur_max == 1; + match_kind = + (fastmap + ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) + | (range >= 0 ? 2 : 0) + | (t != NULL ? 1 : 0)) + : 8); + + for (;; match_first += incr) + { + err = REG_NOMATCH; + if (match_first < left_lim || right_lim < match_first) + goto free_return; + + /* Advance as rapidly as possible through the string, until we + find a plausible place to start matching. This may be done + with varying efficiency, so there are various possibilities: + only the most common of them are specialized, in order to + save on code size. We use a switch statement for speed. */ + switch (match_kind) + { + case 8: + /* No fastmap. */ + break; + + case 7: + /* Fastmap with single-byte translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[t[(unsigned char) string[match_first]]]) + ++match_first; + goto forward_match_found_start_or_reached_end; + + case 6: + /* Fastmap without translation, match forward. */ + while (BE (match_first < right_lim, 1) + && !fastmap[(unsigned char) string[match_first]]) + ++match_first; + + forward_match_found_start_or_reached_end: + if (BE (match_first == right_lim, 0)) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (!fastmap[t ? t[ch] : ch]) + goto free_return; + } + break; + + case 4: + case 5: + /* Fastmap without multi-byte translation, match backwards. */ + while (match_first >= left_lim) + { + ch = match_first >= length + ? 0 : (unsigned char) string[match_first]; + if (fastmap[t ? t[ch] : ch]) + break; + --match_first; + } + if (match_first < left_lim) + goto free_return; + break; + + default: + /* In this case, we can't determine easily the current byte, + since it might be a component byte of a multibyte + character. Then we use the constructed buffer instead. */ + for (;;) + { + /* If MATCH_FIRST is out of the valid range, reconstruct the + buffers. */ + unsigned int offset = match_first - mctx.input.raw_mbs_idx; + if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0)) + { + err = re_string_reconstruct (&mctx.input, match_first, + eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + offset = match_first - mctx.input.raw_mbs_idx; + } + /* If MATCH_FIRST is out of the buffer, leave it as '\0'. + Note that MATCH_FIRST must not be smaller than 0. */ + ch = (match_first >= length + ? 0 : re_string_byte_at (&mctx.input, offset)); + if (fastmap[ch]) + break; + match_first += incr; + if (match_first < left_lim || match_first > right_lim) + { + err = REG_NOMATCH; + goto free_return; + } + } + break; + } + + /* Reconstruct the buffers so that the matcher can assume that + the matching starts from the beginning of the buffer. */ + err = re_string_reconstruct (&mctx.input, match_first, eflags); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + +#ifdef RE_ENABLE_I18N + /* Don't consider this char as a possible match start if it part, + yet isn't the head, of a multibyte character. */ + if (!sb && !re_string_first_byte (&mctx.input, 0)) + continue; +#endif + + /* It seems to be appropriate one, then use the matcher. */ + /* We assume that the matching starts from 0. */ + mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; + match_last = check_matching (&mctx, fl_longest_match, + range >= 0 ? &match_first : NULL); + if (match_last != -1) + { + if (BE (match_last == -2, 0)) + { + err = REG_ESPACE; + goto free_return; + } + else + { + mctx.match_last = match_last; + if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) + { + re_dfastate_t *pstate = mctx.state_log[match_last]; + mctx.last_node = check_halt_state_context (&mctx, pstate, + match_last); + } + if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) + || dfa->nbackref) + { + err = prune_impossible_nodes (&mctx); + if (err == REG_NOERROR) + break; + if (BE (err != REG_NOMATCH, 0)) + goto free_return; + match_last = -1; + } + else + break; /* We found a match. */ + } + } + + match_ctx_clean (&mctx); + } + +#ifdef DEBUG + assert (match_last != -1); + assert (err == REG_NOERROR); +#endif + + /* Set pmatch[] if we need. */ + if (nmatch > 0) + { + int reg_idx; + + /* Initialize registers. */ + for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) + pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; + + /* Set the points where matching start/end. */ + pmatch[0].rm_so = 0; + pmatch[0].rm_eo = mctx.match_last; + + if (!preg->no_sub && nmatch > 1) + { + err = set_regs (preg, &mctx, nmatch, pmatch, + dfa->has_plural_match && dfa->nbackref > 0); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* At last, add the offset to the each registers, since we slided + the buffers so that we could assume that the matching starts + from 0. */ + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so != -1) + { +#ifdef RE_ENABLE_I18N + if (BE (mctx.input.offsets_needed != 0, 0)) + { + if (pmatch[reg_idx].rm_so == mctx.input.valid_len) + pmatch[reg_idx].rm_so += mctx.input.valid_raw_len - mctx.input.valid_len; + else + pmatch[reg_idx].rm_so = mctx.input.offsets[pmatch[reg_idx].rm_so]; + if (pmatch[reg_idx].rm_eo == mctx.input.valid_len) + pmatch[reg_idx].rm_eo += mctx.input.valid_raw_len - mctx.input.valid_len; + else + pmatch[reg_idx].rm_eo = mctx.input.offsets[pmatch[reg_idx].rm_eo]; + } +#else + assert (mctx.input.offsets_needed == 0); +#endif + pmatch[reg_idx].rm_so += match_first; + pmatch[reg_idx].rm_eo += match_first; + } + for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) + { + pmatch[nmatch + reg_idx].rm_so = -1; + pmatch[nmatch + reg_idx].rm_eo = -1; + } + + if (dfa->subexp_map) + for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) + if (dfa->subexp_map[reg_idx] != reg_idx) + { + pmatch[reg_idx + 1].rm_so + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; + pmatch[reg_idx + 1].rm_eo + = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; + } + } + + free_return: + re_free (mctx.state_log); + if (dfa->nbackref) + match_ctx_free (&mctx); + re_string_destruct (&mctx.input); + return err; +} + +static reg_errcode_t +prune_impossible_nodes (mctx) + re_match_context_t *mctx; +{ + re_dfa_t *const dfa = mctx->dfa; + int halt_node, match_last; + reg_errcode_t ret; + re_dfastate_t **sifted_states; + re_dfastate_t **lim_states = NULL; + re_sift_context_t sctx; +#ifdef DEBUG + assert (mctx->state_log != NULL); +#endif + match_last = mctx->match_last; + halt_node = mctx->last_node; + sifted_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (sifted_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + if (dfa->nbackref) + { + lim_states = re_malloc (re_dfastate_t *, match_last + 1); + if (BE (lim_states == NULL, 0)) + { + ret = REG_ESPACE; + goto free_return; + } + while (1) + { + memset (lim_states, '\0', + sizeof (re_dfastate_t *) * (match_last + 1)); + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, + match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + if (sifted_states[0] != NULL || lim_states[0] != NULL) + break; + do + { + --match_last; + if (match_last < 0) + { + ret = REG_NOMATCH; + goto free_return; + } + } while (mctx->state_log[match_last] == NULL + || !mctx->state_log[match_last]->halt); + halt_node = check_halt_state_context (mctx, + mctx->state_log[match_last], + match_last); + } + ret = merge_state_array (dfa, sifted_states, lim_states, + match_last + 1); + re_free (lim_states); + lim_states = NULL; + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + else + { + sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); + ret = sift_states_backward (mctx, &sctx); + re_node_set_free (&sctx.limits); + if (BE (ret != REG_NOERROR, 0)) + goto free_return; + } + re_free (mctx->state_log); + mctx->state_log = sifted_states; + sifted_states = NULL; + mctx->last_node = halt_node; + mctx->match_last = match_last; + ret = REG_NOERROR; + free_return: + re_free (sifted_states); + re_free (lim_states); + return ret; +} + +/* Acquire an initial state and return it. + We must select appropriate initial state depending on the context, + since initial states may have constraints like "\<", "^", etc.. */ + +static inline re_dfastate_t * +acquire_init_state_context (err, mctx, idx) + reg_errcode_t *err; + const re_match_context_t *mctx; + int idx; +{ + re_dfa_t *const dfa = mctx->dfa; + if (dfa->init_state->has_constraint) + { + unsigned int context; + context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return dfa->init_state_word; + else if (IS_ORDINARY_CONTEXT (context)) + return dfa->init_state; + else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_begbuf; + else if (IS_NEWLINE_CONTEXT (context)) + return dfa->init_state_nl; + else if (IS_BEGBUF_CONTEXT (context)) + { + /* It is relatively rare case, then calculate on demand. */ + return re_acquire_state_context (err, dfa, + dfa->init_state->entrance_nodes, + context); + } + else + /* Must not happen? */ + return dfa->init_state; + } + else + return dfa->init_state; +} + +/* Check whether the regular expression match input string INPUT or not, + and return the index where the matching end, return -1 if not match, + or return -2 in case of an error. + FL_LONGEST_MATCH means we want the POSIX longest matching. + If P_MATCH_FIRST is not NULL, and the match fails, it is set to the + next place where we may want to try matching. + Note that the matcher assume that the maching starts from the current + index of the buffer. */ + +static int +check_matching (mctx, fl_longest_match, p_match_first) + re_match_context_t *mctx; + int fl_longest_match; + int *p_match_first; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int match = 0; + int match_last = -1; + int cur_str_idx = re_string_cur_idx (&mctx->input); + re_dfastate_t *cur_state; + int at_init_state = p_match_first != NULL; + int next_start_idx = cur_str_idx; + + err = REG_NOERROR; + cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); + /* An initial state must not be NULL (invalid). */ + if (BE (cur_state == NULL, 0)) + { + assert (err == REG_ESPACE); + return -2; + } + + if (mctx->state_log != NULL) + { + mctx->state_log[cur_str_idx] = cur_state; + + /* Check OP_OPEN_SUBEXP in the initial state in case that we use them + later. E.g. Processing back references. */ + if (BE (dfa->nbackref, 0)) + { + at_init_state = 0; + err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (cur_state->has_backref) + { + err = transit_state_bkref (mctx, &cur_state->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + + /* If the RE accepts NULL string. */ + if (BE (cur_state->halt, 0)) + { + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, cur_str_idx)) + { + if (!fl_longest_match) + return cur_str_idx; + else + { + match_last = cur_str_idx; + match = 1; + } + } + } + + while (!re_string_eoi (&mctx->input)) + { + re_dfastate_t *old_state = cur_state; + int next_char_idx = re_string_cur_idx (&mctx->input) + 1; + + if (BE (next_char_idx >= mctx->input.bufs_len, 0) + || (BE (next_char_idx >= mctx->input.valid_len, 0) + && mctx->input.valid_len < mctx->input.len)) + { + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + { + assert (err == REG_ESPACE); + return -2; + } + } + + cur_state = transit_state (&err, mctx, cur_state); + if (mctx->state_log != NULL) + cur_state = merge_state_with_log (&err, mctx, cur_state); + + if (cur_state == NULL) + { + /* Reached the invalid state or an error. Try to recover a valid + state using the state log, if available and if we have not + already found a valid (even if not the longest) match. */ + if (BE (err != REG_NOERROR, 0)) + return -2; + + if (mctx->state_log == NULL + || (match && !fl_longest_match) + || (cur_state = find_recover_state (&err, mctx)) == NULL) + break; + } + + if (BE (at_init_state, 0)) + { + if (old_state == cur_state) + next_start_idx = next_char_idx; + else + at_init_state = 0; + } + + if (cur_state->halt) + { + /* Reached a halt state. + Check the halt state can satisfy the current context. */ + if (!cur_state->has_constraint + || check_halt_state_context (mctx, cur_state, + re_string_cur_idx (&mctx->input))) + { + /* We found an appropriate halt state. */ + match_last = re_string_cur_idx (&mctx->input); + match = 1; + + /* We found a match, do not modify match_first below. */ + p_match_first = NULL; + if (!fl_longest_match) + break; + } + } + } + + if (p_match_first) + *p_match_first += next_start_idx; + + return match_last; +} + +/* Check NODE match the current context. */ + +static int check_halt_node_context (dfa, node, context) + const re_dfa_t *dfa; + int node; + unsigned int context; +{ + re_token_type_t type = dfa->nodes[node].type; + unsigned int constraint = dfa->nodes[node].constraint; + if (type != END_OF_RE) + return 0; + if (!constraint) + return 1; + if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) + return 0; + return 1; +} + +/* Check the halt state STATE match the current context. + Return 0 if not match, if the node, STATE has, is a halt node and + match the context, return the node. */ + +static int +check_halt_state_context (mctx, state, idx) + const re_match_context_t *mctx; + const re_dfastate_t *state; + int idx; +{ + int i; + unsigned int context; +#ifdef DEBUG + assert (state->halt); +#endif + context = re_string_context_at (&mctx->input, idx, mctx->eflags); + for (i = 0; i < state->nodes.nelem; ++i) + if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) + return state->nodes.elems[i]; + return 0; +} + +/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA + corresponding to the DFA). + Return the destination node, and update EPS_VIA_NODES, return -1 in case + of errors. */ + +static int +proceed_next_node (mctx, nregs, regs, pidx, node, eps_via_nodes, fs) + const re_match_context_t *mctx; + regmatch_t *regs; + int nregs, *pidx, node; + re_node_set *eps_via_nodes; + struct re_fail_stack_t *fs; +{ + re_dfa_t *const dfa = mctx->dfa; + int i, err, dest_node; + dest_node = -1; + if (IS_EPSILON_NODE (dfa->nodes[node].type)) + { + re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; + re_node_set *edests = &dfa->edests[node]; + int dest_node; + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -2; + /* Pick up a valid destination, or return -1 if none is found. */ + for (dest_node = -1, i = 0; i < edests->nelem; ++i) + { + int candidate = edests->elems[i]; + if (!re_node_set_contains (cur_nodes, candidate)) + continue; + if (dest_node == -1) + dest_node = candidate; + + else + { + /* In order to avoid infinite loop like "(a*)*", return the second + epsilon-transition if the first was already considered. */ + if (re_node_set_contains (eps_via_nodes, dest_node)) + return candidate; + + /* Otherwise, push the second epsilon-transition on the fail stack. */ + else if (fs != NULL + && push_fail_stack (fs, *pidx, candidate, nregs, regs, + eps_via_nodes)) + return -2; + + /* We know we are going to exit. */ + break; + } + } + return dest_node; + } + else + { + int naccepted = 0; + re_token_type_t type = dfa->nodes[node].type; + +#ifdef RE_ENABLE_I18N + if (dfa->nodes[node].accept_mb) + naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); + else +#endif /* RE_ENABLE_I18N */ + if (type == OP_BACK_REF) + { + int subexp_idx = dfa->nodes[node].opr.idx + 1; + naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; + if (fs != NULL) + { + if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) + return -1; + else if (naccepted) + { + char *buf = (char *) re_string_get_buffer (&mctx->input); + if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, + naccepted) != 0) + return -1; + } + } + + if (naccepted == 0) + { + err = re_node_set_insert (eps_via_nodes, node); + if (BE (err < 0, 0)) + return -2; + dest_node = dfa->edests[node].elems[0]; + if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node)) + return dest_node; + } + } + + if (naccepted != 0 + || check_node_accept (mctx, dfa->nodes + node, *pidx)) + { + dest_node = dfa->nexts[node]; + *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; + if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL + || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, + dest_node))) + return -1; + re_node_set_empty (eps_via_nodes); + return dest_node; + } + } + return -1; +} + +static reg_errcode_t +push_fail_stack (fs, str_idx, dest_node, nregs, regs, eps_via_nodes) + struct re_fail_stack_t *fs; + int str_idx, dest_node, nregs; + regmatch_t *regs; + re_node_set *eps_via_nodes; +{ + reg_errcode_t err; + int num = fs->num++; + if (fs->num == fs->alloc) + { + struct re_fail_stack_ent_t *new_array; + new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) + * fs->alloc * 2)); + if (new_array == NULL) + return REG_ESPACE; + fs->alloc *= 2; + fs->stack = new_array; + } + fs->stack[num].idx = str_idx; + fs->stack[num].node = dest_node; + fs->stack[num].regs = re_malloc (regmatch_t, nregs); + if (fs->stack[num].regs == NULL) + return REG_ESPACE; + memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); + err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); + return err; +} + +static int +pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes) + struct re_fail_stack_t *fs; + int *pidx, nregs; + regmatch_t *regs; + re_node_set *eps_via_nodes; +{ + int num = --fs->num; + assert (num >= 0); + *pidx = fs->stack[num].idx; + memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); + re_node_set_free (eps_via_nodes); + re_free (fs->stack[num].regs); + *eps_via_nodes = fs->stack[num].eps_via_nodes; + return fs->stack[num].node; +} + +/* Set the positions where the subexpressions are starts/ends to registers + PMATCH. + Note: We assume that pmatch[0] is already set, and + pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ + +static reg_errcode_t +set_regs (preg, mctx, nmatch, pmatch, fl_backtrack) + const regex_t *preg; + const re_match_context_t *mctx; + size_t nmatch; + regmatch_t *pmatch; + int fl_backtrack; +{ + re_dfa_t *dfa = (re_dfa_t *) preg->buffer; + int idx, cur_node; + re_node_set eps_via_nodes; + struct re_fail_stack_t *fs; + struct re_fail_stack_t fs_body = { 0, 2, NULL }; + regmatch_t *prev_idx_match; + +#ifdef DEBUG + assert (nmatch > 1); + assert (mctx->state_log != NULL); +#endif + if (fl_backtrack) + { + fs = &fs_body; + fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); + if (fs->stack == NULL) + return REG_ESPACE; + } + else + fs = NULL; + + cur_node = dfa->init_node; + re_node_set_init_empty (&eps_via_nodes); + + prev_idx_match = (regmatch_t *) alloca (sizeof (regmatch_t) * nmatch); + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + + for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) + { + update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); + + if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) + { + int reg_idx; + if (fs) + { + for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) + if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) + break; + if (reg_idx == nmatch) + { + re_node_set_free (&eps_via_nodes); + return free_fail_stack_return (fs); + } + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + } + else + { + re_node_set_free (&eps_via_nodes); + return REG_NOERROR; + } + } + + /* Proceed to next node. */ + cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, + &eps_via_nodes, fs); + + if (BE (cur_node < 0, 0)) + { + if (BE (cur_node == -2, 0)) + { + re_node_set_free (&eps_via_nodes); + free_fail_stack_return (fs); + return REG_ESPACE; + } + if (fs) + cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, + &eps_via_nodes); + else + { + re_node_set_free (&eps_via_nodes); + return REG_NOMATCH; + } + } + } + re_node_set_free (&eps_via_nodes); + return free_fail_stack_return (fs); +} + +static reg_errcode_t +free_fail_stack_return (fs) + struct re_fail_stack_t *fs; +{ + if (fs) + { + int fs_idx; + for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) + { + re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); + re_free (fs->stack[fs_idx].regs); + } + re_free (fs->stack); + } + return REG_NOERROR; +} + +static void +update_regs (dfa, pmatch, prev_idx_match, cur_node, cur_idx, nmatch) + re_dfa_t *dfa; + regmatch_t *pmatch, *prev_idx_match; + int cur_node, cur_idx, nmatch; +{ + int type = dfa->nodes[cur_node].type; + if (type == OP_OPEN_SUBEXP) + { + int reg_num = dfa->nodes[cur_node].opr.idx + 1; + + /* We are at the first node of this sub expression. */ + if (reg_num < nmatch) + { + pmatch[reg_num].rm_so = cur_idx; + pmatch[reg_num].rm_eo = -1; + } + } + else if (type == OP_CLOSE_SUBEXP) + { + int reg_num = dfa->nodes[cur_node].opr.idx + 1; + if (reg_num < nmatch) + { + /* We are at the last node of this sub expression. */ + if (pmatch[reg_num].rm_so < cur_idx) + { + pmatch[reg_num].rm_eo = cur_idx; + /* This is a non-empty match or we are not inside an optional + subexpression. Accept this right away. */ + memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); + } + else + { + if (dfa->nodes[cur_node].opt_subexp + && prev_idx_match[reg_num].rm_so != -1) + /* We transited through an empty match for an optional + subexpression, like (a?)*, and this is not the subexp's + first match. Copy back the old content of the registers + so that matches of an inner subexpression are undone as + well, like in ((a?))*. */ + memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); + else + /* We completed a subexpression, but it may be part of + an optional one, so do not update PREV_IDX_MATCH. */ + pmatch[reg_num].rm_eo = cur_idx; + } + } + } +} + +/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 + and sift the nodes in each states according to the following rules. + Updated state_log will be wrote to STATE_LOG. + + Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... + 1. When STR_IDX == MATCH_LAST(the last index in the state_log): + If `a' isn't the LAST_NODE and `a' can't epsilon transit to + the LAST_NODE, we throw away the node `a'. + 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts + string `s' and transit to `b': + i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw + away the node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is + thrown away, we throw away the node `a'. + 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': + i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the + node `a'. + ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, + we throw away the node `a'. */ + +#define STATE_NODE_CONTAINS(state,node) \ + ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) + +static reg_errcode_t +sift_states_backward (mctx, sctx) + re_match_context_t *mctx; + re_sift_context_t *sctx; +{ + reg_errcode_t err; + int null_cnt = 0; + int str_idx = sctx->last_str_idx; + re_node_set cur_dest; + +#ifdef DEBUG + assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); +#endif + + /* Build sifted state_log[str_idx]. It has the nodes which can epsilon + transit to the last_node and the last_node itself. */ + err = re_node_set_init_1 (&cur_dest, sctx->last_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* Then check each states in the state_log. */ + while (str_idx > 0) + { + /* Update counters. */ + null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; + if (null_cnt > mctx->max_mb_elem_len) + { + memset (sctx->sifted_states, '\0', + sizeof (re_dfastate_t *) * str_idx); + re_node_set_free (&cur_dest); + return REG_NOERROR; + } + re_node_set_empty (&cur_dest); + --str_idx; + + if (mctx->state_log[str_idx]) + { + err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + + /* Add all the nodes which satisfy the following conditions: + - It can epsilon transit to a node in CUR_DEST. + - It is in CUR_SRC. + And update state_log. */ + err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + err = REG_NOERROR; + free_return: + re_node_set_free (&cur_dest); + return err; +} + +static reg_errcode_t +build_sifted_states (mctx, sctx, str_idx, cur_dest) + re_match_context_t *mctx; + re_sift_context_t *sctx; + int str_idx; + re_node_set *cur_dest; +{ + re_dfa_t *const dfa = mctx->dfa; + re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; + int i; + + /* Then build the next sifted state. + We build the next sifted state on `cur_dest', and update + `sifted_states[str_idx]' with `cur_dest'. + Note: + `cur_dest' is the sifted state from `state_log[str_idx + 1]'. + `cur_src' points the node_set of the old `state_log[str_idx]' + (with the epsilon nodes pre-filtered out). */ + for (i = 0; i < cur_src->nelem; i++) + { + int prev_node = cur_src->elems[i]; + int naccepted = 0; + int ret; + +#ifdef DEBUG + re_token_type_t type = dfa->nodes[prev_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[prev_node].accept_mb) + naccepted = sift_states_iter_mb (mctx, sctx, prev_node, + str_idx, sctx->last_str_idx); +#endif /* RE_ENABLE_I18N */ + + /* We don't check backreferences here. + See update_cur_sifted_state(). */ + if (!naccepted + && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) + && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], + dfa->nexts[prev_node])) + naccepted = 1; + + if (naccepted == 0) + continue; + + if (sctx->limits.nelem) + { + int to_idx = str_idx + naccepted; + if (check_dst_limits (mctx, &sctx->limits, + dfa->nexts[prev_node], to_idx, + prev_node, str_idx)) + continue; + } + ret = re_node_set_insert (cur_dest, prev_node); + if (BE (ret == -1, 0)) + return REG_ESPACE; + } + + return REG_NOERROR; +} + +/* Helper functions. */ + +static reg_errcode_t +clean_state_log_if_needed (mctx, next_state_log_idx) + re_match_context_t *mctx; + int next_state_log_idx; +{ + int top = mctx->state_log_top; + + if (next_state_log_idx >= mctx->input.bufs_len + || (next_state_log_idx >= mctx->input.valid_len + && mctx->input.valid_len < mctx->input.len)) + { + reg_errcode_t err; + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (top < next_state_log_idx) + { + memset (mctx->state_log + top + 1, '\0', + sizeof (re_dfastate_t *) * (next_state_log_idx - top)); + mctx->state_log_top = next_state_log_idx; + } + return REG_NOERROR; +} + +static reg_errcode_t +merge_state_array (dfa, dst, src, num) + re_dfa_t *dfa; + re_dfastate_t **dst; + re_dfastate_t **src; + int num; +{ + int st_idx; + reg_errcode_t err; + for (st_idx = 0; st_idx < num; ++st_idx) + { + if (dst[st_idx] == NULL) + dst[st_idx] = src[st_idx]; + else if (src[st_idx] != NULL) + { + re_node_set merged_set; + err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, + &src[st_idx]->nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); + re_node_set_free (&merged_set); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +static reg_errcode_t +update_cur_sifted_state (mctx, sctx, str_idx, dest_nodes) + re_match_context_t *mctx; + re_sift_context_t *sctx; + int str_idx; + re_node_set *dest_nodes; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + const re_node_set *candidates; + candidates = ((mctx->state_log[str_idx] == NULL) ? NULL + : &mctx->state_log[str_idx]->nodes); + + if (dest_nodes->nelem == 0) + sctx->sifted_states[str_idx] = NULL; + else + { + if (candidates) + { + /* At first, add the nodes which can epsilon transit to a node in + DEST_NODE. */ + err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + + /* Then, check the limitations in the current sift_context. */ + if (sctx->limits.nelem) + { + err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, + mctx->bkref_ents, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + + sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (candidates && mctx->state_log[str_idx]->has_backref) + { + err = sift_states_bkref (mctx, sctx, str_idx, candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} + +static reg_errcode_t +add_epsilon_src_nodes (dfa, dest_nodes, candidates) + re_dfa_t *dfa; + re_node_set *dest_nodes; + const re_node_set *candidates; +{ + reg_errcode_t err = REG_NOERROR; + int i; + + re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + + if (!state->inveclosure.alloc) + { + err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return REG_ESPACE; + for (i = 0; i < dest_nodes->nelem; i++) + re_node_set_merge (&state->inveclosure, + dfa->inveclosures + dest_nodes->elems[i]); + } + return re_node_set_add_intersect (dest_nodes, candidates, + &state->inveclosure); +} + +static reg_errcode_t +sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates) + re_dfa_t *dfa; + int node; + re_node_set *dest_nodes; + const re_node_set *candidates; +{ + int ecl_idx; + reg_errcode_t err; + re_node_set *inv_eclosure = dfa->inveclosures + node; + re_node_set except_nodes; + re_node_set_init_empty (&except_nodes); + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (cur_node == node) + continue; + if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) + { + int edst1 = dfa->edests[cur_node].elems[0]; + int edst2 = ((dfa->edests[cur_node].nelem > 1) + ? dfa->edests[cur_node].elems[1] : -1); + if ((!re_node_set_contains (inv_eclosure, edst1) + && re_node_set_contains (dest_nodes, edst1)) + || (edst2 > 0 + && !re_node_set_contains (inv_eclosure, edst2) + && re_node_set_contains (dest_nodes, edst2))) + { + err = re_node_set_add_intersect (&except_nodes, candidates, + dfa->inveclosures + cur_node); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&except_nodes); + return err; + } + } + } + } + for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) + { + int cur_node = inv_eclosure->elems[ecl_idx]; + if (!re_node_set_contains (&except_nodes, cur_node)) + { + int idx = re_node_set_contains (dest_nodes, cur_node) - 1; + re_node_set_remove_at (dest_nodes, idx); + } + } + re_node_set_free (&except_nodes); + return REG_NOERROR; +} + +static int +check_dst_limits (mctx, limits, dst_node, dst_idx, src_node, src_idx) + re_match_context_t *mctx; + re_node_set *limits; + int dst_node, dst_idx, src_node, src_idx; +{ + re_dfa_t *const dfa = mctx->dfa; + int lim_idx, src_pos, dst_pos; + + int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); + int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = mctx->bkref_ents + limits->elems[lim_idx]; + subexp_idx = dfa->nodes[ent->node].opr.idx; + + dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, dst_node, dst_idx, + dst_bkref_idx); + src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], + subexp_idx, src_node, src_idx, + src_bkref_idx); + + /* In case of: + <src> <dst> ( <subexp> ) + ( <subexp> ) <src> <dst> + ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */ + if (src_pos == dst_pos) + continue; /* This is unrelated limitation. */ + else + return 1; + } + return 0; +} + +static int +check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, from_node, bkref_idx) + re_match_context_t *mctx; + int boundaries, subexp_idx, from_node, bkref_idx; +{ + re_dfa_t *const dfa = mctx->dfa; + re_node_set *eclosures = dfa->eclosures + from_node; + int node_idx; + + /* Else, we are on the boundary: examine the nodes on the epsilon + closure. */ + for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) + { + int node = eclosures->elems[node_idx]; + switch (dfa->nodes[node].type) + { + case OP_BACK_REF: + if (bkref_idx != -1) + { + struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; + do + { + int dst, cpos; + + if (ent->node != node) + continue; + + if (subexp_idx <= 8 * sizeof (ent->eps_reachable_subexps_map) + && !(ent->eps_reachable_subexps_map & (1 << subexp_idx))) + continue; + + /* Recurse trying to reach the OP_OPEN_SUBEXP and + OP_CLOSE_SUBEXP cases below. But, if the + destination node is the same node as the source + node, don't recurse because it would cause an + infinite loop: a regex that exhibits this behavior + is ()\1*\1* */ + dst = dfa->edests[node].elems[0]; + if (dst == from_node) + { + if (boundaries & 1) + return -1; + else /* if (boundaries & 2) */ + return 0; + } + + cpos = + check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + dst, bkref_idx); + if (cpos == -1 /* && (boundaries & 1) */) + return -1; + if (cpos == 0 && (boundaries & 2)) + return 0; + + ent->eps_reachable_subexps_map &= ~(1 << subexp_idx); + } + while (ent++->more); + } + break; + + case OP_OPEN_SUBEXP: + if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) + return -1; + break; + + case OP_CLOSE_SUBEXP: + if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) + return 0; + break; + + default: + break; + } + } + + return (boundaries & 2) ? 1 : 0; +} + +static int +check_dst_limits_calc_pos (mctx, limit, subexp_idx, from_node, str_idx, bkref_idx) + re_match_context_t *mctx; + int limit, subexp_idx, from_node, str_idx, bkref_idx; +{ + struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; + int boundaries; + + /* If we are outside the range of the subexpression, return -1 or 1. */ + if (str_idx < lim->subexp_from) + return -1; + + if (lim->subexp_to < str_idx) + return 1; + + /* If we are within the subexpression, return 0. */ + boundaries = (str_idx == lim->subexp_from); + boundaries |= (str_idx == lim->subexp_to) << 1; + if (boundaries == 0) + return 0; + + /* Else, examine epsilon closure. */ + return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, + from_node, bkref_idx); +} + +/* Check the limitations of sub expressions LIMITS, and remove the nodes + which are against limitations from DEST_NODES. */ + +static reg_errcode_t +check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx) + re_dfa_t *dfa; + re_node_set *dest_nodes; + const re_node_set *candidates; + re_node_set *limits; + struct re_backref_cache_entry *bkref_ents; + int str_idx; +{ + reg_errcode_t err; + int node_idx, lim_idx; + + for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) + { + int subexp_idx; + struct re_backref_cache_entry *ent; + ent = bkref_ents + limits->elems[lim_idx]; + + if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) + continue; /* This is unrelated limitation. */ + + subexp_idx = dfa->nodes[ent->node].opr.idx; + if (ent->subexp_to == str_idx) + { + int ops_node = -1; + int cls_node = -1; + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_OPEN_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + ops_node = node; + else if (type == OP_CLOSE_SUBEXP + && subexp_idx == dfa->nodes[node].opr.idx) + cls_node = node; + } + + /* Check the limitation of the open subexpression. */ + /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ + if (ops_node >= 0) + { + err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + + /* Check the limitation of the close subexpression. */ + if (cls_node >= 0) + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + if (!re_node_set_contains (dfa->inveclosures + node, + cls_node) + && !re_node_set_contains (dfa->eclosures + node, + cls_node)) + { + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + --node_idx; + } + } + } + else /* (ent->subexp_to != str_idx) */ + { + for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) + { + int node = dest_nodes->elems[node_idx]; + re_token_type_t type = dfa->nodes[node].type; + if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) + { + if (subexp_idx != dfa->nodes[node].opr.idx) + continue; + /* It is against this limitation. + Remove it form the current sifted state. */ + err = sub_epsilon_src_nodes (dfa, node, dest_nodes, + candidates); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + } + } + return REG_NOERROR; +} + +static reg_errcode_t +sift_states_bkref (mctx, sctx, str_idx, candidates) + re_match_context_t *mctx; + re_sift_context_t *sctx; + int str_idx; + const re_node_set *candidates; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int node_idx, node; + re_sift_context_t local_sctx; + int first_idx = search_cur_bkref_entry (mctx, str_idx); + + if (first_idx == -1) + return REG_NOERROR; + + local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ + + for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) + { + int enabled_idx; + re_token_type_t type; + struct re_backref_cache_entry *entry; + node = candidates->elems[node_idx]; + type = dfa->nodes[node].type; + /* Avoid infinite loop for the REs like "()\1+". */ + if (node == sctx->last_node && str_idx == sctx->last_str_idx) + continue; + if (type != OP_BACK_REF) + continue; + + entry = mctx->bkref_ents + first_idx; + enabled_idx = first_idx; + do + { + int subexp_len, to_idx, dst_node; + re_dfastate_t *cur_state; + + if (entry->node != node) + continue; + subexp_len = entry->subexp_to - entry->subexp_from; + to_idx = str_idx + subexp_len; + dst_node = (subexp_len ? dfa->nexts[node] + : dfa->edests[node].elems[0]); + + if (to_idx > sctx->last_str_idx + || sctx->sifted_states[to_idx] == NULL + || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) + || check_dst_limits (mctx, &sctx->limits, node, + str_idx, dst_node, to_idx)) + continue; + + if (local_sctx.sifted_states == NULL) + { + local_sctx = *sctx; + err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.last_node = node; + local_sctx.last_str_idx = str_idx; + err = re_node_set_insert (&local_sctx.limits, enabled_idx); + if (BE (err < 0, 0)) + { + err = REG_ESPACE; + goto free_return; + } + cur_state = local_sctx.sifted_states[str_idx]; + err = sift_states_backward (mctx, &local_sctx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + if (sctx->limited_states != NULL) + { + err = merge_state_array (dfa, sctx->limited_states, + local_sctx.sifted_states, + str_idx + 1); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + local_sctx.sifted_states[str_idx] = cur_state; + re_node_set_remove (&local_sctx.limits, enabled_idx); + + /* mctx->bkref_ents may have changed, reload the pointer. */ + entry = mctx->bkref_ents + enabled_idx; + } + while (enabled_idx++, entry++->more); + } + err = REG_NOERROR; + free_return: + if (local_sctx.sifted_states != NULL) + { + re_node_set_free (&local_sctx.limits); + } + + return err; +} + + +#ifdef RE_ENABLE_I18N +static int +sift_states_iter_mb (mctx, sctx, node_idx, str_idx, max_str_idx) + const re_match_context_t *mctx; + re_sift_context_t *sctx; + int node_idx, str_idx, max_str_idx; +{ + re_dfa_t *const dfa = mctx->dfa; + int naccepted; + /* Check the node can accept `multi byte'. */ + naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); + if (naccepted > 0 && str_idx + naccepted <= max_str_idx && + !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], + dfa->nexts[node_idx])) + /* The node can't accept the `multi byte', or the + destination was already thrown away, then the node + could't accept the current input `multi byte'. */ + naccepted = 0; + /* Otherwise, it is sure that the node could accept + `naccepted' bytes input. */ + return naccepted; +} +#endif /* RE_ENABLE_I18N */ + + +/* Functions for state transition. */ + +/* Return the next state to which the current state STATE will transit by + accepting the current input byte, and update STATE_LOG if necessary. + If STATE can accept a multibyte char/collating element/back reference + update the destination of STATE_LOG. */ + +static re_dfastate_t * +transit_state (err, mctx, state) + reg_errcode_t *err; + re_match_context_t *mctx; + re_dfastate_t *state; +{ + re_dfastate_t **trtable; + unsigned char ch; + +#ifdef RE_ENABLE_I18N + /* If the current state can accept multibyte. */ + if (BE (state->accept_mb, 0)) + { + *err = transit_state_mb (mctx, state); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } +#endif /* RE_ENABLE_I18N */ + + /* Then decide the next state with the single byte. */ +#if 0 + if (0) + /* don't use transition table */ + return transit_state_sb (err, mctx, state); +#endif + + /* Use transition table */ + ch = re_string_fetch_byte (&mctx->input); + for (;;) + { + trtable = state->trtable; + if (BE (trtable != NULL, 1)) + return trtable[ch]; + + trtable = state->word_trtable; + if (BE (trtable != NULL, 1)) + { + unsigned int context; + context + = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + if (IS_WORD_CONTEXT (context)) + return trtable[ch + SBC_MAX]; + else + return trtable[ch]; + } + + if (!build_trtable (mctx->dfa, state)) + { + *err = REG_ESPACE; + return NULL; + } + + /* Retry, we now have a transition table. */ + } +} + +/* Update the state_log if we need */ +re_dfastate_t * +merge_state_with_log (err, mctx, next_state) + reg_errcode_t *err; + re_match_context_t *mctx; + re_dfastate_t *next_state; +{ + re_dfa_t *const dfa = mctx->dfa; + int cur_idx = re_string_cur_idx (&mctx->input); + + if (cur_idx > mctx->state_log_top) + { + mctx->state_log[cur_idx] = next_state; + mctx->state_log_top = cur_idx; + } + else if (mctx->state_log[cur_idx] == 0) + { + mctx->state_log[cur_idx] = next_state; + } + else + { + re_dfastate_t *pstate; + unsigned int context; + re_node_set next_nodes, *log_nodes, *table_nodes = NULL; + /* If (state_log[cur_idx] != 0), it implies that cur_idx is + the destination of a multibyte char/collating element/ + back reference. Then the next state is the union set of + these destinations and the results of the transition table. */ + pstate = mctx->state_log[cur_idx]; + log_nodes = pstate->entrance_nodes; + if (next_state != NULL) + { + table_nodes = next_state->entrance_nodes; + *err = re_node_set_init_union (&next_nodes, table_nodes, + log_nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + } + else + next_nodes = *log_nodes; + /* Note: We already add the nodes of the initial state, + then we don't need to add them here. */ + + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input) - 1, + mctx->eflags); + next_state = mctx->state_log[cur_idx] + = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + if (table_nodes != NULL) + re_node_set_free (&next_nodes); + } + + if (BE (dfa->nbackref, 0) && next_state != NULL) + { + /* Check OP_OPEN_SUBEXP in the current state in case that we use them + later. We must check them here, since the back references in the + next state might use them. */ + *err = check_subexp_matching_top (mctx, &next_state->nodes, + cur_idx); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + + /* If the next state has back references. */ + if (next_state->has_backref) + { + *err = transit_state_bkref (mctx, &next_state->nodes); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + next_state = mctx->state_log[cur_idx]; + } + } + + return next_state; +} + +/* Skip bytes in the input that correspond to part of a + multi-byte match, then look in the log for a state + from which to restart matching. */ +re_dfastate_t * +find_recover_state (err, mctx) + reg_errcode_t *err; + re_match_context_t *mctx; +{ + re_dfastate_t *cur_state = NULL; + do + { + int max = mctx->state_log_top; + int cur_str_idx = re_string_cur_idx (&mctx->input); + + do + { + if (++cur_str_idx > max) + return NULL; + re_string_skip_bytes (&mctx->input, 1); + } + while (mctx->state_log[cur_str_idx] == NULL); + + cur_state = merge_state_with_log (err, mctx, NULL); + } + while (err == REG_NOERROR && cur_state == NULL); + return cur_state; +} + +/* Helper functions for transit_state. */ + +/* From the node set CUR_NODES, pick up the nodes whose types are + OP_OPEN_SUBEXP and which have corresponding back references in the regular + expression. And register them to use them later for evaluating the + correspoding back references. */ + +static reg_errcode_t +check_subexp_matching_top (mctx, cur_nodes, str_idx) + re_match_context_t *mctx; + re_node_set *cur_nodes; + int str_idx; +{ + re_dfa_t *const dfa = mctx->dfa; + int node_idx; + reg_errcode_t err; + + /* TODO: This isn't efficient. + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) + { + int node = cur_nodes->elems[node_idx]; + if (dfa->nodes[node].type == OP_OPEN_SUBEXP + && dfa->nodes[node].opr.idx < (8 * sizeof (dfa->used_bkref_map)) + && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx)) + { + err = match_ctx_add_subtop (mctx, node, str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + } + } + return REG_NOERROR; +} + +#if 0 +/* Return the next state to which the current state STATE will transit by + accepting the current input byte. */ + +static re_dfastate_t * +transit_state_sb (err, mctx, state) + reg_errcode_t *err; + re_match_context_t *mctx; + re_dfastate_t *state; +{ + re_dfa_t *const dfa = mctx->dfa; + re_node_set next_nodes; + re_dfastate_t *next_state; + int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); + unsigned int context; + + *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); + if (BE (*err != REG_NOERROR, 0)) + return NULL; + for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) + { + int cur_node = state->nodes.elems[node_cnt]; + if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) + { + *err = re_node_set_merge (&next_nodes, + dfa->eclosures + dfa->nexts[cur_node]); + if (BE (*err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return NULL; + } + } + } + context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); + next_state = re_acquire_state_context (err, dfa, &next_nodes, context); + /* We don't need to check errors here, since the return value of + this function is next_state and ERR is already set. */ + + re_node_set_free (&next_nodes); + re_string_skip_bytes (&mctx->input, 1); + return next_state; +} +#endif + +#ifdef RE_ENABLE_I18N +static reg_errcode_t +transit_state_mb (mctx, pstate) + re_match_context_t *mctx; + re_dfastate_t *pstate; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int i; + + for (i = 0; i < pstate->nodes.nelem; ++i) + { + re_node_set dest_nodes, *new_nodes; + int cur_node_idx = pstate->nodes.elems[i]; + int naccepted, dest_idx; + unsigned int context; + re_dfastate_t *dest_state; + + if (!dfa->nodes[cur_node_idx].accept_mb) + continue; + + if (dfa->nodes[cur_node_idx].constraint) + { + context = re_string_context_at (&mctx->input, + re_string_cur_idx (&mctx->input), + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, + context)) + continue; + } + + /* How many bytes the node can accept? */ + naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, + re_string_cur_idx (&mctx->input)); + if (naccepted == 0) + continue; + + /* The node can accepts `naccepted' bytes. */ + dest_idx = re_string_cur_idx (&mctx->input) + naccepted; + mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted + : mctx->max_mb_elem_len); + err = clean_state_log_if_needed (mctx, dest_idx); + if (BE (err != REG_NOERROR, 0)) + return err; +#ifdef DEBUG + assert (dfa->nexts[cur_node_idx] != -1); +#endif + new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; + + dest_state = mctx->state_log[dest_idx]; + if (dest_state == NULL) + dest_nodes = *new_nodes; + else + { + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, new_nodes); + if (BE (err != REG_NOERROR, 0)) + return err; + } + context = re_string_context_at (&mctx->input, dest_idx - 1, mctx->eflags); + mctx->state_log[dest_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + if (dest_state != NULL) + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) + return err; + } + return REG_NOERROR; +} +#endif /* RE_ENABLE_I18N */ + +static reg_errcode_t +transit_state_bkref (mctx, nodes) + re_match_context_t *mctx; + const re_node_set *nodes; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int i; + int cur_str_idx = re_string_cur_idx (&mctx->input); + + for (i = 0; i < nodes->nelem; ++i) + { + int dest_str_idx, prev_nelem, bkc_idx; + int node_idx = nodes->elems[i]; + unsigned int context; + const re_token_t *node = dfa->nodes + node_idx; + re_node_set *new_dest_nodes; + + /* Check whether `node' is a backreference or not. */ + if (node->type != OP_BACK_REF) + continue; + + if (node->constraint) + { + context = re_string_context_at (&mctx->input, cur_str_idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + continue; + } + + /* `node' is a backreference. + Check the substring which the substring matched. */ + bkc_idx = mctx->nbkref_ents; + err = get_subexp (mctx, node_idx, cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + + /* And add the epsilon closures (which is `new_dest_nodes') of + the backreference to appropriate state_log. */ +#ifdef DEBUG + assert (dfa->nexts[node_idx] != -1); +#endif + for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) + { + int subexp_len; + re_dfastate_t *dest_state; + struct re_backref_cache_entry *bkref_ent; + bkref_ent = mctx->bkref_ents + bkc_idx; + if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) + continue; + subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; + new_dest_nodes = (subexp_len == 0 + ? dfa->eclosures + dfa->edests[node_idx].elems[0] + : dfa->eclosures + dfa->nexts[node_idx]); + dest_str_idx = (cur_str_idx + bkref_ent->subexp_to + - bkref_ent->subexp_from); + context = re_string_context_at (&mctx->input, dest_str_idx - 1, + mctx->eflags); + dest_state = mctx->state_log[dest_str_idx]; + prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 + : mctx->state_log[cur_str_idx]->nodes.nelem); + /* Add `new_dest_node' to state_log. */ + if (dest_state == NULL) + { + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, new_dest_nodes, + context); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + else + { + re_node_set dest_nodes; + err = re_node_set_init_union (&dest_nodes, + dest_state->entrance_nodes, + new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&dest_nodes); + goto free_return; + } + mctx->state_log[dest_str_idx] + = re_acquire_state_context (&err, dfa, &dest_nodes, context); + re_node_set_free (&dest_nodes); + if (BE (mctx->state_log[dest_str_idx] == NULL + && err != REG_NOERROR, 0)) + goto free_return; + } + /* We need to check recursively if the backreference can epsilon + transit. */ + if (subexp_len == 0 + && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) + { + err = check_subexp_matching_top (mctx, new_dest_nodes, + cur_str_idx); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + err = transit_state_bkref (mctx, new_dest_nodes); + if (BE (err != REG_NOERROR, 0)) + goto free_return; + } + } + } + err = REG_NOERROR; + free_return: + return err; +} + +/* Enumerate all the candidates which the backreference BKREF_NODE can match + at BKREF_STR_IDX, and register them by match_ctx_add_entry(). + Note that we might collect inappropriate candidates here. + However, the cost of checking them strictly here is too high, then we + delay these checking for prune_impossible_nodes(). */ + +static reg_errcode_t +get_subexp (mctx, bkref_node, bkref_str_idx) + re_match_context_t *mctx; + int bkref_node, bkref_str_idx; +{ + re_dfa_t *const dfa = mctx->dfa; + int subexp_num, sub_top_idx; + const char *buf = (const char *) re_string_get_buffer (&mctx->input); + /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ + int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); + if (cache_idx != -1) + { + const struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx; + do + if (entry->node == bkref_node) + return REG_NOERROR; /* We already checked it. */ + while (entry++->more); + } + + subexp_num = dfa->nodes[bkref_node].opr.idx; + + /* For each sub expression */ + for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) + { + reg_errcode_t err; + re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; + re_sub_match_last_t *sub_last; + int sub_last_idx, sl_str, bkref_str_off; + + if (dfa->nodes[sub_top->node].opr.idx != subexp_num) + continue; /* It isn't related. */ + + sl_str = sub_top->str_idx; + bkref_str_off = bkref_str_idx; + /* At first, check the last node of sub expressions we already + evaluated. */ + for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) + { + int sl_str_diff; + sub_last = sub_top->lasts[sub_last_idx]; + sl_str_diff = sub_last->str_idx - sl_str; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_diff > 0) + { + if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) + { + /* Not enough chars for a successful match. */ + if (bkref_str_off + sl_str_diff > mctx->input.len) + break; + + err = clean_state_log_if_needed (mctx, + bkref_str_off + + sl_str_diff); + if (BE (err != REG_NOERROR, 0)) + return err; + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) + break; /* We don't need to search this sub expression any more. */ + } + bkref_str_off += sl_str_diff; + sl_str += sl_str_diff; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + + /* Reload buf, since the preceding call might have reallocated + the buffer. */ + buf = (const char *) re_string_get_buffer (&mctx->input); + + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + } + + if (sub_last_idx < sub_top->nlasts) + continue; + if (sub_last_idx > 0) + ++sl_str; + /* Then, search for the other last nodes of the sub expression. */ + for (; sl_str <= bkref_str_idx; ++sl_str) + { + int cls_node, sl_str_off; + const re_node_set *nodes; + sl_str_off = sl_str - sub_top->str_idx; + /* The matched string by the sub expression match with the substring + at the back reference? */ + if (sl_str_off > 0) + { + if (BE (bkref_str_off >= mctx->input.valid_len, 0)) + { + /* If we are at the end of the input, we cannot match. */ + if (bkref_str_off >= mctx->input.len) + break; + + err = extend_buffers (mctx); + if (BE (err != REG_NOERROR, 0)) + return err; + + buf = (const char *) re_string_get_buffer (&mctx->input); + } + if (buf [bkref_str_off++] != buf[sl_str - 1]) + break; /* We don't need to search this sub expression + any more. */ + } + if (mctx->state_log[sl_str] == NULL) + continue; + /* Does this state have a ')' of the sub expression? */ + nodes = &mctx->state_log[sl_str]->nodes; + cls_node = find_subexp_node (dfa, nodes, subexp_num, OP_CLOSE_SUBEXP); + if (cls_node == -1) + continue; /* No. */ + if (sub_top->path == NULL) + { + sub_top->path = calloc (sizeof (state_array_t), + sl_str - sub_top->str_idx + 1); + if (sub_top->path == NULL) + return REG_ESPACE; + } + /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node + in the current context? */ + err = check_arrival (mctx, sub_top->path, sub_top->node, + sub_top->str_idx, cls_node, sl_str, OP_CLOSE_SUBEXP); + if (err == REG_NOMATCH) + continue; + if (BE (err != REG_NOERROR, 0)) + return err; + sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); + if (BE (sub_last == NULL, 0)) + return REG_ESPACE; + err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, + bkref_str_idx); + if (err == REG_NOMATCH) + continue; + } + } + return REG_NOERROR; +} + +/* Helper functions for get_subexp(). */ + +/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. + If it can arrive, register the sub expression expressed with SUB_TOP + and SUB_LAST. */ + +static reg_errcode_t +get_subexp_sub (mctx, sub_top, sub_last, bkref_node, bkref_str) + re_match_context_t *mctx; + const re_sub_match_top_t *sub_top; + re_sub_match_last_t *sub_last; + int bkref_node, bkref_str; +{ + reg_errcode_t err; + int to_idx; + /* Can the subexpression arrive the back reference? */ + err = check_arrival (mctx, &sub_last->path, sub_last->node, + sub_last->str_idx, bkref_node, bkref_str, OP_OPEN_SUBEXP); + if (err != REG_NOERROR) + return err; + err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, + sub_last->str_idx); + if (BE (err != REG_NOERROR, 0)) + return err; + to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; + return clean_state_log_if_needed (mctx, to_idx); +} + +/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. + Search '(' if FL_OPEN, or search ')' otherwise. + TODO: This function isn't efficient... + Because there might be more than one nodes whose types are + OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all + nodes. + E.g. RE: (a){2} */ + +static int +find_subexp_node (dfa, nodes, subexp_idx, type) + const re_dfa_t *dfa; + const re_node_set *nodes; + int subexp_idx, type; +{ + int cls_idx; + for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) + { + int cls_node = nodes->elems[cls_idx]; + const re_token_t *node = dfa->nodes + cls_node; + if (node->type == type + && node->opr.idx == subexp_idx) + return cls_node; + } + return -1; +} + +/* Check whether the node TOP_NODE at TOP_STR can arrive to the node + LAST_NODE at LAST_STR. We record the path onto PATH since it will be + heavily reused. + Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ + +static reg_errcode_t +check_arrival (mctx, path, top_node, top_str, last_node, last_str, + type) + re_match_context_t *mctx; + state_array_t *path; + int top_node, top_str, last_node, last_str, type; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int subexp_num, backup_cur_idx, str_idx, null_cnt; + re_dfastate_t *cur_state = NULL; + re_node_set *cur_nodes, next_nodes; + re_dfastate_t **backup_state_log; + unsigned int context; + + subexp_num = dfa->nodes[top_node].opr.idx; + /* Extend the buffer if we need. */ + if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) + { + re_dfastate_t **new_array; + int old_alloc = path->alloc; + path->alloc += last_str + mctx->max_mb_elem_len + 1; + new_array = re_realloc (path->array, re_dfastate_t *, path->alloc); + if (new_array == NULL) + { + path->alloc = old_alloc; + return REG_ESPACE; + } + path->array = new_array; + memset (new_array + old_alloc, '\0', + sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); + } + + str_idx = path->next_idx == 0 ? top_str : path->next_idx; + + /* Temporary modify MCTX. */ + backup_state_log = mctx->state_log; + backup_cur_idx = mctx->input.cur_idx; + mctx->state_log = path->array; + mctx->input.cur_idx = str_idx; + + /* Setup initial node set. */ + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + if (str_idx == top_str) + { + err = re_node_set_init_1 (&next_nodes, top_node); + if (BE (err != REG_NOERROR, 0)) + return err; + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + else + { + cur_state = mctx->state_log[str_idx]; + if (cur_state && cur_state->has_backref) + { + err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); + if (BE ( err != REG_NOERROR, 0)) + return err; + } + else + re_node_set_init_empty (&next_nodes); + } + if (str_idx == top_str || (cur_state && cur_state->has_backref)) + { + if (next_nodes.nelem) + { + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE ( err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + } + + for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) + { + re_node_set_empty (&next_nodes); + if (mctx->state_log[str_idx + 1]) + { + err = re_node_set_merge (&next_nodes, + &mctx->state_log[str_idx + 1]->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + if (cur_state) + { + err = check_arrival_add_next_nodes (mctx, str_idx, + &cur_state->non_eps_nodes, &next_nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + ++str_idx; + if (next_nodes.nelem) + { + err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + err = expand_bkref_cache (mctx, &next_nodes, str_idx, + subexp_num, type); + if (BE ( err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + } + context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); + cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); + if (BE (cur_state == NULL && err != REG_NOERROR, 0)) + { + re_node_set_free (&next_nodes); + return err; + } + mctx->state_log[str_idx] = cur_state; + null_cnt = cur_state == NULL ? null_cnt + 1 : 0; + } + re_node_set_free (&next_nodes); + cur_nodes = (mctx->state_log[last_str] == NULL ? NULL + : &mctx->state_log[last_str]->nodes); + path->next_idx = str_idx; + + /* Fix MCTX. */ + mctx->state_log = backup_state_log; + mctx->input.cur_idx = backup_cur_idx; + + /* Then check the current node set has the node LAST_NODE. */ + if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) + return REG_NOERROR; + + return REG_NOMATCH; +} + +/* Helper functions for check_arrival. */ + +/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them + to NEXT_NODES. + TODO: This function is similar to the functions transit_state*(), + however this function has many additional works. + Can't we unify them? */ + +static reg_errcode_t +check_arrival_add_next_nodes (mctx, str_idx, cur_nodes, next_nodes) + re_match_context_t *mctx; + int str_idx; + re_node_set *cur_nodes, *next_nodes; +{ + re_dfa_t *const dfa = mctx->dfa; + int result; + int cur_idx; + reg_errcode_t err; + re_node_set union_set; + re_node_set_init_empty (&union_set); + for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) + { + int naccepted = 0; + int cur_node = cur_nodes->elems[cur_idx]; +#ifdef DEBUG + re_token_type_t type = dfa->nodes[cur_node].type; + assert (!IS_EPSILON_NODE (type)); +#endif +#ifdef RE_ENABLE_I18N + /* If the node may accept `multi byte'. */ + if (dfa->nodes[cur_node].accept_mb) + { + naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, + str_idx); + if (naccepted > 1) + { + re_dfastate_t *dest_state; + int next_node = dfa->nexts[cur_node]; + int next_idx = str_idx + naccepted; + dest_state = mctx->state_log[next_idx]; + re_node_set_empty (&union_set); + if (dest_state) + { + err = re_node_set_merge (&union_set, &dest_state->nodes); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + result = re_node_set_insert (&union_set, next_node); + if (BE (result < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + mctx->state_log[next_idx] = re_acquire_state (&err, dfa, + &union_set); + if (BE (mctx->state_log[next_idx] == NULL + && err != REG_NOERROR, 0)) + { + re_node_set_free (&union_set); + return err; + } + } + } +#endif /* RE_ENABLE_I18N */ + if (naccepted + || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) + { + result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); + if (BE (result < 0, 0)) + { + re_node_set_free (&union_set); + return REG_ESPACE; + } + } + } + re_node_set_free (&union_set); + return REG_NOERROR; +} + +/* For all the nodes in CUR_NODES, add the epsilon closures of them to + CUR_NODES, however exclude the nodes which are: + - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. + - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. +*/ + +static reg_errcode_t +check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, type) + re_dfa_t *dfa; + re_node_set *cur_nodes; + int ex_subexp, type; +{ + reg_errcode_t err; + int idx, outside_node; + re_node_set new_nodes; +#ifdef DEBUG + assert (cur_nodes->nelem); +#endif + err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); + if (BE (err != REG_NOERROR, 0)) + return err; + /* Create a new node set NEW_NODES with the nodes which are epsilon + closures of the node in CUR_NODES. */ + + for (idx = 0; idx < cur_nodes->nelem; ++idx) + { + int cur_node = cur_nodes->elems[idx]; + re_node_set *eclosure = dfa->eclosures + cur_node; + outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); + if (outside_node == -1) + { + /* There are no problematic nodes, just merge them. */ + err = re_node_set_merge (&new_nodes, eclosure); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + else + { + /* There are problematic nodes, re-calculate incrementally. */ + err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + { + re_node_set_free (&new_nodes); + return err; + } + } + } + re_node_set_free (cur_nodes); + *cur_nodes = new_nodes; + return REG_NOERROR; +} + +/* Helper function for check_arrival_expand_ecl. + Check incrementally the epsilon closure of TARGET, and if it isn't + problematic append it to DST_NODES. */ + +static reg_errcode_t +check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, type) + re_dfa_t *dfa; + int target, ex_subexp, type; + re_node_set *dst_nodes; +{ + int cur_node; + for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) + { + int err; + + if (dfa->nodes[cur_node].type == type + && dfa->nodes[cur_node].opr.idx == ex_subexp) + { + if (type == OP_CLOSE_SUBEXP) + { + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + } + break; + } + err = re_node_set_insert (dst_nodes, cur_node); + if (BE (err == -1, 0)) + return REG_ESPACE; + if (dfa->edests[cur_node].nelem == 0) + break; + if (dfa->edests[cur_node].nelem == 2) + { + err = check_arrival_expand_ecl_sub (dfa, dst_nodes, + dfa->edests[cur_node].elems[1], + ex_subexp, type); + if (BE (err != REG_NOERROR, 0)) + return err; + } + cur_node = dfa->edests[cur_node].elems[0]; + } + return REG_NOERROR; +} + + +/* For all the back references in the current state, calculate the + destination of the back references by the appropriate entry + in MCTX->BKREF_ENTS. */ + +static reg_errcode_t +expand_bkref_cache (mctx, cur_nodes, cur_str, subexp_num, + type) + re_match_context_t *mctx; + int cur_str, subexp_num, type; + re_node_set *cur_nodes; +{ + re_dfa_t *const dfa = mctx->dfa; + reg_errcode_t err; + int cache_idx_start = search_cur_bkref_entry (mctx, cur_str); + struct re_backref_cache_entry *ent; + + if (cache_idx_start == -1) + return REG_NOERROR; + + restart: + ent = mctx->bkref_ents + cache_idx_start; + do + { + int to_idx, next_node; + + /* Is this entry ENT is appropriate? */ + if (!re_node_set_contains (cur_nodes, ent->node)) + continue; /* No. */ + + to_idx = cur_str + ent->subexp_to - ent->subexp_from; + /* Calculate the destination of the back reference, and append it + to MCTX->STATE_LOG. */ + if (to_idx == cur_str) + { + /* The backreference did epsilon transit, we must re-check all the + node in the current state. */ + re_node_set new_dests; + reg_errcode_t err2, err3; + next_node = dfa->edests[ent->node].elems[0]; + if (re_node_set_contains (cur_nodes, next_node)) + continue; + err = re_node_set_init_1 (&new_dests, next_node); + err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); + err3 = re_node_set_merge (cur_nodes, &new_dests); + re_node_set_free (&new_dests); + if (BE (err != REG_NOERROR || err2 != REG_NOERROR + || err3 != REG_NOERROR, 0)) + { + err = (err != REG_NOERROR ? err + : (err2 != REG_NOERROR ? err2 : err3)); + return err; + } + /* TODO: It is still inefficient... */ + goto restart; + } + else + { + re_node_set union_set; + next_node = dfa->nexts[ent->node]; + if (mctx->state_log[to_idx]) + { + int ret; + if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, + next_node)) + continue; + err = re_node_set_init_copy (&union_set, + &mctx->state_log[to_idx]->nodes); + ret = re_node_set_insert (&union_set, next_node); + if (BE (err != REG_NOERROR || ret < 0, 0)) + { + re_node_set_free (&union_set); + err = err != REG_NOERROR ? err : REG_ESPACE; + return err; + } + } + else + { + err = re_node_set_init_1 (&union_set, next_node); + if (BE (err != REG_NOERROR, 0)) + return err; + } + mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); + re_node_set_free (&union_set); + if (BE (mctx->state_log[to_idx] == NULL + && err != REG_NOERROR, 0)) + return err; + } + } + while (ent++->more); + return REG_NOERROR; +} + +/* Build transition table for the state. + Return 1 if succeeded, otherwise return NULL. */ + +static int +build_trtable (dfa, state) + re_dfa_t *dfa; + re_dfastate_t *state; +{ + reg_errcode_t err; + int i, j, ch, need_word_trtable = 0; + unsigned int elem, mask; + int dests_node_malloced = 0, dest_states_malloced = 0; + int ndests; /* Number of the destination states from `state'. */ + re_dfastate_t **trtable; + re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; + re_node_set follows, *dests_node; + bitset *dests_ch; + bitset acceptable; + + /* We build DFA states which corresponds to the destination nodes + from `state'. `dests_node[i]' represents the nodes which i-th + destination state contains, and `dests_ch[i]' represents the + characters which i-th destination state accepts. */ +#ifdef _LIBC + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX)) + dests_node = (re_node_set *) + alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX); + else +#endif + { + dests_node = (re_node_set *) + malloc ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX); + if (BE (dests_node == NULL, 0)) + return 0; + dests_node_malloced = 1; + } + dests_ch = (bitset *) (dests_node + SBC_MAX); + + /* Initialize transiton table. */ + state->word_trtable = state->trtable = NULL; + + /* At first, group all nodes belonging to `state' into several + destinations. */ + ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); + if (BE (ndests <= 0, 0)) + { + if (dests_node_malloced) + free (dests_node); + /* Return 0 in case of an error, 1 otherwise. */ + if (ndests == 0) + { + state->trtable = (re_dfastate_t **) + calloc (sizeof (re_dfastate_t *), SBC_MAX); + return 1; + } + return 0; + } + + err = re_node_set_alloc (&follows, ndests + 1); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + +#ifdef _LIBC + if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX + + ndests * 3 * sizeof (re_dfastate_t *))) + dest_states = (re_dfastate_t **) + alloca (ndests * 3 * sizeof (re_dfastate_t *)); + else +#endif + { + dest_states = (re_dfastate_t **) + malloc (ndests * 3 * sizeof (re_dfastate_t *)); + if (BE (dest_states == NULL, 0)) + { +out_free: + if (dest_states_malloced) + free (dest_states); + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + if (dests_node_malloced) + free (dests_node); + return 0; + } + dest_states_malloced = 1; + } + dest_states_word = dest_states + ndests; + dest_states_nl = dest_states_word + ndests; + bitset_empty (acceptable); + + /* Then build the states for all destinations. */ + for (i = 0; i < ndests; ++i) + { + int next_node; + re_node_set_empty (&follows); + /* Merge the follows of this destination states. */ + for (j = 0; j < dests_node[i].nelem; ++j) + { + next_node = dfa->nexts[dests_node[i].elems[j]]; + if (next_node != -1) + { + err = re_node_set_merge (&follows, dfa->eclosures + next_node); + if (BE (err != REG_NOERROR, 0)) + goto out_free; + } + } + dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); + if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + /* If the new state has context constraint, + build appropriate states for these contexts. */ + if (dest_states[i]->has_constraint) + { + dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_WORD); + if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + + if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) + need_word_trtable = 1; + + dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, + CONTEXT_NEWLINE); + if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) + goto out_free; + } + else + { + dest_states_word[i] = dest_states[i]; + dest_states_nl[i] = dest_states[i]; + } + bitset_merge (acceptable, dests_ch[i]); + } + + if (!BE (need_word_trtable, 0)) + { + /* We don't care about whether the following character is a word + character, or we are in a single-byte character set so we can + discern by looking at the character code: allocate a + 256-entry transition table. */ + trtable = state->trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_UINTS; ++i) + for (ch = i * UINT_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + if (dfa->word_char[i] & mask) + trtable[ch] = dest_states_word[j]; + else + trtable[ch] = dest_states[j]; + } + } + else + { + /* We care about whether the following character is a word + character, and we are in a multi-byte character set: discern + by looking at the character code: build two 256-entry + transition tables, one starting at trtable[0] and one + starting at trtable[SBC_MAX]. */ + trtable = state->word_trtable = + (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); + if (BE (trtable == NULL, 0)) + goto out_free; + + /* For all characters ch...: */ + for (i = 0; i < BITSET_UINTS; ++i) + for (ch = i * UINT_BITS, elem = acceptable[i], mask = 1; + elem; + mask <<= 1, elem >>= 1, ++ch) + if (BE (elem & 1, 0)) + { + /* There must be exactly one destination which accepts + character ch. See group_nodes_into_DFAstates. */ + for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) + ; + + /* j-th destination accepts the word character ch. */ + trtable[ch] = dest_states[j]; + trtable[ch + SBC_MAX] = dest_states_word[j]; + } + } + + /* new line */ + if (bitset_contain (acceptable, NEWLINE_CHAR)) + { + /* The current state accepts newline character. */ + for (j = 0; j < ndests; ++j) + if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) + { + /* k-th destination accepts newline character. */ + trtable[NEWLINE_CHAR] = dest_states_nl[j]; + if (need_word_trtable) + trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; + /* There must be only one destination which accepts + newline. See group_nodes_into_DFAstates. */ + break; + } + } + + if (dest_states_malloced) + free (dest_states); + + re_node_set_free (&follows); + for (i = 0; i < ndests; ++i) + re_node_set_free (dests_node + i); + + if (dests_node_malloced) + free (dests_node); + + return 1; +} + +/* Group all nodes belonging to STATE into several destinations. + Then for all destinations, set the nodes belonging to the destination + to DESTS_NODE[i] and set the characters accepted by the destination + to DEST_CH[i]. This function return the number of destinations. */ + +static int +group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch) + re_dfa_t *dfa; + const re_dfastate_t *state; + re_node_set *dests_node; + bitset *dests_ch; +{ + reg_errcode_t err; + int result; + int i, j, k; + int ndests; /* Number of the destinations from `state'. */ + bitset accepts; /* Characters a node can accept. */ + const re_node_set *cur_nodes = &state->nodes; + bitset_empty (accepts); + ndests = 0; + + /* For all the nodes belonging to `state', */ + for (i = 0; i < cur_nodes->nelem; ++i) + { + re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; + re_token_type_t type = node->type; + unsigned int constraint = node->constraint; + + /* Enumerate all single byte character this node can accept. */ + if (type == CHARACTER) + bitset_set (accepts, node->opr.c); + else if (type == SIMPLE_BRACKET) + { + bitset_merge (accepts, node->opr.sbcset); + } + else if (type == OP_PERIOD) + { +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + bitset_merge (accepts, dfa->sb_char); + else +#endif + bitset_set_all (accepts); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#ifdef RE_ENABLE_I18N + else if (type == OP_UTF8_PERIOD) + { + memset (accepts, 255, sizeof (unsigned int) * BITSET_UINTS / 2); + if (!(dfa->syntax & RE_DOT_NEWLINE)) + bitset_clear (accepts, '\n'); + if (dfa->syntax & RE_DOT_NOT_NULL) + bitset_clear (accepts, '\0'); + } +#endif + else + continue; + + /* Check the `accepts' and sift the characters which are not + match it the context. */ + if (constraint) + { + if (constraint & NEXT_NEWLINE_CONSTRAINT) + { + int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); + bitset_empty (accepts); + if (accepts_newline) + bitset_set (accepts, NEWLINE_CHAR); + else + continue; + } + if (constraint & NEXT_ENDBUF_CONSTRAINT) + { + bitset_empty (accepts); + continue; + } + + if (constraint & NEXT_WORD_CONSTRAINT) + { + unsigned int any_set = 0; + if (type == CHARACTER && !node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_UINTS; ++j) + any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_UINTS; ++j) + any_set |= (accepts[j] &= dfa->word_char[j]); + if (!any_set) + continue; + } + if (constraint & NEXT_NOTWORD_CONSTRAINT) + { + unsigned int any_set = 0; + if (type == CHARACTER && node->word_char) + { + bitset_empty (accepts); + continue; + } +#ifdef RE_ENABLE_I18N + if (dfa->mb_cur_max > 1) + for (j = 0; j < BITSET_UINTS; ++j) + any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); + else +#endif + for (j = 0; j < BITSET_UINTS; ++j) + any_set |= (accepts[j] &= ~dfa->word_char[j]); + if (!any_set) + continue; + } + } + + /* Then divide `accepts' into DFA states, or create a new + state. Above, we make sure that accepts is not empty. */ + for (j = 0; j < ndests; ++j) + { + bitset intersec; /* Intersection sets, see below. */ + bitset remains; + /* Flags, see below. */ + int has_intersec, not_subset, not_consumed; + + /* Optimization, skip if this state doesn't accept the character. */ + if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) + continue; + + /* Enumerate the intersection set of this state and `accepts'. */ + has_intersec = 0; + for (k = 0; k < BITSET_UINTS; ++k) + has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; + /* And skip if the intersection set is empty. */ + if (!has_intersec) + continue; + + /* Then check if this state is a subset of `accepts'. */ + not_subset = not_consumed = 0; + for (k = 0; k < BITSET_UINTS; ++k) + { + not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; + not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; + } + + /* If this state isn't a subset of `accepts', create a + new group state, which has the `remains'. */ + if (not_subset) + { + bitset_copy (dests_ch[ndests], remains); + bitset_copy (dests_ch[j], intersec); + err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + } + + /* Put the position in the current group. */ + result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); + if (BE (result < 0, 0)) + goto error_return; + + /* If all characters are consumed, go to next node. */ + if (!not_consumed) + break; + } + /* Some characters remain, create a new group. */ + if (j == ndests) + { + bitset_copy (dests_ch[ndests], accepts); + err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); + if (BE (err != REG_NOERROR, 0)) + goto error_return; + ++ndests; + bitset_empty (accepts); + } + } + return ndests; + error_return: + for (j = 0; j < ndests; ++j) + re_node_set_free (dests_node + j); + return -1; +} + +#ifdef RE_ENABLE_I18N +/* Check how many bytes the node `dfa->nodes[node_idx]' accepts. + Return the number of the bytes the node accepts. + STR_IDX is the current index of the input string. + + This function handles the nodes which can accept one character, or + one collating element like '.', '[a-z]', opposite to the other nodes + can only accept one byte. */ + +static int +check_node_accept_bytes (dfa, node_idx, input, str_idx) + re_dfa_t *dfa; + int node_idx, str_idx; + const re_string_t *input; +{ + const re_token_t *node = dfa->nodes + node_idx; + int char_len, elem_len; + int i; + + if (BE (node->type == OP_UTF8_PERIOD, 0)) + { + unsigned char c = re_string_byte_at (input, str_idx), d; + if (BE (c < 0xc2, 1)) + return 0; + + if (str_idx + 2 > input->len) + return 0; + + d = re_string_byte_at (input, str_idx + 1); + if (c < 0xe0) + return (d < 0x80 || d > 0xbf) ? 0 : 2; + else if (c < 0xf0) + { + char_len = 3; + if (c == 0xe0 && d < 0xa0) + return 0; + } + else if (c < 0xf8) + { + char_len = 4; + if (c == 0xf0 && d < 0x90) + return 0; + } + else if (c < 0xfc) + { + char_len = 5; + if (c == 0xf8 && d < 0x88) + return 0; + } + else if (c < 0xfe) + { + char_len = 6; + if (c == 0xfc && d < 0x84) + return 0; + } + else + return 0; + + if (str_idx + char_len > input->len) + return 0; + + for (i = 1; i < char_len; ++i) + { + d = re_string_byte_at (input, str_idx + i); + if (d < 0x80 || d > 0xbf) + return 0; + } + return char_len; + } + + char_len = re_string_char_size_at (input, str_idx); + if (node->type == OP_PERIOD) + { + if (char_len <= 1) + return 0; + /* FIXME: I don't think this if is needed, as both '\n' + and '\0' are char_len == 1. */ + /* '.' accepts any one character except the following two cases. */ + if ((!(dfa->syntax & RE_DOT_NEWLINE) && + re_string_byte_at (input, str_idx) == '\n') || + ((dfa->syntax & RE_DOT_NOT_NULL) && + re_string_byte_at (input, str_idx) == '\0')) + return 0; + return char_len; + } + + elem_len = re_string_elem_size_at (input, str_idx); + if ((elem_len <= 1 && char_len <= 1) || char_len == 0) + return 0; + + if (node->type == COMPLEX_BRACKET) + { + const re_charset_t *cset = node->opr.mbcset; +# ifdef _LIBC + const unsigned char *pin + = ((const unsigned char *) re_string_get_buffer (input) + str_idx); + int j; + uint32_t nrules; +# endif /* _LIBC */ + int match_len = 0; + wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) + ? re_string_wchar_at (input, str_idx) : 0); + + /* match with multibyte character? */ + for (i = 0; i < cset->nmbchars; ++i) + if (wc == cset->mbchars[i]) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + /* match with character_class? */ + for (i = 0; i < cset->nchar_classes; ++i) + { + wctype_t wt = cset->char_classes[i]; + if (__iswctype (wc, wt)) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + +# ifdef _LIBC + nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules != 0) + { + unsigned int in_collseq = 0; + const int32_t *table, *indirect; + const unsigned char *weights, *extra; + const char *collseqwc; + int32_t idx; + /* This #include defines a local function! */ +# include <locale/weight.h> + + /* match with collating_symbol? */ + if (cset->ncoll_syms) + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + for (i = 0; i < cset->ncoll_syms; ++i) + { + const unsigned char *coll_sym = extra + cset->coll_syms[i]; + /* Compare the length of input collating element and + the length of current collating element. */ + if (*coll_sym != elem_len) + continue; + /* Compare each bytes. */ + for (j = 0; j < *coll_sym; j++) + if (pin[j] != coll_sym[1 + j]) + break; + if (j == *coll_sym) + { + /* Match if every bytes is equal. */ + match_len = j; + goto check_node_accept_bytes_match; + } + } + + if (cset->nranges) + { + if (elem_len <= char_len) + { + collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); + in_collseq = __collseq_table_lookup (collseqwc, wc); + } + else + in_collseq = find_collation_sequence_value (pin, elem_len); + } + /* match with range expression? */ + for (i = 0; i < cset->nranges; ++i) + if (cset->range_starts[i] <= in_collseq + && in_collseq <= cset->range_ends[i]) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + + /* match with equivalence_class? */ + if (cset->nequiv_classes) + { + const unsigned char *cp = pin; + table = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); + weights = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); + extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); + indirect = (const int32_t *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); + idx = findidx (&cp); + if (idx > 0) + for (i = 0; i < cset->nequiv_classes; ++i) + { + int32_t equiv_class_idx = cset->equiv_classes[i]; + size_t weight_len = weights[idx]; + if (weight_len == weights[equiv_class_idx]) + { + int cnt = 0; + while (cnt <= weight_len + && (weights[equiv_class_idx + 1 + cnt] + == weights[idx + 1 + cnt])) + ++cnt; + if (cnt > weight_len) + { + match_len = elem_len; + goto check_node_accept_bytes_match; + } + } + } + } + } + else +# endif /* _LIBC */ + { + /* match with range expression? */ +#if __GNUC__ >= 2 + wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; +#else + wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; + cmp_buf[2] = wc; +#endif + for (i = 0; i < cset->nranges; ++i) + { + cmp_buf[0] = cset->range_starts[i]; + cmp_buf[4] = cset->range_ends[i]; + if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 + && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) + { + match_len = char_len; + goto check_node_accept_bytes_match; + } + } + } + check_node_accept_bytes_match: + if (!cset->non_match) + return match_len; + else + { + if (match_len > 0) + return 0; + else + return (elem_len > char_len) ? elem_len : char_len; + } + } + return 0; +} + +# ifdef _LIBC +static unsigned int +find_collation_sequence_value (mbs, mbs_len) + const unsigned char *mbs; + size_t mbs_len; +{ + uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); + if (nrules == 0) + { + if (mbs_len == 1) + { + /* No valid character. Match it as a single byte character. */ + const unsigned char *collseq = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); + return collseq[mbs[0]]; + } + return UINT_MAX; + } + else + { + int32_t idx; + const unsigned char *extra = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); + int32_t extrasize = (const unsigned char *) + _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; + + for (idx = 0; idx < extrasize;) + { + int mbs_cnt, found = 0; + int32_t elem_mbs_len; + /* Skip the name of collating element name. */ + idx = idx + extra[idx] + 1; + elem_mbs_len = extra[idx++]; + if (mbs_len == elem_mbs_len) + { + for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) + if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) + break; + if (mbs_cnt == elem_mbs_len) + /* Found the entry. */ + found = 1; + } + /* Skip the byte sequence of the collating element. */ + idx += elem_mbs_len; + /* Adjust for the alignment. */ + idx = (idx + 3) & ~3; + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + /* Skip the wide char sequence of the collating element. */ + idx = idx + sizeof (uint32_t) * (extra[idx] + 1); + /* If we found the entry, return the sequence value. */ + if (found) + return *(uint32_t *) (extra + idx); + /* Skip the collation sequence value. */ + idx += sizeof (uint32_t); + } + return UINT_MAX; + } +} +# endif /* _LIBC */ +#endif /* RE_ENABLE_I18N */ + +/* Check whether the node accepts the byte which is IDX-th + byte of the INPUT. */ + +static int +check_node_accept (mctx, node, idx) + const re_match_context_t *mctx; + const re_token_t *node; + int idx; +{ + unsigned char ch; + ch = re_string_byte_at (&mctx->input, idx); + switch (node->type) + { + case CHARACTER: + if (node->opr.c != ch) + return 0; + break; + + case SIMPLE_BRACKET: + if (!bitset_contain (node->opr.sbcset, ch)) + return 0; + break; + +#ifdef RE_ENABLE_I18N + case OP_UTF8_PERIOD: + if (ch >= 0x80) + return 0; + /* FALLTHROUGH */ +#endif + case OP_PERIOD: + if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) + || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) + return 0; + break; + + default: + return 0; + } + + if (node->constraint) + { + /* The node has constraints. Check whether the current context + satisfies the constraints. */ + unsigned int context = re_string_context_at (&mctx->input, idx, + mctx->eflags); + if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) + return 0; + } + + return 1; +} + +/* Extend the buffers, if the buffers have run out. */ + +static reg_errcode_t +extend_buffers (mctx) + re_match_context_t *mctx; +{ + reg_errcode_t ret; + re_string_t *pstr = &mctx->input; + + /* Double the lengthes of the buffers. */ + ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); + if (BE (ret != REG_NOERROR, 0)) + return ret; + + if (mctx->state_log != NULL) + { + /* And double the length of state_log. */ + /* XXX We have no indication of the size of this buffer. If this + allocation fail we have no indication that the state_log array + does not have the right size. */ + re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, + pstr->bufs_len + 1); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->state_log = new_array; + } + + /* Then reconstruct the buffers. */ + if (pstr->icase) + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + { + ret = build_wcs_upper_buffer (pstr); + if (BE (ret != REG_NOERROR, 0)) + return ret; + } + else +#endif /* RE_ENABLE_I18N */ + build_upper_buffer (pstr); + } + else + { +#ifdef RE_ENABLE_I18N + if (pstr->mb_cur_max > 1) + build_wcs_buffer (pstr); + else +#endif /* RE_ENABLE_I18N */ + { + if (pstr->trans != NULL) + re_string_translate_buffer (pstr); + } + } + return REG_NOERROR; +} + + +/* Functions for matching context. */ + +/* Initialize MCTX. */ + +static reg_errcode_t +match_ctx_init (mctx, eflags, n) + re_match_context_t *mctx; + int eflags, n; +{ + mctx->eflags = eflags; + mctx->match_last = -1; + if (n > 0) + { + mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); + mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); + if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) + return REG_ESPACE; + } + /* Already zero-ed by the caller. + else + mctx->bkref_ents = NULL; + mctx->nbkref_ents = 0; + mctx->nsub_tops = 0; */ + mctx->abkref_ents = n; + mctx->max_mb_elem_len = 1; + mctx->asub_tops = n; + return REG_NOERROR; +} + +/* Clean the entries which depend on the current input in MCTX. + This function must be invoked when the matcher changes the start index + of the input, or changes the input string. */ + +static void +match_ctx_clean (mctx) + re_match_context_t *mctx; +{ + int st_idx; + for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) + { + int sl_idx; + re_sub_match_top_t *top = mctx->sub_tops[st_idx]; + for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) + { + re_sub_match_last_t *last = top->lasts[sl_idx]; + re_free (last->path.array); + re_free (last); + } + re_free (top->lasts); + if (top->path) + { + re_free (top->path->array); + re_free (top->path); + } + free (top); + } + + mctx->nsub_tops = 0; + mctx->nbkref_ents = 0; +} + +/* Free all the memory associated with MCTX. */ + +static void +match_ctx_free (mctx) + re_match_context_t *mctx; +{ + /* First, free all the memory associated with MCTX->SUB_TOPS. */ + match_ctx_clean (mctx); + re_free (mctx->sub_tops); + re_free (mctx->bkref_ents); +} + +/* Add a new backreference entry to MCTX. + Note that we assume that caller never call this function with duplicate + entry, and call with STR_IDX which isn't smaller than any existing entry. +*/ + +static reg_errcode_t +match_ctx_add_entry (mctx, node, str_idx, from, to) + re_match_context_t *mctx; + int node, str_idx, from, to; +{ + if (mctx->nbkref_ents >= mctx->abkref_ents) + { + struct re_backref_cache_entry* new_entry; + new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, + mctx->abkref_ents * 2); + if (BE (new_entry == NULL, 0)) + { + re_free (mctx->bkref_ents); + return REG_ESPACE; + } + mctx->bkref_ents = new_entry; + memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', + sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); + mctx->abkref_ents *= 2; + } + if (mctx->nbkref_ents > 0 + && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) + mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; + + mctx->bkref_ents[mctx->nbkref_ents].node = node; + mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; + mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; + mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; + + /* This is a cache that saves negative results of check_dst_limits_calc_pos. + If bit N is clear, means that this entry won't epsilon-transition to + an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If + it is set, check_dst_limits_calc_pos_1 will recurse and try to find one + such node. + + A backreference does not epsilon-transition unless it is empty, so set + to all zeros if FROM != TO. */ + mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map + = (from == to ? ~0 : 0); + + mctx->bkref_ents[mctx->nbkref_ents++].more = 0; + if (mctx->max_mb_elem_len < to - from) + mctx->max_mb_elem_len = to - from; + return REG_NOERROR; +} + +/* Search for the first entry which has the same str_idx, or -1 if none is + found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ + +static int +search_cur_bkref_entry (mctx, str_idx) + re_match_context_t *mctx; + int str_idx; +{ + int left, right, mid, last; + last = right = mctx->nbkref_ents; + for (left = 0; left < right;) + { + mid = (left + right) / 2; + if (mctx->bkref_ents[mid].str_idx < str_idx) + left = mid + 1; + else + right = mid; + } + if (left < last && mctx->bkref_ents[left].str_idx == str_idx) + return left; + else + return -1; +} + +/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches + at STR_IDX. */ + +static reg_errcode_t +match_ctx_add_subtop (mctx, node, str_idx) + re_match_context_t *mctx; + int node, str_idx; +{ +#ifdef DEBUG + assert (mctx->sub_tops != NULL); + assert (mctx->asub_tops > 0); +#endif + if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) + { + int new_asub_tops = mctx->asub_tops * 2; + re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, + re_sub_match_top_t *, + new_asub_tops); + if (BE (new_array == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops = new_array; + mctx->asub_tops = new_asub_tops; + } + mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); + if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) + return REG_ESPACE; + mctx->sub_tops[mctx->nsub_tops]->node = node; + mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; + return REG_NOERROR; +} + +/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches + at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ + +static re_sub_match_last_t * +match_ctx_add_sublast (subtop, node, str_idx) + re_sub_match_top_t *subtop; + int node, str_idx; +{ + re_sub_match_last_t *new_entry; + if (BE (subtop->nlasts == subtop->alasts, 0)) + { + int new_alasts = 2 * subtop->alasts + 1; + re_sub_match_last_t **new_array = re_realloc (subtop->lasts, + re_sub_match_last_t *, + new_alasts); + if (BE (new_array == NULL, 0)) + return NULL; + subtop->lasts = new_array; + subtop->alasts = new_alasts; + } + new_entry = calloc (1, sizeof (re_sub_match_last_t)); + if (BE (new_entry != NULL, 1)) + { + subtop->lasts[subtop->nlasts] = new_entry; + new_entry->node = node; + new_entry->str_idx = str_idx; + ++subtop->nlasts; + } + return new_entry; +} + +static void +sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx) + re_sift_context_t *sctx; + re_dfastate_t **sifted_sts, **limited_sts; + int last_node, last_str_idx; +{ + sctx->sifted_states = sifted_sts; + sctx->limited_states = limited_sts; + sctx->last_node = last_node; + sctx->last_str_idx = last_str_idx; + re_node_set_init_empty (&sctx->limits); +} diff --git a/gnu/lib/libssp/Makefile b/gnu/lib/libssp/Makefile new file mode 100644 index 0000000..641874c --- /dev/null +++ b/gnu/lib/libssp/Makefile @@ -0,0 +1,38 @@ +# $FreeBSD$ + +SHLIBDIR?= /lib + +.include <bsd.own.mk> +MK_SSP= no + +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +SRCDIR= ${GCCLIB}/libssp + +.PATH: ${SRCDIR} ${SRCDIR}/ssp + +LIB= ssp +SHLIB_MAJOR= 0 +NO_PROFILE= + +SRCS= ssp.c gets-chk.c memcpy-chk.c memmove-chk.c mempcpy-chk.c \ + memset-chk.c snprintf-chk.c sprintf-chk.c stpcpy-chk.c \ + strcat-chk.c strcpy-chk.c strncat-chk.c strncpy-chk.c \ + vsnprintf-chk.c vsprintf-chk.c + +CFLAGS+= -DHAVE_CONFIG_H +CFLAGS+= -I${.CURDIR} -I${SRCDIR} -I${GCCLIB}/include + +VERSION_MAP= ${SRCDIR}/ssp.map + + +INCS= ssp.h string.h stdio.h unistd.h +INCSDIR=${INCLUDEDIR}/ssp + +CLEANFILES= ssp.h +ssp.h: ssp.h.in + sed -e 's/@ssp_have_usable_vsnprintf@/define/' ${.ALLSRC} > ${.TARGET} + +SUBDIR+= libssp_nonshared + +.include <bsd.lib.mk> diff --git a/gnu/lib/libssp/config.h b/gnu/lib/libssp/config.h new file mode 100644 index 0000000..680f107 --- /dev/null +++ b/gnu/lib/libssp/config.h @@ -0,0 +1,87 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* $FreeBSD$ */ + +/* Define to 1 if you have the <alloca.h> header file. */ +/* #undef HAVE_ALLOCA_H */ + +/* Define to 1 if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* __attribute__((visibility ("hidden"))) supported */ +#define HAVE_HIDDEN_VISIBILITY 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +/* #undef HAVE_MEMPCPY */ + +/* Define to 1 if you have the <paths.h> header file. */ +#define HAVE_PATHS_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strncat' function. */ +#define HAVE_STRNCAT 1 + +/* Define to 1 if you have the `strncpy' function. */ +#define HAVE_STRNCPY 1 + +/* Define to 1 if you have the <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* vsnprintf is present and works */ +#define HAVE_USABLE_VSNPRINTF 1 + +/* Name of package */ +#define PACKAGE "libssp" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libssp" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libssp 1.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libssp" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.0" diff --git a/gnu/lib/libssp/libssp_nonshared/Makefile b/gnu/lib/libssp/libssp_nonshared/Makefile new file mode 100644 index 0000000..9454495 --- /dev/null +++ b/gnu/lib/libssp/libssp_nonshared/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +GCCDIR= ${.CURDIR}/../../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../../contrib/gcclibs + +.PATH: ${GCCLIB}/libssp ${GCCLIB}/libssp/ssp + +LIB= ssp_nonshared +NO_PIC= +NO_PROFILE= + +SRCS= ssp-local.c + +CFLAGS+= -DHAVE_CONFIG_H +CFLAGS+= -I${.CURDIR}/.. -I${GCCLIB}/libssp -I${GCCLIB}/include +CFLAGS+= -fPIC -DPIC -fvisibility=hidden + +.include <bsd.lib.mk> diff --git a/gnu/lib/libstdc++/Makefile b/gnu/lib/libstdc++/Makefile new file mode 100644 index 0000000..56ca626 --- /dev/null +++ b/gnu/lib/libstdc++/Makefile @@ -0,0 +1,627 @@ +# $FreeBSD$ + +GCCVER= 4.2 +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +SRCDIR= ${.CURDIR}/../../../contrib/libstdc++ +SUPDIR= ${SRCDIR}/libsupc++ + +.PATH: ${SRCDIR}/src ${SRCDIR}/libmath ${SRCDIR}/config/io \ + ${SRCDIR}/config/locale/darwin ${SRCDIR}/config/locale/generic \ + ${SRCDIR}/include ${SUPDIR} ${GCCDIR} ${GCCLIB}/libiberty + +LIB= stdc++ +SHLIB_MAJOR= 6 + +CFLAGS+= -DIN_GLIBCPP_V3 -DHAVE_CONFIG_H +.if ${MACHINE_CPUARCH} == "arm" +CFLAGS+= -D_GLIBCXX_SJLJ_EXCEPTIONS=1 +.endif +CFLAGS+= -I${.CURDIR} -I${SUPDIR} -I${GCCDIR} -I${SRCDIR}/include +CFLAGS+= -I${GCCLIB}/include -I${SRCDIR}/include -I. +CFLAGS+= -frandom-seed=RepeatabilityConsideredGood +CXXFLAGS+= -fno-implicit-templates -ffunction-sections -fdata-sections \ + -Wno-deprecated +PO_CXXFLAGS= ${CXXFLAGS:N-ffunction-sections} + +DPADD= ${LIBM} +LDADD= -lm + +# libstdc++ sources +SRCS+= bitmap_allocator.cc pool_allocator.cc \ + mt_allocator.cc codecvt.cc compatibility.cc complex_io.cc \ + ctype.cc debug.cc debug_list.cc functexcept.cc globals_io.cc \ + ios.cc ios_failure.cc ios_init.cc ios_locale.cc limits.cc \ + list.cc locale.cc locale_init.cc locale_facets.cc \ + localename.cc stdexcept.cc strstream.cc tree.cc \ + allocator-inst.cc concept-inst.cc fstream-inst.cc ext-inst.cc \ + ios-inst.cc iostream-inst.cc istream-inst.cc istream.cc \ + locale-inst.cc misc-inst.cc ostream-inst.cc sstream-inst.cc \ + streambuf-inst.cc streambuf.cc string-inst.cc valarray-inst.cc \ + wlocale-inst.cc wstring-inst.cc atomicity.cc \ + codecvt_members.cc collate_members.cc ctype_members.cc \ + messages_members.cc monetary_members.cc numeric_members.cc \ + time_members.cc basic_file_stdio.cc c_locale.cc \ +# compatibility-ldbl.cc + +# target sources +SRCS+= atomicity.cc codecvt_members.cc collate_members.cc ctype_members.cc \ + messages_members.cc monetary_members.cc numeric_members.cc \ + time_members.cc + +# target sources extra +SRCS+= basic_file_stdio.cc c_locale.cc + +# C parts of math +SRCS+= stubs.c + +# Embedded copy of libsupc++ +SRCS+= del_op.cc del_opnt.cc del_opv.cc del_opvnt.cc eh_alloc.cc eh_arm.cc \ + eh_aux_runtime.cc eh_call.cc eh_catch.cc eh_exception.cc eh_globals.cc \ + eh_personality.cc eh_term_handler.cc eh_terminate.cc eh_throw.cc \ + eh_type.cc eh_unex_handler.cc guard.cc new_handler.cc new_op.cc \ + new_opnt.cc new_opv.cc new_opvnt.cc pure.cc tinfo.cc tinfo2.cc \ + vec.cc vterminate.cc + +# from libiberty: +SRCS+= cp-demangle.c + +# MD headers location +.if ${MACHINE_CPUARCH} == "sparc64" +MARCHDIR= sparc +.elif ${MACHINE_CPUARCH} == "i386" && ${MACHINE_CPU} != 'i386' +MARCHDIR= i486 +.elif ${MACHINE_CPUARCH} == "amd64" +MARCHDIR= i486 +.else +MARCHDIR= ${MACHINE_CPUARCH} +.endif + +.if exists(${SRCDIR}/config/cpu/${MARCHDIR}/atomicity.h) +ATOMICITY_H= ${SRCDIR}/config/cpu/${MARCHDIR}/atomicity.h +.else +ATOMICITY_H= ${SRCDIR}/config/cpu/generic/atomicity_mutex/atomicity.h +.endif + +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" +.if exists(${SRCDIR}/config/cpu/generic/atomicity_builtins/atomicity.h) +ATOMICITY_H= ${SRCDIR}/config/cpu/generic/atomicity_builtins/atomicity.h +.endif +.endif + +atomicity.cc: ${ATOMICITY_H} + ln -sf ${.ALLSRC} ${.TARGET} +CLEANFILES+= atomicity.cc + +INCSGROUPS= BITSHDRS BKWHDRS EXTHDRS BASEHDRS BASEXHDRS STDHDRS \ + TARGETHDRS THRHDRS DEBUGHDRS TR1HDRS PBHDRS0 PBHDRS1 +CXXINCLUDEDIR= ${INCLUDEDIR}/c++/${GCCVER} + +STDHDRS= std_algorithm.h std_bitset.h std_complex.h std_deque.h \ + std_fstream.h std_functional.h std_iomanip.h std_ios.h \ + std_iosfwd.h std_iostream.h std_istream.h std_iterator.h \ + std_limits.h std_list.h std_locale.h std_map.h std_memory.h \ + std_numeric.h std_ostream.h std_queue.h std_set.h \ + std_sstream.h std_stack.h std_stdexcept.h std_streambuf.h \ + std_string.h std_utility.h std_valarray.h std_vector.h +.for h in ${STDHDRS} +STDHDRSNAME_$h=${h:R:S;^std_;;} +.endfor +STDHDRS:= ${STDHDRS:S;^;${SRCDIR}/include/std/;} +STDHDRSDIR= ${CXXINCLUDEDIR} + +BITSHDRS= allocator.h basic_ios.h basic_ios.tcc basic_string.h \ + basic_string.tcc boost_concept_check.h char_traits.h codecvt.h \ + concept_check.h cpp_type_traits.h deque.tcc fstream.tcc \ + functexcept.h gslice.h gslice_array.h indirect_array.h \ + ios_base.h istream.tcc list.tcc locale_classes.h \ + locale_facets.h locale_facets.tcc localefwd.h mask_array.h \ + ostream.tcc ostream_insert.h postypes.h stream_iterator.h \ + streambuf_iterator.h slice_array.h sstream.tcc stl_algo.h \ + stl_algobase.h stl_bvector.h stl_construct.h stl_deque.h \ + stl_function.h stl_heap.h stl_iterator.h \ + stl_iterator_base_funcs.h stl_iterator_base_types.h stl_list.h \ + stl_map.h stl_multimap.h stl_multiset.h stl_numeric.h \ + stl_pair.h stl_queue.h stl_raw_storage_iter.h stl_relops.h \ + stl_set.h stl_stack.h stl_tempbuf.h stl_tree.h \ + stl_uninitialized.h stl_vector.h streambuf.tcc stringfwd.h \ + valarray_array.h valarray_array.tcc valarray_before.h \ + valarray_after.h vector.tcc +BITSHDRS:= ${BITSHDRS:S;^;${SRCDIR}/include/bits/;} +BITSHDRSDIR= ${CXXINCLUDEDIR}/bits + +BKWHDRS= complex.h iomanip.h istream.h ostream.h stream.h streambuf.h \ + algo.h algobase.h alloc.h bvector.h defalloc.h deque.h \ + function.h hash_map.h hash_set.h hashtable.h heap.h iostream.h \ + iterator.h list.h map.h multimap.h new.h multiset.h pair.h \ + queue.h rope.h set.h slist.h stack.h tempbuf.h tree.h \ + vector.h fstream.h strstream backward_warning.h +BKWHDRS:= ${BKWHDRS:S;^;${SRCDIR}/include/backward/;} +BKWHDRSDIR= ${CXXINCLUDEDIR}/backward + +EXTHDRS= algorithm atomicity.h array_allocator.h bitmap_allocator.h \ + codecvt_specializations.h concurrence.h debug_allocator.h \ + stdio_filebuf.h stdio_sync_filebuf.h functional \ + hash_map hash_set hash_fun.h hashtable.h iterator \ + malloc_allocator.h memory mt_allocator.h new_allocator.h \ + numeric numeric_traits.h pod_char_traits.h pool_allocator.h \ + rb_tree rope ropeimpl.h slist throw_allocator.h typelist.h \ + type_traits.h rc_string_base.h sso_string_base.h vstring.h \ + vstring.tcc vstring_fwd.h vstring_util.h + +EXTHDRS:= ${EXTHDRS:S;^;${SRCDIR}/include/ext/;} +EXTHDRSDIR= ${CXXINCLUDEDIR}/ext + +TR1HDRS= array bind_repeat.h bind_iterate.h boost_shared_ptr.h cctype \ + cfenv cfloat cinttypes climits cmath common.h complex cstdarg \ + cstdbool cstdint cstdio cstdlib ctgmath ctime ctype.h cwchar \ + cwctype fenv.h float.h functional functional_hash.h \ + functional_iterate.h hashtable hashtable_policy.h inttypes.h \ + limits.h math.h memory mu_iterate.h random random.tcc \ + ref_fwd.h ref_wrap_iterate.h repeat.h stdarg.h stdbool.h \ + stdint.h stdio.h stdlib.h tgmath.h tuple tuple_defs.h \ + tuple_iterate.h type_traits type_traits_fwd.h unordered_set \ + unordered_map utility wchar.h wctype.h + +TR1HDRS:= ${TR1HDRS:S;^;${SRCDIR}/include/tr1/;} +TR1HDRSDIR= ${CXXINCLUDEDIR}/tr1 + +# This is the common subset of files that all three "C" header models use. +BASEHDRS= std_cassert.h std_cctype.h std_cerrno.h std_cfloat.h \ + std_ciso646.h std_climits.h std_clocale.h std_cmath.h \ + std_csetjmp.h std_csignal.h std_cstdarg.h std_cstddef.h \ + std_cstdio.h std_cstdlib.h std_cstring.h std_ctime.h \ + std_cwchar.h std_cwctype.h +.for h in ${BASEHDRS} +BASEHDRSNAME_$h=${h:R:S;^std_;;} +.endfor +BASEHDRS:= ${BASEHDRS:S;^;${SRCDIR}/include/c_std/;} +BASEHDRSDIR= ${CXXINCLUDEDIR} + +# Some of the different "C" header models need extra files. +BASEXHDRS= ${SRCDIR}/include/c_std/cmath.tcc +BASEXHDRSDIR= ${CXXINCLUDEDIR}/bits + +DEBUGHDRS= bitset debug.h deque formatter.h functions.h hash_map \ + hash_map.h hash_multimap.h hash_multiset.h hash_set hash_set.h \ + list macros.h map map.h multimap.h multiset.h safe_base.h \ + safe_iterator.h safe_iterator.tcc safe_sequence.h set set.h \ + string vector +DEBUGHDRS:= ${DEBUGHDRS:S;^;${SRCDIR}/include/debug/;} +DEBUGHDRSDIR= ${CXXINCLUDEDIR}/debug + +# Not installed, but kept here for completeness +COMPATHDRS= assert.h ctype.h errno.h float.h iso646.h limits.h locale.h \ + math.h setjmp.h signal.h stdarg.h stddef.h stdio.h stdlib.h \ + string.h time.h wchar.h wctype.h +COMPATHDRS:= ${COMPATHDRS:S;^;${SRCDIR}/include/debug/;} +COMPATHDRSDIR= ${CXXINCLUDEDIR}/c_compatibility + +PBHDRS0 = \ + assoc_container.hpp \ + exception.hpp \ + hash_policy.hpp \ + list_update_policy.hpp \ + priority_queue.hpp \ + tag_and_trait.hpp \ + tree_policy.hpp \ + trie_policy.hpp +PBHDRS0:= ${PBHDRS0:S;^;${SRCDIR}/include/ext/pb_ds/;} +PBHDRS0DIR= ${CXXINCLUDEDIR}/ext/pb_ds + +PBHDRS1 = \ + basic_types.hpp \ + cond_dealtor.hpp \ + constructors_destructor_fn_imps.hpp \ + container_base_dispatch.hpp \ + map_debug_base.hpp \ + priority_queue_base_dispatch.hpp \ + standard_policies.hpp \ + tree_trace_base.hpp \ + type_utils.hpp \ + types_traits.hpp +PBHDRS1:= ${PBHDRS1:S;^;${SRCDIR}/include/ext/pb_ds/detail/;} +PBHDRS1DIR= ${CXXINCLUDEDIR}/ext/pb_ds/detail + +PBHDRS+= \ + basic_tree_policy/basic_tree_policy_base.hpp \ + basic_tree_policy/null_node_metadata.hpp \ + basic_tree_policy/traits.hpp \ + basic_types.hpp \ + binary_heap_/binary_heap_.hpp \ + binary_heap_/const_iterator.hpp \ + binary_heap_/const_point_iterator.hpp \ + binary_heap_/constructors_destructor_fn_imps.hpp \ + binary_heap_/debug_fn_imps.hpp \ + binary_heap_/entry_cmp.hpp \ + binary_heap_/entry_pred.hpp \ + binary_heap_/erase_fn_imps.hpp \ + binary_heap_/find_fn_imps.hpp \ + binary_heap_/info_fn_imps.hpp \ + binary_heap_/insert_fn_imps.hpp \ + binary_heap_/iterators_fn_imps.hpp \ + binary_heap_/policy_access_fn_imps.hpp \ + binary_heap_/resize_policy.hpp \ + binary_heap_/split_join_fn_imps.hpp \ + binary_heap_/trace_fn_imps.hpp \ + binomial_heap_base_/binomial_heap_base_.hpp \ + binomial_heap_base_/constructors_destructor_fn_imps.hpp \ + binomial_heap_base_/debug_fn_imps.hpp \ + binomial_heap_base_/erase_fn_imps.hpp \ + binomial_heap_base_/find_fn_imps.hpp \ + binomial_heap_base_/insert_fn_imps.hpp \ + binomial_heap_base_/split_join_fn_imps.hpp \ + binomial_heap_/binomial_heap_.hpp \ + binomial_heap_/constructors_destructor_fn_imps.hpp \ + binomial_heap_/debug_fn_imps.hpp \ + bin_search_tree_/bin_search_tree_.hpp \ + bin_search_tree_/cond_dtor_entry_dealtor.hpp \ + bin_search_tree_/cond_key_dtor_entry_dealtor.hpp + +PBHDRS+= \ + bin_search_tree_/constructors_destructor_fn_imps.hpp \ + bin_search_tree_/debug_fn_imps.hpp \ + bin_search_tree_/erase_fn_imps.hpp \ + bin_search_tree_/find_fn_imps.hpp \ + bin_search_tree_/info_fn_imps.hpp \ + bin_search_tree_/insert_fn_imps.hpp \ + bin_search_tree_/iterators_fn_imps.hpp \ + bin_search_tree_/node_iterators.hpp \ + bin_search_tree_/point_iterators.hpp \ + bin_search_tree_/policy_access_fn_imps.hpp \ + bin_search_tree_/r_erase_fn_imps.hpp \ + bin_search_tree_/rotate_fn_imps.hpp \ + bin_search_tree_/split_join_fn_imps.hpp \ + bin_search_tree_/traits.hpp \ + cc_hash_table_map_/cc_ht_map_.hpp \ + cc_hash_table_map_/cmp_fn_imps.hpp \ + cc_hash_table_map_/cond_key_dtor_entry_dealtor.hpp \ + cc_hash_table_map_/constructor_destructor_fn_imps.hpp \ + cc_hash_table_map_/constructor_destructor_no_store_hash_fn_imps.hpp \ + cc_hash_table_map_/constructor_destructor_store_hash_fn_imps.hpp \ + cc_hash_table_map_/debug_fn_imps.hpp \ + cc_hash_table_map_/debug_no_store_hash_fn_imps.hpp \ + cc_hash_table_map_/debug_store_hash_fn_imps.hpp \ + cc_hash_table_map_/entry_list_fn_imps.hpp \ + cc_hash_table_map_/erase_fn_imps.hpp \ + cc_hash_table_map_/erase_no_store_hash_fn_imps.hpp \ + cc_hash_table_map_/erase_store_hash_fn_imps.hpp \ + cc_hash_table_map_/find_fn_imps.hpp \ + cc_hash_table_map_/find_store_hash_fn_imps.hpp \ + cc_hash_table_map_/info_fn_imps.hpp \ + cc_hash_table_map_/insert_fn_imps.hpp \ + cc_hash_table_map_/insert_no_store_hash_fn_imps.hpp \ + cc_hash_table_map_/insert_store_hash_fn_imps.hpp \ + cc_hash_table_map_/iterators_fn_imps.hpp \ + cc_hash_table_map_/policy_access_fn_imps.hpp \ + cc_hash_table_map_/resize_fn_imps.hpp \ + cc_hash_table_map_/resize_no_store_hash_fn_imps.hpp \ + cc_hash_table_map_/resize_store_hash_fn_imps.hpp \ + cc_hash_table_map_/size_fn_imps.hpp \ + cc_hash_table_map_/standard_policies.hpp + +PBHDRS+= \ + cc_hash_table_map_/trace_fn_imps.hpp \ + cond_dealtor.hpp \ + constructors_destructor_fn_imps.hpp \ + container_base_dispatch.hpp \ + eq_fn/eq_by_less.hpp \ + eq_fn/hash_eq_fn.hpp \ + gp_hash_table_map_/constructor_destructor_fn_imps.hpp \ + gp_hash_table_map_/constructor_destructor_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/constructor_destructor_store_hash_fn_imps.hpp \ + gp_hash_table_map_/debug_fn_imps.hpp \ + gp_hash_table_map_/debug_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/debug_store_hash_fn_imps.hpp \ + gp_hash_table_map_/erase_fn_imps.hpp \ + gp_hash_table_map_/erase_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/erase_store_hash_fn_imps.hpp \ + gp_hash_table_map_/find_fn_imps.hpp \ + gp_hash_table_map_/find_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/find_store_hash_fn_imps.hpp \ + gp_hash_table_map_/gp_ht_map_.hpp \ + gp_hash_table_map_/info_fn_imps.hpp \ + gp_hash_table_map_/insert_fn_imps.hpp \ + gp_hash_table_map_/insert_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/insert_store_hash_fn_imps.hpp \ + gp_hash_table_map_/iterator_fn_imps.hpp \ + gp_hash_table_map_/policy_access_fn_imps.hpp \ + gp_hash_table_map_/resize_fn_imps.hpp \ + gp_hash_table_map_/resize_no_store_hash_fn_imps.hpp \ + gp_hash_table_map_/resize_store_hash_fn_imps.hpp \ + gp_hash_table_map_/standard_policies.hpp \ + gp_hash_table_map_/trace_fn_imps.hpp \ + hash_fn/direct_mask_range_hashing_imp.hpp \ + hash_fn/direct_mod_range_hashing_imp.hpp \ + hash_fn/linear_probe_fn_imp.hpp \ + hash_fn/mask_based_range_hashing.hpp \ + hash_fn/mod_based_range_hashing.hpp \ + hash_fn/probe_fn_base.hpp \ + hash_fn/quadratic_probe_fn_imp.hpp \ + hash_fn/ranged_hash_fn.hpp \ + hash_fn/ranged_probe_fn.hpp + +PBHDRS+= \ + hash_fn/sample_probe_fn.hpp \ + hash_fn/sample_ranged_hash_fn.hpp \ + hash_fn/sample_ranged_probe_fn.hpp \ + hash_fn/sample_range_hashing.hpp \ + left_child_next_sibling_heap_/const_iterator.hpp \ + left_child_next_sibling_heap_/const_point_iterator.hpp \ + left_child_next_sibling_heap_/constructors_destructor_fn_imps.hpp \ + left_child_next_sibling_heap_/debug_fn_imps.hpp \ + left_child_next_sibling_heap_/erase_fn_imps.hpp \ + left_child_next_sibling_heap_/info_fn_imps.hpp \ + left_child_next_sibling_heap_/insert_fn_imps.hpp \ + left_child_next_sibling_heap_/iterators_fn_imps.hpp \ + left_child_next_sibling_heap_/left_child_next_sibling_heap_.hpp \ + left_child_next_sibling_heap_/node.hpp \ + left_child_next_sibling_heap_/null_metadata.hpp \ + left_child_next_sibling_heap_/policy_access_fn_imps.hpp \ + left_child_next_sibling_heap_/trace_fn_imps.hpp \ + list_update_map_/constructor_destructor_fn_imps.hpp \ + list_update_map_/debug_fn_imps.hpp \ + list_update_map_/entry_metadata_base.hpp \ + list_update_map_/erase_fn_imps.hpp \ + list_update_map_/find_fn_imps.hpp \ + list_update_map_/info_fn_imps.hpp \ + list_update_map_/insert_fn_imps.hpp \ + list_update_map_/iterators_fn_imps.hpp \ + list_update_map_/lu_map_.hpp \ + list_update_map_/trace_fn_imps.hpp \ + list_update_policy/counter_lu_metadata.hpp \ + list_update_policy/counter_lu_policy_imp.hpp \ + list_update_policy/mtf_lu_policy_imp.hpp \ + list_update_policy/sample_update_policy.hpp \ + map_debug_base.hpp \ + ov_tree_map_/cond_dtor.hpp \ + ov_tree_map_/constructors_destructor_fn_imps.hpp \ + ov_tree_map_/debug_fn_imps.hpp \ + ov_tree_map_/erase_fn_imps.hpp \ + ov_tree_map_/info_fn_imps.hpp \ + ov_tree_map_/insert_fn_imps.hpp \ + ov_tree_map_/iterators_fn_imps.hpp \ + ov_tree_map_/node_iterators.hpp \ + ov_tree_map_/ov_tree_map_.hpp + +PBHDRS+= \ + ov_tree_map_/policy_access_fn_imps.hpp \ + ov_tree_map_/split_join_fn_imps.hpp \ + ov_tree_map_/traits.hpp \ + pairing_heap_/constructors_destructor_fn_imps.hpp \ + pairing_heap_/debug_fn_imps.hpp \ + pairing_heap_/erase_fn_imps.hpp \ + pairing_heap_/find_fn_imps.hpp \ + pairing_heap_/insert_fn_imps.hpp \ + pairing_heap_/pairing_heap_.hpp \ + pairing_heap_/split_join_fn_imps.hpp \ + pat_trie_/child_iterator.hpp \ + pat_trie_/cond_dtor_entry_dealtor.hpp \ + pat_trie_/const_child_iterator.hpp \ + pat_trie_/constructors_destructor_fn_imps.hpp \ + pat_trie_/debug_fn_imps.hpp \ + pat_trie_/erase_fn_imps.hpp \ + pat_trie_/find_fn_imps.hpp \ + pat_trie_/head.hpp \ + pat_trie_/info_fn_imps.hpp \ + pat_trie_/insert_join_fn_imps.hpp \ + pat_trie_/internal_node.hpp \ + pat_trie_/iterators_fn_imps.hpp \ + pat_trie_/leaf.hpp \ + pat_trie_/node_base.hpp \ + pat_trie_/node_iterators.hpp \ + pat_trie_/node_metadata_base.hpp \ + pat_trie_/pat_trie_.hpp \ + pat_trie_/point_iterators.hpp \ + pat_trie_/policy_access_fn_imps.hpp \ + pat_trie_/r_erase_fn_imps.hpp \ + pat_trie_/rotate_fn_imps.hpp \ + pat_trie_/split_fn_imps.hpp \ + pat_trie_/split_join_branch_bag.hpp \ + pat_trie_/synth_e_access_traits.hpp \ + pat_trie_/trace_fn_imps.hpp \ + pat_trie_/traits.hpp \ + pat_trie_/update_fn_imps.hpp \ + priority_queue_base_dispatch.hpp \ + rb_tree_map_/constructors_destructor_fn_imps.hpp \ + rb_tree_map_/debug_fn_imps.hpp + +PBHDRS+= \ + rb_tree_map_/erase_fn_imps.hpp \ + rb_tree_map_/find_fn_imps.hpp \ + rb_tree_map_/info_fn_imps.hpp \ + rb_tree_map_/insert_fn_imps.hpp \ + rb_tree_map_/node.hpp \ + rb_tree_map_/rb_tree_.hpp \ + rb_tree_map_/split_join_fn_imps.hpp \ + rb_tree_map_/traits.hpp \ + rc_binomial_heap_/constructors_destructor_fn_imps.hpp \ + rc_binomial_heap_/debug_fn_imps.hpp \ + rc_binomial_heap_/erase_fn_imps.hpp \ + rc_binomial_heap_/insert_fn_imps.hpp \ + rc_binomial_heap_/rc_binomial_heap_.hpp \ + rc_binomial_heap_/rc.hpp \ + rc_binomial_heap_/split_join_fn_imps.hpp \ + rc_binomial_heap_/trace_fn_imps.hpp \ + resize_policy/cc_hash_max_collision_check_resize_trigger_imp.hpp \ + resize_policy/hash_exponential_size_policy_imp.hpp \ + resize_policy/hash_load_check_resize_trigger_imp.hpp \ + resize_policy/hash_load_check_resize_trigger_size_base.hpp \ + resize_policy/hash_prime_size_policy_imp.hpp \ + resize_policy/hash_standard_resize_policy_imp.hpp \ + resize_policy/sample_resize_policy.hpp \ + resize_policy/sample_resize_trigger.hpp \ + resize_policy/sample_size_policy.hpp \ + splay_tree_/constructors_destructor_fn_imps.hpp \ + splay_tree_/debug_fn_imps.hpp \ + splay_tree_/erase_fn_imps.hpp \ + splay_tree_/find_fn_imps.hpp \ + splay_tree_/info_fn_imps.hpp \ + splay_tree_/insert_fn_imps.hpp \ + splay_tree_/node.hpp \ + splay_tree_/splay_fn_imps.hpp \ + splay_tree_/splay_tree_.hpp \ + splay_tree_/split_join_fn_imps.hpp \ + splay_tree_/traits.hpp \ + standard_policies.hpp \ + thin_heap_/constructors_destructor_fn_imps.hpp \ + thin_heap_/debug_fn_imps.hpp \ + thin_heap_/erase_fn_imps.hpp + +PBHDRS+= \ + thin_heap_/find_fn_imps.hpp \ + thin_heap_/insert_fn_imps.hpp \ + thin_heap_/split_join_fn_imps.hpp \ + thin_heap_/thin_heap_.hpp \ + thin_heap_/trace_fn_imps.hpp \ + tree_policy/node_metadata_selector.hpp \ + tree_policy/null_node_update_imp.hpp \ + tree_policy/order_statistics_imp.hpp \ + tree_policy/sample_tree_node_update.hpp \ + tree_trace_base.hpp \ + trie_policy/node_metadata_selector.hpp \ + trie_policy/null_node_update_imp.hpp \ + trie_policy/order_statistics_imp.hpp \ + trie_policy/prefix_search_node_update_imp.hpp \ + trie_policy/sample_trie_e_access_traits.hpp \ + trie_policy/sample_trie_node_update.hpp \ + trie_policy/string_trie_e_access_traits_imp.hpp \ + trie_policy/trie_policy_base.hpp \ + types_traits.hpp \ + type_utils.hpp \ + unordered_iterator/const_iterator.hpp \ + unordered_iterator/const_point_iterator.hpp \ + unordered_iterator/iterator.hpp \ + unordered_iterator/point_iterator.hpp + +PBHDRSDIRS= \ + pairing_heap_ \ + splay_tree_ \ + list_update_map_ \ + basic_tree_policy \ + trie_policy \ + gp_hash_table_map_ \ + tree_policy \ + binomial_heap_base_ \ + resize_policy \ + bin_search_tree_ \ + binomial_heap_ \ + thin_heap_ \ + pat_trie_ \ + cc_hash_table_map_ \ + rc_binomial_heap_ \ + left_child_next_sibling_heap_ \ + unordered_iterator \ + binary_heap_ \ + ov_tree_map_ \ + hash_fn \ + eq_fn \ + rb_tree_map_ \ + list_update_policy + +.for D in ${PBHDRSDIRS} +PHDRGRP$D:= ${PBHDRS:M$D/*:S;^;${SRCDIR}/include/ext/pb_ds/detail/;} +PHDRGRP$DDIR= ${CXXINCLUDEDIR}/ext/pb_ds/detail/$D +INCSGROUPS+= PHDRGRP$D +.endfor + +TARGETHDRS= abi/compatibility.h \ + allocator/new_allocator_base.h \ + io/basic_file_stdio.h \ + io/c_io_stdio.h \ + locale/generic/c_locale.h \ + locale/generic/c++locale_internal.h \ + locale/generic/messages_members.h \ + locale/generic/time_members.h \ + os/bsd/freebsd/ctype_base.h \ + os/bsd/freebsd/ctype_inline.h \ + os/bsd/freebsd/ctype_noninline.h \ + os/bsd/freebsd/os_defines.h +TARGETHDRS:= ${TARGETHDRS:S;^;${SRCDIR}/config/;} +TARGETHDRS+= c++config.h +TARGETHDRSNAME_basic_file_stdio.h= basic_file.h +TARGETHDRSNAME_c_io_stdio.h= c++io.h +TARGETHDRSNAME_c_locale.h= c++locale.h +TARGETHDRSNAME_new_allocator_base.h= c++allocator.h +TARGETHDRSDIR= ${CXXINCLUDEDIR}/bits + +MARCHHDRS= atomic_word.h cpu_defines.h cxxabi_tweaks.h +.for h in ${MARCHHDRS} +.if exists(${SRCDIR}/config/cpu/${MARCHDIR}/${h}) +TARGETHDRS+= ${SRCDIR}/config/cpu/${MARCHDIR}/${h} +.else +TARGETHDRS+= ${SRCDIR}/config/cpu/generic/${h} +.endif +.endfor + +THRHDRS= gthr.h gthr-single.h gthr-posix.h gthr-tpf.h gthr-default.h +THRHDRSDIR= ${CXXINCLUDEDIR}/bits +uppercase = [ABCDEFGHIJKLMNOPQRSTUVWXYZ_] + +gthr.h: ${GCCDIR}/gthr.h + sed -e '/^#pragma/b' \ + -e '/^#/s/\(${uppercase}${uppercase}*\)/_GLIBCXX_\1/g' \ + -e 's/_GLIBCXX_SUPPORTS_WEAK/__GXX_WEAK__/g' \ + -e 's,^#include "\(.*\)",#include <bits/\1>,g' \ + < ${.ALLSRC} > ${.TARGET} + +gthr-single.h: ${GCCDIR}/gthr-single.h + sed -e 's/\(UNUSED\)/_GLIBCXX_\1/g' \ + -e 's/\(GCC${uppercase}*_H\)/_GLIBCXX_\1/g' \ + < ${.ALLSRC} > ${.TARGET} + +gthr-posix.h: ${GCCDIR}/gthr-posix.h + sed -e 's/\(UNUSED\)/_GLIBCXX_\1/g' \ + -e 's/\(GCC${uppercase}*_H\)/_GLIBCXX_\1/g' \ + -e 's/SUPPORTS_WEAK/__GXX_WEAK__/g' \ + -e 's/\(${uppercase}*USE_WEAK\)/_GLIBCXX_\1/g' \ + < ${.ALLSRC} > ${.TARGET} + +gthr-tpf.h: ${GCCDIR}/gthr-posix.h + sed -e 's/\(UNUSED\)/_GLIBCXX_\1/g' \ + -e 's/\(GCC${uppercase}*_H\)/_GLIBCXX_\1/g' \ + -e 's/SUPPORTS_WEAK/__GXX_WEAK__/g' \ + -e 's/\(${uppercase}*USE_WEAK\)/_GLIBCXX_\1/g' \ + < ${.ALLSRC} > ${.TARGET} + +gthr-default.h: ${GCCDIR}/gthr-posix.h + sed -e 's/\(UNUSED\)/_GLIBCXX_\1/g' \ + -e 's/\(GCC${uppercase}*_H\)/_GLIBCXX_\1/g' \ + -e 's/SUPPORTS_WEAK/__GXX_WEAK__/g' \ + -e 's/\(${uppercase}*USE_WEAK\)/_GLIBCXX_\1/g' \ + -e 's,^#include "\(.*\)",#include <bits/\1>,g' \ + < ${.ALLSRC} > ${.TARGET} + +CLEANFILES+= ${THRHDRS} + +unwind.h: ${GCCDIR}/unwind-generic.h + ln -sf ${.ALLSRC} ${.TARGET} + +SRCS+= unwind.h +CLEANFILES+= unwind.h + +DATESTAMP!= cat ${GCCDIR}/DATESTAMP + +c++config.h: ${.CURDIR}/config.h ${SRCDIR}/include/bits/c++config + sed -e "s/\\(define __GLIBCXX__\\)/\1 ${DATESTAMP}/" \ + -e 's/\(define _GLIBCXX_HAVE_ATTRIBUTE_VISIBILITY\)/\1 1/' \ + -e 's/\(define _GLIBCXX_NAMESPACE_ASSOCIATION_VERSION\)/\1 0/' \ + < ${SRCDIR}/include/bits/c++config > ${.TARGET} && \ + sed -e 's/HAVE_/_GLIBCXX_HAVE_/g' \ + -e 's/PACKAGE/_GLIBCXX_PACKAGE/g' \ + -e 's/VERSION/_GLIBCXX_VERSION/g' \ + -e 's/WORDS_/_GLIBCXX_WORDS_/g' \ + < ${.CURDIR}/config.h >> ${.TARGET} && \ + echo "#endif // _CXXCONFIG_" >> ${.TARGET} +CLEANFILES+= c++config.h + +# Symbol versioning. + +VERSION_MAP= libstdc++.map + +${VERSION_MAP}: ${SRCDIR}/config/abi/pre/gnu.ver + ln -sf ${.ALLSRC} ${.TARGET} + +CLEANFILES+= ${VERSION_MAP} + +.include <bsd.lib.mk> diff --git a/gnu/lib/libstdc++/config.h b/gnu/lib/libstdc++/config.h new file mode 100644 index 0000000..4109752 --- /dev/null +++ b/gnu/lib/libstdc++/config.h @@ -0,0 +1,1103 @@ +/* $FreeBSD$ */ +#ifndef __ISO_C_VISIBLE +#include <sys/cdefs.h> +#endif + +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `acosf' function. */ +#define HAVE_ACOSF 1 + +/* Define to 1 if you have the `acosl' function. */ +/* #undef HAVE_ACOSL */ + +/* Define to 1 if you have the `asinf' function. */ +#define HAVE_ASINF 1 + +/* Define to 1 if you have the `asinl' function. */ +/* #undef HAVE_ASINL */ + +/* Define to 1 if you have the `atan2f' function. */ +#define HAVE_ATAN2F 1 + +/* Define to 1 if you have the `atan2l' function. */ +/* #undef HAVE_ATAN2L */ + +/* Define to 1 if you have the `atanf' function. */ +#define HAVE_ATANF 1 + +/* Define to 1 if you have the `atanl' function. */ +/* #undef HAVE_ATANL */ + +/* Define to 1 if you have the `ceilf' function. */ +#define HAVE_CEILF 1 + +/* Define to 1 if you have the `ceill' function. */ +#define HAVE_CEILL 1 + +/* Define to 1 if you have the <complex.h> header file. */ +#define HAVE_COMPLEX_H 1 + +/* Define to 1 if you have the `copysign' function. */ +#define HAVE_COPYSIGN 1 + +/* Define to 1 if you have the `copysignf' function. */ +#define HAVE_COPYSIGNF 1 + +/* Define to 1 if you have the `copysignl' function. */ +#define HAVE_COPYSIGNL 1 + +/* Define to 1 if you have the `cosf' function. */ +#define HAVE_COSF 1 + +/* Define to 1 if you have the `coshf' function. */ +#define HAVE_COSHF 1 + +/* Define to 1 if you have the `coshl' function. */ +/* #undef HAVE_COSHL */ + +/* Define to 1 if you have the `cosl' function. */ +/* #undef HAVE_COSL */ + +/* Define to 1 if you have the <endian.h> header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define to 1 if you have the `expf' function. */ +#define HAVE_EXPF 1 + +/* Define to 1 if you have the `expl' function. */ +/* #undef HAVE_EXPL */ + +/* Define to 1 if you have the `fabsf' function. */ +#define HAVE_FABSF 1 + +/* Define to 1 if you have the `fabsl' function. */ +#define HAVE_FABSL 1 + +/* Define to 1 if you have the <fenv.h> header file. */ +#define HAVE_FENV_H 1 + +/* Define to 1 if you have the `finite' function. */ +#define HAVE_FINITE 1 + +/* Define to 1 if you have the `finitef' function. */ +#define HAVE_FINITEF 1 + +/* Define to 1 if you have the `finitel' function. */ +/* #undef HAVE_FINITEL */ + +/* Define to 1 if you have the <float.h> header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the `floorf' function. */ +#define HAVE_FLOORF 1 + +/* Define to 1 if you have the `floorl' function. */ +#define HAVE_FLOORL 1 + +/* Define to 1 if you have the `fmodf' function. */ +#define HAVE_FMODF 1 + +/* Define to 1 if you have the `fmodl' function. */ +/* #undef HAVE_FMODL */ + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the <fp.h> header file. */ +/* #undef HAVE_FP_H */ + +/* Define to 1 if you have the `frexpf' function. */ +#define HAVE_FREXPF 1 + +/* Define to 1 if you have the `frexpl' function. */ +#define HAVE_FREXPL 1 + +/* Define to 1 if you have the <gconv.h> header file. */ +/* #undef HAVE_GCONV_H */ + +/* Define if _Unwind_GetIPInfo is available. */ +#define HAVE_GETIPINFO 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define if gthr-default.h exists (meaning that threading support is + enabled). */ +#define HAVE_GTHR_DEFAULT 1 + +/* Define to 1 if you have the `hypot' function. */ +#define HAVE_HYPOT 1 + +/* Define to 1 if you have the `hypotf' function. */ +#define HAVE_HYPOTF 1 + +/* Define to 1 if you have the `hypotl' function. */ +/* #undef HAVE_HYPOTL */ + +/* Define to 1 if you have the `iconv' function. */ +/* #undef HAVE_ICONV */ + +/* Define to 1 if you have the `iconv_close' function. */ +/* #undef HAVE_ICONV_CLOSE */ + +/* Define to 1 if you have the `iconv_open' function. */ +/* #undef HAVE_ICONV_OPEN */ + +/* Define to 1 if you have the <ieeefp.h> header file. */ +#define HAVE_IEEEFP_H 1 + +/* Define if int64_t is available in <stdint.h>. */ +#define HAVE_INT64_T 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `isinf' function. */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the `isinff' function. */ +/* #undef HAVE_ISINFF */ + +/* Define to 1 if you have the `isinfl' function. */ +/* #undef HAVE_ISINFL */ + +/* Define to 1 if you have the `isnan' function. */ +#define HAVE_ISNAN 1 + +/* Define to 1 if you have the `isnanf' function. */ +#define HAVE_ISNANF 1 + +/* Define to 1 if you have the `isnanl' function. */ +/* #undef HAVE_ISNANL */ + +/* Defined if iswblank exists. */ +#define HAVE_ISWBLANK 1 + +/* Define if LC_MESSAGES is available in <locale.h>. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if you have the `ldexpf' function. */ +#define HAVE_LDEXPF 1 + +/* Define to 1 if you have the `ldexpl' function. */ +#define HAVE_LDEXPL 1 + +/* Define to 1 if you have the <libintl.h> header file. */ +/* #undef HAVE_LIBINTL_H */ + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Only used in build directory testsuite_hooks.h. */ +#define HAVE_LIMIT_AS 1 + +/* Only used in build directory testsuite_hooks.h. */ +#define HAVE_LIMIT_DATA 1 + +/* Only used in build directory testsuite_hooks.h. */ +#define HAVE_LIMIT_FSIZE 1 + +/* Only used in build directory testsuite_hooks.h. */ +#define HAVE_LIMIT_RSS 1 + +/* Only used in build directory testsuite_hooks.h. */ +#define HAVE_LIMIT_VMEM 1 + +/* Define to 1 if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `log10f' function. */ +#define HAVE_LOG10F 1 + +/* Define to 1 if you have the `log10l' function. */ +/* #undef HAVE_LOG10L */ + +/* Define to 1 if you have the `logf' function. */ +#define HAVE_LOGF 1 + +/* Define to 1 if you have the `logl' function. */ +/* #undef HAVE_LOGL */ + +/* Define to 1 if you have the <machine/endian.h> header file. */ +#define HAVE_MACHINE_ENDIAN_H 1 + +/* Define to 1 if you have the <machine/param.h> header file. */ +#define HAVE_MACHINE_PARAM_H 1 + +/* Define if mbstate_t exists in wchar.h. */ +#define HAVE_MBSTATE_T 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the `modf' function. */ +#define HAVE_MODF 1 + +/* Define to 1 if you have the `modff' function. */ +#define HAVE_MODFF 1 + +/* Define to 1 if you have the `modfl' function. */ +#define HAVE_MODFL 1 + +/* Define to 1 if you have the <nan.h> header file. */ +/* #undef HAVE_NAN_H */ + +/* Define to 1 if you have the `nl_langinfo' function. */ +#define HAVE_NL_LANGINFO 1 + +/* Define if poll is available in <poll.h>. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the `powf' function. */ +#define HAVE_POWF 1 + +/* Define to 1 if you have the `powl' function. */ +/* #undef HAVE_POWL */ + +/* Define to 1 if you have the `qfpclass' function. */ +/* #undef HAVE_QFPCLASS */ + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define if sigsetjmp is available. */ +#define HAVE_SIGSETJMP 1 + +/* Define to 1 if you have the `sincos' function. */ +/* #undef HAVE_SINCOS */ + +/* Define to 1 if you have the `sincosf' function. */ +/* #undef HAVE_SINCOSF */ + +/* Define to 1 if you have the `sincosl' function. */ +/* #undef HAVE_SINCOSL */ + +/* Define to 1 if you have the `sinf' function. */ +#define HAVE_SINF 1 + +/* Define to 1 if you have the `sinhf' function. */ +#define HAVE_SINHF 1 + +/* Define to 1 if you have the `sinhl' function. */ +/* #undef HAVE_SINHL */ + +/* Define to 1 if you have the `sinl' function. */ +/* #undef HAVE_SINL */ + +/* Define to 1 if you have the `sqrtf' function. */ +#define HAVE_SQRTF 1 + +/* Define to 1 if you have the `sqrtl' function. */ +/* #undef HAVE_SQRTL */ + +/* Define to 1 if you have the <stdbool.h> header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtof' function. */ +#define HAVE_STRTOF 1 + +/* Define to 1 if you have the `strtold' function. */ +#define HAVE_STRTOLD 1 + +/* Define to 1 if you have the <sys/filio.h> header file. */ +#define HAVE_SYS_FILIO_H 1 + +/* Define to 1 if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the <sys/ipc.h> header file. */ +#define HAVE_SYS_IPC_H 1 + +/* Define to 1 if you have the <sys/isa_defs.h> header file. */ +/* #undef HAVE_SYS_ISA_DEFS_H */ + +/* Define to 1 if you have the <sys/machine.h> header file. */ +/* #undef HAVE_SYS_MACHINE_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +/* #undef HAVE_SYS_PARAM_H */ + +/* Define to 1 if you have the <sys/resource.h> header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the <sys/sem.h> header file. */ +#define HAVE_SYS_SEM_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define if S_IFREG is available in <sys/stat.h>. */ +/* #undef HAVE_S_IFREG */ + +/* Define if S_IFREG is available in <sys/stat.h>. */ +#define HAVE_S_ISREG 1 + +/* Define to 1 if you have the `tanf' function. */ +#define HAVE_TANF 1 + +/* Define to 1 if you have the `tanhf' function. */ +#define HAVE_TANHF 1 + +/* Define to 1 if you have the `tanhl' function. */ +/* #undef HAVE_TANHL */ + +/* Define to 1 if you have the `tanl' function. */ +/* #undef HAVE_TANL */ + +/* Define to 1 if the target supports thread-local storage. */ +#if !defined(__arm__) && !defined(__mips__) +#define HAVE_TLS 1 +#endif + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +#if __ISO_C_VISIBLE >= 1999 +/* Defined if vfwscanf exists. */ +#define HAVE_VFWSCANF 1 + +/* Defined if vswscanf exists. */ +#define HAVE_VSWSCANF 1 + +/* Defined if vwscanf exists. */ +#define HAVE_VWSCANF 1 +#endif /* __ISO_C_VISIBLE >= 1999 */ + +/* Define to 1 if you have the <wchar.h> header file. */ +#define HAVE_WCHAR_H 1 + +#if __ISO_C_VISIBLE >= 1999 +/* Defined if wcstof exists. */ +#define HAVE_WCSTOF 1 +#endif /* __ISO_C_VISIBLE >= 1999 */ + +/* Define to 1 if you have the <wctype.h> header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define if writev is available in <sys/uio.h>. */ +#define HAVE_WRITEV 1 + +/* Define to 1 if you have the `_acosf' function. */ +/* #undef HAVE__ACOSF */ + +/* Define to 1 if you have the `_acosl' function. */ +/* #undef HAVE__ACOSL */ + +/* Define to 1 if you have the `_asinf' function. */ +/* #undef HAVE__ASINF */ + +/* Define to 1 if you have the `_asinl' function. */ +/* #undef HAVE__ASINL */ + +/* Define to 1 if you have the `_atan2f' function. */ +/* #undef HAVE__ATAN2F */ + +/* Define to 1 if you have the `_atan2l' function. */ +/* #undef HAVE__ATAN2L */ + +/* Define to 1 if you have the `_atanf' function. */ +/* #undef HAVE__ATANF */ + +/* Define to 1 if you have the `_atanl' function. */ +/* #undef HAVE__ATANL */ + +/* Define to 1 if you have the `_ceilf' function. */ +/* #undef HAVE__CEILF */ + +/* Define to 1 if you have the `_ceill' function. */ +/* #undef HAVE__CEILL */ + +/* Define to 1 if you have the `_copysign' function. */ +/* #undef HAVE__COPYSIGN */ + +/* Define to 1 if you have the `_copysignl' function. */ +/* #undef HAVE__COPYSIGNL */ + +/* Define to 1 if you have the `_cosf' function. */ +/* #undef HAVE__COSF */ + +/* Define to 1 if you have the `_coshf' function. */ +/* #undef HAVE__COSHF */ + +/* Define to 1 if you have the `_coshl' function. */ +/* #undef HAVE__COSHL */ + +/* Define to 1 if you have the `_cosl' function. */ +/* #undef HAVE__COSL */ + +/* Define to 1 if you have the `_expf' function. */ +/* #undef HAVE__EXPF */ + +/* Define to 1 if you have the `_expl' function. */ +/* #undef HAVE__EXPL */ + +/* Define to 1 if you have the `_fabsf' function. */ +/* #undef HAVE__FABSF */ + +/* Define to 1 if you have the `_fabsl' function. */ +/* #undef HAVE__FABSL */ + +/* Define to 1 if you have the `_finite' function. */ +/* #undef HAVE__FINITE */ + +/* Define to 1 if you have the `_finitef' function. */ +/* #undef HAVE__FINITEF */ + +/* Define to 1 if you have the `_finitel' function. */ +/* #undef HAVE__FINITEL */ + +/* Define to 1 if you have the `_floorf' function. */ +/* #undef HAVE__FLOORF */ + +/* Define to 1 if you have the `_floorl' function. */ +/* #undef HAVE__FLOORL */ + +/* Define to 1 if you have the `_fmodf' function. */ +/* #undef HAVE__FMODF */ + +/* Define to 1 if you have the `_fmodl' function. */ +/* #undef HAVE__FMODL */ + +/* Define to 1 if you have the `_fpclass' function. */ +/* #undef HAVE__FPCLASS */ + +/* Define to 1 if you have the `_frexpf' function. */ +/* #undef HAVE__FREXPF */ + +/* Define to 1 if you have the `_frexpl' function. */ +/* #undef HAVE__FREXPL */ + +/* Define to 1 if you have the `_hypot' function. */ +/* #undef HAVE__HYPOT */ + +/* Define to 1 if you have the `_hypotf' function. */ +/* #undef HAVE__HYPOTF */ + +/* Define to 1 if you have the `_hypotl' function. */ +/* #undef HAVE__HYPOTL */ + +/* Define to 1 if you have the `_isinf' function. */ +/* #undef HAVE__ISINF */ + +/* Define to 1 if you have the `_isinff' function. */ +/* #undef HAVE__ISINFF */ + +/* Define to 1 if you have the `_isinfl' function. */ +/* #undef HAVE__ISINFL */ + +/* Define to 1 if you have the `_isnan' function. */ +/* #undef HAVE__ISNAN */ + +/* Define to 1 if you have the `_isnanf' function. */ +/* #undef HAVE__ISNANF */ + +/* Define to 1 if you have the `_isnanl' function. */ +/* #undef HAVE__ISNANL */ + +/* Define to 1 if you have the `_ldexpf' function. */ +/* #undef HAVE__LDEXPF */ + +/* Define to 1 if you have the `_ldexpl' function. */ +/* #undef HAVE__LDEXPL */ + +/* Define to 1 if you have the `_log10f' function. */ +/* #undef HAVE__LOG10F */ + +/* Define to 1 if you have the `_log10l' function. */ +/* #undef HAVE__LOG10L */ + +/* Define to 1 if you have the `_logf' function. */ +/* #undef HAVE__LOGF */ + +/* Define to 1 if you have the `_logl' function. */ +/* #undef HAVE__LOGL */ + +/* Define to 1 if you have the `_modf' function. */ +/* #undef HAVE__MODF */ + +/* Define to 1 if you have the `_modff' function. */ +/* #undef HAVE__MODFF */ + +/* Define to 1 if you have the `_modfl' function. */ +/* #undef HAVE__MODFL */ + +/* Define to 1 if you have the `_powf' function. */ +/* #undef HAVE__POWF */ + +/* Define to 1 if you have the `_powl' function. */ +/* #undef HAVE__POWL */ + +/* Define to 1 if you have the `_qfpclass' function. */ +/* #undef HAVE__QFPCLASS */ + +/* Define to 1 if you have the `_sincos' function. */ +/* #undef HAVE__SINCOS */ + +/* Define to 1 if you have the `_sincosf' function. */ +/* #undef HAVE__SINCOSF */ + +/* Define to 1 if you have the `_sincosl' function. */ +/* #undef HAVE__SINCOSL */ + +/* Define to 1 if you have the `_sinf' function. */ +/* #undef HAVE__SINF */ + +/* Define to 1 if you have the `_sinhf' function. */ +/* #undef HAVE__SINHF */ + +/* Define to 1 if you have the `_sinhl' function. */ +/* #undef HAVE__SINHL */ + +/* Define to 1 if you have the `_sinl' function. */ +/* #undef HAVE__SINL */ + +/* Define to 1 if you have the `_sqrtf' function. */ +/* #undef HAVE__SQRTF */ + +/* Define to 1 if you have the `_sqrtl' function. */ +/* #undef HAVE__SQRTL */ + +/* Define to 1 if you have the `_tanf' function. */ +/* #undef HAVE__TANF */ + +/* Define to 1 if you have the `_tanhf' function. */ +/* #undef HAVE__TANHF */ + +/* Define to 1 if you have the `_tanhl' function. */ +/* #undef HAVE__TANHL */ + +/* Define to 1 if you have the `_tanl' function. */ +/* #undef HAVE__TANL */ + +/* Define if the compiler/host combination has __builtin_abs. */ +#define HAVE___BUILTIN_ABS 1 + +/* Define if the compiler/host combination has __builtin_cos. */ +#define HAVE___BUILTIN_COS 1 + +/* Define if the compiler/host combination has __builtin_cosf. */ +#define HAVE___BUILTIN_COSF 1 + +/* Define if the compiler/host combination has __builtin_cosl. */ +#define HAVE___BUILTIN_COSL 1 + +/* Define if the compiler/host combination has __builtin_fabs. */ +#define HAVE___BUILTIN_FABS 1 + +/* Define if the compiler/host combination has __builtin_fabsf. */ +#define HAVE___BUILTIN_FABSF 1 + +/* Define if the compiler/host combination has __builtin_fabsl. */ +#define HAVE___BUILTIN_FABSL 1 + +/* Define if the compiler/host combination has __builtin_labs. */ +#define HAVE___BUILTIN_LABS 1 + +/* Define if the compiler/host combination has __builtin_sin. */ +#define HAVE___BUILTIN_SIN 1 + +/* Define if the compiler/host combination has __builtin_sinf. */ +#define HAVE___BUILTIN_SINF 1 + +/* Define if the compiler/host combination has __builtin_sinl. */ +#define HAVE___BUILTIN_SINL 1 + +/* Define if the compiler/host combination has __builtin_sqrt. */ +#define HAVE___BUILTIN_SQRT 1 + +/* Define if the compiler/host combination has __builtin_sqrtf. */ +#define HAVE___BUILTIN_SQRTF 1 + +/* Define if the compiler/host combination has __builtin_sqrtl. */ +#define HAVE___BUILTIN_SQRTL 1 + +/* Define to 1 if you have the `__signbit' function. */ +#define HAVE___SIGNBIT 1 + +/* Define to 1 if you have the `__signbitf' function. */ +#define HAVE___SIGNBITF 1 + +/* Define to 1 if you have the `__signbitl' function. */ +#define HAVE___SIGNBITL 1 + +/* Name of package */ +/* #undef PACKAGE */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "package-unused" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "package-unused version-unused" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libstdc++" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "version-unused" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +/* #undef VERSION */ + +/* Define if builtin atomic operations are supported on this host. */ +#if defined(__amd64__) || defined(__i386__) +#define _GLIBCXX_ATOMIC_BUILTINS 1 +#endif + +/* Define to use concept checking code from the boost libraries. */ +/* #undef _GLIBCXX_CONCEPT_CHECKS */ + +/* Define if a fully dynamic basic_string is wanted. */ +/* #undef _GLIBCXX_FULLY_DYNAMIC_STRING */ + +/* Define to 1 if a full hosted library is built, or 0 if freestanding. */ +#define _GLIBCXX_HOSTED 1 + +/* Define if compatibility should be provided for -mlong-double-64. */ +/* #undef _GLIBCXX_LONG_DOUBLE_COMPAT */ + +/* Define if ptrdiff_t is int. */ +#if !defined(__LP64__) +#define _GLIBCXX_PTRDIFF_T_IS_INT 1 +#endif + +/* Define if using setrlimit to set resource limits during "make check" */ +#define _GLIBCXX_RES_LIMITS 1 + +/* Define if size_t is unsigned int. */ +#if !defined(__LP64__) +#define _GLIBCXX_SIZE_T_IS_UINT 1 +#endif + +/* Define if the compiler is configured for setjmp/longjmp exceptions. */ +/* #undef _GLIBCXX_SJLJ_EXCEPTIONS */ + +/* Define to use symbol versioning in the shared library. */ +#define _GLIBCXX_SYMVER 1 + +/* Define to use darwin versioning in the shared library. */ +/* #undef _GLIBCXX_SYMVER_DARWIN */ + +/* Define to use GNU versioning in the shared library. */ +#define _GLIBCXX_SYMVER_GNU 1 + +/* Define to use GNU namespace versioning in the shared library. */ +/* #undef _GLIBCXX_SYMVER_GNU_NAMESPACE */ + +/* Define if C99 functions or macros from <wchar.h>, <math.h>, <complex.h>, + <stdio.h>, and <stdlib.h> can be used or exposed. */ +/* #undef _GLIBCXX_USE_C99 */ + +/* Define if C99 functions in <complex.h> should be used in <complex>. Using + compiler builtins for these functions requires corresponding C99 library + functions to be present. */ +/* #undef _GLIBCXX_USE_C99_COMPLEX */ + +/* Define if C99 functions in <complex.h> should be used in <tr1/complex>. + Using compiler builtins for these functions requires corresponding C99 + library functions to be present. */ +/* #undef _GLIBCXX_USE_C99_COMPLEX_TR1 */ + +/* Define if C99 functions in <ctype.h> should be imported in <tr1/cctype> in + namespace std::tr1. */ +#define _GLIBCXX_USE_C99_CTYPE_TR1 1 + +/* Define if C99 functions in <fenv.h> should be imported in <tr1/cfenv> in + namespace std::tr1. */ +#define _GLIBCXX_USE_C99_FENV_TR1 1 + +/* Define if C99 functions in <inttypes.h> should be imported in + <tr1/cinttypes> in namespace std::tr1. */ +#define _GLIBCXX_USE_C99_INTTYPES_TR1 1 + +/* Define if C99 functions or macros in <math.h> should be imported in <cmath> + in namespace std. */ +#define _GLIBCXX_USE_C99_MATH 1 + +/* Define if C99 functions or macros in <math.h> should be imported in + <tr1/cmath> in namespace std::tr1. */ +/* #undef _GLIBCXX_USE_C99_MATH_TR1 */ + +/* Define if C99 types in <stdint.h> should be imported in <tr1/cstdint> in + namespace std::tr1. */ +#define _GLIBCXX_USE_C99_STDINT_TR1 1 + +/* Define if iconv and related functions exist and are usable. */ +#define _GLIBCXX_USE_ICONV 1 + +/* Define if LFS support is available. */ +/* #undef _GLIBCXX_USE_LFS */ + +/* Define if code specialized for long long should be used. */ +#define _GLIBCXX_USE_LONG_LONG 1 + +/* Define if NLS translations are to be used. */ +/* #undef _GLIBCXX_USE_NLS */ + +/* Define if dev/random and dev/urandom are available for the random_device of + TR1 (Chapter 5.1). */ +#define _GLIBCXX_USE_RANDOM_TR1 1 + +/* Define if code specialized for wchar_t should be used. */ +#define _GLIBCXX_USE_WCHAR_T 1 + +#if defined (HAVE__ACOSF) && ! defined (HAVE_ACOSF) +# define HAVE_ACOSF 1 +# define acosf _acosf +#endif + +#if defined (HAVE__ACOSL) && ! defined (HAVE_ACOSL) +# define HAVE_ACOSL 1 +# define acosl _acosl +#endif + +#if defined (HAVE__ASINF) && ! defined (HAVE_ASINF) +# define HAVE_ASINF 1 +# define asinf _asinf +#endif + +#if defined (HAVE__ASINL) && ! defined (HAVE_ASINL) +# define HAVE_ASINL 1 +# define asinl _asinl +#endif + +#if defined (HAVE__ATAN2F) && ! defined (HAVE_ATAN2F) +# define HAVE_ATAN2F 1 +# define atan2f _atan2f +#endif + +#if defined (HAVE__ATAN2L) && ! defined (HAVE_ATAN2L) +# define HAVE_ATAN2L 1 +# define atan2l _atan2l +#endif + +#if defined (HAVE__ATANF) && ! defined (HAVE_ATANF) +# define HAVE_ATANF 1 +# define atanf _atanf +#endif + +#if defined (HAVE__ATANL) && ! defined (HAVE_ATANL) +# define HAVE_ATANL 1 +# define atanl _atanl +#endif + +#if defined (HAVE__CEILF) && ! defined (HAVE_CEILF) +# define HAVE_CEILF 1 +# define ceilf _ceilf +#endif + +#if defined (HAVE__CEILL) && ! defined (HAVE_CEILL) +# define HAVE_CEILL 1 +# define ceill _ceill +#endif + +#if defined (HAVE__COPYSIGN) && ! defined (HAVE_COPYSIGN) +# define HAVE_COPYSIGN 1 +# define copysign _copysign +#endif + +#if defined (HAVE__COPYSIGNL) && ! defined (HAVE_COPYSIGNL) +# define HAVE_COPYSIGNL 1 +# define copysignl _copysignl +#endif + +#if defined (HAVE__COSF) && ! defined (HAVE_COSF) +# define HAVE_COSF 1 +# define cosf _cosf +#endif + +#if defined (HAVE__COSHF) && ! defined (HAVE_COSHF) +# define HAVE_COSHF 1 +# define coshf _coshf +#endif + +#if defined (HAVE__COSHL) && ! defined (HAVE_COSHL) +# define HAVE_COSHL 1 +# define coshl _coshl +#endif + +#if defined (HAVE__COSL) && ! defined (HAVE_COSL) +# define HAVE_COSL 1 +# define cosl _cosl +#endif + +#if defined (HAVE__EXPF) && ! defined (HAVE_EXPF) +# define HAVE_EXPF 1 +# define expf _expf +#endif + +#if defined (HAVE__EXPL) && ! defined (HAVE_EXPL) +# define HAVE_EXPL 1 +# define expl _expl +#endif + +#if defined (HAVE__FABSF) && ! defined (HAVE_FABSF) +# define HAVE_FABSF 1 +# define fabsf _fabsf +#endif + +#if defined (HAVE__FABSL) && ! defined (HAVE_FABSL) +# define HAVE_FABSL 1 +# define fabsl _fabsl +#endif + +#if defined (HAVE__FINITE) && ! defined (HAVE_FINITE) +# define HAVE_FINITE 1 +# define finite _finite +#endif + +#if defined (HAVE__FINITEF) && ! defined (HAVE_FINITEF) +# define HAVE_FINITEF 1 +# define finitef _finitef +#endif + +#if defined (HAVE__FINITEL) && ! defined (HAVE_FINITEL) +# define HAVE_FINITEL 1 +# define finitel _finitel +#endif + +#if defined (HAVE__FLOORF) && ! defined (HAVE_FLOORF) +# define HAVE_FLOORF 1 +# define floorf _floorf +#endif + +#if defined (HAVE__FLOORL) && ! defined (HAVE_FLOORL) +# define HAVE_FLOORL 1 +# define floorl _floorl +#endif + +#if defined (HAVE__FMODF) && ! defined (HAVE_FMODF) +# define HAVE_FMODF 1 +# define fmodf _fmodf +#endif + +#if defined (HAVE__FMODL) && ! defined (HAVE_FMODL) +# define HAVE_FMODL 1 +# define fmodl _fmodl +#endif + +#if defined (HAVE__FPCLASS) && ! defined (HAVE_FPCLASS) +# define HAVE_FPCLASS 1 +# define fpclass _fpclass +#endif + +#if defined (HAVE__FREXPF) && ! defined (HAVE_FREXPF) +# define HAVE_FREXPF 1 +# define frexpf _frexpf +#endif + +#if defined (HAVE__FREXPL) && ! defined (HAVE_FREXPL) +# define HAVE_FREXPL 1 +# define frexpl _frexpl +#endif + +#if defined (HAVE__HYPOT) && ! defined (HAVE_HYPOT) +# define HAVE_HYPOT 1 +# define hypot _hypot +#endif + +#if defined (HAVE__HYPOTF) && ! defined (HAVE_HYPOTF) +# define HAVE_HYPOTF 1 +# define hypotf _hypotf +#endif + +#if defined (HAVE__HYPOTL) && ! defined (HAVE_HYPOTL) +# define HAVE_HYPOTL 1 +# define hypotl _hypotl +#endif + +#if defined (HAVE__ISINF) && ! defined (HAVE_ISINF) +# define HAVE_ISINF 1 +# define isinf _isinf +#endif + +#if defined (HAVE__ISINFF) && ! defined (HAVE_ISINFF) +# define HAVE_ISINFF 1 +# define isinff _isinff +#endif + +#if defined (HAVE__ISINFL) && ! defined (HAVE_ISINFL) +# define HAVE_ISINFL 1 +# define isinfl _isinfl +#endif + +#if defined (HAVE__ISNAN) && ! defined (HAVE_ISNAN) +# define HAVE_ISNAN 1 +# define isnan _isnan +#endif + +#if defined (HAVE__ISNANF) && ! defined (HAVE_ISNANF) +# define HAVE_ISNANF 1 +# define isnanf _isnanf +#endif + +#if defined (HAVE__ISNANL) && ! defined (HAVE_ISNANL) +# define HAVE_ISNANL 1 +# define isnanl _isnanl +#endif + +#if defined (HAVE__LDEXPF) && ! defined (HAVE_LDEXPF) +# define HAVE_LDEXPF 1 +# define ldexpf _ldexpf +#endif + +#if defined (HAVE__LDEXPL) && ! defined (HAVE_LDEXPL) +# define HAVE_LDEXPL 1 +# define ldexpl _ldexpl +#endif + +#if defined (HAVE__LOG10F) && ! defined (HAVE_LOG10F) +# define HAVE_LOG10F 1 +# define log10f _log10f +#endif + +#if defined (HAVE__LOG10L) && ! defined (HAVE_LOG10L) +# define HAVE_LOG10L 1 +# define log10l _log10l +#endif + +#if defined (HAVE__LOGF) && ! defined (HAVE_LOGF) +# define HAVE_LOGF 1 +# define logf _logf +#endif + +#if defined (HAVE__LOGL) && ! defined (HAVE_LOGL) +# define HAVE_LOGL 1 +# define logl _logl +#endif + +#if defined (HAVE__MODF) && ! defined (HAVE_MODF) +# define HAVE_MODF 1 +# define modf _modf +#endif + +#if defined (HAVE__MODFF) && ! defined (HAVE_MODFF) +# define HAVE_MODFF 1 +# define modff _modff +#endif + +#if defined (HAVE__MODFL) && ! defined (HAVE_MODFL) +# define HAVE_MODFL 1 +# define modfl _modfl +#endif + +#if defined (HAVE__POWF) && ! defined (HAVE_POWF) +# define HAVE_POWF 1 +# define powf _powf +#endif + +#if defined (HAVE__POWL) && ! defined (HAVE_POWL) +# define HAVE_POWL 1 +# define powl _powl +#endif + +#if defined (HAVE__QFPCLASS) && ! defined (HAVE_QFPCLASS) +# define HAVE_QFPCLASS 1 +# define qfpclass _qfpclass +#endif + +#if defined (HAVE__SINCOS) && ! defined (HAVE_SINCOS) +# define HAVE_SINCOS 1 +# define sincos _sincos +#endif + +#if defined (HAVE__SINCOSF) && ! defined (HAVE_SINCOSF) +# define HAVE_SINCOSF 1 +# define sincosf _sincosf +#endif + +#if defined (HAVE__SINCOSL) && ! defined (HAVE_SINCOSL) +# define HAVE_SINCOSL 1 +# define sincosl _sincosl +#endif + +#if defined (HAVE__SINF) && ! defined (HAVE_SINF) +# define HAVE_SINF 1 +# define sinf _sinf +#endif + +#if defined (HAVE__SINHF) && ! defined (HAVE_SINHF) +# define HAVE_SINHF 1 +# define sinhf _sinhf +#endif + +#if defined (HAVE__SINHL) && ! defined (HAVE_SINHL) +# define HAVE_SINHL 1 +# define sinhl _sinhl +#endif + +#if defined (HAVE__SINL) && ! defined (HAVE_SINL) +# define HAVE_SINL 1 +# define sinl _sinl +#endif + +#if defined (HAVE__SQRTF) && ! defined (HAVE_SQRTF) +# define HAVE_SQRTF 1 +# define sqrtf _sqrtf +#endif + +#if defined (HAVE__SQRTL) && ! defined (HAVE_SQRTL) +# define HAVE_SQRTL 1 +# define sqrtl _sqrtl +#endif + +#if defined (HAVE__STRTOF) && ! defined (HAVE_STRTOF) +# define HAVE_STRTOF 1 +# define strtof _strtof +#endif + +#if defined (HAVE__STRTOLD) && ! defined (HAVE_STRTOLD) +# define HAVE_STRTOLD 1 +# define strtold _strtold +#endif + +#if defined (HAVE__TANF) && ! defined (HAVE_TANF) +# define HAVE_TANF 1 +# define tanf _tanf +#endif + +#if defined (HAVE__TANHF) && ! defined (HAVE_TANHF) +# define HAVE_TANHF 1 +# define tanhf _tanhf +#endif + +#if defined (HAVE__TANHL) && ! defined (HAVE_TANHL) +# define HAVE_TANHL 1 +# define tanhl _tanhl +#endif + +#if defined (HAVE__TANL) && ! defined (HAVE_TANL) +# define HAVE_TANL 1 +# define tanl _tanl +#endif diff --git a/gnu/lib/libstdc++/doc/Makefile b/gnu/lib/libstdc++/doc/Makefile new file mode 100644 index 0000000..b028519 --- /dev/null +++ b/gnu/lib/libstdc++/doc/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +SRCDIR= ${.CURDIR}/../../../../contrib/libstdc++ + +.PATH: ${SRCDIR}/../libio + +INFO = iostream + +INFOENTRY_iostream= "* iostream: (iostream). The GNU C++ I/O library." + +MAKEINFOFLAGS+= -I ${SRCDIR}/../libio + +.include <bsd.info.mk> diff --git a/gnu/lib/libsupc++/Makefile b/gnu/lib/libsupc++/Makefile new file mode 100644 index 0000000..f2fe8837 --- /dev/null +++ b/gnu/lib/libsupc++/Makefile @@ -0,0 +1,39 @@ +# $FreeBSD$ + +GCCVER= 4.2 +GCCDIR= ${.CURDIR}/../../../contrib/gcc +GCCLIB= ${.CURDIR}/../../../contrib/gcclibs +SRCDIR= ${.CURDIR}/../../../contrib/libstdc++/libsupc++ + +.PATH: ${SRCDIR} ${GCCLIB}/libiberty + +# Static only. +LIB= supc++ +SRCS+= del_op.cc del_opnt.cc del_opv.cc del_opvnt.cc eh_alloc.cc eh_arm.cc \ + eh_aux_runtime.cc eh_call.cc eh_catch.cc eh_exception.cc eh_globals.cc \ + eh_personality.cc eh_term_handler.cc eh_terminate.cc eh_throw.cc \ + eh_type.cc eh_unex_handler.cc guard.cc new_handler.cc new_op.cc \ + new_opnt.cc new_opv.cc new_opvnt.cc pure.cc tinfo.cc tinfo2.cc \ + vec.cc vterminate.cc + +# from libiberty: +SRCS+= cp-demangle.c + +CFLAGS+= -DIN_GLIBCPP_V3 -DHAVE_CONFIG_H +CFLAGS+= -I${GCCLIB}/include -I${SRCDIR} -I${GCCDIR} +CFLAGS+= -I${.CURDIR}/../libstdc++ -I. +CFLAGS+= -frandom-seed=RepeatabilityConsideredGood +CXXFLAGS+= -fno-implicit-templates -ffunction-sections -fdata-sections +PO_CXXFLAGS= ${CXXFLAGS:N-ffunction-sections} + +HDRS= exception new typeinfo cxxabi.h exception_defines.h +INCS= ${HDRS:S;^;${SRCDIR}/;} +INCSDIR=${INCLUDEDIR}/c++/${GCCVER} + +unwind.h: ${GCCDIR}/unwind-generic.h + ln -sf ${.ALLSRC} ${.TARGET} + +SRCS+= unwind.h +CLEANFILES+= unwind.h + +.include <bsd.lib.mk> |